mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
xen-netback: fix unlimited guest Rx internal queue and carrier flapping
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>
This commit is contained in:
parent
bc96f648df
commit
f48da8b14d
4 changed files with 161 additions and 178 deletions
|
@ -176,10 +176,9 @@ struct xenvif_queue { /* Per-queue data for xenvif */
|
||||||
char rx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-rx */
|
char rx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-rx */
|
||||||
struct xen_netif_rx_back_ring rx;
|
struct xen_netif_rx_back_ring rx;
|
||||||
struct sk_buff_head rx_queue;
|
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];
|
struct gnttab_copy grant_copy_op[MAX_GRANT_COPY_OPS];
|
||||||
|
|
||||||
|
@ -199,18 +198,14 @@ struct xenvif_queue { /* Per-queue data for xenvif */
|
||||||
struct xenvif_stats stats;
|
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 {
|
enum state_bit_shift {
|
||||||
/* This bit marks that the vif is connected */
|
/* This bit marks that the vif is connected */
|
||||||
VIF_STATUS_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 {
|
struct xenvif {
|
||||||
|
@ -246,6 +241,14 @@ struct xenvif {
|
||||||
struct net_device *dev;
|
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)
|
static inline struct xenbus_device *xenvif_to_xenbus_device(struct xenvif *vif)
|
||||||
{
|
{
|
||||||
return to_xenbus_device(vif->dev->dev.parent);
|
return to_xenbus_device(vif->dev->dev.parent);
|
||||||
|
@ -291,6 +294,8 @@ void xenvif_kick_thread(struct xenvif_queue *queue);
|
||||||
|
|
||||||
int xenvif_dealloc_kthread(void *data);
|
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,
|
/* Determine whether the needed number of slots (req) are available,
|
||||||
* and set req_event if not.
|
* and set req_event if not.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -43,6 +43,9 @@
|
||||||
#define XENVIF_QUEUE_LENGTH 32
|
#define XENVIF_QUEUE_LENGTH 32
|
||||||
#define XENVIF_NAPI_WEIGHT 64
|
#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
|
/* This function is used to set SKBTX_DEV_ZEROCOPY as well as
|
||||||
* increasing the inflight counter. We need to increase the inflight
|
* increasing the inflight counter. We need to increase the inflight
|
||||||
* counter because core driver calls into xenvif_zerocopy_callback
|
* counter because core driver calls into xenvif_zerocopy_callback
|
||||||
|
@ -63,7 +66,8 @@ void xenvif_skb_zerocopy_complete(struct xenvif_queue *queue)
|
||||||
int xenvif_schedulable(struct xenvif *vif)
|
int xenvif_schedulable(struct xenvif *vif)
|
||||||
{
|
{
|
||||||
return netif_running(vif->dev) &&
|
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)
|
static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id)
|
||||||
|
@ -104,16 +108,7 @@ int xenvif_poll(struct napi_struct *napi, int budget)
|
||||||
static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id)
|
static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct xenvif_queue *queue = 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);
|
xenvif_kick_thread(queue);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
@ -141,24 +136,13 @@ void xenvif_wake_queue(struct xenvif_queue *queue)
|
||||||
netif_tx_wake_queue(netdev_get_tx_queue(dev, id));
|
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)
|
static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct xenvif *vif = netdev_priv(dev);
|
struct xenvif *vif = netdev_priv(dev);
|
||||||
struct xenvif_queue *queue = NULL;
|
struct xenvif_queue *queue = NULL;
|
||||||
unsigned int num_queues = vif->num_queues;
|
unsigned int num_queues = vif->num_queues;
|
||||||
u16 index;
|
u16 index;
|
||||||
int min_slots_needed;
|
struct xenvif_rx_cb *cb;
|
||||||
|
|
||||||
BUG_ON(skb->dev != dev);
|
BUG_ON(skb->dev != dev);
|
||||||
|
|
||||||
|
@ -181,30 +165,10 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
!xenvif_schedulable(vif))
|
!xenvif_schedulable(vif))
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
/* At best we'll need one slot for the header and one for each
|
cb = XENVIF_RX_CB(skb);
|
||||||
* frag.
|
cb->expires = jiffies + rx_drain_timeout_jiffies;
|
||||||
*/
|
|
||||||
min_slots_needed = 1 + skb_shinfo(skb)->nr_frags;
|
|
||||||
|
|
||||||
/* If the skb is GSO then we'll also need an extra slot for the
|
xenvif_rx_queue_tail(queue, skb);
|
||||||
* 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_kick_thread(queue);
|
xenvif_kick_thread(queue);
|
||||||
|
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
|
@ -498,6 +462,8 @@ int xenvif_init_queue(struct xenvif_queue *queue)
|
||||||
init_timer(&queue->credit_timeout);
|
init_timer(&queue->credit_timeout);
|
||||||
queue->credit_window_start = get_jiffies_64();
|
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->rx_queue);
|
||||||
skb_queue_head_init(&queue->tx_queue);
|
skb_queue_head_init(&queue->tx_queue);
|
||||||
|
|
||||||
|
@ -529,8 +495,6 @@ int xenvif_init_queue(struct xenvif_queue *queue)
|
||||||
queue->grant_tx_handle[i] = NETBACK_INVALID_HANDLE;
|
queue->grant_tx_handle[i] = NETBACK_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_timer(&queue->rx_stalled);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,7 +628,6 @@ void xenvif_disconnect(struct xenvif *vif)
|
||||||
netif_napi_del(&queue->napi);
|
netif_napi_del(&queue->napi);
|
||||||
|
|
||||||
if (queue->task) {
|
if (queue->task) {
|
||||||
del_timer_sync(&queue->rx_stalled);
|
|
||||||
kthread_stop(queue->task);
|
kthread_stop(queue->task);
|
||||||
queue->task = NULL;
|
queue->task = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,8 @@
|
||||||
bool separate_tx_rx_irq = 1;
|
bool separate_tx_rx_irq = 1;
|
||||||
module_param(separate_tx_rx_irq, bool, 0644);
|
module_param(separate_tx_rx_irq, bool, 0644);
|
||||||
|
|
||||||
/* When guest ring is filled up, qdisc queues the packets for us, but we have
|
/* The time that packets can stay on the guest Rx internal queue
|
||||||
* to timeout them, otherwise other guests' packets can get stuck there
|
* before they are dropped.
|
||||||
*/
|
*/
|
||||||
unsigned int rx_drain_timeout_msecs = 10000;
|
unsigned int rx_drain_timeout_msecs = 10000;
|
||||||
module_param(rx_drain_timeout_msecs, uint, 0444);
|
module_param(rx_drain_timeout_msecs, uint, 0444);
|
||||||
|
@ -83,7 +83,6 @@ static void make_tx_response(struct xenvif_queue *queue,
|
||||||
s8 st);
|
s8 st);
|
||||||
|
|
||||||
static inline int tx_work_todo(struct xenvif_queue *queue);
|
static inline int tx_work_todo(struct xenvif_queue *queue);
|
||||||
static inline int rx_work_todo(struct xenvif_queue *queue);
|
|
||||||
|
|
||||||
static struct xen_netif_rx_response *make_rx_response(struct xenvif_queue *queue,
|
static struct xen_netif_rx_response *make_rx_response(struct xenvif_queue *queue,
|
||||||
u16 id,
|
u16 id,
|
||||||
|
@ -163,6 +162,69 @@ bool xenvif_rx_ring_slots_available(struct xenvif_queue *queue, int needed)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void xenvif_rx_queue_tail(struct xenvif_queue *queue, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&queue->rx_queue.lock, flags);
|
||||||
|
|
||||||
|
__skb_queue_tail(&queue->rx_queue, skb);
|
||||||
|
|
||||||
|
queue->rx_queue_len += skb->len;
|
||||||
|
if (queue->rx_queue_len > queue->rx_queue_max)
|
||||||
|
netif_tx_stop_queue(netdev_get_tx_queue(queue->vif->dev, queue->id));
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&queue->rx_queue.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *xenvif_rx_dequeue(struct xenvif_queue *queue)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
spin_lock_irq(&queue->rx_queue.lock);
|
||||||
|
|
||||||
|
skb = __skb_dequeue(&queue->rx_queue);
|
||||||
|
if (skb)
|
||||||
|
queue->rx_queue_len -= skb->len;
|
||||||
|
|
||||||
|
spin_unlock_irq(&queue->rx_queue.lock);
|
||||||
|
|
||||||
|
return skb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xenvif_rx_queue_maybe_wake(struct xenvif_queue *queue)
|
||||||
|
{
|
||||||
|
spin_lock_irq(&queue->rx_queue.lock);
|
||||||
|
|
||||||
|
if (queue->rx_queue_len < queue->rx_queue_max)
|
||||||
|
netif_tx_wake_queue(netdev_get_tx_queue(queue->vif->dev, queue->id));
|
||||||
|
|
||||||
|
spin_unlock_irq(&queue->rx_queue.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void xenvif_rx_queue_purge(struct xenvif_queue *queue)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
while ((skb = xenvif_rx_dequeue(queue)) != NULL)
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xenvif_rx_queue_drop_expired(struct xenvif_queue *queue)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
skb = skb_peek(&queue->rx_queue);
|
||||||
|
if (!skb)
|
||||||
|
break;
|
||||||
|
if (time_before(jiffies, XENVIF_RX_CB(skb)->expires))
|
||||||
|
break;
|
||||||
|
xenvif_rx_dequeue(queue);
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns true if we should start a new receive buffer instead of
|
* Returns true if we should start a new receive buffer instead of
|
||||||
* adding 'size' bytes to a buffer which currently contains 'offset'
|
* adding 'size' bytes to a buffer which currently contains 'offset'
|
||||||
|
@ -237,13 +299,6 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif_queue *queue,
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct xenvif_rx_cb {
|
|
||||||
int meta_slots_used;
|
|
||||||
bool full_coalesce;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define XENVIF_RX_CB(skb) ((struct xenvif_rx_cb *)(skb)->cb)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the grant operations for this fragment. If it's a flipping
|
* Set up the grant operations for this fragment. If it's a flipping
|
||||||
* interface, we also set up the unmap request from here.
|
* interface, we also set up the unmap request from here.
|
||||||
|
@ -587,7 +642,8 @@ static void xenvif_rx_action(struct xenvif_queue *queue)
|
||||||
|
|
||||||
skb_queue_head_init(&rxq);
|
skb_queue_head_init(&rxq);
|
||||||
|
|
||||||
while ((skb = skb_dequeue(&queue->rx_queue)) != NULL) {
|
while (xenvif_rx_ring_slots_available(queue, XEN_NETBK_RX_SLOTS_MAX)
|
||||||
|
&& (skb = xenvif_rx_dequeue(queue)) != NULL) {
|
||||||
RING_IDX max_slots_needed;
|
RING_IDX max_slots_needed;
|
||||||
RING_IDX old_req_cons;
|
RING_IDX old_req_cons;
|
||||||
RING_IDX ring_slots_used;
|
RING_IDX ring_slots_used;
|
||||||
|
@ -634,15 +690,6 @@ static void xenvif_rx_action(struct xenvif_queue *queue)
|
||||||
skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6))
|
skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6))
|
||||||
max_slots_needed++;
|
max_slots_needed++;
|
||||||
|
|
||||||
/* If the skb may not fit then bail out now */
|
|
||||||
if (!xenvif_rx_ring_slots_available(queue, max_slots_needed)) {
|
|
||||||
skb_queue_head(&queue->rx_queue, skb);
|
|
||||||
need_to_notify = true;
|
|
||||||
queue->rx_last_skb_slots = max_slots_needed;
|
|
||||||
break;
|
|
||||||
} else
|
|
||||||
queue->rx_last_skb_slots = 0;
|
|
||||||
|
|
||||||
old_req_cons = queue->rx.req_cons;
|
old_req_cons = queue->rx.req_cons;
|
||||||
XENVIF_RX_CB(skb)->meta_slots_used = xenvif_gop_skb(skb, &npo, queue);
|
XENVIF_RX_CB(skb)->meta_slots_used = xenvif_gop_skb(skb, &npo, queue);
|
||||||
ring_slots_used = queue->rx.req_cons - old_req_cons;
|
ring_slots_used = queue->rx.req_cons - old_req_cons;
|
||||||
|
@ -1869,12 +1916,6 @@ void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int rx_work_todo(struct xenvif_queue *queue)
|
|
||||||
{
|
|
||||||
return (!skb_queue_empty(&queue->rx_queue) &&
|
|
||||||
xenvif_rx_ring_slots_available(queue, queue->rx_last_skb_slots));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int tx_work_todo(struct xenvif_queue *queue)
|
static inline int tx_work_todo(struct xenvif_queue *queue)
|
||||||
{
|
{
|
||||||
if (likely(RING_HAS_UNCONSUMED_REQUESTS(&queue->tx)))
|
if (likely(RING_HAS_UNCONSUMED_REQUESTS(&queue->tx)))
|
||||||
|
@ -1931,92 +1972,64 @@ int xenvif_map_frontend_rings(struct xenvif_queue *queue,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xenvif_start_queue(struct xenvif_queue *queue)
|
static bool xenvif_have_rx_work(struct xenvif_queue *queue)
|
||||||
{
|
{
|
||||||
if (xenvif_schedulable(queue->vif))
|
return (!skb_queue_empty(&queue->rx_queue)
|
||||||
xenvif_wake_queue(queue);
|
&& xenvif_rx_ring_slots_available(queue, XEN_NETBK_RX_SLOTS_MAX))
|
||||||
|
|| kthread_should_stop()
|
||||||
|
|| queue->vif->disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only called from the queue's thread, it handles the situation when the guest
|
static long xenvif_rx_queue_timeout(struct xenvif_queue *queue)
|
||||||
* doesn't post enough requests on the receiving ring.
|
|
||||||
* First xenvif_start_xmit disables QDisc and start a timer, and then either the
|
|
||||||
* timer fires, or the guest send an interrupt after posting new request. If it
|
|
||||||
* is the timer, the carrier is turned off here.
|
|
||||||
* */
|
|
||||||
static void xenvif_rx_purge_event(struct xenvif_queue *queue)
|
|
||||||
{
|
{
|
||||||
/* Either the last unsuccesful skb or at least 1 slot should fit */
|
struct sk_buff *skb;
|
||||||
int needed = queue->rx_last_skb_slots ?
|
long timeout;
|
||||||
queue->rx_last_skb_slots : 1;
|
|
||||||
|
|
||||||
/* It is assumed that if the guest post new slots after this, the RX
|
skb = skb_peek(&queue->rx_queue);
|
||||||
* interrupt will set the QUEUE_STATUS_RX_PURGE_EVENT bit and wake up
|
if (!skb)
|
||||||
* the thread again
|
return MAX_SCHEDULE_TIMEOUT;
|
||||||
*/
|
|
||||||
set_bit(QUEUE_STATUS_RX_STALLED, &queue->status);
|
|
||||||
if (!xenvif_rx_ring_slots_available(queue, needed)) {
|
|
||||||
rtnl_lock();
|
|
||||||
if (netif_carrier_ok(queue->vif->dev)) {
|
|
||||||
/* Timer fired and there are still no slots. Turn off
|
|
||||||
* everything except the interrupts
|
|
||||||
*/
|
|
||||||
netif_carrier_off(queue->vif->dev);
|
|
||||||
skb_queue_purge(&queue->rx_queue);
|
|
||||||
queue->rx_last_skb_slots = 0;
|
|
||||||
if (net_ratelimit())
|
|
||||||
netdev_err(queue->vif->dev, "Carrier off due to lack of guest response on queue %d\n", queue->id);
|
|
||||||
} else {
|
|
||||||
/* Probably an another queue already turned the carrier
|
|
||||||
* off, make sure nothing is stucked in the internal
|
|
||||||
* queue of this queue
|
|
||||||
*/
|
|
||||||
skb_queue_purge(&queue->rx_queue);
|
|
||||||
queue->rx_last_skb_slots = 0;
|
|
||||||
}
|
|
||||||
rtnl_unlock();
|
|
||||||
} else if (!netif_carrier_ok(queue->vif->dev)) {
|
|
||||||
unsigned int num_queues = queue->vif->num_queues;
|
|
||||||
unsigned int i;
|
|
||||||
/* The carrier was down, but an interrupt kicked
|
|
||||||
* the thread again after new requests were
|
|
||||||
* posted
|
|
||||||
*/
|
|
||||||
clear_bit(QUEUE_STATUS_RX_STALLED,
|
|
||||||
&queue->status);
|
|
||||||
rtnl_lock();
|
|
||||||
netif_carrier_on(queue->vif->dev);
|
|
||||||
netif_tx_wake_all_queues(queue->vif->dev);
|
|
||||||
rtnl_unlock();
|
|
||||||
|
|
||||||
for (i = 0; i < num_queues; i++) {
|
timeout = XENVIF_RX_CB(skb)->expires - jiffies;
|
||||||
struct xenvif_queue *temp = &queue->vif->queues[i];
|
return timeout < 0 ? 0 : timeout;
|
||||||
|
}
|
||||||
|
|
||||||
xenvif_napi_schedule_or_enable_events(temp);
|
/* Wait until the guest Rx thread has work.
|
||||||
}
|
*
|
||||||
if (net_ratelimit())
|
* The timeout needs to be adjusted based on the current head of the
|
||||||
netdev_err(queue->vif->dev, "Carrier on again\n");
|
* queue (and not just the head at the beginning). In particular, if
|
||||||
} else {
|
* the queue is initially empty an infinite timeout is used and this
|
||||||
/* Queuing were stopped, but the guest posted
|
* needs to be reduced when a skb is queued.
|
||||||
* new requests and sent an interrupt
|
*
|
||||||
*/
|
* This cannot be done with wait_event_timeout() because it only
|
||||||
clear_bit(QUEUE_STATUS_RX_STALLED,
|
* calculates the timeout once.
|
||||||
&queue->status);
|
*/
|
||||||
del_timer_sync(&queue->rx_stalled);
|
static void xenvif_wait_for_rx_work(struct xenvif_queue *queue)
|
||||||
xenvif_start_queue(queue);
|
{
|
||||||
|
DEFINE_WAIT(wait);
|
||||||
|
|
||||||
|
if (xenvif_have_rx_work(queue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
prepare_to_wait(&queue->wq, &wait, TASK_INTERRUPTIBLE);
|
||||||
|
if (xenvif_have_rx_work(queue))
|
||||||
|
break;
|
||||||
|
ret = schedule_timeout(xenvif_rx_queue_timeout(queue));
|
||||||
|
if (!ret)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
finish_wait(&queue->wq, &wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
int xenvif_kthread_guest_rx(void *data)
|
int xenvif_kthread_guest_rx(void *data)
|
||||||
{
|
{
|
||||||
struct xenvif_queue *queue = data;
|
struct xenvif_queue *queue = data;
|
||||||
struct sk_buff *skb;
|
struct xenvif *vif = queue->vif;
|
||||||
|
|
||||||
while (!kthread_should_stop()) {
|
for (;;) {
|
||||||
wait_event_interruptible(queue->wq,
|
xenvif_wait_for_rx_work(queue);
|
||||||
rx_work_todo(queue) ||
|
|
||||||
queue->vif->disabled ||
|
|
||||||
test_bit(QUEUE_STATUS_RX_PURGE_EVENT, &queue->status) ||
|
|
||||||
kthread_should_stop());
|
|
||||||
|
|
||||||
if (kthread_should_stop())
|
if (kthread_should_stop())
|
||||||
break;
|
break;
|
||||||
|
@ -2028,35 +2041,29 @@ int xenvif_kthread_guest_rx(void *data)
|
||||||
* context so we defer it here, if this thread is
|
* context so we defer it here, if this thread is
|
||||||
* associated with queue 0.
|
* associated with queue 0.
|
||||||
*/
|
*/
|
||||||
if (unlikely(queue->vif->disabled && queue->id == 0)) {
|
if (unlikely(vif->disabled && queue->id == 0)) {
|
||||||
xenvif_carrier_off(queue->vif);
|
xenvif_carrier_off(vif);
|
||||||
} else if (unlikely(queue->vif->disabled)) {
|
xenvif_rx_queue_purge(queue);
|
||||||
/* kthread_stop() would be called upon this thread soon,
|
continue;
|
||||||
* be a bit proactive
|
|
||||||
*/
|
|
||||||
skb_queue_purge(&queue->rx_queue);
|
|
||||||
queue->rx_last_skb_slots = 0;
|
|
||||||
} else if (unlikely(test_and_clear_bit(QUEUE_STATUS_RX_PURGE_EVENT,
|
|
||||||
&queue->status))) {
|
|
||||||
xenvif_rx_purge_event(queue);
|
|
||||||
} else if (!netif_carrier_ok(queue->vif->dev)) {
|
|
||||||
/* Another queue stalled and turned the carrier off, so
|
|
||||||
* purge the internal queue of queues which were not
|
|
||||||
* blocked
|
|
||||||
*/
|
|
||||||
skb_queue_purge(&queue->rx_queue);
|
|
||||||
queue->rx_last_skb_slots = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skb_queue_empty(&queue->rx_queue))
|
if (!skb_queue_empty(&queue->rx_queue))
|
||||||
xenvif_rx_action(queue);
|
xenvif_rx_action(queue);
|
||||||
|
|
||||||
|
/* Queued packets may have foreign pages from other
|
||||||
|
* domains. These cannot be queued indefinitely as
|
||||||
|
* this would starve guests of grant refs and transmit
|
||||||
|
* slots.
|
||||||
|
*/
|
||||||
|
xenvif_rx_queue_drop_expired(queue);
|
||||||
|
|
||||||
|
xenvif_rx_queue_maybe_wake(queue);
|
||||||
|
|
||||||
cond_resched();
|
cond_resched();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bin any remaining skbs */
|
/* Bin any remaining skbs */
|
||||||
while ((skb = skb_dequeue(&queue->rx_queue)) != NULL)
|
xenvif_rx_queue_purge(queue);
|
||||||
dev_kfree_skb(skb);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ static int xenvif_read_io_ring(struct seq_file *m, void *v)
|
||||||
struct xenvif_queue *queue = m->private;
|
struct xenvif_queue *queue = m->private;
|
||||||
struct xen_netif_tx_back_ring *tx_ring = &queue->tx;
|
struct xen_netif_tx_back_ring *tx_ring = &queue->tx;
|
||||||
struct xen_netif_rx_back_ring *rx_ring = &queue->rx;
|
struct xen_netif_rx_back_ring *rx_ring = &queue->rx;
|
||||||
|
struct netdev_queue *dev_queue;
|
||||||
|
|
||||||
if (tx_ring->sring) {
|
if (tx_ring->sring) {
|
||||||
struct xen_netif_tx_sring *sring = tx_ring->sring;
|
struct xen_netif_tx_sring *sring = tx_ring->sring;
|
||||||
|
@ -112,6 +113,13 @@ static int xenvif_read_io_ring(struct seq_file *m, void *v)
|
||||||
queue->credit_timeout.expires,
|
queue->credit_timeout.expires,
|
||||||
jiffies);
|
jiffies);
|
||||||
|
|
||||||
|
dev_queue = netdev_get_tx_queue(queue->vif->dev, queue->id);
|
||||||
|
|
||||||
|
seq_printf(m, "\nRx internal queue: len %u max %u pkts %u %s\n",
|
||||||
|
queue->rx_queue_len, queue->rx_queue_max,
|
||||||
|
skb_queue_len(&queue->rx_queue),
|
||||||
|
netif_tx_queue_stopped(dev_queue) ? "stopped" : "running");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue