Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 90d683af authored by Steve Hodgson's avatar Steve Hodgson Committed by David S. Miller
Browse files

sfc: Remove efx_rx_queue::add_lock



Ensure that efx_fast_push_rx_descriptors() must only run
from efx_process_channel() [NAPI], or when napi_disable()
has been executed.

Reimplement the slow fill by sending an event to the
channel, so that NAPI runs, and hanging the subsequent
fast fill off the event handler. Replace the sfc_refill
workqueue and delayed work items with a timer. We do
not need to stop this timer in efx_flush_all() because
it's safe to send the event always; receiving it will
be delayed until NAPI is restarted.

Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d730dc52
Loading
Loading
Loading
Loading
+7 −37
Original line number Diff line number Diff line
@@ -93,13 +93,6 @@ const char *efx_reset_type_names[] = {

#define EFX_MAX_MTU (9 * 1024)

/* RX slow fill workqueue. If memory allocation fails in the fast path,
 * a work item is pushed onto this work queue to retry the allocation later,
 * to avoid the NIC being starved of RX buffers. Since this is a per cpu
 * workqueue, there is nothing to be gained in making it per NIC
 */
static struct workqueue_struct *refill_workqueue;

/* Reset workqueue. If any NIC has a hardware failure then a reset will be
 * queued onto this work queue. This is not a per-nic work queue, because
 * efx_reset_work() acquires the rtnl lock, so resets are naturally serialised.
@@ -516,11 +509,11 @@ static void efx_start_channel(struct efx_channel *channel)
	channel->enabled = true;
	smp_wmb();

	napi_enable(&channel->napi_str);

	/* Load up RX descriptors */
	/* Fill the queues before enabling NAPI */
	efx_for_each_channel_rx_queue(rx_queue, channel)
		efx_fast_push_rx_descriptors(rx_queue);

	napi_enable(&channel->napi_str);
}

/* This disables event queue processing and packet transmission.
@@ -529,8 +522,6 @@ static void efx_start_channel(struct efx_channel *channel)
 */
static void efx_stop_channel(struct efx_channel *channel)
{
	struct efx_rx_queue *rx_queue;

	if (!channel->enabled)
		return;

@@ -538,12 +529,6 @@ static void efx_stop_channel(struct efx_channel *channel)

	channel->enabled = false;
	napi_disable(&channel->napi_str);

	/* Ensure that any worker threads have exited or will be no-ops */
	efx_for_each_channel_rx_queue(rx_queue, channel) {
		spin_lock_bh(&rx_queue->add_lock);
		spin_unlock_bh(&rx_queue->add_lock);
	}
}

static void efx_fini_channels(struct efx_nic *efx)
@@ -595,9 +580,9 @@ static void efx_remove_channel(struct efx_channel *channel)
	efx_remove_eventq(channel);
}

void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay)
void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue)
{
	queue_delayed_work(refill_workqueue, &rx_queue->work, delay);
	mod_timer(&rx_queue->slow_fill, jiffies + msecs_to_jiffies(100));
}

/**************************************************************************
@@ -1242,15 +1227,8 @@ static void efx_start_all(struct efx_nic *efx)
 * since we're holding the rtnl_lock at this point. */
static void efx_flush_all(struct efx_nic *efx)
{
	struct efx_rx_queue *rx_queue;

	/* Make sure the hardware monitor is stopped */
	cancel_delayed_work_sync(&efx->monitor_work);

	/* Ensure that all RX slow refills are complete. */
	efx_for_each_rx_queue(rx_queue, efx)
		cancel_delayed_work_sync(&rx_queue->work);

	/* Stop scheduled port reconfigurations */
	cancel_work_sync(&efx->mac_work);
}
@@ -2064,8 +2042,8 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
		rx_queue->queue = i;
		rx_queue->channel = &efx->channel[0]; /* for safety */
		rx_queue->buffer = NULL;
		spin_lock_init(&rx_queue->add_lock);
		INIT_DELAYED_WORK(&rx_queue->work, efx_rx_work);
		setup_timer(&rx_queue->slow_fill, efx_rx_slow_fill,
			    (unsigned long)rx_queue);
	}

	efx->type = type;
@@ -2436,11 +2414,6 @@ static int __init efx_init_module(void)
	if (rc)
		goto err_notifier;

	refill_workqueue = create_workqueue("sfc_refill");
	if (!refill_workqueue) {
		rc = -ENOMEM;
		goto err_refill;
	}
	reset_workqueue = create_singlethread_workqueue("sfc_reset");
	if (!reset_workqueue) {
		rc = -ENOMEM;
@@ -2456,8 +2429,6 @@ static int __init efx_init_module(void)
 err_pci:
	destroy_workqueue(reset_workqueue);
 err_reset:
	destroy_workqueue(refill_workqueue);
 err_refill:
	unregister_netdevice_notifier(&efx_netdev_notifier);
 err_notifier:
	return rc;
@@ -2469,7 +2440,6 @@ static void __exit efx_exit_module(void)

	pci_unregister_driver(&efx_pci_driver);
	destroy_workqueue(reset_workqueue);
	destroy_workqueue(refill_workqueue);
	unregister_netdevice_notifier(&efx_netdev_notifier);

}
+2 −2
Original line number Diff line number Diff line
@@ -47,12 +47,12 @@ extern void efx_init_rx_queue(struct efx_rx_queue *rx_queue);
extern void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
extern void efx_rx_strategy(struct efx_channel *channel);
extern void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue);
extern void efx_rx_work(struct work_struct *data);
extern void efx_rx_slow_fill(unsigned long context);
extern void __efx_rx_packet(struct efx_channel *channel,
			    struct efx_rx_buffer *rx_buf, bool checksummed);
extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
			  unsigned int len, bool checksummed, bool discard);
extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay);
extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
#define EFX_RXQ_SIZE 1024
#define EFX_RXQ_MASK (EFX_RXQ_SIZE - 1)

+3 −7
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/timer.h>
#include <linux/mdio.h>
#include <linux/list.h>
#include <linux/pci.h>
@@ -242,10 +243,6 @@ struct efx_rx_buffer {
 * @added_count: Number of buffers added to the receive queue.
 * @notified_count: Number of buffers given to NIC (<= @added_count).
 * @removed_count: Number of buffers removed from the receive queue.
 * @add_lock: Receive queue descriptor add spin lock.
 *	This lock must be held in order to add buffers to the RX
 *	descriptor ring (rxd and buffer) and to update added_count (but
 *	not removed_count).
 * @max_fill: RX descriptor maximum fill level (<= ring size)
 * @fast_fill_trigger: RX descriptor fill level that will trigger a fast fill
 *	(<= @max_fill)
@@ -259,7 +256,7 @@ struct efx_rx_buffer {
 *	overflow was observed.  It should never be set.
 * @alloc_page_count: RX allocation strategy counter.
 * @alloc_skb_count: RX allocation strategy counter.
 * @work: Descriptor push work thread
 * @slow_fill: Timer used to defer efx_nic_generate_fill_event().
 * @buf_page: Page for next RX buffer.
 *	We can use a single page for multiple RX buffers. This tracks
 *	the remaining space in the allocation.
@@ -277,7 +274,6 @@ struct efx_rx_queue {
	int added_count;
	int notified_count;
	int removed_count;
	spinlock_t add_lock;
	unsigned int max_fill;
	unsigned int fast_fill_trigger;
	unsigned int fast_fill_limit;
@@ -285,7 +281,7 @@ struct efx_rx_queue {
	unsigned int min_overfill;
	unsigned int alloc_page_count;
	unsigned int alloc_skb_count;
	struct delayed_work work;
	struct timer_list slow_fill;
	unsigned int slow_fill_count;

	struct page *buf_page;
+39 −10
Original line number Diff line number Diff line
@@ -79,10 +79,14 @@ MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold");
/* Depth of RX flush request fifo */
#define EFX_RX_FLUSH_COUNT 4

/* Magic value for efx_generate_test_event() */
#define EFX_CHANNEL_MAGIC(_channel)			\
/* Generated event code for efx_generate_test_event() */
#define EFX_CHANNEL_MAGIC_TEST(_channel)	\
	(0x00010100 + (_channel)->channel)

/* Generated event code for efx_generate_fill_event() */
#define EFX_CHANNEL_MAGIC_FILL(_channel)	\
	(0x00010200 + (_channel)->channel)

/**************************************************************************
 *
 * Solarstorm hardware access
@@ -854,6 +858,26 @@ efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event)
		      checksummed, discard);
}

static void
efx_handle_generated_event(struct efx_channel *channel, efx_qword_t *event)
{
	struct efx_nic *efx = channel->efx;
	unsigned code;

	code = EFX_QWORD_FIELD(*event, FSF_AZ_DRV_GEN_EV_MAGIC);
	if (code == EFX_CHANNEL_MAGIC_TEST(channel))
		++channel->magic_count;
	else if (code == EFX_CHANNEL_MAGIC_FILL(channel))
		/* The queue must be empty, so we won't receive any rx
		 * events, so efx_process_channel() won't refill the
		 * queue. Refill it here */
		efx_fast_push_rx_descriptors(&efx->rx_queue[channel->channel]);
	else
		EFX_LOG(efx, "channel %d received generated "
			"event "EFX_QWORD_FMT"\n", channel->channel,
			EFX_QWORD_VAL(*event));
}

/* Global events are basically PHY events */
static void
efx_handle_global_event(struct efx_channel *channel, efx_qword_t *event)
@@ -997,13 +1021,7 @@ int efx_nic_process_eventq(struct efx_channel *channel, int budget)
			}
			break;
		case FSE_AZ_EV_CODE_DRV_GEN_EV:
			if (EFX_QWORD_FIELD(event, FSF_AZ_DRV_GEN_EV_MAGIC)
			    == EFX_CHANNEL_MAGIC(channel))
				++channel->magic_count;

			EFX_LOG(channel->efx, "channel %d received generated "
				"event "EFX_QWORD_FMT"\n", channel->channel,
				EFX_QWORD_VAL(event));
			efx_handle_generated_event(channel, &event);
			break;
		case FSE_AZ_EV_CODE_GLOBAL_EV:
			efx_handle_global_event(channel, &event);
@@ -1096,7 +1114,18 @@ void efx_nic_remove_eventq(struct efx_channel *channel)

void efx_nic_generate_test_event(struct efx_channel *channel)
{
	unsigned int magic = EFX_CHANNEL_MAGIC(channel);
	unsigned int magic = EFX_CHANNEL_MAGIC_TEST(channel);
	efx_qword_t test_event;

	EFX_POPULATE_QWORD_2(test_event, FSF_AZ_EV_CODE,
			     FSE_AZ_EV_CODE_DRV_GEN_EV,
			     FSF_AZ_DRV_GEN_EV_MAGIC, magic);
	efx_generate_event(channel, &test_event);
}

void efx_nic_generate_fill_event(struct efx_channel *channel)
{
	unsigned int magic = EFX_CHANNEL_MAGIC_FILL(channel);
	efx_qword_t test_event;

	EFX_POPULATE_QWORD_2(test_event, FSF_AZ_EV_CODE,
+1 −0
Original line number Diff line number Diff line
@@ -191,6 +191,7 @@ extern int efx_nic_rx_xoff_thresh, efx_nic_rx_xon_thresh;
extern int efx_nic_init_interrupt(struct efx_nic *efx);
extern void efx_nic_enable_interrupts(struct efx_nic *efx);
extern void efx_nic_generate_test_event(struct efx_channel *channel);
extern void efx_nic_generate_fill_event(struct efx_channel *channel);
extern void efx_nic_generate_interrupt(struct efx_nic *efx);
extern void efx_nic_disable_interrupts(struct efx_nic *efx);
extern void efx_nic_fini_interrupt(struct efx_nic *efx);
Loading