aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Cooper <jcooper@solarflare.com>2014-06-11 09:33:08 -0400
committerDavid S. Miller <davem@davemloft.net>2014-06-11 18:36:21 -0400
commitdaf37b556e437ec1ea1a597dcfeff338068380e1 (patch)
tree968e108487ae6ac6fc76c3df46683f83a2a468cb
parent5882a07c72093dc3a18e2d2b129fb200686bb6ee (diff)
sfc: PIO:Restrict to 64bit arch and use 64-bit writes.
Fixes:ee45fd92c739 ("sfc: Use TX PIO for sufficiently small packets") The linux net driver uses memcpy_toio() in order to copy into the PIO buffers. Even on a 64bit machine this causes 32bit accesses to a write- combined memory region. There are hardware limitations that mean that only 64bit naturally aligned accesses are safe in all cases. Due to being write-combined memory region two 32bit accesses may be coalesced to form a 64bit non 64bit aligned access. Solution was to open-code the memory copy routines using pointers and to only enable PIO for x86_64 machines. Not tested on platforms other than x86_64 because this patch disables the PIO feature on other platforms. Compile-tested on x86 to ensure that works. The WARN_ON_ONCE() code in the previous version of this patch has been moved into the internal sfc debug driver as the assertion was unnecessary in the upstream kernel code. This bug fix applies to v3.13 and v3.14 stable branches. Signed-off-by: Shradha Shah <sshah@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/sfc/io.h7
-rw-r--r--drivers/net/ethernet/sfc/tx.c22
2 files changed, 24 insertions, 5 deletions
diff --git a/drivers/net/ethernet/sfc/io.h b/drivers/net/ethernet/sfc/io.h
index 4d3f119b67b3..afb94aa2c15e 100644
--- a/drivers/net/ethernet/sfc/io.h
+++ b/drivers/net/ethernet/sfc/io.h
@@ -66,10 +66,17 @@
66#define EFX_USE_QWORD_IO 1 66#define EFX_USE_QWORD_IO 1
67#endif 67#endif
68 68
69/* Hardware issue requires that only 64-bit naturally aligned writes
70 * are seen by hardware. Its not strictly necessary to restrict to
71 * x86_64 arch, but done for safety since unusual write combining behaviour
72 * can break PIO.
73 */
74#ifdef CONFIG_X86_64
69/* PIO is a win only if write-combining is possible */ 75/* PIO is a win only if write-combining is possible */
70#ifdef ARCH_HAS_IOREMAP_WC 76#ifdef ARCH_HAS_IOREMAP_WC
71#define EFX_USE_PIO 1 77#define EFX_USE_PIO 1
72#endif 78#endif
79#endif
73 80
74#ifdef EFX_USE_QWORD_IO 81#ifdef EFX_USE_QWORD_IO
75static inline void _efx_writeq(struct efx_nic *efx, __le64 value, 82static inline void _efx_writeq(struct efx_nic *efx, __le64 value,
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index fa9475300411..ede8dcca0ff3 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -189,6 +189,18 @@ struct efx_short_copy_buffer {
189 u8 buf[L1_CACHE_BYTES]; 189 u8 buf[L1_CACHE_BYTES];
190}; 190};
191 191
192/* Copy in explicit 64-bit writes. */
193static void efx_memcpy_64(void __iomem *dest, void *src, size_t len)
194{
195 u64 *src64 = src;
196 u64 __iomem *dest64 = dest;
197 size_t l64 = len / 8;
198 size_t i;
199
200 for (i = 0; i < l64; i++)
201 writeq(src64[i], &dest64[i]);
202}
203
192/* Copy to PIO, respecting that writes to PIO buffers must be dword aligned. 204/* Copy to PIO, respecting that writes to PIO buffers must be dword aligned.
193 * Advances piobuf pointer. Leaves additional data in the copy buffer. 205 * Advances piobuf pointer. Leaves additional data in the copy buffer.
194 */ 206 */
@@ -198,7 +210,7 @@ static void efx_memcpy_toio_aligned(struct efx_nic *efx, u8 __iomem **piobuf,
198{ 210{
199 int block_len = len & ~(sizeof(copy_buf->buf) - 1); 211 int block_len = len & ~(sizeof(copy_buf->buf) - 1);
200 212
201 memcpy_toio(*piobuf, data, block_len); 213 efx_memcpy_64(*piobuf, data, block_len);
202 *piobuf += block_len; 214 *piobuf += block_len;
203 len -= block_len; 215 len -= block_len;
204 216
@@ -230,7 +242,7 @@ static void efx_memcpy_toio_aligned_cb(struct efx_nic *efx, u8 __iomem **piobuf,
230 if (copy_buf->used < sizeof(copy_buf->buf)) 242 if (copy_buf->used < sizeof(copy_buf->buf))
231 return; 243 return;
232 244
233 memcpy_toio(*piobuf, copy_buf->buf, sizeof(copy_buf->buf)); 245 efx_memcpy_64(*piobuf, copy_buf->buf, sizeof(copy_buf->buf));
234 *piobuf += sizeof(copy_buf->buf); 246 *piobuf += sizeof(copy_buf->buf);
235 data += copy_to_buf; 247 data += copy_to_buf;
236 len -= copy_to_buf; 248 len -= copy_to_buf;
@@ -245,7 +257,7 @@ static void efx_flush_copy_buffer(struct efx_nic *efx, u8 __iomem *piobuf,
245{ 257{
246 /* if there's anything in it, write the whole buffer, including junk */ 258 /* if there's anything in it, write the whole buffer, including junk */
247 if (copy_buf->used) 259 if (copy_buf->used)
248 memcpy_toio(piobuf, copy_buf->buf, sizeof(copy_buf->buf)); 260 efx_memcpy_64(piobuf, copy_buf->buf, sizeof(copy_buf->buf));
249} 261}
250 262
251/* Traverse skb structure and copy fragments in to PIO buffer. 263/* Traverse skb structure and copy fragments in to PIO buffer.
@@ -304,8 +316,8 @@ efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
304 */ 316 */
305 BUILD_BUG_ON(L1_CACHE_BYTES > 317 BUILD_BUG_ON(L1_CACHE_BYTES >
306 SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); 318 SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
307 memcpy_toio(tx_queue->piobuf, skb->data, 319 efx_memcpy_64(tx_queue->piobuf, skb->data,
308 ALIGN(skb->len, L1_CACHE_BYTES)); 320 ALIGN(skb->len, L1_CACHE_BYTES));
309 } 321 }
310 322
311 EFX_POPULATE_QWORD_5(buffer->option, 323 EFX_POPULATE_QWORD_5(buffer->option,