Commit b35507a4 authored by Anton Ivanov's avatar Anton Ivanov Committed by Richard Weinberger

um: Migrate vector drivers to NAPI

Migrate UML vector drivers from a bespoke scheduling mechanism
to NAPI.
Signed-off-by: default avatarAnton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 39508aab
...@@ -67,6 +67,7 @@ static LIST_HEAD(vector_devices); ...@@ -67,6 +67,7 @@ static LIST_HEAD(vector_devices);
static int driver_registered; static int driver_registered;
static void vector_eth_configure(int n, struct arglist *def); static void vector_eth_configure(int n, struct arglist *def);
static int vector_mmsg_rx(struct vector_private *vp, int budget);
/* Argument accessors to set variables (and/or set default values) /* Argument accessors to set variables (and/or set default values)
* mtu, buffer sizing, default headroom, etc * mtu, buffer sizing, default headroom, etc
...@@ -77,7 +78,6 @@ static void vector_eth_configure(int n, struct arglist *def); ...@@ -77,7 +78,6 @@ static void vector_eth_configure(int n, struct arglist *def);
#define DEFAULT_VECTOR_SIZE 64 #define DEFAULT_VECTOR_SIZE 64
#define TX_SMALL_PACKET 128 #define TX_SMALL_PACKET 128
#define MAX_IOV_SIZE (MAX_SKB_FRAGS + 1) #define MAX_IOV_SIZE (MAX_SKB_FRAGS + 1)
#define MAX_ITERATIONS 64
static const struct { static const struct {
const char string[ETH_GSTRING_LEN]; const char string[ETH_GSTRING_LEN];
...@@ -458,7 +458,6 @@ static int vector_send(struct vector_queue *qi) ...@@ -458,7 +458,6 @@ static int vector_send(struct vector_queue *qi)
vp->estats.tx_queue_running_average = vp->estats.tx_queue_running_average =
(vp->estats.tx_queue_running_average + result) >> 1; (vp->estats.tx_queue_running_average + result) >> 1;
} }
netif_trans_update(qi->dev);
netif_wake_queue(qi->dev); netif_wake_queue(qi->dev);
/* if TX is busy, break out of the send loop, /* if TX is busy, break out of the send loop,
* poll write IRQ will reschedule xmit for us * poll write IRQ will reschedule xmit for us
...@@ -470,8 +469,6 @@ static int vector_send(struct vector_queue *qi) ...@@ -470,8 +469,6 @@ static int vector_send(struct vector_queue *qi)
} }
} }
spin_unlock(&qi->head_lock); spin_unlock(&qi->head_lock);
} else {
tasklet_schedule(&vp->tx_poll);
} }
return queue_depth; return queue_depth;
} }
...@@ -608,7 +605,7 @@ static struct vector_queue *create_queue( ...@@ -608,7 +605,7 @@ static struct vector_queue *create_queue(
/* /*
* We do not use the RX queue as a proper wraparound queue for now * We do not use the RX queue as a proper wraparound queue for now
* This is not necessary because the consumption via netif_rx() * This is not necessary because the consumption via napi_gro_receive()
* happens in-line. While we can try using the return code of * happens in-line. While we can try using the return code of
* netif_rx() for flow control there are no drivers doing this today. * netif_rx() for flow control there are no drivers doing this today.
* For this RX specific use we ignore the tail/head locks and * For this RX specific use we ignore the tail/head locks and
...@@ -896,7 +893,7 @@ static int vector_legacy_rx(struct vector_private *vp) ...@@ -896,7 +893,7 @@ static int vector_legacy_rx(struct vector_private *vp)
skb->protocol = eth_type_trans(skb, skb->dev); skb->protocol = eth_type_trans(skb, skb->dev);
vp->dev->stats.rx_bytes += skb->len; vp->dev->stats.rx_bytes += skb->len;
vp->dev->stats.rx_packets++; vp->dev->stats.rx_packets++;
netif_rx(skb); napi_gro_receive(&vp->napi, skb);
} else { } else {
dev_kfree_skb_irq(skb); dev_kfree_skb_irq(skb);
} }
...@@ -955,7 +952,7 @@ static int writev_tx(struct vector_private *vp, struct sk_buff *skb) ...@@ -955,7 +952,7 @@ static int writev_tx(struct vector_private *vp, struct sk_buff *skb)
* mmsg vector matched to an skb vector which we prepared earlier. * mmsg vector matched to an skb vector which we prepared earlier.
*/ */
static int vector_mmsg_rx(struct vector_private *vp) static int vector_mmsg_rx(struct vector_private *vp, int budget)
{ {
int packet_count, i; int packet_count, i;
struct vector_queue *qi = vp->rx_queue; struct vector_queue *qi = vp->rx_queue;
...@@ -972,6 +969,9 @@ static int vector_mmsg_rx(struct vector_private *vp) ...@@ -972,6 +969,9 @@ static int vector_mmsg_rx(struct vector_private *vp)
/* Fire the Lazy Gun - get as many packets as we can in one go. */ /* Fire the Lazy Gun - get as many packets as we can in one go. */
if (budget > qi->max_depth)
budget = qi->max_depth;
packet_count = uml_vector_recvmmsg( packet_count = uml_vector_recvmmsg(
vp->fds->rx_fd, qi->mmsg_vector, qi->max_depth, 0); vp->fds->rx_fd, qi->mmsg_vector, qi->max_depth, 0);
...@@ -1021,7 +1021,7 @@ static int vector_mmsg_rx(struct vector_private *vp) ...@@ -1021,7 +1021,7 @@ static int vector_mmsg_rx(struct vector_private *vp)
*/ */
vp->dev->stats.rx_bytes += skb->len; vp->dev->stats.rx_bytes += skb->len;
vp->dev->stats.rx_packets++; vp->dev->stats.rx_packets++;
netif_rx(skb); napi_gro_receive(&vp->napi, skb);
} else { } else {
/* Overlay header too short to do anything - discard. /* Overlay header too short to do anything - discard.
* We can actually keep this skb and reuse it, * We can actually keep this skb and reuse it,
...@@ -1044,23 +1044,6 @@ static int vector_mmsg_rx(struct vector_private *vp) ...@@ -1044,23 +1044,6 @@ static int vector_mmsg_rx(struct vector_private *vp)
return packet_count; return packet_count;
} }
static void vector_rx(struct vector_private *vp)
{
int err;
int iter = 0;
if ((vp->options & VECTOR_RX) > 0)
while (((err = vector_mmsg_rx(vp)) > 0) && (iter < MAX_ITERATIONS))
iter++;
else
while (((err = vector_legacy_rx(vp)) > 0) && (iter < MAX_ITERATIONS))
iter++;
if ((err != 0) && net_ratelimit())
netdev_err(vp->dev, "vector_rx: error(%d)\n", err);
if (iter == MAX_ITERATIONS)
netdev_err(vp->dev, "vector_rx: device stuck, remote end may have closed the connection\n");
}
static int vector_net_start_xmit(struct sk_buff *skb, struct net_device *dev) static int vector_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct vector_private *vp = netdev_priv(dev); struct vector_private *vp = netdev_priv(dev);
...@@ -1085,25 +1068,15 @@ static int vector_net_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1085,25 +1068,15 @@ static int vector_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
netdev_sent_queue(vp->dev, skb->len); netdev_sent_queue(vp->dev, skb->len);
queue_depth = vector_enqueue(vp->tx_queue, skb); queue_depth = vector_enqueue(vp->tx_queue, skb);
/* if the device queue is full, stop the upper layers and if (queue_depth < vp->tx_queue->max_depth && netdev_xmit_more()) {
* flush it.
*/
if (queue_depth >= vp->tx_queue->max_depth - 1) {
vp->estats.tx_kicks++;
netif_stop_queue(dev);
vector_send(vp->tx_queue);
return NETDEV_TX_OK;
}
if (netdev_xmit_more()) {
mod_timer(&vp->tl, vp->coalesce); mod_timer(&vp->tl, vp->coalesce);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} else {
queue_depth = vector_send(vp->tx_queue);
if (queue_depth > 0)
napi_schedule(&vp->napi);
} }
if (skb->len < TX_SMALL_PACKET) {
vp->estats.tx_kicks++;
vector_send(vp->tx_queue);
} else
tasklet_schedule(&vp->tx_poll);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
...@@ -1114,7 +1087,7 @@ static irqreturn_t vector_rx_interrupt(int irq, void *dev_id) ...@@ -1114,7 +1087,7 @@ static irqreturn_t vector_rx_interrupt(int irq, void *dev_id)
if (!netif_running(dev)) if (!netif_running(dev))
return IRQ_NONE; return IRQ_NONE;
vector_rx(vp); napi_schedule(&vp->napi);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1133,8 +1106,7 @@ static irqreturn_t vector_tx_interrupt(int irq, void *dev_id) ...@@ -1133,8 +1106,7 @@ static irqreturn_t vector_tx_interrupt(int irq, void *dev_id)
* tweaking the IRQ mask less costly * tweaking the IRQ mask less costly
*/ */
if (vp->in_write_poll) napi_schedule(&vp->napi);
tasklet_schedule(&vp->tx_poll);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1161,7 +1133,8 @@ static int vector_net_close(struct net_device *dev) ...@@ -1161,7 +1133,8 @@ static int vector_net_close(struct net_device *dev)
um_free_irq(vp->tx_irq, dev); um_free_irq(vp->tx_irq, dev);
vp->tx_irq = 0; vp->tx_irq = 0;
} }
tasklet_kill(&vp->tx_poll); napi_disable(&vp->napi);
netif_napi_del(&vp->napi);
if (vp->fds->rx_fd > 0) { if (vp->fds->rx_fd > 0) {
if (vp->bpf) if (vp->bpf)
uml_vector_detach_bpf(vp->fds->rx_fd, vp->bpf); uml_vector_detach_bpf(vp->fds->rx_fd, vp->bpf);
...@@ -1193,15 +1166,32 @@ static int vector_net_close(struct net_device *dev) ...@@ -1193,15 +1166,32 @@ static int vector_net_close(struct net_device *dev)
return 0; return 0;
} }
/* TX tasklet */ static int vector_poll(struct napi_struct *napi, int budget)
static void vector_tx_poll(struct tasklet_struct *t)
{ {
struct vector_private *vp = from_tasklet(vp, t, tx_poll); struct vector_private *vp = container_of(napi, struct vector_private, napi);
int work_done = 0;
int err;
bool tx_enqueued = false;
vp->estats.tx_kicks++; if ((vp->options & VECTOR_TX) != 0)
vector_send(vp->tx_queue); tx_enqueued = (vector_send(vp->tx_queue) > 0);
if ((vp->options & VECTOR_RX) > 0)
err = vector_mmsg_rx(vp, budget);
else {
err = vector_legacy_rx(vp);
if (err > 0)
err = 1;
}
if (err > 0)
work_done += err;
if (tx_enqueued || err > 0)
napi_schedule(napi);
if (work_done < budget)
napi_complete_done(napi, work_done);
return work_done;
} }
static void vector_reset_tx(struct work_struct *work) static void vector_reset_tx(struct work_struct *work)
{ {
struct vector_private *vp = struct vector_private *vp =
...@@ -1265,6 +1255,9 @@ static int vector_net_open(struct net_device *dev) ...@@ -1265,6 +1255,9 @@ static int vector_net_open(struct net_device *dev)
goto out_close; goto out_close;
} }
netif_napi_add(vp->dev, &vp->napi, vector_poll, get_depth(vp->parsed));
napi_enable(&vp->napi);
/* READ IRQ */ /* READ IRQ */
err = um_request_irq( err = um_request_irq(
irq_rr + VECTOR_BASE_IRQ, vp->fds->rx_fd, irq_rr + VECTOR_BASE_IRQ, vp->fds->rx_fd,
...@@ -1306,15 +1299,15 @@ static int vector_net_open(struct net_device *dev) ...@@ -1306,15 +1299,15 @@ static int vector_net_open(struct net_device *dev)
uml_vector_attach_bpf(vp->fds->rx_fd, vp->bpf); uml_vector_attach_bpf(vp->fds->rx_fd, vp->bpf);
netif_start_queue(dev); netif_start_queue(dev);
vector_reset_stats(vp);
/* clear buffer - it can happen that the host side of the interface /* clear buffer - it can happen that the host side of the interface
* is full when we get here. In this case, new data is never queued, * is full when we get here. In this case, new data is never queued,
* SIGIOs never arrive, and the net never works. * SIGIOs never arrive, and the net never works.
*/ */
vector_rx(vp); napi_schedule(&vp->napi);
vector_reset_stats(vp);
vdevice = find_device(vp->unit); vdevice = find_device(vp->unit);
vdevice->opened = 1; vdevice->opened = 1;
...@@ -1543,15 +1536,16 @@ static const struct net_device_ops vector_netdev_ops = { ...@@ -1543,15 +1536,16 @@ static const struct net_device_ops vector_netdev_ops = {
#endif #endif
}; };
static void vector_timer_expire(struct timer_list *t) static void vector_timer_expire(struct timer_list *t)
{ {
struct vector_private *vp = from_timer(vp, t, tl); struct vector_private *vp = from_timer(vp, t, tl);
vp->estats.tx_kicks++; vp->estats.tx_kicks++;
vector_send(vp->tx_queue); napi_schedule(&vp->napi);
} }
static void vector_eth_configure( static void vector_eth_configure(
int n, int n,
struct arglist *def struct arglist *def
...@@ -1634,7 +1628,6 @@ static void vector_eth_configure( ...@@ -1634,7 +1628,6 @@ static void vector_eth_configure(
}); });
dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST); dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST);
tasklet_setup(&vp->tx_poll, vector_tx_poll);
INIT_WORK(&vp->reset_tx, vector_reset_tx); INIT_WORK(&vp->reset_tx, vector_reset_tx);
timer_setup(&vp->tl, vector_timer_expire, 0); timer_setup(&vp->tl, vector_timer_expire, 0);
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include "vector_user.h" #include "vector_user.h"
/* Queue structure specially adapted for multiple enqueue/dequeue /* Queue structure specially adapted for multiple enqueue/dequeue
...@@ -72,6 +73,7 @@ struct vector_private { ...@@ -72,6 +73,7 @@ struct vector_private {
struct list_head list; struct list_head list;
spinlock_t lock; spinlock_t lock;
struct net_device *dev; struct net_device *dev;
struct napi_struct napi ____cacheline_aligned;
int unit; int unit;
...@@ -115,7 +117,6 @@ struct vector_private { ...@@ -115,7 +117,6 @@ struct vector_private {
spinlock_t stats_lock; spinlock_t stats_lock;
struct tasklet_struct tx_poll;
bool rexmit_scheduled; bool rexmit_scheduled;
bool opened; bool opened;
bool in_write_poll; bool in_write_poll;
......
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