Skip to content

Commit

Permalink
xen-netback: fix unlimited guest Rx internal queue and carrier flapping
Browse files Browse the repository at this point in the history
Netback needs to discard old to-guest skb's (guest Rx queue drain) and
it needs detect guest Rx stalls (to disable the carrier so packets are
discarded earlier), but the current implementation is very broken.

1. The check in hard_start_xmit of the slot availability did not
   consider the number of packets that were already in the guest Rx
   queue.  This could allow the queue to grow without bound.

   The guest stops consuming packets and the ring was allowed to fill
   leaving S slot free.  Netback queues a packet requiring more than S
   slots (ensuring that the ring stays with S slots free).  Netback
   queue indefinately packets provided that then require S or fewer
   slots.

2. The Rx stall detection is not triggered in this case since the
   (host) Tx queue is not stopped.

3. If the Tx queue is stopped and a guest Rx interrupt occurs, netback
   will consider this an Rx purge event which may result in it taking
   the carrier down unnecessarily.  It also considers a queue with
   only 1 slot free as unstalled (even though the next packet might
   not fit in this).

The internal guest Rx queue is limited by a byte length (to 512 Kib,
enough for half the ring).  The (host) Tx queue is stopped and started
based on this limit.  This sets an upper bound on the amount of memory
used by packets on the internal queue.

This allows the estimatation of the number of slots for an skb to be
removed (it wasn't a very good estimate anyway).  Instead, the guest
Rx thread just waits for enough free slots for a maximum sized packet.

skbs queued on the internal queue have an 'expires' time (set to the
current time plus the drain timeout).  The guest Rx thread will detect
when the skb at the head of the queue has expired and discard expired
skbs.  This sets a clear upper bound on the length of time an skb can
be queued for.  For a guest being destroyed the maximum time needed to
wait for all the packets it sent to be dropped is still the drain
timeout (10 s) since it will not be sending new packets.

Rx stall detection is reintroduced in a later commit.

Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Wei Liu <wei.liu2@citrix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David Vrabel authored and davem330 committed Oct 25, 2014
1 parent bc96f64 commit f48da8b
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 178 deletions.
29 changes: 17 additions & 12 deletions drivers/net/xen-netback/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,9 @@ struct xenvif_queue { /* Per-queue data for xenvif */
char rx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-rx */
struct xen_netif_rx_back_ring rx;
struct sk_buff_head rx_queue;
RING_IDX rx_last_skb_slots;
unsigned long status;

struct timer_list rx_stalled;
unsigned int rx_queue_max;
unsigned int rx_queue_len;

struct gnttab_copy grant_copy_op[MAX_GRANT_COPY_OPS];

Expand All @@ -199,18 +198,14 @@ struct xenvif_queue { /* Per-queue data for xenvif */
struct xenvif_stats stats;
};

/* Maximum number of Rx slots a to-guest packet may use, including the
* slot needed for GSO meta-data.
*/
#define XEN_NETBK_RX_SLOTS_MAX (MAX_SKB_FRAGS + 1)

enum state_bit_shift {
/* This bit marks that the vif is connected */
VIF_STATUS_CONNECTED,
/* This bit signals the RX thread that queuing was stopped (in
* start_xmit), and either the timer fired or an RX interrupt came
*/
QUEUE_STATUS_RX_PURGE_EVENT,
/* This bit tells the interrupt handler that this queue was the reason
* for the carrier off, so it should kick the thread. Only queues which
* brought it down can turn on the carrier.
*/
QUEUE_STATUS_RX_STALLED
};

struct xenvif {
Expand Down Expand Up @@ -246,6 +241,14 @@ struct xenvif {
struct net_device *dev;
};

struct xenvif_rx_cb {
unsigned long expires;
int meta_slots_used;
bool full_coalesce;
};

#define XENVIF_RX_CB(skb) ((struct xenvif_rx_cb *)(skb)->cb)

static inline struct xenbus_device *xenvif_to_xenbus_device(struct xenvif *vif)
{
return to_xenbus_device(vif->dev->dev.parent);
Expand Down Expand Up @@ -291,6 +294,8 @@ void xenvif_kick_thread(struct xenvif_queue *queue);

int xenvif_dealloc_kthread(void *data);

void xenvif_rx_queue_tail(struct xenvif_queue *queue, struct sk_buff *skb);

/* Determine whether the needed number of slots (req) are available,
* and set req_event if not.
*/
Expand Down
59 changes: 11 additions & 48 deletions drivers/net/xen-netback/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
#define XENVIF_QUEUE_LENGTH 32
#define XENVIF_NAPI_WEIGHT 64

/* Number of bytes allowed on the internal guest Rx queue. */
#define XENVIF_RX_QUEUE_BYTES (XEN_NETIF_RX_RING_SIZE/2 * PAGE_SIZE)

/* This function is used to set SKBTX_DEV_ZEROCOPY as well as
* increasing the inflight counter. We need to increase the inflight
* counter because core driver calls into xenvif_zerocopy_callback
Expand All @@ -63,7 +66,8 @@ void xenvif_skb_zerocopy_complete(struct xenvif_queue *queue)
int xenvif_schedulable(struct xenvif *vif)
{
return netif_running(vif->dev) &&
test_bit(VIF_STATUS_CONNECTED, &vif->status);
test_bit(VIF_STATUS_CONNECTED, &vif->status) &&
!vif->disabled;
}

static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id)
Expand Down Expand Up @@ -104,16 +108,7 @@ int xenvif_poll(struct napi_struct *napi, int budget)
static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id)
{
struct xenvif_queue *queue = dev_id;
struct netdev_queue *net_queue =
netdev_get_tx_queue(queue->vif->dev, queue->id);

/* QUEUE_STATUS_RX_PURGE_EVENT is only set if either QDisc was off OR
* the carrier went down and this queue was previously blocked
*/
if (unlikely(netif_tx_queue_stopped(net_queue) ||
(!netif_carrier_ok(queue->vif->dev) &&
test_bit(QUEUE_STATUS_RX_STALLED, &queue->status))))
set_bit(QUEUE_STATUS_RX_PURGE_EVENT, &queue->status);
xenvif_kick_thread(queue);

return IRQ_HANDLED;
Expand Down Expand Up @@ -141,24 +136,13 @@ void xenvif_wake_queue(struct xenvif_queue *queue)
netif_tx_wake_queue(netdev_get_tx_queue(dev, id));
}

/* Callback to wake the queue's thread and turn the carrier off on timeout */
static void xenvif_rx_stalled(unsigned long data)
{
struct xenvif_queue *queue = (struct xenvif_queue *)data;

if (xenvif_queue_stopped(queue)) {
set_bit(QUEUE_STATUS_RX_PURGE_EVENT, &queue->status);
xenvif_kick_thread(queue);
}
}

static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct xenvif *vif = netdev_priv(dev);
struct xenvif_queue *queue = NULL;
unsigned int num_queues = vif->num_queues;
u16 index;
int min_slots_needed;
struct xenvif_rx_cb *cb;

BUG_ON(skb->dev != dev);

Expand All @@ -181,30 +165,10 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
!xenvif_schedulable(vif))
goto drop;

/* At best we'll need one slot for the header and one for each
* frag.
*/
min_slots_needed = 1 + skb_shinfo(skb)->nr_frags;
cb = XENVIF_RX_CB(skb);
cb->expires = jiffies + rx_drain_timeout_jiffies;

/* If the skb is GSO then we'll also need an extra slot for the
* metadata.
*/
if (skb_is_gso(skb))
min_slots_needed++;

/* If the skb can't possibly fit in the remaining slots
* then turn off the queue to give the ring a chance to
* drain.
*/
if (!xenvif_rx_ring_slots_available(queue, min_slots_needed)) {
queue->rx_stalled.function = xenvif_rx_stalled;
queue->rx_stalled.data = (unsigned long)queue;
netif_tx_stop_queue(netdev_get_tx_queue(dev, queue->id));
mod_timer(&queue->rx_stalled,
jiffies + rx_drain_timeout_jiffies);
}

skb_queue_tail(&queue->rx_queue, skb);
xenvif_rx_queue_tail(queue, skb);
xenvif_kick_thread(queue);

return NETDEV_TX_OK;
Expand Down Expand Up @@ -498,6 +462,8 @@ int xenvif_init_queue(struct xenvif_queue *queue)
init_timer(&queue->credit_timeout);
queue->credit_window_start = get_jiffies_64();

queue->rx_queue_max = XENVIF_RX_QUEUE_BYTES;

skb_queue_head_init(&queue->rx_queue);
skb_queue_head_init(&queue->tx_queue);

Expand Down Expand Up @@ -529,8 +495,6 @@ int xenvif_init_queue(struct xenvif_queue *queue)
queue->grant_tx_handle[i] = NETBACK_INVALID_HANDLE;
}

init_timer(&queue->rx_stalled);

return 0;
}

Expand Down Expand Up @@ -664,7 +628,6 @@ void xenvif_disconnect(struct xenvif *vif)
netif_napi_del(&queue->napi);

if (queue->task) {
del_timer_sync(&queue->rx_stalled);
kthread_stop(queue->task);
queue->task = NULL;
}
Expand Down
Loading

0 comments on commit f48da8b

Please sign in to comment.