Commit 183233be authored by Ben Hutchings's avatar Ben Hutchings

sfc: Allocate and link PIO buffers; map them with write-combining

Try to allocate a segment of PIO buffer to each TX channel.  If
allocation fails, log an error but continue.

PIO buffers must be mapped separately from the NIC registers, with
write-combining enabled.  Where the host page size is 4K, we could
potentially map each VI's registers and PIO buffer separately.
However, this would add significant complexity, and we also need to
support architectures such as POWER which have a greater page size.
So make a single contiguous write-combining mapping after the
uncacheable mapping, aligned to the host page size, and link PIO
buffers there.  Where necessary, allocate additional VIs within
the write-combining mapping purely for access to PIO buffers.

Link all TX buffers to TX queues and the additional VIs in
efx_ef10_dimension_resources() and in efx_ef10_init_nic() after
an MC reboot.
Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
parent dfa50be9
...@@ -285,6 +285,181 @@ static int efx_ef10_free_vis(struct efx_nic *efx) ...@@ -285,6 +285,181 @@ static int efx_ef10_free_vis(struct efx_nic *efx)
return rc; return rc;
} }
#ifdef EFX_USE_PIO
static void efx_ef10_free_piobufs(struct efx_nic *efx)
{
struct efx_ef10_nic_data *nic_data = efx->nic_data;
MCDI_DECLARE_BUF(inbuf, MC_CMD_FREE_PIOBUF_IN_LEN);
unsigned int i;
int rc;
BUILD_BUG_ON(MC_CMD_FREE_PIOBUF_OUT_LEN != 0);
for (i = 0; i < nic_data->n_piobufs; i++) {
MCDI_SET_DWORD(inbuf, FREE_PIOBUF_IN_PIOBUF_HANDLE,
nic_data->piobuf_handle[i]);
rc = efx_mcdi_rpc(efx, MC_CMD_FREE_PIOBUF, inbuf, sizeof(inbuf),
NULL, 0, NULL);
WARN_ON(rc);
}
nic_data->n_piobufs = 0;
}
static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
{
struct efx_ef10_nic_data *nic_data = efx->nic_data;
MCDI_DECLARE_BUF(outbuf, MC_CMD_ALLOC_PIOBUF_OUT_LEN);
unsigned int i;
size_t outlen;
int rc = 0;
BUILD_BUG_ON(MC_CMD_ALLOC_PIOBUF_IN_LEN != 0);
for (i = 0; i < n; i++) {
rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0,
outbuf, sizeof(outbuf), &outlen);
if (rc)
break;
if (outlen < MC_CMD_ALLOC_PIOBUF_OUT_LEN) {
rc = -EIO;
break;
}
nic_data->piobuf_handle[i] =
MCDI_DWORD(outbuf, ALLOC_PIOBUF_OUT_PIOBUF_HANDLE);
netif_dbg(efx, probe, efx->net_dev,
"allocated PIO buffer %u handle %x\n", i,
nic_data->piobuf_handle[i]);
}
nic_data->n_piobufs = i;
if (rc)
efx_ef10_free_piobufs(efx);
return rc;
}
static int efx_ef10_link_piobufs(struct efx_nic *efx)
{
struct efx_ef10_nic_data *nic_data = efx->nic_data;
MCDI_DECLARE_BUF(inbuf,
max(MC_CMD_LINK_PIOBUF_IN_LEN,
MC_CMD_UNLINK_PIOBUF_IN_LEN));
struct efx_channel *channel;
struct efx_tx_queue *tx_queue;
unsigned int offset, index;
int rc;
BUILD_BUG_ON(MC_CMD_LINK_PIOBUF_OUT_LEN != 0);
BUILD_BUG_ON(MC_CMD_UNLINK_PIOBUF_OUT_LEN != 0);
/* Link a buffer to each VI in the write-combining mapping */
for (index = 0; index < nic_data->n_piobufs; ++index) {
MCDI_SET_DWORD(inbuf, LINK_PIOBUF_IN_PIOBUF_HANDLE,
nic_data->piobuf_handle[index]);
MCDI_SET_DWORD(inbuf, LINK_PIOBUF_IN_TXQ_INSTANCE,
nic_data->pio_write_vi_base + index);
rc = efx_mcdi_rpc(efx, MC_CMD_LINK_PIOBUF,
inbuf, MC_CMD_LINK_PIOBUF_IN_LEN,
NULL, 0, NULL);
if (rc) {
netif_err(efx, drv, efx->net_dev,
"failed to link VI %u to PIO buffer %u (%d)\n",
nic_data->pio_write_vi_base + index, index,
rc);
goto fail;
}
netif_dbg(efx, probe, efx->net_dev,
"linked VI %u to PIO buffer %u\n",
nic_data->pio_write_vi_base + index, index);
}
/* Link a buffer to each TX queue */
efx_for_each_channel(channel, efx) {
efx_for_each_channel_tx_queue(tx_queue, channel) {
/* We assign the PIO buffers to queues in
* reverse order to allow for the following
* special case.
*/
offset = ((efx->tx_channel_offset + efx->n_tx_channels -
tx_queue->channel->channel - 1) *
efx_piobuf_size);
index = offset / ER_DZ_TX_PIOBUF_SIZE;
offset = offset % ER_DZ_TX_PIOBUF_SIZE;
/* When the host page size is 4K, the first
* host page in the WC mapping may be within
* the same VI page as the last TX queue. We
* can only link one buffer to each VI.
*/
if (tx_queue->queue == nic_data->pio_write_vi_base) {
BUG_ON(index != 0);
rc = 0;
} else {
MCDI_SET_DWORD(inbuf,
LINK_PIOBUF_IN_PIOBUF_HANDLE,
nic_data->piobuf_handle[index]);
MCDI_SET_DWORD(inbuf,
LINK_PIOBUF_IN_TXQ_INSTANCE,
tx_queue->queue);
rc = efx_mcdi_rpc(efx, MC_CMD_LINK_PIOBUF,
inbuf, MC_CMD_LINK_PIOBUF_IN_LEN,
NULL, 0, NULL);
}
if (rc) {
/* This is non-fatal; the TX path just
* won't use PIO for this queue
*/
netif_err(efx, drv, efx->net_dev,
"failed to link VI %u to PIO buffer %u (%d)\n",
tx_queue->queue, index, rc);
tx_queue->piobuf = NULL;
} else {
tx_queue->piobuf =
nic_data->pio_write_base +
index * EFX_VI_PAGE_SIZE + offset;
tx_queue->piobuf_offset = offset;
netif_dbg(efx, probe, efx->net_dev,
"linked VI %u to PIO buffer %u offset %x addr %p\n",
tx_queue->queue, index,
tx_queue->piobuf_offset,
tx_queue->piobuf);
}
}
}
return 0;
fail:
while (index--) {
MCDI_SET_DWORD(inbuf, UNLINK_PIOBUF_IN_TXQ_INSTANCE,
nic_data->pio_write_vi_base + index);
efx_mcdi_rpc(efx, MC_CMD_UNLINK_PIOBUF,
inbuf, MC_CMD_UNLINK_PIOBUF_IN_LEN,
NULL, 0, NULL);
}
return rc;
}
#else /* !EFX_USE_PIO */
static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
{
return n == 0 ? 0 : -ENOBUFS;
}
static int efx_ef10_link_piobufs(struct efx_nic *efx)
{
return 0;
}
static void efx_ef10_free_piobufs(struct efx_nic *efx)
{
}
#endif /* EFX_USE_PIO */
static void efx_ef10_remove(struct efx_nic *efx) static void efx_ef10_remove(struct efx_nic *efx)
{ {
struct efx_ef10_nic_data *nic_data = efx->nic_data; struct efx_ef10_nic_data *nic_data = efx->nic_data;
...@@ -295,9 +470,15 @@ static void efx_ef10_remove(struct efx_nic *efx) ...@@ -295,9 +470,15 @@ static void efx_ef10_remove(struct efx_nic *efx)
/* This needs to be after efx_ptp_remove_channel() with no filters */ /* This needs to be after efx_ptp_remove_channel() with no filters */
efx_ef10_rx_free_indir_table(efx); efx_ef10_rx_free_indir_table(efx);
if (nic_data->wc_membase)
iounmap(nic_data->wc_membase);
rc = efx_ef10_free_vis(efx); rc = efx_ef10_free_vis(efx);
WARN_ON(rc != 0); WARN_ON(rc != 0);
if (!nic_data->must_restore_piobufs)
efx_ef10_free_piobufs(efx);
efx_mcdi_fini(efx); efx_mcdi_fini(efx);
efx_nic_free_buffer(efx, &nic_data->mcdi_buf); efx_nic_free_buffer(efx, &nic_data->mcdi_buf);
kfree(nic_data); kfree(nic_data);
...@@ -330,12 +511,126 @@ static int efx_ef10_alloc_vis(struct efx_nic *efx, ...@@ -330,12 +511,126 @@ static int efx_ef10_alloc_vis(struct efx_nic *efx,
return 0; return 0;
} }
/* Note that the failure path of this function does not free
* resources, as this will be done by efx_ef10_remove().
*/
static int efx_ef10_dimension_resources(struct efx_nic *efx) static int efx_ef10_dimension_resources(struct efx_nic *efx)
{ {
unsigned int n_vis = struct efx_ef10_nic_data *nic_data = efx->nic_data;
max(efx->n_channels, efx->n_tx_channels * EFX_TXQ_TYPES); unsigned int uc_mem_map_size, wc_mem_map_size;
unsigned int min_vis, pio_write_vi_base, max_vis;
void __iomem *membase;
int rc;
min_vis = max(efx->n_channels, efx->n_tx_channels * EFX_TXQ_TYPES);
#ifdef EFX_USE_PIO
/* Try to allocate PIO buffers if wanted and if the full
* number of PIO buffers would be sufficient to allocate one
* copy-buffer per TX channel. Failure is non-fatal, as there
* are only a small number of PIO buffers shared between all
* functions of the controller.
*/
if (efx_piobuf_size != 0 &&
ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size * EF10_TX_PIOBUF_COUNT >=
efx->n_tx_channels) {
unsigned int n_piobufs =
DIV_ROUND_UP(efx->n_tx_channels,
ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size);
rc = efx_ef10_alloc_piobufs(efx, n_piobufs);
if (rc)
netif_err(efx, probe, efx->net_dev,
"failed to allocate PIO buffers (%d)\n", rc);
else
netif_dbg(efx, probe, efx->net_dev,
"allocated %u PIO buffers\n", n_piobufs);
}
#else
nic_data->n_piobufs = 0;
#endif
/* PIO buffers should be mapped with write-combining enabled,
* and we want to make single UC and WC mappings rather than
* several of each (in fact that's the only option if host
* page size is >4K). So we may allocate some extra VIs just
* for writing PIO buffers through.
*/
uc_mem_map_size = PAGE_ALIGN((min_vis - 1) * EFX_VI_PAGE_SIZE +
ER_DZ_TX_PIOBUF);
if (nic_data->n_piobufs) {
pio_write_vi_base = uc_mem_map_size / EFX_VI_PAGE_SIZE;
wc_mem_map_size = (PAGE_ALIGN((pio_write_vi_base +
nic_data->n_piobufs) *
EFX_VI_PAGE_SIZE) -
uc_mem_map_size);
max_vis = pio_write_vi_base + nic_data->n_piobufs;
} else {
pio_write_vi_base = 0;
wc_mem_map_size = 0;
max_vis = min_vis;
}
/* In case the last attached driver failed to free VIs, do it now */
rc = efx_ef10_free_vis(efx);
if (rc != 0)
return rc;
rc = efx_ef10_alloc_vis(efx, min_vis, max_vis);
if (rc != 0)
return rc;
/* If we didn't get enough VIs to map all the PIO buffers, free the
* PIO buffers
*/
if (nic_data->n_piobufs &&
nic_data->n_allocated_vis <
pio_write_vi_base + nic_data->n_piobufs) {
netif_dbg(efx, probe, efx->net_dev,
"%u VIs are not sufficient to map %u PIO buffers\n",
nic_data->n_allocated_vis, nic_data->n_piobufs);
efx_ef10_free_piobufs(efx);
}
/* Shrink the original UC mapping of the memory BAR */
membase = ioremap_nocache(efx->membase_phys, uc_mem_map_size);
if (!membase) {
netif_err(efx, probe, efx->net_dev,
"could not shrink memory BAR to %x\n",
uc_mem_map_size);
return -ENOMEM;
}
iounmap(efx->membase);
efx->membase = membase;
/* Set up the WC mapping if needed */
if (wc_mem_map_size) {
nic_data->wc_membase = ioremap_wc(efx->membase_phys +
uc_mem_map_size,
wc_mem_map_size);
if (!nic_data->wc_membase) {
netif_err(efx, probe, efx->net_dev,
"could not allocate WC mapping of size %x\n",
wc_mem_map_size);
return -ENOMEM;
}
nic_data->pio_write_vi_base = pio_write_vi_base;
nic_data->pio_write_base =
nic_data->wc_membase +
(pio_write_vi_base * EFX_VI_PAGE_SIZE + ER_DZ_TX_PIOBUF -
uc_mem_map_size);
return efx_ef10_alloc_vis(efx, n_vis, n_vis); rc = efx_ef10_link_piobufs(efx);
if (rc)
efx_ef10_free_piobufs(efx);
}
netif_dbg(efx, probe, efx->net_dev,
"memory BAR at %pa (virtual %p+%x UC, %p+%x WC)\n",
&efx->membase_phys, efx->membase, uc_mem_map_size,
nic_data->wc_membase, wc_mem_map_size);
return 0;
} }
static int efx_ef10_init_nic(struct efx_nic *efx) static int efx_ef10_init_nic(struct efx_nic *efx)
...@@ -359,6 +654,21 @@ static int efx_ef10_init_nic(struct efx_nic *efx) ...@@ -359,6 +654,21 @@ static int efx_ef10_init_nic(struct efx_nic *efx)
nic_data->must_realloc_vis = false; nic_data->must_realloc_vis = false;
} }
if (nic_data->must_restore_piobufs && nic_data->n_piobufs) {
rc = efx_ef10_alloc_piobufs(efx, nic_data->n_piobufs);
if (rc == 0) {
rc = efx_ef10_link_piobufs(efx);
if (rc)
efx_ef10_free_piobufs(efx);
}
/* Log an error on failure, but this is non-fatal */
if (rc)
netif_err(efx, drv, efx->net_dev,
"failed to restore PIO buffers (%d)\n", rc);
nic_data->must_restore_piobufs = false;
}
efx_ef10_rx_push_indir_table(efx); efx_ef10_rx_push_indir_table(efx);
return 0; return 0;
} }
...@@ -716,6 +1026,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) ...@@ -716,6 +1026,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx)
/* All our allocations have been reset */ /* All our allocations have been reset */
nic_data->must_realloc_vis = true; nic_data->must_realloc_vis = true;
nic_data->must_restore_filters = true; nic_data->must_restore_filters = true;
nic_data->must_restore_piobufs = true;
nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID; nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
/* The datapath firmware might have been changed */ /* The datapath firmware might have been changed */
......
...@@ -30,6 +30,7 @@ efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); ...@@ -30,6 +30,7 @@ efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb);
extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc); extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc);
extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx);
extern unsigned int efx_piobuf_size;
/* RX */ /* RX */
extern void efx_rx_config_page_split(struct efx_nic *efx); extern void efx_rx_config_page_split(struct efx_nic *efx);
......
...@@ -66,6 +66,11 @@ ...@@ -66,6 +66,11 @@
#define EFX_USE_QWORD_IO 1 #define EFX_USE_QWORD_IO 1
#endif #endif
/* PIO is a win only if write-combining is possible */
#ifdef ARCH_HAS_IOREMAP_WC
#define EFX_USE_PIO 1
#endif
#ifdef EFX_USE_QWORD_IO #ifdef EFX_USE_QWORD_IO
static inline void _efx_writeq(struct efx_nic *efx, __le64 value, static inline void _efx_writeq(struct efx_nic *efx, __le64 value,
unsigned int reg) unsigned int reg)
......
...@@ -182,6 +182,9 @@ struct efx_tx_buffer { ...@@ -182,6 +182,9 @@ struct efx_tx_buffer {
* @tsoh_page: Array of pages of TSO header buffers * @tsoh_page: Array of pages of TSO header buffers
* @txd: The hardware descriptor ring * @txd: The hardware descriptor ring
* @ptr_mask: The size of the ring minus 1. * @ptr_mask: The size of the ring minus 1.
* @piobuf: PIO buffer region for this TX queue (shared with its partner).
* Size of the region is efx_piobuf_size.
* @piobuf_offset: Buffer offset to be specified in PIO descriptors
* @initialised: Has hardware queue been initialised? * @initialised: Has hardware queue been initialised?
* @read_count: Current read pointer. * @read_count: Current read pointer.
* This is the number of buffers that have been removed from both rings. * This is the number of buffers that have been removed from both rings.
...@@ -223,6 +226,8 @@ struct efx_tx_queue { ...@@ -223,6 +226,8 @@ struct efx_tx_queue {
struct efx_buffer *tsoh_page; struct efx_buffer *tsoh_page;
struct efx_special_buffer txd; struct efx_special_buffer txd;
unsigned int ptr_mask; unsigned int ptr_mask;
void __iomem *piobuf;
unsigned int piobuf_offset;
bool initialised; bool initialised;
/* Members used mainly on the completion path */ /* Members used mainly on the completion path */
......
...@@ -389,6 +389,12 @@ enum { ...@@ -389,6 +389,12 @@ enum {
EF10_STAT_COUNT EF10_STAT_COUNT
}; };
/* Maximum number of TX PIO buffers we may allocate to a function.
* This matches the total number of buffers on each SFC9100-family
* controller.
*/
#define EF10_TX_PIOBUF_COUNT 16
/** /**
* struct efx_ef10_nic_data - EF10 architecture NIC state * struct efx_ef10_nic_data - EF10 architecture NIC state
* @mcdi_buf: DMA buffer for MCDI * @mcdi_buf: DMA buffer for MCDI
...@@ -397,6 +403,13 @@ enum { ...@@ -397,6 +403,13 @@ enum {
* @n_allocated_vis: Number of VIs allocated to this function * @n_allocated_vis: Number of VIs allocated to this function
* @must_realloc_vis: Flag: VIs have yet to be reallocated after MC reboot * @must_realloc_vis: Flag: VIs have yet to be reallocated after MC reboot
* @must_restore_filters: Flag: filters have yet to be restored after MC reboot * @must_restore_filters: Flag: filters have yet to be restored after MC reboot
* @n_piobufs: Number of PIO buffers allocated to this function
* @wc_membase: Base address of write-combining mapping of the memory BAR
* @pio_write_base: Base address for writing PIO buffers
* @pio_write_vi_base: Relative VI number for @pio_write_base
* @piobuf_handle: Handle of each PIO buffer allocated
* @must_restore_piobufs: Flag: PIO buffers have yet to be restored after MC
* reboot
* @rx_rss_context: Firmware handle for our RSS context * @rx_rss_context: Firmware handle for our RSS context
* @stats: Hardware statistics * @stats: Hardware statistics
* @workaround_35388: Flag: firmware supports workaround for bug 35388 * @workaround_35388: Flag: firmware supports workaround for bug 35388
...@@ -412,6 +425,11 @@ struct efx_ef10_nic_data { ...@@ -412,6 +425,11 @@ struct efx_ef10_nic_data {
unsigned int n_allocated_vis; unsigned int n_allocated_vis;
bool must_realloc_vis; bool must_realloc_vis;
bool must_restore_filters; bool must_restore_filters;
unsigned int n_piobufs;
void __iomem *wc_membase, *pio_write_base;
unsigned int pio_write_vi_base;
unsigned int piobuf_handle[EF10_TX_PIOBUF_COUNT];
bool must_restore_piobufs;
u32 rx_rss_context; u32 rx_rss_context;
u64 stats[EF10_STAT_COUNT]; u64 stats[EF10_STAT_COUNT];
bool workaround_35388; bool workaround_35388;
......
...@@ -17,12 +17,22 @@ ...@@ -17,12 +17,22 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/cache.h>
#include "net_driver.h" #include "net_driver.h"
#include "efx.h" #include "efx.h"
#include "io.h"
#include "nic.h" #include "nic.h"
#include "workarounds.h" #include "workarounds.h"
#include "ef10_regs.h" #include "ef10_regs.h"
#ifdef EFX_USE_PIO
#define EFX_PIOBUF_SIZE_MAX ER_DZ_TX_PIOBUF_SIZE
#define EFX_PIOBUF_SIZE_DEF ALIGN(256, L1_CACHE_BYTES)
unsigned int efx_piobuf_size __read_mostly = EFX_PIOBUF_SIZE_DEF;
#endif /* EFX_USE_PIO */
static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue,
struct efx_tx_buffer *buffer, struct efx_tx_buffer *buffer,
unsigned int *pkts_compl, unsigned int *pkts_compl,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment