[lng-odp] [PATCH] helper : Fix UDP checksum computation
From: Grigore IonThis patch fixes the following problems: - checksum computation for LE platforms - checksum is computed in the CPU endianness. The returned result must be converted to the BE ordering when it is used to update the UDP checksum in a packet. - checksum computation for packets having the UDP length not a multiple of 2. Signed-off-by: Grigore Ion --- helper/include/odp/helper/udp.h | 55 -- 1 files changed, 23 insertions(+), 32 deletions(-) diff --git a/helper/include/odp/helper/udp.h b/helper/include/odp/helper/udp.h index 06c439b..f56b310 100644 --- a/helper/include/odp/helper/udp.h +++ b/helper/include/odp/helper/udp.h @@ -4,7 +4,6 @@ * SPDX-License-Identifier: BSD-3-Clause */ - /** * @file * @@ -22,7 +21,6 @@ extern "C" { #include #include - /** @addtogroup odph_header ODPH HEADER * @{ */ @@ -44,46 +42,39 @@ typedef struct ODP_PACKED { * This function uses odp packet to calc checksum * * @param pkt calculate chksum for pkt - * @return checksum value + * @return checksum value in CPU endianness */ static inline uint16_t odph_ipv4_udp_chksum(odp_packet_t pkt) { - uint32_t sum = 0; - odph_udphdr_t *udph; - odph_ipv4hdr_t *iph; - uint16_t udplen; - uint8_t *buf; - - if (!odp_packet_l3_offset(pkt)) - return 0; + odph_ipv4hdr_t *iph; + odph_udphdr_t *udph; + uint32_tsum; + uint16_tudplen, *buf; - if (!odp_packet_l4_offset(pkt)) + if (!odp_packet_l3_offset(pkt) || !odp_packet_l3_offset(pkt)) return 0; - iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); - udplen = odp_be_to_cpu_16(udph->length); - - /* 32-bit sum of all 16-bit words covered by UDP chksum */ + /* 32-bit sum of UDP pseudo-header */ sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + - (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + - (uint16_t)iph->proto + udplen; - for (buf = (uint8_t *)udph; udplen > 1; udplen -= 2) { - sum += ((*buf << 8) + *(buf + 1)); - buf += 2; - } - - /* Fold sum to 16 bits: add carrier to result */ - while (sum >> 16) - sum = (sum & 0x) + (sum >> 16); - + (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + + odp_be_to_cpu_16(iph->proto) + udph->length; + udplen = odp_be_to_cpu_16(udph->length); + buf = (uint16_t *)((void *)udph); + /* 32-bit sum of UDP header (checksum field cleared) and UDP data */ + for ( ; udplen > 1; udplen -= 2) + sum += *buf++; + /* Length is not a multiple of 2 bytes */ + if (udplen) + sum += odp_be_to_cpu_16(*((uint8_t *)buf) << 8); + /* Fold sum to 16 bits */ + sum = (sum & 0x) + (sum >> 16); + /* Add carrier (0/1) to result */ + sum += (sum >> 16); /* 1's complement */ sum = ~sum; - - /* set computation result */ - sum = (sum == 0x0) ? 0x : sum; - - return sum; + /* Set computation result */ + return (sum == 0x0) ? 0x : odp_be_to_cpu_16(sum); } /** @internal Compile time assert */ -- 1.7.3.4 ___ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp
Re: [lng-odp] [PATCH] helper : Fix UDP checksum computation
On Thu, Oct 1, 2015 at 8:45 AM,wrote: > From: Grigore Ion > > This patch fixes the following problems: > - checksum computation for LE platforms > - checksum is computed in the CPU endianness. The returned result > must be converted to the BE ordering when it is used to update the > UDP checksum in a packet. > - checksum computation for packets having the UDP length not a > multiple of 2. > > Signed-off-by: Grigore Ion > --- > helper/include/odp/helper/udp.h | 55 > -- > 1 files changed, 23 insertions(+), 32 deletions(-) > > diff --git a/helper/include/odp/helper/udp.h > b/helper/include/odp/helper/udp.h > index 06c439b..5d6154f 100644 > --- a/helper/include/odp/helper/udp.h > +++ b/helper/include/odp/helper/udp.h > @@ -4,7 +4,6 @@ > * SPDX-License-Identifier: BSD-3-Clause > */ > > - > /** > * @file > * > @@ -22,7 +21,6 @@ extern "C" { > #include > #include > > - > /** @addtogroup odph_header ODPH HEADER > * @{ > */ > @@ -44,46 +42,39 @@ typedef struct ODP_PACKED { > * This function uses odp packet to calc checksum > * > * @param pkt calculate chksum for pkt > - * @return checksum value > + * @return checksum value in CPU endianness > */ > static inline uint16_t odph_ipv4_udp_chksum(odp_packet_t pkt) > { > - uint32_t sum = 0; > - odph_udphdr_t *udph; > - odph_ipv4hdr_t *iph; > - uint16_t udplen; > - uint8_t *buf; > - > - if (!odp_packet_l3_offset(pkt)) > - return 0; > + odph_ipv4hdr_t *iph; > + odph_udphdr_t *udph; > + uint32_tsum; > + uint16_tudplen, *buf; > > - if (!odp_packet_l4_offset(pkt)) > + if (!odp_packet_l3_offset(pkt) || !odp_packet_l4_offset(pkt)) > Both this code and the base code are incorrect. odp_packet_l(2/3/4)_offset() return ODP_PACKET_OFFSET_INVALID if the packet contains no corresponding offset and that value may or may not be zero. Correct test is: if (odp_packet_l4_offset(pkt) != ODP_PACKET_OFFSET_INVALID) ... There's no need to check the L3 offset since if the packet has a recognized L4 offset then it's guaranteed to have a valid L3 offset as well. > return 0; > - > iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); > udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); > - udplen = odp_be_to_cpu_16(udph->length); > - > - /* 32-bit sum of all 16-bit words covered by UDP chksum */ > + /* 32-bit sum of UDP pseudo-header */ > sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + > - (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + > - (uint16_t)iph->proto + udplen; > - for (buf = (uint8_t *)udph; udplen > 1; udplen -= 2) { > - sum += ((*buf << 8) + *(buf + 1)); > - buf += 2; > - } > - > - /* Fold sum to 16 bits: add carrier to result */ > - while (sum >> 16) > - sum = (sum & 0x) + (sum >> 16); > - > + (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + > + odp_be_to_cpu_16(iph->proto) + udph->length; > + udplen = odp_be_to_cpu_16(udph->length); > + buf = (uint16_t *)((void *)udph); > + /* 32-bit sum of UDP header (checksum field cleared) and UDP data > */ > + for ( ; udplen > 1; udplen -= 2) > + sum += *buf++; > + /* Length is not a multiple of 2 bytes */ > + if (udplen) > + sum += odp_be_to_cpu_16(*((uint8_t *)buf) << 8); > + /* Fold sum to 16 bits */ > + sum = (sum & 0x) + (sum >> 16); > + /* Add carrier (0/1) to result */ > + sum += (sum >> 16); > /* 1's complement */ > sum = ~sum; > - > - /* set computation result */ > - sum = (sum == 0x0) ? 0x : sum; > - > - return sum; > + /* Set computation result */ > + return (sum == 0x0) ? 0x : odp_be_to_cpu_16(sum); > } > > /** @internal Compile time assert */ > -- > 1.7.3.4 > > ___ > lng-odp mailing list > lng-odp@lists.linaro.org > https://lists.linaro.org/mailman/listinfo/lng-odp > ___ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp
[lng-odp] [PATCH] helper : Fix UDP checksum computation
From: Grigore IonThis patch fixes the following problems: - checksum computation for LE platforms - checksum is computed in the CPU endianness. The returned result must be converted to the BE ordering when it is used to update the UDP checksum in a packet. - checksum computation for packets having the UDP length not a multiple of 2. Signed-off-by: Grigore Ion --- helper/include/odp/helper/udp.h | 55 -- 1 files changed, 23 insertions(+), 32 deletions(-) diff --git a/helper/include/odp/helper/udp.h b/helper/include/odp/helper/udp.h index 06c439b..5d6154f 100644 --- a/helper/include/odp/helper/udp.h +++ b/helper/include/odp/helper/udp.h @@ -4,7 +4,6 @@ * SPDX-License-Identifier: BSD-3-Clause */ - /** * @file * @@ -22,7 +21,6 @@ extern "C" { #include #include - /** @addtogroup odph_header ODPH HEADER * @{ */ @@ -44,46 +42,39 @@ typedef struct ODP_PACKED { * This function uses odp packet to calc checksum * * @param pkt calculate chksum for pkt - * @return checksum value + * @return checksum value in CPU endianness */ static inline uint16_t odph_ipv4_udp_chksum(odp_packet_t pkt) { - uint32_t sum = 0; - odph_udphdr_t *udph; - odph_ipv4hdr_t *iph; - uint16_t udplen; - uint8_t *buf; - - if (!odp_packet_l3_offset(pkt)) - return 0; + odph_ipv4hdr_t *iph; + odph_udphdr_t *udph; + uint32_tsum; + uint16_tudplen, *buf; - if (!odp_packet_l4_offset(pkt)) + if (!odp_packet_l3_offset(pkt) || !odp_packet_l4_offset(pkt)) return 0; - iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); - udplen = odp_be_to_cpu_16(udph->length); - - /* 32-bit sum of all 16-bit words covered by UDP chksum */ + /* 32-bit sum of UDP pseudo-header */ sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + - (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + - (uint16_t)iph->proto + udplen; - for (buf = (uint8_t *)udph; udplen > 1; udplen -= 2) { - sum += ((*buf << 8) + *(buf + 1)); - buf += 2; - } - - /* Fold sum to 16 bits: add carrier to result */ - while (sum >> 16) - sum = (sum & 0x) + (sum >> 16); - + (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + + odp_be_to_cpu_16(iph->proto) + udph->length; + udplen = odp_be_to_cpu_16(udph->length); + buf = (uint16_t *)((void *)udph); + /* 32-bit sum of UDP header (checksum field cleared) and UDP data */ + for ( ; udplen > 1; udplen -= 2) + sum += *buf++; + /* Length is not a multiple of 2 bytes */ + if (udplen) + sum += odp_be_to_cpu_16(*((uint8_t *)buf) << 8); + /* Fold sum to 16 bits */ + sum = (sum & 0x) + (sum >> 16); + /* Add carrier (0/1) to result */ + sum += (sum >> 16); /* 1's complement */ sum = ~sum; - - /* set computation result */ - sum = (sum == 0x0) ? 0x : sum; - - return sum; + /* Set computation result */ + return (sum == 0x0) ? 0x : odp_be_to_cpu_16(sum); } /** @internal Compile time assert */ -- 1.7.3.4 ___ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp
Re: [lng-odp] [PATCH] helper : Fix UDP checksum computation
Hi, Now all variables are declared on top of block. The patch passed checkpatch verification. I superseded the old patch. Thanks, Grig -Original Message- From: lng-odp [mailto:lng-odp-boun...@lists.linaro.org] On Behalf Of Maxim Uvarov Sent: Wednesday, September 23, 2015 8:34 PM To: lng-odp@lists.linaro.org Subject: Re: [lng-odp] [PATCH] helper : Fix UDP checksum computation On 09/21/15 08:45, ion.grig...@freescale.com wrote: > From: Grigore Ion <ion.grig...@freescale.com> > > This patch fixes the following problems: > - checksum computation for LE platforms > - checksum is computed in the CPU endianess. The returned result must > be converted to the BE ordering when it is used to update the UDP checksum in > a packet. > - checksum computation for packets having the UDP length not a multiple of 2. > > Signed-off-by: Grigore Ion <ion.grig...@freescale.com> > --- > helper/include/odp/helper/udp.h | 54 > ++- > 1 files changed, 25 insertions(+), 29 deletions(-) > > diff --git a/helper/include/odp/helper/udp.h > b/helper/include/odp/helper/udp.h index 06c439b..74fad3c 100644 > --- a/helper/include/odp/helper/udp.h > +++ b/helper/include/odp/helper/udp.h > @@ -22,7 +22,6 @@ extern "C" { > #include > #include > > - > /** @addtogroup odph_header ODPH HEADER >* @{ >*/ > @@ -44,46 +43,43 @@ typedef struct ODP_PACKED { >* This function uses odp packet to calc checksum >* >* @param pkt calculate chksum for pkt > - * @return checksum value > + * @return checksum value in CPU endianess >*/ > static inline uint16_t odph_ipv4_udp_chksum(odp_packet_t pkt) > { > - uint32_t sum = 0; > - odph_udphdr_t *udph; > - odph_ipv4hdr_t *iph; > - uint16_t udplen; > - uint8_t *buf; > - > - if (!odp_packet_l3_offset(pkt)) > + if (!odp_packet_l3_offset(pkt) || !odp_packet_l3_offset(pkt)) that looks strange :) > return 0; > > - if (!odp_packet_l4_offset(pkt)) > - return 0; > + odph_ipv4hdr_t *iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); > + odph_udphdr_t *udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); iph and udph should be defined on top of the block. Interesting if checkpatch did not said about that. The same for all other variables. So just not delete them from top and run ./script/checkpatch.pl before sending. Thanks, Maxim. > + > + /* 32-bit sum of UDP pseudo-header */ > + uint32_t sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + > + (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + > + odp_be_to_cpu_16(iph->proto) + udph->length; > > - iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); > - udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); > - udplen = odp_be_to_cpu_16(udph->length); > + uint16_t udplen = odp_be_to_cpu_16(udph->length); > + uint16_t *buf = (uint16_t *)((void *)udph); > > - /* 32-bit sum of all 16-bit words covered by UDP chksum */ > - sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + > - (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + > - (uint16_t)iph->proto + udplen; > - for (buf = (uint8_t *)udph; udplen > 1; udplen -= 2) { > - sum += ((*buf << 8) + *(buf + 1)); > - buf += 2; > - } > + /* 32-bit sum of UDP header (checksum field cleared) and UDP data */ > + for ( ; udplen > 1; udplen -= 2) > + sum += *buf++; > > - /* Fold sum to 16 bits: add carrier to result */ > - while (sum >> 16) > - sum = (sum & 0x) + (sum >> 16); > + /* Length is not a multiple of 2 bytes */ > + if (udplen) > + sum += odp_be_to_cpu_16(*((uint8_t *)buf) << 8); > + > + /* Fold sum to 16 bits */ > + sum = (sum & 0x) + (sum >> 16); > + > + /* Add carrier (0/1) to result */ > + sum += (sum >> 16); > > /* 1's complement */ > sum = ~sum; > > - /* set computation result */ > - sum = (sum == 0x0) ? 0x : sum; > - > - return sum; > + /* Set computation result */ > + return (sum == 0x0) ? 0x : odp_be_to_cpu_16(sum); > } > > /** @internal Compile time assert */ ___ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp ___ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp
Re: [lng-odp] [PATCH] helper : Fix UDP checksum computation
On 09/21/15 08:45, ion.grig...@freescale.com wrote: From: Grigore IonThis patch fixes the following problems: - checksum computation for LE platforms - checksum is computed in the CPU endianess. The returned result must be converted to the BE ordering when it is used to update the UDP checksum in a packet. - checksum computation for packets having the UDP length not a multiple of 2. Signed-off-by: Grigore Ion --- helper/include/odp/helper/udp.h | 54 ++- 1 files changed, 25 insertions(+), 29 deletions(-) diff --git a/helper/include/odp/helper/udp.h b/helper/include/odp/helper/udp.h index 06c439b..74fad3c 100644 --- a/helper/include/odp/helper/udp.h +++ b/helper/include/odp/helper/udp.h @@ -22,7 +22,6 @@ extern "C" { #include #include - /** @addtogroup odph_header ODPH HEADER * @{ */ @@ -44,46 +43,43 @@ typedef struct ODP_PACKED { * This function uses odp packet to calc checksum * * @param pkt calculate chksum for pkt - * @return checksum value + * @return checksum value in CPU endianess */ static inline uint16_t odph_ipv4_udp_chksum(odp_packet_t pkt) { - uint32_t sum = 0; - odph_udphdr_t *udph; - odph_ipv4hdr_t *iph; - uint16_t udplen; - uint8_t *buf; - - if (!odp_packet_l3_offset(pkt)) + if (!odp_packet_l3_offset(pkt) || !odp_packet_l3_offset(pkt)) that looks strange :) return 0; - if (!odp_packet_l4_offset(pkt)) - return 0; + odph_ipv4hdr_t *iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + odph_udphdr_t *udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); iph and udph should be defined on top of the block. Interesting if checkpatch did not said about that. The same for all other variables. So just not delete them from top and run ./script/checkpatch.pl before sending. Thanks, Maxim. + + /* 32-bit sum of UDP pseudo-header */ + uint32_t sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + + (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + + odp_be_to_cpu_16(iph->proto) + udph->length; - iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); - udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); - udplen = odp_be_to_cpu_16(udph->length); + uint16_t udplen = odp_be_to_cpu_16(udph->length); + uint16_t *buf = (uint16_t *)((void *)udph); - /* 32-bit sum of all 16-bit words covered by UDP chksum */ - sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + - (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + - (uint16_t)iph->proto + udplen; - for (buf = (uint8_t *)udph; udplen > 1; udplen -= 2) { - sum += ((*buf << 8) + *(buf + 1)); - buf += 2; - } + /* 32-bit sum of UDP header (checksum field cleared) and UDP data */ + for ( ; udplen > 1; udplen -= 2) + sum += *buf++; - /* Fold sum to 16 bits: add carrier to result */ - while (sum >> 16) - sum = (sum & 0x) + (sum >> 16); + /* Length is not a multiple of 2 bytes */ + if (udplen) + sum += odp_be_to_cpu_16(*((uint8_t *)buf) << 8); + + /* Fold sum to 16 bits */ + sum = (sum & 0x) + (sum >> 16); + + /* Add carrier (0/1) to result */ + sum += (sum >> 16); /* 1's complement */ sum = ~sum; - /* set computation result */ - sum = (sum == 0x0) ? 0x : sum; - - return sum; + /* Set computation result */ + return (sum == 0x0) ? 0x : odp_be_to_cpu_16(sum); } /** @internal Compile time assert */ ___ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp
[lng-odp] [PATCH] helper : Fix UDP checksum computation
From: Grigore IonThis patch fixes the following problems: - checksum computation for LE platforms - checksum is computed in the CPU endianess. The returned result must be converted to the BE ordering when it is used to update the UDP checksum in a packet. - checksum computation for packets having the UDP length not a multiple of 2. Signed-off-by: Grigore Ion --- helper/include/odp/helper/udp.h | 54 ++- 1 files changed, 25 insertions(+), 29 deletions(-) diff --git a/helper/include/odp/helper/udp.h b/helper/include/odp/helper/udp.h index 06c439b..74fad3c 100644 --- a/helper/include/odp/helper/udp.h +++ b/helper/include/odp/helper/udp.h @@ -22,7 +22,6 @@ extern "C" { #include #include - /** @addtogroup odph_header ODPH HEADER * @{ */ @@ -44,46 +43,43 @@ typedef struct ODP_PACKED { * This function uses odp packet to calc checksum * * @param pkt calculate chksum for pkt - * @return checksum value + * @return checksum value in CPU endianess */ static inline uint16_t odph_ipv4_udp_chksum(odp_packet_t pkt) { - uint32_t sum = 0; - odph_udphdr_t *udph; - odph_ipv4hdr_t *iph; - uint16_t udplen; - uint8_t *buf; - - if (!odp_packet_l3_offset(pkt)) + if (!odp_packet_l3_offset(pkt) || !odp_packet_l3_offset(pkt)) return 0; - if (!odp_packet_l4_offset(pkt)) - return 0; + odph_ipv4hdr_t *iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + odph_udphdr_t *udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); + + /* 32-bit sum of UDP pseudo-header */ + uint32_t sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + + (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + + odp_be_to_cpu_16(iph->proto) + udph->length; - iph = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); - udph = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL); - udplen = odp_be_to_cpu_16(udph->length); + uint16_t udplen = odp_be_to_cpu_16(udph->length); + uint16_t *buf = (uint16_t *)((void *)udph); - /* 32-bit sum of all 16-bit words covered by UDP chksum */ - sum = (iph->src_addr & 0x) + (iph->src_addr >> 16) + - (iph->dst_addr & 0x) + (iph->dst_addr >> 16) + - (uint16_t)iph->proto + udplen; - for (buf = (uint8_t *)udph; udplen > 1; udplen -= 2) { - sum += ((*buf << 8) + *(buf + 1)); - buf += 2; - } + /* 32-bit sum of UDP header (checksum field cleared) and UDP data */ + for ( ; udplen > 1; udplen -= 2) + sum += *buf++; - /* Fold sum to 16 bits: add carrier to result */ - while (sum >> 16) - sum = (sum & 0x) + (sum >> 16); + /* Length is not a multiple of 2 bytes */ + if (udplen) + sum += odp_be_to_cpu_16(*((uint8_t *)buf) << 8); + + /* Fold sum to 16 bits */ + sum = (sum & 0x) + (sum >> 16); + + /* Add carrier (0/1) to result */ + sum += (sum >> 16); /* 1's complement */ sum = ~sum; - /* set computation result */ - sum = (sum == 0x0) ? 0x : sum; - - return sum; + /* Set computation result */ + return (sum == 0x0) ? 0x : odp_be_to_cpu_16(sum); } /** @internal Compile time assert */ -- 1.7.3.4 ___ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp