[NET PATCH 8/9] venet: add a tx-complete event for out-of-order support

Gregory Haskins
2009-10-14T16:09:58Z

This paves the way for zero-copy support since we cannot predict
the order in which paged-skbs may actually be consumed.

Signed-off-by: Gregory Haskins <ghaskins@novell.com>
---

drivers/net/vbus-enet.c | 77 ++++++++++++++++++++++++++++++++++++++---------
include/linux/venet.h | 8 +++++
2 files changed, 70 insertions(+), 15 deletions(-)

diff --git a/drivers/net/vbus-enet.c b/drivers/net/vbus-enet.c
index 3032169..e8a0553 100644
--- a/drivers/net/vbus-enet.c
+++ b/drivers/net/vbus-enet.c
@@ -72,6 +72,7 @@ struct vbus_enet_priv {
struct {
bool enabled;
bool linkstate;
+ bool txc;
unsigned long evsize;
struct vbus_enet_queue veq;
struct tasklet_struct task;
@@ -649,6 +650,17 @@ vbus_enet_tx_start(struct sk_buff *skb, struct net_device *dev)
return 0;
}

+/* assumes priv->lock held */
+static void
+vbus_enet_skb_complete(struct vbus_enet_priv *priv, struct sk_buff *skb)
+{
+ PDEBUG(priv->dev, "completed sending %d bytes\n",
+ skb->len);
+
+ __skb_unlink(skb, &priv->tx.outstanding);
+ dev_kfree_skb(skb);
+}
+
/*
* reclaim any outstanding completed tx packets
*
@@ -677,26 +689,28 @@ vbus_enet_tx_reap(struct vbus_enet_priv *priv)
* owned by the south-side
*/
while (iter.desc->valid && !iter.desc->sown) {
- struct sk_buff *skb;

- if (priv->sg) {
- struct venet_sg *vsg;
+ if (!priv->evq.txc) {
+ struct sk_buff *skb;

- vsg = (struct venet_sg *)iter.desc->cookie;
- skb = (struct sk_buff *)vsg->cookie;
+ if (priv->sg) {
+ struct venet_sg *vsg;

- } else {
- skb = (struct sk_buff *)iter.desc->cookie;
- }
+ vsg = (struct venet_sg *)iter.desc->cookie;
+ skb = (struct sk_buff *)vsg->cookie;
+ } else
+ skb = (struct sk_buff *)iter.desc->cookie;

- PDEBUG(priv->dev, "completed sending %d bytes\n", skb->len);
+ /*
+ * If TXC is not enabled, we are required to free
+ * the buffer resources now
+ */
+ vbus_enet_skb_complete(priv, skb);
+ }

/* Reset the descriptor */
iter.desc->valid = 0;

- __skb_unlink(skb, &priv->tx.outstanding);
- dev_kfree_skb(skb);
-
/* Advance the valid-index head */
ret = ioq_iter_pop(&iter, 0);
BUG_ON(ret < 0);
@@ -787,6 +801,22 @@ evq_linkstate_event(struct vbus_enet_priv *priv,
}

static void
+evq_txc_event(struct vbus_enet_priv *priv,
+ struct venet_event_header *header)
+{
+ struct venet_event_txc *event =
+ (struct venet_event_txc *)header;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ vbus_enet_tx_reap(priv);
+ vbus_enet_skb_complete(priv, (struct sk_buff *)event->cookie);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void
deferred_evq_isr(unsigned long data)
{
struct vbus_enet_priv *priv = (struct vbus_enet_priv *)data;
@@ -817,6 +847,9 @@ deferred_evq_isr(unsigned long data)
case VENET_EVENT_LINKSTATE:
evq_linkstate_event(priv, header);
break;
+ case VENET_EVENT_TXC:
+ evq_txc_event(priv, header);
+ break;
default:
panic("venet: unexpected event id:%d of size %d\n",
header->id, header->size);
@@ -901,6 +934,7 @@ vbus_enet_evq_negcap(struct vbus_enet_priv *priv, unsigned long count)

caps.gid = VENET_CAP_GROUP_EVENTQ;
caps.bits |= VENET_CAP_EVQ_LINKSTATE;
+ caps.bits |= VENET_CAP_EVQ_TXC;

ret = devcall(priv, VENET_FUNC_NEGCAP, &caps, sizeof(caps));
if (ret < 0)
@@ -925,6 +959,9 @@ vbus_enet_evq_negcap(struct vbus_enet_priv *priv, unsigned long count)
priv->evq.linkstate = true;
}

+ if (caps.bits & VENET_CAP_EVQ_TXC)
+ priv->evq.txc = true;
+
memset(&query, 0, sizeof(query));

ret = devcall(priv, VENET_FUNC_EVQQUERY, &query, sizeof(query));
@@ -1051,7 +1088,6 @@ vbus_enet_probe(struct vbus_device_proxy *vdev)
goto out_free;
}

- tasklet_init(&priv->tx.task, deferred_tx_isr, (unsigned long)priv);
skb_queue_head_init(&priv->tx.outstanding);

queue_init(priv, &priv->rxq, VENET_QUEUE_RX, rx_ringlen, rx_isr);
@@ -1060,8 +1096,19 @@ vbus_enet_probe(struct vbus_device_proxy *vdev)
rx_setup(priv);
tx_setup(priv);

- ioq_notify_enable(priv->rxq.queue, 0); /* enable interrupts */
- ioq_notify_enable(priv->tx.veq.queue, 0);
+ ioq_notify_enable(priv->rxq.queue, 0); /* enable rx interrupts */
+
+ if (!priv->evq.txc) {
+ /*
+ * If the TXC feature is present, we will recieve our
+ * tx-complete notification via the event-channel. Therefore,
+ * we only enable txq interrupts if the TXC feature is not
+ * present.
+ */
+ tasklet_init(&priv->tx.task, deferred_tx_isr,
+ (unsigned long)priv);
+ ioq_notify_enable(priv->tx.veq.queue, 0);
+ }

dev->netdev_ops = &vbus_enet_netdev_ops;
dev->watchdog_timeo = 5 * HZ;
diff --git a/include/linux/venet.h b/include/linux/venet.h
index 16b0156..b6bfd91 100644
--- a/include/linux/venet.h
+++ b/include/linux/venet.h
@@ -50,6 +50,7 @@ struct venet_capabilities {

/* CAPABILITIES-GROUP EVENTQ */
#define VENET_CAP_EVQ_LINKSTATE (1 << 0)
+#define VENET_CAP_EVQ_TXC (1 << 1) /* tx-complete */

struct venet_iov {
__u32 len;
@@ -89,6 +90,7 @@ struct venet_eventq_query {
};

#define VENET_EVENT_LINKSTATE 0
+#define VENET_EVENT_TXC 1

struct venet_event_header {
__u32 flags;
@@ -101,6 +103,12 @@ struct venet_event_linkstate {
__u8 state; /* 0 = down, 1 = up */
};

+struct venet_event_txc {
+ struct venet_event_header header;
+ __u32 txqid;
+ __u64 cookie;
+};
+
#define VSG_DESC_SIZE(count) (sizeof(struct venet_sg) + \
sizeof(struct venet_iov) * ((count) - 1))