summaryrefslogtreecommitdiff
path: root/drivers/net/xen-netback
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/xen-netback')
-rw-r--r--drivers/net/xen-netback/common.h4
-rw-r--r--drivers/net/xen-netback/interface.c6
-rw-r--r--drivers/net/xen-netback/netback.c54
-rw-r--r--drivers/net/xen-netback/xenbus.c57
4 files changed, 98 insertions, 23 deletions
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 589fa256256b..8a495b318b6f 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -238,6 +238,8 @@ struct xenvif {
unsigned int num_queues; /* active queues, resource allocated */
unsigned int stalled_queues;
+ struct xenbus_watch credit_watch;
+
spinlock_t lock;
#ifdef CONFIG_DEBUG_FS
@@ -260,6 +262,8 @@ static inline struct xenbus_device *xenvif_to_xenbus_device(struct xenvif *vif)
return to_xenbus_device(vif->dev->dev.parent);
}
+void xenvif_tx_credit_callback(unsigned long data);
+
struct xenvif *xenvif_alloc(struct device *parent,
domid_t domid,
unsigned int handle);
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index f38227afe099..1a83e190fc15 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -340,12 +340,11 @@ static void xenvif_get_ethtool_stats(struct net_device *dev,
unsigned int num_queues = vif->num_queues;
int i;
unsigned int queue_index;
- struct xenvif_stats *vif_stats;
for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) {
unsigned long accum = 0;
for (queue_index = 0; queue_index < num_queues; ++queue_index) {
- vif_stats = &vif->queues[queue_index].stats;
+ void *vif_stats = &vif->queues[queue_index].stats;
accum += *(unsigned long *)(vif_stats + xenvif_stats[i].offset);
}
data[i] = accum;
@@ -438,7 +437,7 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
* stolen by an Ethernet bridge for STP purposes.
* (FE:FF:FF:FF:FF:FF)
*/
- memset(dev->dev_addr, 0xFF, ETH_ALEN);
+ eth_broadcast_addr(dev->dev_addr);
dev->dev_addr[0] &= ~0x01;
netif_carrier_off(dev);
@@ -464,6 +463,7 @@ int xenvif_init_queue(struct xenvif_queue *queue)
queue->credit_bytes = queue->remaining_credit = ~0UL;
queue->credit_usec = 0UL;
init_timer(&queue->credit_timeout);
+ queue->credit_timeout.function = xenvif_tx_credit_callback;
queue->credit_window_start = get_jiffies_64();
queue->rx_queue_max = XENVIF_RX_QUEUE_BYTES;
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index f7a31d2cb3f1..4de46aa61d95 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -96,6 +96,7 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx,
static void make_tx_response(struct xenvif_queue *queue,
struct xen_netif_tx_request *txp,
s8 st);
+static void push_tx_responses(struct xenvif_queue *queue);
static inline int tx_work_todo(struct xenvif_queue *queue);
@@ -641,7 +642,7 @@ static void tx_add_credit(struct xenvif_queue *queue)
queue->remaining_credit = min(max_credit, max_burst);
}
-static void tx_credit_callback(unsigned long data)
+void xenvif_tx_credit_callback(unsigned long data)
{
struct xenvif_queue *queue = (struct xenvif_queue *)data;
tx_add_credit(queue);
@@ -657,6 +658,7 @@ static void xenvif_tx_err(struct xenvif_queue *queue,
do {
spin_lock_irqsave(&queue->response_lock, flags);
make_tx_response(queue, txp, XEN_NETIF_RSP_ERROR);
+ push_tx_responses(queue);
spin_unlock_irqrestore(&queue->response_lock, flags);
if (cons == end)
break;
@@ -1163,8 +1165,6 @@ static bool tx_credit_exceeded(struct xenvif_queue *queue, unsigned size)
if (size > queue->remaining_credit) {
queue->credit_timeout.data =
(unsigned long)queue;
- queue->credit_timeout.function =
- tx_credit_callback;
mod_timer(&queue->credit_timeout,
next_credit);
queue->credit_window_start = next_credit;
@@ -1343,7 +1343,7 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s
{
unsigned int offset = skb_headlen(skb);
skb_frag_t frags[MAX_SKB_FRAGS];
- int i;
+ int i, f;
struct ubuf_info *uarg;
struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
@@ -1383,23 +1383,25 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s
frags[i].page_offset = 0;
skb_frag_size_set(&frags[i], len);
}
- /* swap out with old one */
- memcpy(skb_shinfo(skb)->frags,
- frags,
- i * sizeof(skb_frag_t));
- skb_shinfo(skb)->nr_frags = i;
- skb->truesize += i * PAGE_SIZE;
- /* remove traces of mapped pages and frag_list */
+ /* Copied all the bits from the frag list -- free it. */
skb_frag_list_init(skb);
+ xenvif_skb_zerocopy_prepare(queue, nskb);
+ kfree_skb(nskb);
+
+ /* Release all the original (foreign) frags. */
+ for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
+ skb_frag_unref(skb, f);
uarg = skb_shinfo(skb)->destructor_arg;
/* increase inflight counter to offset decrement in callback */
atomic_inc(&queue->inflight_packets);
uarg->callback(uarg, true);
skb_shinfo(skb)->destructor_arg = NULL;
- xenvif_skb_zerocopy_prepare(queue, nskb);
- kfree_skb(nskb);
+ /* Fill the skb with the new (local) frags. */
+ memcpy(skb_shinfo(skb)->frags, frags, i * sizeof(skb_frag_t));
+ skb_shinfo(skb)->nr_frags = i;
+ skb->truesize += i * PAGE_SIZE;
return 0;
}
@@ -1652,13 +1654,20 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx,
unsigned long flags;
pending_tx_info = &queue->pending_tx_info[pending_idx];
+
spin_lock_irqsave(&queue->response_lock, flags);
+
make_tx_response(queue, &pending_tx_info->req, status);
- index = pending_index(queue->pending_prod);
+
+ /* Release the pending index before pusing the Tx response so
+ * its available before a new Tx request is pushed by the
+ * frontend.
+ */
+ index = pending_index(queue->pending_prod++);
queue->pending_ring[index] = pending_idx;
- /* TX shouldn't use the index before we give it back here */
- mb();
- queue->pending_prod++;
+
+ push_tx_responses(queue);
+
spin_unlock_irqrestore(&queue->response_lock, flags);
}
@@ -1669,7 +1678,6 @@ static void make_tx_response(struct xenvif_queue *queue,
{
RING_IDX i = queue->tx.rsp_prod_pvt;
struct xen_netif_tx_response *resp;
- int notify;
resp = RING_GET_RESPONSE(&queue->tx, i);
resp->id = txp->id;
@@ -1679,6 +1687,12 @@ static void make_tx_response(struct xenvif_queue *queue,
RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL;
queue->tx.rsp_prod_pvt = ++i;
+}
+
+static void push_tx_responses(struct xenvif_queue *queue)
+{
+ int notify;
+
RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify);
if (notify)
notify_remote_via_irq(queue->tx_irq);
@@ -1766,7 +1780,7 @@ int xenvif_map_frontend_rings(struct xenvif_queue *queue,
int err = -ENOMEM;
err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif),
- tx_ring_ref, &addr);
+ &tx_ring_ref, 1, &addr);
if (err)
goto err;
@@ -1774,7 +1788,7 @@ int xenvif_map_frontend_rings(struct xenvif_queue *queue,
BACK_RING_INIT(&queue->tx, txs, PAGE_SIZE);
err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif),
- rx_ring_ref, &addr);
+ &rx_ring_ref, 1, &addr);
if (err)
goto err;
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index 794204e34fba..3d8dbf5f2d39 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -41,6 +41,7 @@ static void connect(struct backend_info *be);
static int read_xenbus_vif_flags(struct backend_info *be);
static int backend_create_xenvif(struct backend_info *be);
static void unregister_hotplug_status_watch(struct backend_info *be);
+static void xen_unregister_watchers(struct xenvif *vif);
static void set_backend_state(struct backend_info *be,
enum xenbus_state state);
@@ -232,6 +233,7 @@ static int netback_remove(struct xenbus_device *dev)
unregister_hotplug_status_watch(be);
if (be->vif) {
kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
+ xen_unregister_watchers(be->vif);
xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status");
xenvif_free(be->vif);
be->vif = NULL;
@@ -430,6 +432,7 @@ static int backend_create_xenvif(struct backend_info *be)
static void backend_disconnect(struct backend_info *be)
{
if (be->vif) {
+ xen_unregister_watchers(be->vif);
#ifdef CONFIG_DEBUG_FS
xenvif_debugfs_delif(be->vif);
#endif /* CONFIG_DEBUG_FS */
@@ -645,6 +648,59 @@ static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[])
return 0;
}
+static void xen_net_rate_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ struct xenvif *vif = container_of(watch, struct xenvif, credit_watch);
+ struct xenbus_device *dev = xenvif_to_xenbus_device(vif);
+ unsigned long credit_bytes;
+ unsigned long credit_usec;
+ unsigned int queue_index;
+
+ xen_net_read_rate(dev, &credit_bytes, &credit_usec);
+ for (queue_index = 0; queue_index < vif->num_queues; queue_index++) {
+ struct xenvif_queue *queue = &vif->queues[queue_index];
+
+ queue->credit_bytes = credit_bytes;
+ queue->credit_usec = credit_usec;
+ if (!mod_timer_pending(&queue->credit_timeout, jiffies) &&
+ queue->remaining_credit > queue->credit_bytes) {
+ queue->remaining_credit = queue->credit_bytes;
+ }
+ }
+}
+
+static int xen_register_watchers(struct xenbus_device *dev, struct xenvif *vif)
+{
+ int err = 0;
+ char *node;
+ unsigned maxlen = strlen(dev->nodename) + sizeof("/rate");
+
+ node = kmalloc(maxlen, GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+ snprintf(node, maxlen, "%s/rate", dev->nodename);
+ vif->credit_watch.node = node;
+ vif->credit_watch.callback = xen_net_rate_changed;
+ err = register_xenbus_watch(&vif->credit_watch);
+ if (err) {
+ pr_err("Failed to set watcher %s\n", vif->credit_watch.node);
+ kfree(node);
+ vif->credit_watch.node = NULL;
+ vif->credit_watch.callback = NULL;
+ }
+ return err;
+}
+
+static void xen_unregister_watchers(struct xenvif *vif)
+{
+ if (vif->credit_watch.node) {
+ unregister_xenbus_watch(&vif->credit_watch);
+ kfree(vif->credit_watch.node);
+ vif->credit_watch.node = NULL;
+ }
+}
+
static void unregister_hotplug_status_watch(struct backend_info *be)
{
if (be->have_hotplug_status_watch) {
@@ -709,6 +765,7 @@ static void connect(struct backend_info *be)
}
xen_net_read_rate(dev, &credit_bytes, &credit_usec);
+ xen_register_watchers(dev, be->vif);
read_xenbus_vif_flags(be);
/* Use the number of queues requested by the frontend */