RE: [PATCH 1/2] mm: Fix struct page layout on 32-bit systems
From: David Laight <hidden>
Date: 2021-04-20 08:39:59
Also in:
linux-mips, linux-mm, linuxppc-dev, lkml, netdev
From: Geert Uytterhoeven
Sent: 20 April 2021 08:40 Hi Willy, On Sat, Apr 17, 2021 at 4:49 AM Matthew Wilcox [off-list ref] wrote:quoted
Replacement patch to fix compiler warning. 32-bit architectures which expect 8-byte alignment for 8-byte integers and need 64-bit DMA addresses (arc, arm, mips, ppc) had their struct page inadvertently expanded in 2019. When the dma_addr_t was added, it forced the alignment of the union to 8 bytes, which inserted a 4 byte gap between 'flags' and the union. Fix this by storing the dma_addr_t in one or two adjacent unsigned longs. This restores the alignment to that of an unsigned long, and also fixes a potential problem where (on a big endian platform), the bit used to denote PageTail could inadvertently get set, and a racing get_user_pages_fast() could dereference a bogus compound_head(). Fixes: c25fff7171be ("mm: add dma_addr_t to struct page") Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>Thanks for your patch!quoted
--- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h@@ -97,10 +97,10 @@ struct page { }; struct { /* page_pool used by netstack */ /** - * @dma_addr: might require a 64-bit value even on + * @dma_addr: might require a 64-bit value on * 32-bit architectures. */ - dma_addr_t dma_addr; + unsigned long dma_addr[2];So we get two 64-bit words on 64-bit platforms, while only one is needed? Would unsigned long _dma_addr[sizeof(dma_addr_t) / sizeof(unsigned long)]; work? Or will the compiler become too overzealous, and warn about the use of ...[1] below, even when unreachable? I wouldn't mind an #ifdef instead of an if () in the code below, though.
You could use [ARRAY_SIZE()-1] instead of [1]. Or, since IIRC it is the last member of that specific struct, define as: unsigned long dma_addr[]; ...
quoted
- return page->dma_addr; + dma_addr_t ret = page->dma_addr[0]; + if (sizeof(dma_addr_t) > sizeof(unsigned long)) + ret |= (dma_addr_t)page->dma_addr[1] << 16 << 16;We don't seem to have a handy macro for a 32-bit left shift yet... But you can also avoid the warning using ret |= (u64)page->dma_addr[1] << 32;
Or: ret |= page->dma_addr[1] + 0ull << 32; Which relies in integer promotion rather than a cast. David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales) _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel