* [PATCH net-next 0/3] xen-netback: switch to NAPI + kthread 1:1 model
From: Wei Liu @ 2013-08-26 11:59 UTC (permalink / raw)
To: netdev; +Cc: xen-devel, ian.campbell, msw, konrad.wilk, annie.li, Wei Liu
This series implements NAPI + kthread 1:1 model for Xen netback.
This model
- provides better scheduling fairness among vifs
- is prerequisite for implementing multiqueue for Xen network driver
The second patch has the real meat:
- make use of NAPI to mitigate interrupt
- kthreads are not bound to CPUs any more, so that we can take
advantage of backend scheduler and trust it to do the right thing
Benchmark is done on a Dell T3400 workstation with 4 cores, running 4
DomUs. Netserver runs in Dom0. DomUs do netperf to Dom0 with
following command: /root/netperf -H Dom0 -fm -l120
IRQs are distributed to 4 cores by hand in the new model, while in the
old model vifs are automatically distributed to 4 kthreads.
* New model
%Cpu0 : 0.5 us, 20.3 sy, 0.0 ni, 28.9 id, 0.0 wa, 0.0 hi, 24.4 si, 25.9 st
%Cpu1 : 0.5 us, 17.8 sy, 0.0 ni, 28.8 id, 0.0 wa, 0.0 hi, 27.7 si, 25.1 st
%Cpu2 : 0.5 us, 18.8 sy, 0.0 ni, 30.7 id, 0.0 wa, 0.0 hi, 22.9 si, 27.1 st
%Cpu3 : 0.0 us, 20.1 sy, 0.0 ni, 30.4 id, 0.0 wa, 0.0 hi, 22.7 si, 26.8 st
Throughputs: 2027.89 2025.95 2018.57 2016.23 aggregated: 8088.64
* Old model
%Cpu0 : 0.5 us, 68.8 sy, 0.0 ni, 16.1 id, 0.5 wa, 0.0 hi, 2.8 si, 11.5 st
%Cpu1 : 0.4 us, 45.1 sy, 0.0 ni, 31.1 id, 0.4 wa, 0.0 hi, 2.1 si, 20.9 st
%Cpu2 : 0.9 us, 44.8 sy, 0.0 ni, 30.9 id, 0.0 wa, 0.0 hi, 1.3 si, 22.2 st
%Cpu3 : 0.8 us, 46.4 sy, 0.0 ni, 28.3 id, 1.3 wa, 0.0 hi, 2.1 si, 21.1 st
Throughputs: 1899.14 2280.43 1963.33 1893.47 aggregated: 8036.37
We can see that the impact is mainly on CPU usage. The new model moves
processing from kthread to NAPI (software interrupt).
Wei Liu (3):
xen-netback: remove page tracking facility
xen-netback: switch to NAPI + kthread 1:1 model
xen-netback: rename functions
drivers/net/xen-netback/common.h | 150 +++++--
drivers/net/xen-netback/interface.c | 135 ++++--
drivers/net/xen-netback/netback.c | 833 +++++++++++------------------------
3 files changed, 445 insertions(+), 673 deletions(-)
--
1.7.10.4
^ permalink raw reply
* [PATCH net-next 1/3] xen-netback: remove page tracking facility
From: Wei Liu @ 2013-08-26 11:59 UTC (permalink / raw)
To: netdev; +Cc: xen-devel, ian.campbell, msw, konrad.wilk, annie.li, Wei Liu
In-Reply-To: <1377518379-26503-1-git-send-email-wei.liu2@citrix.com>
The data flow from DomU to DomU on the same host in current copying
scheme with tracking facility:
copy
DomU --------> Dom0 DomU
| ^
|____________________________|
copy
The page in Dom0 is a page with valid MFN. So we can always copy from
page Dom0, thus removing the need for a tracking facility.
copy copy
DomU --------> Dom0 -------> DomU
Simple iperf test shows no performance regression (obviously we copy
twice either way):
W/ tracking: ~5.3Gb/s
W/o tracking: ~5.4Gb/s
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
Acked-by: Matt Wilson <msw@amazon.com>
---
drivers/net/xen-netback/netback.c | 77 +------------------------------------
1 file changed, 2 insertions(+), 75 deletions(-)
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 64828de..91f163d 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -95,21 +95,6 @@ struct netbk_rx_meta {
#define MAX_BUFFER_OFFSET PAGE_SIZE
-/* extra field used in struct page */
-union page_ext {
- struct {
-#if BITS_PER_LONG < 64
-#define IDX_WIDTH 8
-#define GROUP_WIDTH (BITS_PER_LONG - IDX_WIDTH)
- unsigned int group:GROUP_WIDTH;
- unsigned int idx:IDX_WIDTH;
-#else
- unsigned int group, idx;
-#endif
- } e;
- void *mapping;
-};
-
struct xen_netbk {
wait_queue_head_t wq;
struct task_struct *task;
@@ -214,45 +199,6 @@ static inline unsigned long idx_to_kaddr(struct xen_netbk *netbk,
return (unsigned long)pfn_to_kaddr(idx_to_pfn(netbk, idx));
}
-/* extra field used in struct page */
-static inline void set_page_ext(struct page *pg, struct xen_netbk *netbk,
- unsigned int idx)
-{
- unsigned int group = netbk - xen_netbk;
- union page_ext ext = { .e = { .group = group + 1, .idx = idx } };
-
- BUILD_BUG_ON(sizeof(ext) > sizeof(ext.mapping));
- pg->mapping = ext.mapping;
-}
-
-static int get_page_ext(struct page *pg,
- unsigned int *pgroup, unsigned int *pidx)
-{
- union page_ext ext = { .mapping = pg->mapping };
- struct xen_netbk *netbk;
- unsigned int group, idx;
-
- group = ext.e.group - 1;
-
- if (group < 0 || group >= xen_netbk_group_nr)
- return 0;
-
- netbk = &xen_netbk[group];
-
- idx = ext.e.idx;
-
- if ((idx < 0) || (idx >= MAX_PENDING_REQS))
- return 0;
-
- if (netbk->mmap_pages[idx] != pg)
- return 0;
-
- *pgroup = group;
- *pidx = idx;
-
- return 1;
-}
-
/*
* This is the amount of packet we copy rather than map, so that the
* guest can't fiddle with the contents of the headers while we do
@@ -453,12 +399,6 @@ static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
{
struct gnttab_copy *copy_gop;
struct netbk_rx_meta *meta;
- /*
- * These variables are used iff get_page_ext returns true,
- * in which case they are guaranteed to be initialized.
- */
- unsigned int uninitialized_var(group), uninitialized_var(idx);
- int foreign = get_page_ext(page, &group, &idx);
unsigned long bytes;
/* Data must not cross a page boundary. */
@@ -494,20 +434,9 @@ static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
copy_gop = npo->copy + npo->copy_prod++;
copy_gop->flags = GNTCOPY_dest_gref;
- if (foreign) {
- struct xen_netbk *netbk = &xen_netbk[group];
- struct pending_tx_info *src_pend;
+ copy_gop->source.domid = DOMID_SELF;
+ copy_gop->source.u.gmfn = virt_to_mfn(page_address(page));
- src_pend = &netbk->pending_tx_info[idx];
-
- copy_gop->source.domid = src_pend->vif->domid;
- copy_gop->source.u.ref = src_pend->req.gref;
- copy_gop->flags |= GNTCOPY_source_gref;
- } else {
- void *vaddr = page_address(page);
- copy_gop->source.domid = DOMID_SELF;
- copy_gop->source.u.gmfn = virt_to_mfn(vaddr);
- }
copy_gop->source.offset = offset;
copy_gop->dest.domid = vif->domid;
@@ -1047,7 +976,6 @@ static struct page *xen_netbk_alloc_page(struct xen_netbk *netbk,
page = alloc_page(GFP_KERNEL|__GFP_COLD);
if (!page)
return NULL;
- set_page_ext(page, netbk, pending_idx);
netbk->mmap_pages[pending_idx] = page;
return page;
}
@@ -1155,7 +1083,6 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
first->req.offset = 0;
first->req.size = dst_offset;
first->head = start_idx;
- set_page_ext(page, netbk, head_idx);
netbk->mmap_pages[head_idx] = page;
frag_set_pending_idx(&frags[shinfo->nr_frags], head_idx);
}
--
1.7.10.4
^ permalink raw reply related
* [PATCH net-next 3/3] xen-netback: rename functions
From: Wei Liu @ 2013-08-26 11:59 UTC (permalink / raw)
To: netdev; +Cc: xen-devel, ian.campbell, msw, konrad.wilk, annie.li, Wei Liu
In-Reply-To: <1377518379-26503-1-git-send-email-wei.liu2@citrix.com>
As we move to 1:1 model and melt xen_netbk and xenvif together, it would
be better to use single prefix for all functions in xen-netback.
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
---
drivers/net/xen-netback/common.h | 24 ++--
drivers/net/xen-netback/interface.c | 20 ++--
drivers/net/xen-netback/netback.c | 223 ++++++++++++++++++-----------------
3 files changed, 134 insertions(+), 133 deletions(-)
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 9c1f158..a197743 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -190,21 +190,21 @@ void xenvif_xenbus_fini(void);
int xenvif_schedulable(struct xenvif *vif);
-int xen_netbk_rx_ring_full(struct xenvif *vif);
+int xenvif_rx_ring_full(struct xenvif *vif);
-int xen_netbk_must_stop_queue(struct xenvif *vif);
+int xenvif_must_stop_queue(struct xenvif *vif);
/* (Un)Map communication rings. */
-void xen_netbk_unmap_frontend_rings(struct xenvif *vif);
-int xen_netbk_map_frontend_rings(struct xenvif *vif,
- grant_ref_t tx_ring_ref,
- grant_ref_t rx_ring_ref);
+void xenvif_unmap_frontend_rings(struct xenvif *vif);
+int xenvif_map_frontend_rings(struct xenvif *vif,
+ grant_ref_t tx_ring_ref,
+ grant_ref_t rx_ring_ref);
/* Check for SKBs from frontend and schedule backend processing */
-void xen_netbk_check_rx_xenvif(struct xenvif *vif);
+void xenvif_check_rx_xenvif(struct xenvif *vif);
/* Queue an SKB for transmission to the frontend */
-void xen_netbk_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb);
+void xenvif_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb);
/* Notify xenvif that ring now has space to send an skb to the frontend */
void xenvif_notify_tx_completion(struct xenvif *vif);
@@ -212,12 +212,12 @@ void xenvif_notify_tx_completion(struct xenvif *vif);
void xenvif_carrier_off(struct xenvif *vif);
/* Returns number of ring slots required to send an skb to the frontend */
-unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb);
+unsigned int xenvif_count_skb_slots(struct xenvif *vif, struct sk_buff *skb);
-int xen_netbk_tx_action(struct xenvif *vif, int budget);
-void xen_netbk_rx_action(struct xenvif *vif);
+int xenvif_tx_action(struct xenvif *vif, int budget);
+void xenvif_rx_action(struct xenvif *vif);
-int xen_netbk_kthread(void *data);
+int xenvif_kthread(void *data);
extern bool separate_tx_rx_irq;
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index 44d6b70..625c6f4 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -48,7 +48,7 @@ int xenvif_schedulable(struct xenvif *vif)
static int xenvif_rx_schedulable(struct xenvif *vif)
{
- return xenvif_schedulable(vif) && !xen_netbk_rx_ring_full(vif);
+ return xenvif_schedulable(vif) && !xenvif_rx_ring_full(vif);
}
static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id)
@@ -66,7 +66,7 @@ static int xenvif_poll(struct napi_struct *napi, int budget)
struct xenvif *vif = container_of(napi, struct xenvif, napi);
int work_done;
- work_done = xen_netbk_tx_action(vif, budget);
+ work_done = xenvif_tx_action(vif, budget);
if (work_done < budget) {
int more_to_do = 0;
@@ -133,12 +133,12 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto drop;
/* Reserve ring slots for the worst-case number of fragments. */
- vif->rx_req_cons_peek += xen_netbk_count_skb_slots(vif, skb);
+ vif->rx_req_cons_peek += xenvif_count_skb_slots(vif, skb);
- if (vif->can_queue && xen_netbk_must_stop_queue(vif))
+ if (vif->can_queue && xenvif_must_stop_queue(vif))
netif_stop_queue(dev);
- xen_netbk_queue_tx_skb(vif, skb);
+ xenvif_queue_tx_skb(vif, skb);
return NETDEV_TX_OK;
@@ -166,7 +166,7 @@ static void xenvif_up(struct xenvif *vif)
enable_irq(vif->tx_irq);
if (vif->tx_irq != vif->rx_irq)
enable_irq(vif->rx_irq);
- xen_netbk_check_rx_xenvif(vif);
+ xenvif_check_rx_xenvif(vif);
}
static void xenvif_down(struct xenvif *vif)
@@ -368,7 +368,7 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
__module_get(THIS_MODULE);
- err = xen_netbk_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref);
+ err = xenvif_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref);
if (err < 0)
goto err;
@@ -405,7 +405,7 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
}
init_waitqueue_head(&vif->wq);
- vif->task = kthread_create(xen_netbk_kthread,
+ vif->task = kthread_create(xenvif_kthread,
(void *)vif, vif->dev->name);
if (IS_ERR(vif->task)) {
pr_warn("Could not allocate kthread for %s\n", vif->dev->name);
@@ -433,7 +433,7 @@ err_tx_unbind:
unbind_from_irqhandler(vif->tx_irq, vif);
vif->tx_irq = 0;
err_unmap:
- xen_netbk_unmap_frontend_rings(vif);
+ xenvif_unmap_frontend_rings(vif);
err:
module_put(THIS_MODULE);
return err;
@@ -481,7 +481,7 @@ void xenvif_disconnect(struct xenvif *vif)
unregister_netdev(vif->dev);
- xen_netbk_unmap_frontend_rings(vif);
+ xenvif_unmap_frontend_rings(vif);
free_netdev(vif->dev);
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 44ccc67..956130c 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -80,8 +80,9 @@ static inline int pending_tx_is_head(struct xenvif *vif, RING_IDX idx)
return vif->pending_tx_info[idx].head != INVALID_PENDING_RING_IDX;
}
-static void xen_netbk_idx_release(struct xenvif *vif, u16 pending_idx,
- u8 status);
+static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx,
+ u8 status);
+
static void make_tx_response(struct xenvif *vif,
struct xen_netif_tx_request *txp,
s8 st);
@@ -150,7 +151,7 @@ static int max_required_rx_slots(struct xenvif *vif)
return max;
}
-int xen_netbk_rx_ring_full(struct xenvif *vif)
+int xenvif_rx_ring_full(struct xenvif *vif)
{
RING_IDX peek = vif->rx_req_cons_peek;
RING_IDX needed = max_required_rx_slots(vif);
@@ -159,16 +160,16 @@ int xen_netbk_rx_ring_full(struct xenvif *vif)
((vif->rx.rsp_prod_pvt + XEN_NETIF_RX_RING_SIZE - peek) < needed);
}
-int xen_netbk_must_stop_queue(struct xenvif *vif)
+int xenvif_must_stop_queue(struct xenvif *vif)
{
- if (!xen_netbk_rx_ring_full(vif))
+ if (!xenvif_rx_ring_full(vif))
return 0;
vif->rx.sring->req_event = vif->rx_req_cons_peek +
max_required_rx_slots(vif);
mb(); /* request notification /then/ check the queue */
- return xen_netbk_rx_ring_full(vif);
+ return xenvif_rx_ring_full(vif);
}
/*
@@ -214,9 +215,9 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
/*
* Figure out how many ring slots we're going to need to send @skb to
* the guest. This function is essentially a dry run of
- * netbk_gop_frag_copy.
+ * xenvif_gop_frag_copy.
*/
-unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb)
+unsigned int xenvif_count_skb_slots(struct xenvif *vif, struct sk_buff *skb)
{
unsigned int count;
int i, copy_off;
@@ -296,10 +297,10 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif,
* Set up the grant operations for this fragment. If it's a flipping
* interface, we also set up the unmap request from here.
*/
-static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
- struct netrx_pending_operations *npo,
- struct page *page, unsigned long size,
- unsigned long offset, int *head)
+static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
+ struct netrx_pending_operations *npo,
+ struct page *page, unsigned long size,
+ unsigned long offset, int *head)
{
struct gnttab_copy *copy_gop;
struct xenvif_rx_meta *meta;
@@ -382,8 +383,8 @@ static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
* zero GSO descriptors (for non-GSO packets) or one descriptor (for
* frontend-side LRO).
*/
-static int netbk_gop_skb(struct sk_buff *skb,
- struct netrx_pending_operations *npo)
+static int xenvif_gop_skb(struct sk_buff *skb,
+ struct netrx_pending_operations *npo)
{
struct xenvif *vif = netdev_priv(skb->dev);
int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -426,30 +427,30 @@ static int netbk_gop_skb(struct sk_buff *skb,
if (data + len > skb_tail_pointer(skb))
len = skb_tail_pointer(skb) - data;
- netbk_gop_frag_copy(vif, skb, npo,
- virt_to_page(data), len, offset, &head);
+ xenvif_gop_frag_copy(vif, skb, npo,
+ virt_to_page(data), len, offset, &head);
data += len;
}
for (i = 0; i < nr_frags; i++) {
- netbk_gop_frag_copy(vif, skb, npo,
- skb_frag_page(&skb_shinfo(skb)->frags[i]),
- skb_frag_size(&skb_shinfo(skb)->frags[i]),
- skb_shinfo(skb)->frags[i].page_offset,
- &head);
+ xenvif_gop_frag_copy(vif, skb, npo,
+ skb_frag_page(&skb_shinfo(skb)->frags[i]),
+ skb_frag_size(&skb_shinfo(skb)->frags[i]),
+ skb_shinfo(skb)->frags[i].page_offset,
+ &head);
}
return npo->meta_prod - old_meta_prod;
}
/*
- * This is a twin to netbk_gop_skb. Assume that netbk_gop_skb was
+ * This is a twin to xenvif_gop_skb. Assume that xenvif_gop_skb was
* used to set up the operations on the top of
* netrx_pending_operations, which have since been done. Check that
* they didn't give any errors and advance over them.
*/
-static int netbk_check_gop(struct xenvif *vif, int nr_meta_slots,
- struct netrx_pending_operations *npo)
+static int xenvif_check_gop(struct xenvif *vif, int nr_meta_slots,
+ struct netrx_pending_operations *npo)
{
struct gnttab_copy *copy_op;
int status = XEN_NETIF_RSP_OKAY;
@@ -468,9 +469,9 @@ static int netbk_check_gop(struct xenvif *vif, int nr_meta_slots,
return status;
}
-static void netbk_add_frag_responses(struct xenvif *vif, int status,
- struct xenvif_rx_meta *meta,
- int nr_meta_slots)
+static void xenvif_add_frag_responses(struct xenvif *vif, int status,
+ struct xenvif_rx_meta *meta,
+ int nr_meta_slots)
{
int i;
unsigned long offset;
@@ -498,12 +499,12 @@ struct skb_cb_overlay {
int meta_slots_used;
};
-static void xen_netbk_kick_thread(struct xenvif *vif)
+static void xenvif_kick_thread(struct xenvif *vif)
{
wake_up(&vif->wq);
}
-void xen_netbk_rx_action(struct xenvif *vif)
+void xenvif_rx_action(struct xenvif *vif)
{
s8 status;
u16 flags;
@@ -532,7 +533,7 @@ void xen_netbk_rx_action(struct xenvif *vif)
nr_frags = skb_shinfo(skb)->nr_frags;
sco = (struct skb_cb_overlay *)skb->cb;
- sco->meta_slots_used = netbk_gop_skb(skb, &npo);
+ sco->meta_slots_used = xenvif_gop_skb(skb, &npo);
count += nr_frags + 1;
@@ -575,7 +576,7 @@ void xen_netbk_rx_action(struct xenvif *vif)
vif->dev->stats.tx_bytes += skb->len;
vif->dev->stats.tx_packets++;
- status = netbk_check_gop(vif, sco->meta_slots_used, &npo);
+ status = xenvif_check_gop(vif, sco->meta_slots_used, &npo);
if (sco->meta_slots_used == 1)
flags = 0;
@@ -611,9 +612,9 @@ void xen_netbk_rx_action(struct xenvif *vif)
gso->flags = 0;
}
- netbk_add_frag_responses(vif, status,
- vif->meta + npo.meta_cons + 1,
- sco->meta_slots_used);
+ xenvif_add_frag_responses(vif, status,
+ vif->meta + npo.meta_cons + 1,
+ sco->meta_slots_used);
RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->rx, ret);
@@ -631,17 +632,17 @@ void xen_netbk_rx_action(struct xenvif *vif)
/* More work to do? */
if (!skb_queue_empty(&vif->rx_queue))
- xen_netbk_kick_thread(vif);
+ xenvif_kick_thread(vif);
}
-void xen_netbk_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb)
+void xenvif_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb)
{
skb_queue_tail(&vif->rx_queue, skb);
- xen_netbk_kick_thread(vif);
+ xenvif_kick_thread(vif);
}
-void xen_netbk_check_rx_xenvif(struct xenvif *vif)
+void xenvif_check_rx_xenvif(struct xenvif *vif)
{
int more_to_do;
@@ -675,11 +676,11 @@ static void tx_credit_callback(unsigned long data)
{
struct xenvif *vif = (struct xenvif *)data;
tx_add_credit(vif);
- xen_netbk_check_rx_xenvif(vif);
+ xenvif_check_rx_xenvif(vif);
}
-static void netbk_tx_err(struct xenvif *vif,
- struct xen_netif_tx_request *txp, RING_IDX end)
+static void xenvif_tx_err(struct xenvif *vif,
+ struct xen_netif_tx_request *txp, RING_IDX end)
{
RING_IDX cons = vif->tx.req_cons;
@@ -692,16 +693,16 @@ static void netbk_tx_err(struct xenvif *vif,
vif->tx.req_cons = cons;
}
-static void netbk_fatal_tx_err(struct xenvif *vif)
+static void xenvif_fatal_tx_err(struct xenvif *vif)
{
netdev_err(vif->dev, "fatal error; disabling device\n");
xenvif_carrier_off(vif);
}
-static int netbk_count_requests(struct xenvif *vif,
- struct xen_netif_tx_request *first,
- struct xen_netif_tx_request *txp,
- int work_to_do)
+static int xenvif_count_requests(struct xenvif *vif,
+ struct xen_netif_tx_request *first,
+ struct xen_netif_tx_request *txp,
+ int work_to_do)
{
RING_IDX cons = vif->tx.req_cons;
int slots = 0;
@@ -718,7 +719,7 @@ static int netbk_count_requests(struct xenvif *vif,
netdev_err(vif->dev,
"Asked for %d slots but exceeds this limit\n",
work_to_do);
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
return -ENODATA;
}
@@ -729,7 +730,7 @@ static int netbk_count_requests(struct xenvif *vif,
netdev_err(vif->dev,
"Malicious frontend using %d slots, threshold %u\n",
slots, fatal_skb_slots);
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
return -E2BIG;
}
@@ -777,7 +778,7 @@ static int netbk_count_requests(struct xenvif *vif,
if (unlikely((txp->offset + txp->size) > PAGE_SIZE)) {
netdev_err(vif->dev, "Cross page boundary, txp->offset: %x, size: %u\n",
txp->offset, txp->size);
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
return -EINVAL;
}
@@ -789,15 +790,15 @@ static int netbk_count_requests(struct xenvif *vif,
} while (more_data);
if (drop_err) {
- netbk_tx_err(vif, first, cons + slots);
+ xenvif_tx_err(vif, first, cons + slots);
return drop_err;
}
return slots;
}
-static struct page *xen_netbk_alloc_page(struct xenvif *vif,
- u16 pending_idx)
+static struct page *xenvif_alloc_page(struct xenvif *vif,
+ u16 pending_idx)
{
struct page *page;
@@ -809,10 +810,10 @@ static struct page *xen_netbk_alloc_page(struct xenvif *vif,
return page;
}
-static struct gnttab_copy *xen_netbk_get_requests(struct xenvif *vif,
- struct sk_buff *skb,
- struct xen_netif_tx_request *txp,
- struct gnttab_copy *gop)
+static struct gnttab_copy *xenvif_get_requests(struct xenvif *vif,
+ struct sk_buff *skb,
+ struct xen_netif_tx_request *txp,
+ struct gnttab_copy *gop)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
skb_frag_t *frags = shinfo->frags;
@@ -835,7 +836,7 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xenvif *vif,
/* Coalesce tx requests, at this point the packet passed in
* should be <= 64K. Any packets larger than 64K have been
- * handled in netbk_count_requests().
+ * handled in xenvif_count_requests().
*/
for (shinfo->nr_frags = slot = start; slot < nr_slots;
shinfo->nr_frags++) {
@@ -918,20 +919,20 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xenvif *vif,
err:
/* Unwind, freeing all pages and sending error responses. */
while (shinfo->nr_frags-- > start) {
- xen_netbk_idx_release(vif,
+ xenvif_idx_release(vif,
frag_get_pending_idx(&frags[shinfo->nr_frags]),
XEN_NETIF_RSP_ERROR);
}
/* The head too, if necessary. */
if (start)
- xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+ xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
return NULL;
}
-static int xen_netbk_tx_check_gop(struct xenvif *vif,
- struct sk_buff *skb,
- struct gnttab_copy **gopp)
+static int xenvif_tx_check_gop(struct xenvif *vif,
+ struct sk_buff *skb,
+ struct gnttab_copy **gopp)
{
struct gnttab_copy *gop = *gopp;
u16 pending_idx = *((u16 *)skb->data);
@@ -944,7 +945,7 @@ static int xen_netbk_tx_check_gop(struct xenvif *vif,
/* Check status of header. */
err = gop->status;
if (unlikely(err))
- xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+ xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
/* Skip first skb fragment if it is on same page as header fragment. */
start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx);
@@ -968,13 +969,13 @@ static int xen_netbk_tx_check_gop(struct xenvif *vif,
if (likely(!newerr)) {
/* Had a previous error? Invalidate this fragment. */
if (unlikely(err))
- xen_netbk_idx_release(vif, pending_idx,
- XEN_NETIF_RSP_OKAY);
+ xenvif_idx_release(vif, pending_idx,
+ XEN_NETIF_RSP_OKAY);
continue;
}
/* Error on this fragment: respond to client with an error. */
- xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+ xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
/* Not the first error? Preceding frags already invalidated. */
if (err)
@@ -982,11 +983,11 @@ static int xen_netbk_tx_check_gop(struct xenvif *vif,
/* First error: invalidate header and preceding fragments. */
pending_idx = *((u16 *)skb->data);
- xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
+ xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
for (j = start; j < i; j++) {
pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
- xen_netbk_idx_release(vif, pending_idx,
- XEN_NETIF_RSP_OKAY);
+ xenvif_idx_release(vif, pending_idx,
+ XEN_NETIF_RSP_OKAY);
}
/* Remember the error: invalidate all subsequent fragments. */
@@ -997,7 +998,7 @@ static int xen_netbk_tx_check_gop(struct xenvif *vif,
return err;
}
-static void xen_netbk_fill_frags(struct xenvif *vif, struct sk_buff *skb)
+static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
int nr_frags = shinfo->nr_frags;
@@ -1018,13 +1019,13 @@ static void xen_netbk_fill_frags(struct xenvif *vif, struct sk_buff *skb)
skb->data_len += txp->size;
skb->truesize += txp->size;
- /* Take an extra reference to offset xen_netbk_idx_release */
+ /* Take an extra reference to offset xenvif_idx_release */
get_page(vif->mmap_pages[pending_idx]);
- xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
+ xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
}
}
-static int xen_netbk_get_extras(struct xenvif *vif,
+static int xenvif_get_extras(struct xenvif *vif,
struct xen_netif_extra_info *extras,
int work_to_do)
{
@@ -1034,7 +1035,7 @@ static int xen_netbk_get_extras(struct xenvif *vif,
do {
if (unlikely(work_to_do-- <= 0)) {
netdev_err(vif->dev, "Missing extra info\n");
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
return -EBADR;
}
@@ -1045,7 +1046,7 @@ static int xen_netbk_get_extras(struct xenvif *vif,
vif->tx.req_cons = ++cons;
netdev_err(vif->dev,
"Invalid extra type: %d\n", extra.type);
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
return -EINVAL;
}
@@ -1056,20 +1057,20 @@ static int xen_netbk_get_extras(struct xenvif *vif,
return work_to_do;
}
-static int netbk_set_skb_gso(struct xenvif *vif,
- struct sk_buff *skb,
- struct xen_netif_extra_info *gso)
+static int xenvif_set_skb_gso(struct xenvif *vif,
+ struct sk_buff *skb,
+ struct xen_netif_extra_info *gso)
{
if (!gso->u.gso.size) {
netdev_err(vif->dev, "GSO size must not be zero.\n");
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
return -EINVAL;
}
/* Currently only TCPv4 S.O. is supported. */
if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) {
netdev_err(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type);
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
return -EINVAL;
}
@@ -1180,7 +1181,7 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
return false;
}
-static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
+static unsigned xenvif_tx_build_gops(struct xenvif *vif)
{
struct gnttab_copy *gop = vif->tx_copy_ops, *request_gop;
struct sk_buff *skb;
@@ -1205,7 +1206,7 @@ static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
"req_prod %d, req_cons %d, size %ld\n",
vif->tx.sring->req_prod, vif->tx.req_cons,
XEN_NETIF_TX_RING_SIZE);
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
continue;
}
@@ -1229,14 +1230,14 @@ static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
memset(extras, 0, sizeof(extras));
if (txreq.flags & XEN_NETTXF_extra_info) {
- work_to_do = xen_netbk_get_extras(vif, extras,
- work_to_do);
+ work_to_do = xenvif_get_extras(vif, extras,
+ work_to_do);
idx = vif->tx.req_cons;
if (unlikely(work_to_do < 0))
break;
}
- ret = netbk_count_requests(vif, &txreq, txfrags, work_to_do);
+ ret = xenvif_count_requests(vif, &txreq, txfrags, work_to_do);
if (unlikely(ret < 0))
break;
@@ -1245,7 +1246,7 @@ static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
if (unlikely(txreq.size < ETH_HLEN)) {
netdev_dbg(vif->dev,
"Bad packet size: %d\n", txreq.size);
- netbk_tx_err(vif, &txreq, idx);
+ xenvif_tx_err(vif, &txreq, idx);
break;
}
@@ -1255,7 +1256,7 @@ static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
"txreq.offset: %x, size: %u, end: %lu\n",
txreq.offset, txreq.size,
(txreq.offset&~PAGE_MASK) + txreq.size);
- netbk_fatal_tx_err(vif);
+ xenvif_fatal_tx_err(vif);
break;
}
@@ -1271,7 +1272,7 @@ static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
if (unlikely(skb == NULL)) {
netdev_dbg(vif->dev,
"Can't allocate a skb in start_xmit.\n");
- netbk_tx_err(vif, &txreq, idx);
+ xenvif_tx_err(vif, &txreq, idx);
break;
}
@@ -1282,18 +1283,18 @@ static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
struct xen_netif_extra_info *gso;
gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1];
- if (netbk_set_skb_gso(vif, skb, gso)) {
- /* Failure in netbk_set_skb_gso is fatal. */
+ if (xenvif_set_skb_gso(vif, skb, gso)) {
+ /* Failure in xenvif_set_skb_gso is fatal. */
kfree_skb(skb);
break;
}
}
/* XXX could copy straight to head */
- page = xen_netbk_alloc_page(vif, pending_idx);
+ page = xenvif_alloc_page(vif, pending_idx);
if (!page) {
kfree_skb(skb);
- netbk_tx_err(vif, &txreq, idx);
+ xenvif_tx_err(vif, &txreq, idx);
break;
}
@@ -1329,10 +1330,10 @@ static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
vif->pending_cons++;
- request_gop = xen_netbk_get_requests(vif, skb, txfrags, gop);
+ request_gop = xenvif_get_requests(vif, skb, txfrags, gop);
if (request_gop == NULL) {
kfree_skb(skb);
- netbk_tx_err(vif, &txreq, idx);
+ xenvif_tx_err(vif, &txreq, idx);
break;
}
gop = request_gop;
@@ -1349,7 +1350,7 @@ static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
}
-static int xen_netbk_tx_submit(struct xenvif *vif, int budget)
+static int xenvif_tx_submit(struct xenvif *vif, int budget)
{
struct gnttab_copy *gop = vif->tx_copy_ops;
struct sk_buff *skb;
@@ -1365,7 +1366,7 @@ static int xen_netbk_tx_submit(struct xenvif *vif, int budget)
txp = &vif->pending_tx_info[pending_idx].req;
/* Check the remap error code. */
- if (unlikely(xen_netbk_tx_check_gop(vif, skb, &gop))) {
+ if (unlikely(xenvif_tx_check_gop(vif, skb, &gop))) {
netdev_dbg(vif->dev, "netback grant failed.\n");
skb_shinfo(skb)->nr_frags = 0;
kfree_skb(skb);
@@ -1382,8 +1383,8 @@ static int xen_netbk_tx_submit(struct xenvif *vif, int budget)
txp->size -= data_len;
} else {
/* Schedule a response immediately. */
- xen_netbk_idx_release(vif, pending_idx,
- XEN_NETIF_RSP_OKAY);
+ xenvif_idx_release(vif, pending_idx,
+ XEN_NETIF_RSP_OKAY);
}
if (txp->flags & XEN_NETTXF_csum_blank)
@@ -1391,7 +1392,7 @@ static int xen_netbk_tx_submit(struct xenvif *vif, int budget)
else if (txp->flags & XEN_NETTXF_data_validated)
skb->ip_summed = CHECKSUM_UNNECESSARY;
- xen_netbk_fill_frags(vif, skb);
+ xenvif_fill_frags(vif, skb);
/*
* If the initial fragment was < PKT_PROT_LEN then
@@ -1428,7 +1429,7 @@ static int xen_netbk_tx_submit(struct xenvif *vif, int budget)
}
/* Called after netfront has transmitted */
-int xen_netbk_tx_action(struct xenvif *vif, int budget)
+int xenvif_tx_action(struct xenvif *vif, int budget)
{
unsigned nr_gops;
int work_done;
@@ -1436,20 +1437,20 @@ int xen_netbk_tx_action(struct xenvif *vif, int budget)
if (unlikely(!tx_work_todo(vif)))
return 0;
- nr_gops = xen_netbk_tx_build_gops(vif);
+ nr_gops = xenvif_tx_build_gops(vif);
if (nr_gops == 0)
return 0;
gnttab_batch_copy(vif->tx_copy_ops, nr_gops);
- work_done = xen_netbk_tx_submit(vif, nr_gops);
+ work_done = xenvif_tx_submit(vif, nr_gops);
return work_done;
}
-static void xen_netbk_idx_release(struct xenvif *vif, u16 pending_idx,
- u8 status)
+static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx,
+ u8 status)
{
struct pending_tx_info *pending_tx_info;
pending_ring_idx_t head;
@@ -1554,7 +1555,7 @@ static inline int tx_work_todo(struct xenvif *vif)
return 0;
}
-void xen_netbk_unmap_frontend_rings(struct xenvif *vif)
+void xenvif_unmap_frontend_rings(struct xenvif *vif)
{
if (vif->tx.sring)
xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif),
@@ -1564,9 +1565,9 @@ void xen_netbk_unmap_frontend_rings(struct xenvif *vif)
vif->rx.sring);
}
-int xen_netbk_map_frontend_rings(struct xenvif *vif,
- grant_ref_t tx_ring_ref,
- grant_ref_t rx_ring_ref)
+int xenvif_map_frontend_rings(struct xenvif *vif,
+ grant_ref_t tx_ring_ref,
+ grant_ref_t rx_ring_ref)
{
void *addr;
struct xen_netif_tx_sring *txs;
@@ -1595,11 +1596,11 @@ int xen_netbk_map_frontend_rings(struct xenvif *vif,
return 0;
err:
- xen_netbk_unmap_frontend_rings(vif);
+ xenvif_unmap_frontend_rings(vif);
return err;
}
-int xen_netbk_kthread(void *data)
+int xenvif_kthread(void *data)
{
struct xenvif *vif = data;
@@ -1611,7 +1612,7 @@ int xen_netbk_kthread(void *data)
break;
if (rx_work_todo(vif))
- xen_netbk_rx_action(vif);
+ xenvif_rx_action(vif);
cond_resched();
}
--
1.7.10.4
^ permalink raw reply related
* [PATCH net-next 2/3] xen-netback: switch to NAPI + kthread 1:1 model
From: Wei Liu @ 2013-08-26 11:59 UTC (permalink / raw)
To: netdev; +Cc: xen-devel, ian.campbell, msw, konrad.wilk, annie.li, Wei Liu
In-Reply-To: <1377518379-26503-1-git-send-email-wei.liu2@citrix.com>
This patch implements 1:1 model netback. NAPI and kthread are utilized
to do the weight-lifting job:
- NAPI is used for guest side TX (host side RX)
- kthread is used for guest side RX (host side TX)
Xenvif and xen_netbk are made into one structure to reduce code size.
This model provides better scheduling fairness among vifs. It is also
prerequisite for implementing multiqueue for Xen netback.
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
---
drivers/net/xen-netback/common.h | 132 +++++---
drivers/net/xen-netback/interface.c | 119 ++++---
drivers/net/xen-netback/netback.c | 607 ++++++++++-------------------------
3 files changed, 351 insertions(+), 507 deletions(-)
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 8a4d77e..9c1f158 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -45,31 +45,109 @@
#include <xen/grant_table.h>
#include <xen/xenbus.h>
-struct xen_netbk;
+typedef unsigned int pending_ring_idx_t;
+#define INVALID_PENDING_RING_IDX (~0U)
+
+/* For the head field in pending_tx_info: it is used to indicate
+ * whether this tx info is the head of one or more coalesced requests.
+ *
+ * When head != INVALID_PENDING_RING_IDX, it means the start of a new
+ * tx requests queue and the end of previous queue.
+ *
+ * An example sequence of head fields (I = INVALID_PENDING_RING_IDX):
+ *
+ * ...|0 I I I|5 I|9 I I I|...
+ * -->|<-INUSE----------------
+ *
+ * After consuming the first slot(s) we have:
+ *
+ * ...|V V V V|5 I|9 I I I|...
+ * -----FREE->|<-INUSE--------
+ *
+ * where V stands for "valid pending ring index". Any number other
+ * than INVALID_PENDING_RING_IDX is OK. These entries are considered
+ * free and can contain any number other than
+ * INVALID_PENDING_RING_IDX. In practice we use 0.
+ *
+ * The in use non-INVALID_PENDING_RING_IDX (say 0, 5 and 9 in the
+ * above example) number is the index into pending_tx_info and
+ * mmap_pages arrays.
+ */
+struct pending_tx_info {
+ struct xen_netif_tx_request req; /* coalesced tx request */
+ pending_ring_idx_t head; /* head != INVALID_PENDING_RING_IDX
+ * if it is head of one or more tx
+ * reqs
+ */
+};
+
+#define XEN_NETIF_TX_RING_SIZE __CONST_RING_SIZE(xen_netif_tx, PAGE_SIZE)
+#define XEN_NETIF_RX_RING_SIZE __CONST_RING_SIZE(xen_netif_rx, PAGE_SIZE)
+
+struct xenvif_rx_meta {
+ int id;
+ int size;
+ int gso_size;
+};
+
+/* Discriminate from any valid pending_idx value. */
+#define INVALID_PENDING_IDX 0xFFFF
+
+#define MAX_BUFFER_OFFSET PAGE_SIZE
+
+#define MAX_PENDING_REQS 256
struct xenvif {
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
- /* Reference to netback processing backend. */
- struct xen_netbk *netbk;
+ /* Use NAPI for guest TX */
+ struct napi_struct napi;
+ /* When feature-split-event-channels = 0, tx_irq = rx_irq. */
+ unsigned int tx_irq;
+ /* Only used when feature-split-event-channels = 1 */
+ char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */
+ struct xen_netif_tx_back_ring tx;
+ struct sk_buff_head tx_queue;
+ struct page *mmap_pages[MAX_PENDING_REQS];
+ pending_ring_idx_t pending_prod;
+ pending_ring_idx_t pending_cons;
+ u16 pending_ring[MAX_PENDING_REQS];
+ struct pending_tx_info pending_tx_info[MAX_PENDING_REQS];
+
+ /* Coalescing tx requests before copying makes number of grant
+ * copy ops greater or equal to number of slots required. In
+ * worst case a tx request consumes 2 gnttab_copy.
+ */
+ struct gnttab_copy tx_copy_ops[2*MAX_PENDING_REQS];
- u8 fe_dev_addr[6];
+ /* Use kthread for guest RX */
+ struct task_struct *task;
+ wait_queue_head_t wq;
/* When feature-split-event-channels = 0, tx_irq = rx_irq. */
- unsigned int tx_irq;
unsigned int rx_irq;
/* Only used when feature-split-event-channels = 1 */
- char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */
char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */
+ struct xen_netif_rx_back_ring rx;
+ struct sk_buff_head rx_queue;
- /* List of frontends to notify after a batch of frames sent. */
- struct list_head notify_list;
+ /* Allow xenvif_start_xmit() to peek ahead in the rx request
+ * ring. This is a prediction of what rx_req_cons will be
+ * once all queued skbs are put on the ring.
+ */
+ RING_IDX rx_req_cons_peek;
+
+ /* Given MAX_BUFFER_OFFSET of 4096 the worst case is that each
+ * head/fragment page uses 2 copy operations because it
+ * straddles two buffers in the frontend.
+ */
+ struct gnttab_copy grant_copy_op[2*XEN_NETIF_RX_RING_SIZE];
+ struct xenvif_rx_meta meta[2*XEN_NETIF_RX_RING_SIZE];
- /* The shared rings and indexes. */
- struct xen_netif_tx_back_ring tx;
- struct xen_netif_rx_back_ring rx;
+
+ u8 fe_dev_addr[6];
/* Frontend feature information. */
u8 can_sg:1;
@@ -80,13 +158,6 @@ struct xenvif {
/* Internal feature information. */
u8 can_queue:1; /* can queue packets for receiver? */
- /*
- * Allow xenvif_start_xmit() to peek ahead in the rx request
- * ring. This is a prediction of what rx_req_cons will be
- * once all queued skbs are put on the ring.
- */
- RING_IDX rx_req_cons_peek;
-
/* Transmit shaping: allow 'credit_bytes' every 'credit_usec'. */
unsigned long credit_bytes;
unsigned long credit_usec;
@@ -97,11 +168,7 @@ struct xenvif {
unsigned long rx_gso_checksum_fixup;
/* Miscellaneous private stuff. */
- struct list_head schedule_list;
- atomic_t refcnt;
struct net_device *dev;
-
- wait_queue_head_t waiting_to_free;
};
static inline struct xenbus_device *xenvif_to_xenbus_device(struct xenvif *vif)
@@ -109,9 +176,6 @@ static inline struct xenbus_device *xenvif_to_xenbus_device(struct xenvif *vif)
return to_xenbus_device(vif->dev->dev.parent);
}
-#define XEN_NETIF_TX_RING_SIZE __CONST_RING_SIZE(xen_netif_tx, PAGE_SIZE)
-#define XEN_NETIF_RX_RING_SIZE __CONST_RING_SIZE(xen_netif_rx, PAGE_SIZE)
-
struct xenvif *xenvif_alloc(struct device *parent,
domid_t domid,
unsigned int handle);
@@ -121,9 +185,6 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
unsigned int rx_evtchn);
void xenvif_disconnect(struct xenvif *vif);
-void xenvif_get(struct xenvif *vif);
-void xenvif_put(struct xenvif *vif);
-
int xenvif_xenbus_init(void);
void xenvif_xenbus_fini(void);
@@ -139,18 +200,8 @@ int xen_netbk_map_frontend_rings(struct xenvif *vif,
grant_ref_t tx_ring_ref,
grant_ref_t rx_ring_ref);
-/* (De)Register a xenvif with the netback backend. */
-void xen_netbk_add_xenvif(struct xenvif *vif);
-void xen_netbk_remove_xenvif(struct xenvif *vif);
-
-/* (De)Schedule backend processing for a xenvif */
-void xen_netbk_schedule_xenvif(struct xenvif *vif);
-void xen_netbk_deschedule_xenvif(struct xenvif *vif);
-
/* Check for SKBs from frontend and schedule backend processing */
void xen_netbk_check_rx_xenvif(struct xenvif *vif);
-/* Receive an SKB from the frontend */
-void xenvif_receive_skb(struct xenvif *vif, struct sk_buff *skb);
/* Queue an SKB for transmission to the frontend */
void xen_netbk_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb);
@@ -163,6 +214,11 @@ void xenvif_carrier_off(struct xenvif *vif);
/* Returns number of ring slots required to send an skb to the frontend */
unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb);
+int xen_netbk_tx_action(struct xenvif *vif, int budget);
+void xen_netbk_rx_action(struct xenvif *vif);
+
+int xen_netbk_kthread(void *data);
+
extern bool separate_tx_rx_irq;
#endif /* __XEN_NETBACK__COMMON_H__ */
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index 087d2db..44d6b70 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -30,6 +30,7 @@
#include "common.h"
+#include <linux/kthread.h>
#include <linux/ethtool.h>
#include <linux/rtnetlink.h>
#include <linux/if_vlan.h>
@@ -38,17 +39,7 @@
#include <asm/xen/hypercall.h>
#define XENVIF_QUEUE_LENGTH 32
-
-void xenvif_get(struct xenvif *vif)
-{
- atomic_inc(&vif->refcnt);
-}
-
-void xenvif_put(struct xenvif *vif)
-{
- if (atomic_dec_and_test(&vif->refcnt))
- wake_up(&vif->waiting_to_free);
-}
+#define XENVIF_NAPI_WEIGHT 64
int xenvif_schedulable(struct xenvif *vif)
{
@@ -64,21 +55,55 @@ static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id)
{
struct xenvif *vif = dev_id;
- if (vif->netbk == NULL)
- return IRQ_HANDLED;
-
- xen_netbk_schedule_xenvif(vif);
+ if (RING_HAS_UNCONSUMED_REQUESTS(&vif->tx))
+ napi_schedule(&vif->napi);
return IRQ_HANDLED;
}
+static int xenvif_poll(struct napi_struct *napi, int budget)
+{
+ struct xenvif *vif = container_of(napi, struct xenvif, napi);
+ int work_done;
+
+ work_done = xen_netbk_tx_action(vif, budget);
+
+ if (work_done < budget) {
+ int more_to_do = 0;
+ unsigned long flags;
+
+ /* It is necessary to disable IRQ before calling
+ * RING_HAS_UNCONSUMED_REQUESTS. Otherwise we might
+ * lose event from the frontend.
+ *
+ * Consider:
+ * RING_HAS_UNCONSUMED_REQUESTS
+ * <frontend generates event to trigger napi_schedule>
+ * __napi_complete
+ *
+ * This handler is still in scheduled state so the
+ * event has no effect at all. After __napi_complete
+ * this handler is descheduled and cannot get
+ * scheduled again. We lose event in this case and the ring
+ * will be completely stalled.
+ */
+
+ local_irq_save(flags);
+
+ RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, more_to_do);
+ if (!more_to_do)
+ __napi_complete(napi);
+
+ local_irq_restore(flags);
+ }
+
+ return work_done;
+}
+
static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id)
{
struct xenvif *vif = dev_id;
- if (vif->netbk == NULL)
- return IRQ_HANDLED;
-
if (xenvif_rx_schedulable(vif))
netif_wake_queue(vif->dev);
@@ -99,7 +124,8 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
BUG_ON(skb->dev != dev);
- if (vif->netbk == NULL)
+ /* Drop the packet if vif is not ready */
+ if (vif->task == NULL)
goto drop;
/* Drop the packet if the target domain has no receive buffers. */
@@ -108,7 +134,6 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Reserve ring slots for the worst-case number of fragments. */
vif->rx_req_cons_peek += xen_netbk_count_skb_slots(vif, skb);
- xenvif_get(vif);
if (vif->can_queue && xen_netbk_must_stop_queue(vif))
netif_stop_queue(dev);
@@ -123,11 +148,6 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
-void xenvif_receive_skb(struct xenvif *vif, struct sk_buff *skb)
-{
- netif_rx_ni(skb);
-}
-
void xenvif_notify_tx_completion(struct xenvif *vif)
{
if (netif_queue_stopped(vif->dev) && xenvif_rx_schedulable(vif))
@@ -142,7 +162,7 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
static void xenvif_up(struct xenvif *vif)
{
- xen_netbk_add_xenvif(vif);
+ napi_enable(&vif->napi);
enable_irq(vif->tx_irq);
if (vif->tx_irq != vif->rx_irq)
enable_irq(vif->rx_irq);
@@ -151,12 +171,11 @@ static void xenvif_up(struct xenvif *vif)
static void xenvif_down(struct xenvif *vif)
{
+ napi_disable(&vif->napi);
disable_irq(vif->tx_irq);
if (vif->tx_irq != vif->rx_irq)
disable_irq(vif->rx_irq);
del_timer_sync(&vif->credit_timeout);
- xen_netbk_deschedule_xenvif(vif);
- xen_netbk_remove_xenvif(vif);
}
static int xenvif_open(struct net_device *dev)
@@ -272,11 +291,12 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
struct net_device *dev;
struct xenvif *vif;
char name[IFNAMSIZ] = {};
+ int i;
snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle);
dev = alloc_netdev(sizeof(struct xenvif), name, ether_setup);
if (dev == NULL) {
- pr_warn("Could not allocate netdev\n");
+ pr_warn("Could not allocate netdev for %s\n", name);
return ERR_PTR(-ENOMEM);
}
@@ -285,14 +305,9 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
vif = netdev_priv(dev);
vif->domid = domid;
vif->handle = handle;
- vif->netbk = NULL;
vif->can_sg = 1;
vif->csum = 1;
- atomic_set(&vif->refcnt, 1);
- init_waitqueue_head(&vif->waiting_to_free);
vif->dev = dev;
- INIT_LIST_HEAD(&vif->schedule_list);
- INIT_LIST_HEAD(&vif->notify_list);
vif->credit_bytes = vif->remaining_credit = ~0UL;
vif->credit_usec = 0UL;
@@ -307,6 +322,16 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
dev->tx_queue_len = XENVIF_QUEUE_LENGTH;
+ skb_queue_head_init(&vif->rx_queue);
+ skb_queue_head_init(&vif->tx_queue);
+
+ vif->pending_cons = 0;
+ vif->pending_prod = MAX_PENDING_REQS;
+ for (i = 0; i < MAX_PENDING_REQS; i++)
+ vif->pending_ring[i] = i;
+ for (i = 0; i < MAX_PENDING_REQS; i++)
+ vif->mmap_pages[i] = NULL;
+
/*
* Initialise a dummy MAC address. We choose the numerically
* largest non-broadcast address to prevent the address getting
@@ -316,6 +341,8 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
memset(dev->dev_addr, 0xFF, ETH_ALEN);
dev->dev_addr[0] &= ~0x01;
+ netif_napi_add(dev, &vif->napi, xenvif_poll, XENVIF_NAPI_WEIGHT);
+
netif_carrier_off(dev);
err = register_netdev(dev);
@@ -377,7 +404,14 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
disable_irq(vif->rx_irq);
}
- xenvif_get(vif);
+ init_waitqueue_head(&vif->wq);
+ vif->task = kthread_create(xen_netbk_kthread,
+ (void *)vif, vif->dev->name);
+ if (IS_ERR(vif->task)) {
+ pr_warn("Could not allocate kthread for %s\n", vif->dev->name);
+ err = PTR_ERR(vif->task);
+ goto err_rx_unbind;
+ }
rtnl_lock();
if (!vif->can_sg && vif->dev->mtu > ETH_DATA_LEN)
@@ -388,7 +422,13 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
xenvif_up(vif);
rtnl_unlock();
+ wake_up_process(vif->task);
+
return 0;
+
+err_rx_unbind:
+ unbind_from_irqhandler(vif->rx_irq, vif);
+ vif->rx_irq = 0;
err_tx_unbind:
unbind_from_irqhandler(vif->tx_irq, vif);
vif->tx_irq = 0;
@@ -408,7 +448,6 @@ void xenvif_carrier_off(struct xenvif *vif)
if (netif_running(dev))
xenvif_down(vif);
rtnl_unlock();
- xenvif_put(vif);
}
void xenvif_disconnect(struct xenvif *vif)
@@ -422,9 +461,6 @@ void xenvif_disconnect(struct xenvif *vif)
if (netif_carrier_ok(vif->dev))
xenvif_carrier_off(vif);
- atomic_dec(&vif->refcnt);
- wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0);
-
if (vif->tx_irq) {
if (vif->tx_irq == vif->rx_irq)
unbind_from_irqhandler(vif->tx_irq, vif);
@@ -438,6 +474,11 @@ void xenvif_disconnect(struct xenvif *vif)
need_module_put = 1;
}
+ if (vif->task)
+ kthread_stop(vif->task);
+
+ netif_napi_del(&vif->napi);
+
unregister_netdev(vif->dev);
xen_netbk_unmap_frontend_rings(vif);
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 91f163d..44ccc67 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -70,116 +70,25 @@ module_param(fatal_skb_slots, uint, 0444);
*/
#define XEN_NETBK_LEGACY_SLOTS_MAX XEN_NETIF_NR_SLOTS_MIN
-typedef unsigned int pending_ring_idx_t;
-#define INVALID_PENDING_RING_IDX (~0U)
-
-struct pending_tx_info {
- struct xen_netif_tx_request req; /* coalesced tx request */
- struct xenvif *vif;
- pending_ring_idx_t head; /* head != INVALID_PENDING_RING_IDX
- * if it is head of one or more tx
- * reqs
- */
-};
-
-struct netbk_rx_meta {
- int id;
- int size;
- int gso_size;
-};
-
-#define MAX_PENDING_REQS 256
-
-/* Discriminate from any valid pending_idx value. */
-#define INVALID_PENDING_IDX 0xFFFF
-
-#define MAX_BUFFER_OFFSET PAGE_SIZE
-
-struct xen_netbk {
- wait_queue_head_t wq;
- struct task_struct *task;
-
- struct sk_buff_head rx_queue;
- struct sk_buff_head tx_queue;
-
- struct timer_list net_timer;
-
- struct page *mmap_pages[MAX_PENDING_REQS];
-
- pending_ring_idx_t pending_prod;
- pending_ring_idx_t pending_cons;
- struct list_head net_schedule_list;
-
- /* Protect the net_schedule_list in netif. */
- spinlock_t net_schedule_list_lock;
-
- atomic_t netfront_count;
-
- struct pending_tx_info pending_tx_info[MAX_PENDING_REQS];
- /* Coalescing tx requests before copying makes number of grant
- * copy ops greater or equal to number of slots required. In
- * worst case a tx request consumes 2 gnttab_copy.
- */
- struct gnttab_copy tx_copy_ops[2*MAX_PENDING_REQS];
-
- u16 pending_ring[MAX_PENDING_REQS];
-
- /*
- * Given MAX_BUFFER_OFFSET of 4096 the worst case is that each
- * head/fragment page uses 2 copy operations because it
- * straddles two buffers in the frontend.
- */
- struct gnttab_copy grant_copy_op[2*XEN_NETIF_RX_RING_SIZE];
- struct netbk_rx_meta meta[2*XEN_NETIF_RX_RING_SIZE];
-};
-
-static struct xen_netbk *xen_netbk;
-static int xen_netbk_group_nr;
-
/*
* If head != INVALID_PENDING_RING_IDX, it means this tx request is head of
* one or more merged tx requests, otherwise it is the continuation of
* previous tx request.
*/
-static inline int pending_tx_is_head(struct xen_netbk *netbk, RING_IDX idx)
-{
- return netbk->pending_tx_info[idx].head != INVALID_PENDING_RING_IDX;
-}
-
-void xen_netbk_add_xenvif(struct xenvif *vif)
-{
- int i;
- int min_netfront_count;
- int min_group = 0;
- struct xen_netbk *netbk;
-
- min_netfront_count = atomic_read(&xen_netbk[0].netfront_count);
- for (i = 0; i < xen_netbk_group_nr; i++) {
- int netfront_count = atomic_read(&xen_netbk[i].netfront_count);
- if (netfront_count < min_netfront_count) {
- min_group = i;
- min_netfront_count = netfront_count;
- }
- }
-
- netbk = &xen_netbk[min_group];
-
- vif->netbk = netbk;
- atomic_inc(&netbk->netfront_count);
-}
-
-void xen_netbk_remove_xenvif(struct xenvif *vif)
+static inline int pending_tx_is_head(struct xenvif *vif, RING_IDX idx)
{
- struct xen_netbk *netbk = vif->netbk;
- vif->netbk = NULL;
- atomic_dec(&netbk->netfront_count);
+ return vif->pending_tx_info[idx].head != INVALID_PENDING_RING_IDX;
}
-static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx,
+static void xen_netbk_idx_release(struct xenvif *vif, u16 pending_idx,
u8 status);
static void make_tx_response(struct xenvif *vif,
struct xen_netif_tx_request *txp,
s8 st);
+
+static inline int tx_work_todo(struct xenvif *vif);
+static inline int rx_work_todo(struct xenvif *vif);
+
static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
u16 id,
s8 st,
@@ -187,16 +96,16 @@ static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
u16 size,
u16 flags);
-static inline unsigned long idx_to_pfn(struct xen_netbk *netbk,
+static inline unsigned long idx_to_pfn(struct xenvif *vif,
u16 idx)
{
- return page_to_pfn(netbk->mmap_pages[idx]);
+ return page_to_pfn(vif->mmap_pages[idx]);
}
-static inline unsigned long idx_to_kaddr(struct xen_netbk *netbk,
+static inline unsigned long idx_to_kaddr(struct xenvif *vif,
u16 idx)
{
- return (unsigned long)pfn_to_kaddr(idx_to_pfn(netbk, idx));
+ return (unsigned long)pfn_to_kaddr(idx_to_pfn(vif, idx));
}
/*
@@ -224,15 +133,10 @@ static inline pending_ring_idx_t pending_index(unsigned i)
return i & (MAX_PENDING_REQS-1);
}
-static inline pending_ring_idx_t nr_pending_reqs(struct xen_netbk *netbk)
+static inline pending_ring_idx_t nr_pending_reqs(struct xenvif *vif)
{
return MAX_PENDING_REQS -
- netbk->pending_prod + netbk->pending_cons;
-}
-
-static void xen_netbk_kick_thread(struct xen_netbk *netbk)
-{
- wake_up(&netbk->wq);
+ vif->pending_prod + vif->pending_cons;
}
static int max_required_rx_slots(struct xenvif *vif)
@@ -364,15 +268,15 @@ struct netrx_pending_operations {
unsigned copy_prod, copy_cons;
unsigned meta_prod, meta_cons;
struct gnttab_copy *copy;
- struct netbk_rx_meta *meta;
+ struct xenvif_rx_meta *meta;
int copy_off;
grant_ref_t copy_gref;
};
-static struct netbk_rx_meta *get_next_rx_buffer(struct xenvif *vif,
- struct netrx_pending_operations *npo)
+static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif,
+ struct netrx_pending_operations *npo)
{
- struct netbk_rx_meta *meta;
+ struct xenvif_rx_meta *meta;
struct xen_netif_rx_request *req;
req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
@@ -398,7 +302,7 @@ static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
unsigned long offset, int *head)
{
struct gnttab_copy *copy_gop;
- struct netbk_rx_meta *meta;
+ struct xenvif_rx_meta *meta;
unsigned long bytes;
/* Data must not cross a page boundary. */
@@ -434,15 +338,15 @@ static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
copy_gop = npo->copy + npo->copy_prod++;
copy_gop->flags = GNTCOPY_dest_gref;
+ copy_gop->len = bytes;
+
copy_gop->source.domid = DOMID_SELF;
copy_gop->source.u.gmfn = virt_to_mfn(page_address(page));
-
copy_gop->source.offset = offset;
- copy_gop->dest.domid = vif->domid;
+ copy_gop->dest.domid = vif->domid;
copy_gop->dest.offset = npo->copy_off;
copy_gop->dest.u.ref = npo->copy_gref;
- copy_gop->len = bytes;
npo->copy_off += bytes;
meta->size += bytes;
@@ -485,7 +389,7 @@ static int netbk_gop_skb(struct sk_buff *skb,
int nr_frags = skb_shinfo(skb)->nr_frags;
int i;
struct xen_netif_rx_request *req;
- struct netbk_rx_meta *meta;
+ struct xenvif_rx_meta *meta;
unsigned char *data;
int head = 1;
int old_meta_prod;
@@ -565,7 +469,7 @@ static int netbk_check_gop(struct xenvif *vif, int nr_meta_slots,
}
static void netbk_add_frag_responses(struct xenvif *vif, int status,
- struct netbk_rx_meta *meta,
+ struct xenvif_rx_meta *meta,
int nr_meta_slots)
{
int i;
@@ -594,9 +498,13 @@ struct skb_cb_overlay {
int meta_slots_used;
};
-static void xen_netbk_rx_action(struct xen_netbk *netbk)
+static void xen_netbk_kick_thread(struct xenvif *vif)
+{
+ wake_up(&vif->wq);
+}
+
+void xen_netbk_rx_action(struct xenvif *vif)
{
- struct xenvif *vif = NULL, *tmp;
s8 status;
u16 flags;
struct xen_netif_rx_response *resp;
@@ -608,17 +516,18 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk)
int count;
unsigned long offset;
struct skb_cb_overlay *sco;
+ int need_to_notify = 0;
struct netrx_pending_operations npo = {
- .copy = netbk->grant_copy_op,
- .meta = netbk->meta,
+ .copy = vif->grant_copy_op,
+ .meta = vif->meta,
};
skb_queue_head_init(&rxq);
count = 0;
- while ((skb = skb_dequeue(&netbk->rx_queue)) != NULL) {
+ while ((skb = skb_dequeue(&vif->rx_queue)) != NULL) {
vif = netdev_priv(skb->dev);
nr_frags = skb_shinfo(skb)->nr_frags;
@@ -635,27 +544,27 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk)
break;
}
- BUG_ON(npo.meta_prod > ARRAY_SIZE(netbk->meta));
+ BUG_ON(npo.meta_prod > ARRAY_SIZE(vif->meta));
if (!npo.copy_prod)
return;
- BUG_ON(npo.copy_prod > ARRAY_SIZE(netbk->grant_copy_op));
- gnttab_batch_copy(netbk->grant_copy_op, npo.copy_prod);
+ BUG_ON(npo.copy_prod > ARRAY_SIZE(vif->grant_copy_op));
+ gnttab_batch_copy(vif->grant_copy_op, npo.copy_prod);
while ((skb = __skb_dequeue(&rxq)) != NULL) {
sco = (struct skb_cb_overlay *)skb->cb;
vif = netdev_priv(skb->dev);
- if (netbk->meta[npo.meta_cons].gso_size && vif->gso_prefix) {
+ if (vif->meta[npo.meta_cons].gso_size && vif->gso_prefix) {
resp = RING_GET_RESPONSE(&vif->rx,
- vif->rx.rsp_prod_pvt++);
+ vif->rx.rsp_prod_pvt++);
resp->flags = XEN_NETRXF_gso_prefix | XEN_NETRXF_more_data;
- resp->offset = netbk->meta[npo.meta_cons].gso_size;
- resp->id = netbk->meta[npo.meta_cons].id;
+ resp->offset = vif->meta[npo.meta_cons].gso_size;
+ resp->id = vif->meta[npo.meta_cons].id;
resp->status = sco->meta_slots_used;
npo.meta_cons++;
@@ -680,12 +589,12 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk)
flags |= XEN_NETRXF_data_validated;
offset = 0;
- resp = make_rx_response(vif, netbk->meta[npo.meta_cons].id,
+ resp = make_rx_response(vif, vif->meta[npo.meta_cons].id,
status, offset,
- netbk->meta[npo.meta_cons].size,
+ vif->meta[npo.meta_cons].size,
flags);
- if (netbk->meta[npo.meta_cons].gso_size && !vif->gso_prefix) {
+ if (vif->meta[npo.meta_cons].gso_size && !vif->gso_prefix) {
struct xen_netif_extra_info *gso =
(struct xen_netif_extra_info *)
RING_GET_RESPONSE(&vif->rx,
@@ -693,7 +602,7 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk)
resp->flags |= XEN_NETRXF_extra_info;
- gso->u.gso.size = netbk->meta[npo.meta_cons].gso_size;
+ gso->u.gso.size = vif->meta[npo.meta_cons].gso_size;
gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4;
gso->u.gso.pad = 0;
gso->u.gso.features = 0;
@@ -703,112 +612,33 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk)
}
netbk_add_frag_responses(vif, status,
- netbk->meta + npo.meta_cons + 1,
+ vif->meta + npo.meta_cons + 1,
sco->meta_slots_used);
RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->rx, ret);
+ if (ret)
+ need_to_notify = 1;
+
xenvif_notify_tx_completion(vif);
- if (ret && list_empty(&vif->notify_list))
- list_add_tail(&vif->notify_list, ¬ify);
- else
- xenvif_put(vif);
npo.meta_cons += sco->meta_slots_used;
dev_kfree_skb(skb);
}
- list_for_each_entry_safe(vif, tmp, ¬ify, notify_list) {
+ if (need_to_notify)
notify_remote_via_irq(vif->rx_irq);
- list_del_init(&vif->notify_list);
- xenvif_put(vif);
- }
/* More work to do? */
- if (!skb_queue_empty(&netbk->rx_queue) &&
- !timer_pending(&netbk->net_timer))
- xen_netbk_kick_thread(netbk);
+ if (!skb_queue_empty(&vif->rx_queue))
+ xen_netbk_kick_thread(vif);
}
void xen_netbk_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb)
{
- struct xen_netbk *netbk = vif->netbk;
+ skb_queue_tail(&vif->rx_queue, skb);
- skb_queue_tail(&netbk->rx_queue, skb);
-
- xen_netbk_kick_thread(netbk);
-}
-
-static void xen_netbk_alarm(unsigned long data)
-{
- struct xen_netbk *netbk = (struct xen_netbk *)data;
- xen_netbk_kick_thread(netbk);
-}
-
-static int __on_net_schedule_list(struct xenvif *vif)
-{
- return !list_empty(&vif->schedule_list);
-}
-
-/* Must be called with net_schedule_list_lock held */
-static void remove_from_net_schedule_list(struct xenvif *vif)
-{
- if (likely(__on_net_schedule_list(vif))) {
- list_del_init(&vif->schedule_list);
- xenvif_put(vif);
- }
-}
-
-static struct xenvif *poll_net_schedule_list(struct xen_netbk *netbk)
-{
- struct xenvif *vif = NULL;
-
- spin_lock_irq(&netbk->net_schedule_list_lock);
- if (list_empty(&netbk->net_schedule_list))
- goto out;
-
- vif = list_first_entry(&netbk->net_schedule_list,
- struct xenvif, schedule_list);
- if (!vif)
- goto out;
-
- xenvif_get(vif);
-
- remove_from_net_schedule_list(vif);
-out:
- spin_unlock_irq(&netbk->net_schedule_list_lock);
- return vif;
-}
-
-void xen_netbk_schedule_xenvif(struct xenvif *vif)
-{
- unsigned long flags;
- struct xen_netbk *netbk = vif->netbk;
-
- if (__on_net_schedule_list(vif))
- goto kick;
-
- spin_lock_irqsave(&netbk->net_schedule_list_lock, flags);
- if (!__on_net_schedule_list(vif) &&
- likely(xenvif_schedulable(vif))) {
- list_add_tail(&vif->schedule_list, &netbk->net_schedule_list);
- xenvif_get(vif);
- }
- spin_unlock_irqrestore(&netbk->net_schedule_list_lock, flags);
-
-kick:
- smp_mb();
- if ((nr_pending_reqs(netbk) < (MAX_PENDING_REQS/2)) &&
- !list_empty(&netbk->net_schedule_list))
- xen_netbk_kick_thread(netbk);
-}
-
-void xen_netbk_deschedule_xenvif(struct xenvif *vif)
-{
- struct xen_netbk *netbk = vif->netbk;
- spin_lock_irq(&netbk->net_schedule_list_lock);
- remove_from_net_schedule_list(vif);
- spin_unlock_irq(&netbk->net_schedule_list_lock);
+ xen_netbk_kick_thread(vif);
}
void xen_netbk_check_rx_xenvif(struct xenvif *vif)
@@ -818,7 +648,7 @@ void xen_netbk_check_rx_xenvif(struct xenvif *vif)
RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, more_to_do);
if (more_to_do)
- xen_netbk_schedule_xenvif(vif);
+ napi_schedule(&vif->napi);
}
static void tx_add_credit(struct xenvif *vif)
@@ -860,15 +690,12 @@ static void netbk_tx_err(struct xenvif *vif,
txp = RING_GET_REQUEST(&vif->tx, cons++);
} while (1);
vif->tx.req_cons = cons;
- xen_netbk_check_rx_xenvif(vif);
- xenvif_put(vif);
}
static void netbk_fatal_tx_err(struct xenvif *vif)
{
netdev_err(vif->dev, "fatal error; disabling device\n");
xenvif_carrier_off(vif);
- xenvif_put(vif);
}
static int netbk_count_requests(struct xenvif *vif,
@@ -969,19 +796,20 @@ static int netbk_count_requests(struct xenvif *vif,
return slots;
}
-static struct page *xen_netbk_alloc_page(struct xen_netbk *netbk,
+static struct page *xen_netbk_alloc_page(struct xenvif *vif,
u16 pending_idx)
{
struct page *page;
- page = alloc_page(GFP_KERNEL|__GFP_COLD);
+
+ page = alloc_page(GFP_ATOMIC|__GFP_COLD);
if (!page)
return NULL;
- netbk->mmap_pages[pending_idx] = page;
+ vif->mmap_pages[pending_idx] = page;
+
return page;
}
-static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
- struct xenvif *vif,
+static struct gnttab_copy *xen_netbk_get_requests(struct xenvif *vif,
struct sk_buff *skb,
struct xen_netif_tx_request *txp,
struct gnttab_copy *gop)
@@ -1012,9 +840,9 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
for (shinfo->nr_frags = slot = start; slot < nr_slots;
shinfo->nr_frags++) {
struct pending_tx_info *pending_tx_info =
- netbk->pending_tx_info;
+ vif->pending_tx_info;
- page = alloc_page(GFP_KERNEL|__GFP_COLD);
+ page = alloc_page(GFP_ATOMIC|__GFP_COLD);
if (!page)
goto err;
@@ -1049,21 +877,18 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
gop->len = txp->size;
dst_offset += gop->len;
- index = pending_index(netbk->pending_cons++);
+ index = pending_index(vif->pending_cons++);
- pending_idx = netbk->pending_ring[index];
+ pending_idx = vif->pending_ring[index];
memcpy(&pending_tx_info[pending_idx].req, txp,
sizeof(*txp));
- xenvif_get(vif);
-
- pending_tx_info[pending_idx].vif = vif;
/* Poison these fields, corresponding
* fields for head tx req will be set
* to correct values after the loop.
*/
- netbk->mmap_pages[pending_idx] = (void *)(~0UL);
+ vif->mmap_pages[pending_idx] = (void *)(~0UL);
pending_tx_info[pending_idx].head =
INVALID_PENDING_RING_IDX;
@@ -1083,7 +908,7 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
first->req.offset = 0;
first->req.size = dst_offset;
first->head = start_idx;
- netbk->mmap_pages[head_idx] = page;
+ vif->mmap_pages[head_idx] = page;
frag_set_pending_idx(&frags[shinfo->nr_frags], head_idx);
}
@@ -1093,18 +918,18 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
err:
/* Unwind, freeing all pages and sending error responses. */
while (shinfo->nr_frags-- > start) {
- xen_netbk_idx_release(netbk,
+ xen_netbk_idx_release(vif,
frag_get_pending_idx(&frags[shinfo->nr_frags]),
XEN_NETIF_RSP_ERROR);
}
/* The head too, if necessary. */
if (start)
- xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR);
+ xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
return NULL;
}
-static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
+static int xen_netbk_tx_check_gop(struct xenvif *vif,
struct sk_buff *skb,
struct gnttab_copy **gopp)
{
@@ -1119,7 +944,7 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
/* Check status of header. */
err = gop->status;
if (unlikely(err))
- xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR);
+ xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
/* Skip first skb fragment if it is on same page as header fragment. */
start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx);
@@ -1129,7 +954,7 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
pending_ring_idx_t head;
pending_idx = frag_get_pending_idx(&shinfo->frags[i]);
- tx_info = &netbk->pending_tx_info[pending_idx];
+ tx_info = &vif->pending_tx_info[pending_idx];
head = tx_info->head;
/* Check error status: if okay then remember grant handle. */
@@ -1137,18 +962,19 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
newerr = (++gop)->status;
if (newerr)
break;
- peek = netbk->pending_ring[pending_index(++head)];
- } while (!pending_tx_is_head(netbk, peek));
+ peek = vif->pending_ring[pending_index(++head)];
+ } while (!pending_tx_is_head(vif, peek));
if (likely(!newerr)) {
/* Had a previous error? Invalidate this fragment. */
if (unlikely(err))
- xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ xen_netbk_idx_release(vif, pending_idx,
+ XEN_NETIF_RSP_OKAY);
continue;
}
/* Error on this fragment: respond to client with an error. */
- xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR);
+ xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
/* Not the first error? Preceding frags already invalidated. */
if (err)
@@ -1156,10 +982,11 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
/* First error: invalidate header and preceding fragments. */
pending_idx = *((u16 *)skb->data);
- xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
for (j = start; j < i; j++) {
pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
- xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ xen_netbk_idx_release(vif, pending_idx,
+ XEN_NETIF_RSP_OKAY);
}
/* Remember the error: invalidate all subsequent fragments. */
@@ -1170,7 +997,7 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
return err;
}
-static void xen_netbk_fill_frags(struct xen_netbk *netbk, struct sk_buff *skb)
+static void xen_netbk_fill_frags(struct xenvif *vif, struct sk_buff *skb)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
int nr_frags = shinfo->nr_frags;
@@ -1184,16 +1011,16 @@ static void xen_netbk_fill_frags(struct xen_netbk *netbk, struct sk_buff *skb)
pending_idx = frag_get_pending_idx(frag);
- txp = &netbk->pending_tx_info[pending_idx].req;
- page = virt_to_page(idx_to_kaddr(netbk, pending_idx));
+ txp = &vif->pending_tx_info[pending_idx].req;
+ page = virt_to_page(idx_to_kaddr(vif, pending_idx));
__skb_fill_page_desc(skb, i, page, txp->offset, txp->size);
skb->len += txp->size;
skb->data_len += txp->size;
skb->truesize += txp->size;
/* Take an extra reference to offset xen_netbk_idx_release */
- get_page(netbk->mmap_pages[pending_idx]);
- xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ get_page(vif->mmap_pages[pending_idx]);
+ xen_netbk_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
}
}
@@ -1353,16 +1180,14 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
return false;
}
-static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
+static unsigned xen_netbk_tx_build_gops(struct xenvif *vif)
{
- struct gnttab_copy *gop = netbk->tx_copy_ops, *request_gop;
+ struct gnttab_copy *gop = vif->tx_copy_ops, *request_gop;
struct sk_buff *skb;
int ret;
- while ((nr_pending_reqs(netbk) + XEN_NETBK_LEGACY_SLOTS_MAX
- < MAX_PENDING_REQS) &&
- !list_empty(&netbk->net_schedule_list)) {
- struct xenvif *vif;
+ while ((nr_pending_reqs(vif) + XEN_NETBK_LEGACY_SLOTS_MAX
+ < MAX_PENDING_REQS)) {
struct xen_netif_tx_request txreq;
struct xen_netif_tx_request txfrags[XEN_NETBK_LEGACY_SLOTS_MAX];
struct page *page;
@@ -1373,16 +1198,6 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
unsigned int data_len;
pending_ring_idx_t index;
- /* Get a netif from the list with work to do. */
- vif = poll_net_schedule_list(netbk);
- /* This can sometimes happen because the test of
- * list_empty(net_schedule_list) at the top of the
- * loop is unlocked. Just go back and have another
- * look.
- */
- if (!vif)
- continue;
-
if (vif->tx.sring->req_prod - vif->tx.req_cons >
XEN_NETIF_TX_RING_SIZE) {
netdev_err(vif->dev,
@@ -1395,10 +1210,8 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
}
RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, work_to_do);
- if (!work_to_do) {
- xenvif_put(vif);
- continue;
- }
+ if (!work_to_do)
+ break;
idx = vif->tx.req_cons;
rmb(); /* Ensure that we see the request before we copy it. */
@@ -1406,10 +1219,8 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
/* Credit-based scheduling. */
if (txreq.size > vif->remaining_credit &&
- tx_credit_exceeded(vif, txreq.size)) {
- xenvif_put(vif);
- continue;
- }
+ tx_credit_exceeded(vif, txreq.size))
+ break;
vif->remaining_credit -= txreq.size;
@@ -1422,12 +1233,12 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
work_to_do);
idx = vif->tx.req_cons;
if (unlikely(work_to_do < 0))
- continue;
+ break;
}
ret = netbk_count_requests(vif, &txreq, txfrags, work_to_do);
if (unlikely(ret < 0))
- continue;
+ break;
idx += ret;
@@ -1435,7 +1246,7 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
netdev_dbg(vif->dev,
"Bad packet size: %d\n", txreq.size);
netbk_tx_err(vif, &txreq, idx);
- continue;
+ break;
}
/* No crossing a page as the payload mustn't fragment. */
@@ -1445,11 +1256,11 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
txreq.offset, txreq.size,
(txreq.offset&~PAGE_MASK) + txreq.size);
netbk_fatal_tx_err(vif);
- continue;
+ break;
}
- index = pending_index(netbk->pending_cons);
- pending_idx = netbk->pending_ring[index];
+ index = pending_index(vif->pending_cons);
+ pending_idx = vif->pending_ring[index];
data_len = (txreq.size > PKT_PROT_LEN &&
ret < XEN_NETBK_LEGACY_SLOTS_MAX) ?
@@ -1474,16 +1285,16 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
if (netbk_set_skb_gso(vif, skb, gso)) {
/* Failure in netbk_set_skb_gso is fatal. */
kfree_skb(skb);
- continue;
+ break;
}
}
/* XXX could copy straight to head */
- page = xen_netbk_alloc_page(netbk, pending_idx);
+ page = xen_netbk_alloc_page(vif, pending_idx);
if (!page) {
kfree_skb(skb);
netbk_tx_err(vif, &txreq, idx);
- continue;
+ break;
}
gop->source.u.ref = txreq.gref;
@@ -1499,10 +1310,9 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
gop++;
- memcpy(&netbk->pending_tx_info[pending_idx].req,
+ memcpy(&vif->pending_tx_info[pending_idx].req,
&txreq, sizeof(txreq));
- netbk->pending_tx_info[pending_idx].vif = vif;
- netbk->pending_tx_info[pending_idx].head = index;
+ vif->pending_tx_info[pending_idx].head = index;
*((u16 *)skb->data) = pending_idx;
__skb_put(skb, data_len);
@@ -1517,46 +1327,45 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
INVALID_PENDING_IDX);
}
- netbk->pending_cons++;
+ vif->pending_cons++;
- request_gop = xen_netbk_get_requests(netbk, vif,
- skb, txfrags, gop);
+ request_gop = xen_netbk_get_requests(vif, skb, txfrags, gop);
if (request_gop == NULL) {
kfree_skb(skb);
netbk_tx_err(vif, &txreq, idx);
- continue;
+ break;
}
gop = request_gop;
- __skb_queue_tail(&netbk->tx_queue, skb);
+ __skb_queue_tail(&vif->tx_queue, skb);
vif->tx.req_cons = idx;
- xen_netbk_check_rx_xenvif(vif);
- if ((gop-netbk->tx_copy_ops) >= ARRAY_SIZE(netbk->tx_copy_ops))
+ if ((gop-vif->tx_copy_ops) >= ARRAY_SIZE(vif->tx_copy_ops))
break;
}
- return gop - netbk->tx_copy_ops;
+ return gop - vif->tx_copy_ops;
}
-static void xen_netbk_tx_submit(struct xen_netbk *netbk)
+
+static int xen_netbk_tx_submit(struct xenvif *vif, int budget)
{
- struct gnttab_copy *gop = netbk->tx_copy_ops;
+ struct gnttab_copy *gop = vif->tx_copy_ops;
struct sk_buff *skb;
+ int work_done = 0;
- while ((skb = __skb_dequeue(&netbk->tx_queue)) != NULL) {
+ while (work_done < budget &&
+ (skb = __skb_dequeue(&vif->tx_queue)) != NULL) {
struct xen_netif_tx_request *txp;
- struct xenvif *vif;
u16 pending_idx;
unsigned data_len;
pending_idx = *((u16 *)skb->data);
- vif = netbk->pending_tx_info[pending_idx].vif;
- txp = &netbk->pending_tx_info[pending_idx].req;
+ txp = &vif->pending_tx_info[pending_idx].req;
/* Check the remap error code. */
- if (unlikely(xen_netbk_tx_check_gop(netbk, skb, &gop))) {
+ if (unlikely(xen_netbk_tx_check_gop(vif, skb, &gop))) {
netdev_dbg(vif->dev, "netback grant failed.\n");
skb_shinfo(skb)->nr_frags = 0;
kfree_skb(skb);
@@ -1565,7 +1374,7 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk)
data_len = skb->len;
memcpy(skb->data,
- (void *)(idx_to_kaddr(netbk, pending_idx)|txp->offset),
+ (void *)(idx_to_kaddr(vif, pending_idx)|txp->offset),
data_len);
if (data_len < txp->size) {
/* Append the packet payload as a fragment. */
@@ -1573,7 +1382,8 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk)
txp->size -= data_len;
} else {
/* Schedule a response immediately. */
- xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ xen_netbk_idx_release(vif, pending_idx,
+ XEN_NETIF_RSP_OKAY);
}
if (txp->flags & XEN_NETTXF_csum_blank)
@@ -1581,7 +1391,7 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk)
else if (txp->flags & XEN_NETTXF_data_validated)
skb->ip_summed = CHECKSUM_UNNECESSARY;
- xen_netbk_fill_frags(netbk, skb);
+ xen_netbk_fill_frags(vif, skb);
/*
* If the initial fragment was < PKT_PROT_LEN then
@@ -1609,53 +1419,61 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk)
vif->dev->stats.rx_bytes += skb->len;
vif->dev->stats.rx_packets++;
- xenvif_receive_skb(vif, skb);
+ work_done++;
+
+ netif_receive_skb(skb);
}
+
+ return work_done;
}
/* Called after netfront has transmitted */
-static void xen_netbk_tx_action(struct xen_netbk *netbk)
+int xen_netbk_tx_action(struct xenvif *vif, int budget)
{
unsigned nr_gops;
+ int work_done;
- nr_gops = xen_netbk_tx_build_gops(netbk);
+ if (unlikely(!tx_work_todo(vif)))
+ return 0;
+
+ nr_gops = xen_netbk_tx_build_gops(vif);
if (nr_gops == 0)
- return;
+ return 0;
+
+ gnttab_batch_copy(vif->tx_copy_ops, nr_gops);
- gnttab_batch_copy(netbk->tx_copy_ops, nr_gops);
+ work_done = xen_netbk_tx_submit(vif, nr_gops);
- xen_netbk_tx_submit(netbk);
+ return work_done;
}
-static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx,
+static void xen_netbk_idx_release(struct xenvif *vif, u16 pending_idx,
u8 status)
{
- struct xenvif *vif;
struct pending_tx_info *pending_tx_info;
pending_ring_idx_t head;
u16 peek; /* peek into next tx request */
- BUG_ON(netbk->mmap_pages[pending_idx] == (void *)(~0UL));
+ BUG_ON(vif->mmap_pages[pending_idx] == (void *)(~0UL));
/* Already complete? */
- if (netbk->mmap_pages[pending_idx] == NULL)
+ if (vif->mmap_pages[pending_idx] == NULL)
return;
- pending_tx_info = &netbk->pending_tx_info[pending_idx];
+ pending_tx_info = &vif->pending_tx_info[pending_idx];
- vif = pending_tx_info->vif;
head = pending_tx_info->head;
- BUG_ON(!pending_tx_is_head(netbk, head));
- BUG_ON(netbk->pending_ring[pending_index(head)] != pending_idx);
+ BUG_ON(!pending_tx_is_head(vif, head));
+ BUG_ON(vif->pending_ring[pending_index(head)] != pending_idx);
do {
pending_ring_idx_t index;
pending_ring_idx_t idx = pending_index(head);
- u16 info_idx = netbk->pending_ring[idx];
+ u16 info_idx = vif->pending_ring[idx];
- pending_tx_info = &netbk->pending_tx_info[info_idx];
+ pending_tx_info = &vif->pending_tx_info[info_idx];
make_tx_response(vif, &pending_tx_info->req, status);
/* Setting any number other than
@@ -1664,18 +1482,15 @@ static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx,
*/
pending_tx_info->head = 0;
- index = pending_index(netbk->pending_prod++);
- netbk->pending_ring[index] = netbk->pending_ring[info_idx];
+ index = pending_index(vif->pending_prod++);
+ vif->pending_ring[index] = vif->pending_ring[info_idx];
- xenvif_put(vif);
+ peek = vif->pending_ring[pending_index(++head)];
- peek = netbk->pending_ring[pending_index(++head)];
+ } while (!pending_tx_is_head(vif, peek));
- } while (!pending_tx_is_head(netbk, peek));
-
- netbk->mmap_pages[pending_idx]->mapping = 0;
- put_page(netbk->mmap_pages[pending_idx]);
- netbk->mmap_pages[pending_idx] = NULL;
+ put_page(vif->mmap_pages[pending_idx]);
+ vif->mmap_pages[pending_idx] = NULL;
}
@@ -1723,45 +1538,22 @@ static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
return resp;
}
-static inline int rx_work_todo(struct xen_netbk *netbk)
+static inline int rx_work_todo(struct xenvif *vif)
{
- return !skb_queue_empty(&netbk->rx_queue);
+ return !skb_queue_empty(&vif->rx_queue);
}
-static inline int tx_work_todo(struct xen_netbk *netbk)
+static inline int tx_work_todo(struct xenvif *vif)
{
- if ((nr_pending_reqs(netbk) + XEN_NETBK_LEGACY_SLOTS_MAX
- < MAX_PENDING_REQS) &&
- !list_empty(&netbk->net_schedule_list))
+ if (likely(RING_HAS_UNCONSUMED_REQUESTS(&vif->tx)) &&
+ (nr_pending_reqs(vif) + XEN_NETBK_LEGACY_SLOTS_MAX
+ < MAX_PENDING_REQS))
return 1;
return 0;
}
-static int xen_netbk_kthread(void *data)
-{
- struct xen_netbk *netbk = data;
- while (!kthread_should_stop()) {
- wait_event_interruptible(netbk->wq,
- rx_work_todo(netbk) ||
- tx_work_todo(netbk) ||
- kthread_should_stop());
- cond_resched();
-
- if (kthread_should_stop())
- break;
-
- if (rx_work_todo(netbk))
- xen_netbk_rx_action(netbk);
-
- if (tx_work_todo(netbk))
- xen_netbk_tx_action(netbk);
- }
-
- return 0;
-}
-
void xen_netbk_unmap_frontend_rings(struct xenvif *vif)
{
if (vif->tx.sring)
@@ -1807,11 +1599,29 @@ err:
return err;
}
+int xen_netbk_kthread(void *data)
+{
+ struct xenvif *vif = data;
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(vif->wq,
+ rx_work_todo(vif) ||
+ kthread_should_stop());
+ if (kthread_should_stop())
+ break;
+
+ if (rx_work_todo(vif))
+ xen_netbk_rx_action(vif);
+
+ cond_resched();
+ }
+
+ return 0;
+}
+
static int __init netback_init(void)
{
- int i;
int rc = 0;
- int group;
if (!xen_domain())
return -ENODEV;
@@ -1822,48 +1632,6 @@ static int __init netback_init(void)
fatal_skb_slots = XEN_NETBK_LEGACY_SLOTS_MAX;
}
- xen_netbk_group_nr = num_online_cpus();
- xen_netbk = vzalloc(sizeof(struct xen_netbk) * xen_netbk_group_nr);
- if (!xen_netbk)
- return -ENOMEM;
-
- for (group = 0; group < xen_netbk_group_nr; group++) {
- struct xen_netbk *netbk = &xen_netbk[group];
- skb_queue_head_init(&netbk->rx_queue);
- skb_queue_head_init(&netbk->tx_queue);
-
- init_timer(&netbk->net_timer);
- netbk->net_timer.data = (unsigned long)netbk;
- netbk->net_timer.function = xen_netbk_alarm;
-
- netbk->pending_cons = 0;
- netbk->pending_prod = MAX_PENDING_REQS;
- for (i = 0; i < MAX_PENDING_REQS; i++)
- netbk->pending_ring[i] = i;
-
- init_waitqueue_head(&netbk->wq);
- netbk->task = kthread_create(xen_netbk_kthread,
- (void *)netbk,
- "netback/%u", group);
-
- if (IS_ERR(netbk->task)) {
- pr_alert("kthread_create() fails at netback\n");
- del_timer(&netbk->net_timer);
- rc = PTR_ERR(netbk->task);
- goto failed_init;
- }
-
- kthread_bind(netbk->task, group);
-
- INIT_LIST_HEAD(&netbk->net_schedule_list);
-
- spin_lock_init(&netbk->net_schedule_list_lock);
-
- atomic_set(&netbk->netfront_count, 0);
-
- wake_up_process(netbk->task);
- }
-
rc = xenvif_xenbus_init();
if (rc)
goto failed_init;
@@ -1871,35 +1639,14 @@ static int __init netback_init(void)
return 0;
failed_init:
- while (--group >= 0) {
- struct xen_netbk *netbk = &xen_netbk[group];
- del_timer(&netbk->net_timer);
- kthread_stop(netbk->task);
- }
- vfree(xen_netbk);
return rc;
-
}
module_init(netback_init);
static void __exit netback_fini(void)
{
- int i, j;
-
xenvif_xenbus_fini();
-
- for (i = 0; i < xen_netbk_group_nr; i++) {
- struct xen_netbk *netbk = &xen_netbk[i];
- del_timer_sync(&netbk->net_timer);
- kthread_stop(netbk->task);
- for (j = 0; j < MAX_PENDING_REQS; j++) {
- if (netbk->mmap_pages[j])
- __free_page(netbk->mmap_pages[j]);
- }
- }
-
- vfree(xen_netbk);
}
module_exit(netback_fini);
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH] net/cadence/macb: fix kernel Oops if no PHY were discovered during probe
From: boris brezillon @ 2013-08-26 12:01 UTC (permalink / raw)
To: Bo Shen; +Cc: Nicolas Ferre, netdev, linux-kernel, linux-arm-kernel
In-Reply-To: <521B1B56.8060903@atmel.com>
Hello Bo,
On 26/08/2013 11:09, Bo Shen wrote:
> Hi Boris,
>
> On 08/25/2013 03:21 AM, Boris BREZILLON wrote:
>> Test the presence of a PHY device before printing attached PHY
>> informations.
>>
>> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
>> ---
>> drivers/net/ethernet/cadence/macb.c | 6 ++++--
>> 1 file changed, 4 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/cadence/macb.c
>> b/drivers/net/ethernet/cadence/macb.c
>> index e866608..fd3b67f 100644
>> --- a/drivers/net/ethernet/cadence/macb.c
>> +++ b/drivers/net/ethernet/cadence/macb.c
>> @@ -1868,8 +1868,10 @@ static int __init macb_probe(struct
>> platform_device *pdev)
>> dev->irq, dev->dev_addr);
>>
>> phydev = bp->phy_dev;
>> - netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s,
>> irq=%d)\n",
>> - phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
>> + if (phydev)
>> + netdev_info(dev, "attached PHY driver [%s]
>> (mii_bus:phy_addr=%s, irq=%d)\n",
>> + phydev->drv->name, dev_name(&phydev->dev),
>> + phydev->irq);
>
> Actually no need this check, if PHY is attached failed, the macb
> driver probe will fail, then it won't show this message.
You're right (thanks for pointing this out).
Indeed this is a bug I introduced in this patch series (
https://lkml.org/lkml/2013/8/22/381).
In macb_mii_init function, the err field may be set to 0 (instead of the
default -ENXIO value) if no PHY
is discovered.
As macb_mii_probe return code is not copied to err, this result in a 0
return even if macb_mii_probe
failed.
Nicolas, forget about this patch, I'll fix this in the appropriate patch
series (except if you plan to add
dynamic MAC/PHY association, and treat missing PHY during probe process
as an acceptable result :-)).
Best Regards,
Boris BREZILLON
>
>> return 0;
>>
>>
>
> Best Regards,
> Bo Shen
^ permalink raw reply
* Re: [PATCH net-next 07/16] sfc: Limit scope of a Falcon A1 IRQ workaround
From: Sergei Shtylyov @ 2013-08-26 12:31 UTC (permalink / raw)
To: Ben Hutchings; +Cc: David Miller, netdev, linux-net-drivers
In-Reply-To: <1377471823.2586.76.camel@deadeye.wl.decadent.org.uk>
Hello.
On 26-08-2013 3:03, Ben Hutchings wrote:
> We unconditionally acknowledge legacy interrupts just before disabling
> them. This workaround is needed on Falcon A1 but probably not on
> later chips where the legacy interrupt mechanism is different. It was
> also originally done after the IRQ handler was removed, not before.
> Restore the original behaviour for Falcon A1 only by doing this
> acknowledgement in the efx_nic_type::fini operation.
> Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
> ---
> drivers/net/ethernet/sfc/falcon.c | 4 ++--
> drivers/net/ethernet/sfc/nic.c | 7 -------
> drivers/net/ethernet/sfc/nic.h | 1 -
> 3 files changed, 2 insertions(+), 10 deletions(-)
> diff --git a/drivers/net/ethernet/sfc/falcon.c b/drivers/net/ethernet/sfc/falcon.c
> index f8de382..4492129 100644
> --- a/drivers/net/ethernet/sfc/falcon.c
> +++ b/drivers/net/ethernet/sfc/falcon.c
> @@ -336,7 +336,7 @@ static void falcon_prepare_flush(struct efx_nic *efx)
> *
> * NB most hardware supports MSI interrupts
> */
> -inline void falcon_irq_ack_a1(struct efx_nic *efx)
> +static inline void falcon_irq_ack_a1(struct efx_nic *efx)
Does inline make sense still when this now is referenced indirectly?
> {
> efx_dword_t reg;
>
> @@ -2343,7 +2343,7 @@ const struct efx_nic_type falcon_a1_nic_type = {
> .remove = falcon_remove_nic,
> .init = falcon_init_nic,
> .dimension_resources = falcon_dimension_resources,
> - .fini = efx_port_dummy_op_void,
> + .fini = falcon_irq_ack_a1,
> .monitor = falcon_monitor,
> .map_reset_reason = falcon_map_reset_reason,
> .map_reset_flags = falcon_map_reset_flags,
WBR, Sergei
^ permalink raw reply
* [PATCH v2 0/2] net/cadence/macb: add support for dt phy definition
From: Boris BREZILLON @ 2013-08-26 12:33 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
Ian Campbell, Russell King, Nicolas Ferre
Cc: devicetree, linux-arm-kernel, linux-kernel, netdev,
Boris BREZILLON
Hello,
This patch series adds support for ethernet phy definition using device
tree.
This may help in moving some at91 boards to dt (some of them define an
interrupt pin).
Tested on samad31ek.
Best Regards,
Boris
Changes since v1:
- fix wrong macb_mii_init return code when no PHY device is discovered
Boris BREZILLON (2):
net/cadence/macb: add support for dt phy definition
ARM: at91/dt: define phy available on sama5d3 mother board
arch/arm/boot/dts/sama5d3xmb.dtsi | 8 ++++++
drivers/net/ethernet/cadence/macb.c | 47 +++++++++++++++++++++++++++--------
2 files changed, 45 insertions(+), 10 deletions(-)
--
1.7.9.5
^ permalink raw reply
* [PATCH v2 1/2] net/cadence/macb: add support for dt phy definition
From: Boris BREZILLON @ 2013-08-26 12:34 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
Ian Campbell, Russell King, Nicolas Ferre
Cc: devicetree, linux-arm-kernel, linux-kernel, netdev,
Boris BREZILLON
In-Reply-To: <1377520431-21807-1-git-send-email-b.brezillon@overkiz.com>
The macb driver only handle PHY description through platform_data
(macb_platform_data).
Thus, when using dt you cannot define phy properties like phy address or
phy irq pin.
This patch makes use of the of_mdiobus_register to add support for
phy device definition using dt.
A fallback to the autoscan procedure is added in case there is no phy
devices defined in dt.
Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
---
drivers/net/ethernet/cadence/macb.c | 47 +++++++++++++++++++++++++++--------
1 file changed, 37 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index e866608..7660c45 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -27,6 +27,7 @@
#include <linux/phy.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/pinctrl/consumer.h>
@@ -275,7 +276,7 @@ static int macb_mii_probe(struct net_device *dev)
phydev = phy_find_first(bp->mii_bus);
if (!phydev) {
netdev_err(dev, "no PHY found\n");
- return -1;
+ return -ENXIO;
}
pdata = dev_get_platdata(&bp->pdev->dev);
@@ -314,6 +315,7 @@ static int macb_mii_probe(struct net_device *dev)
int macb_mii_init(struct macb *bp)
{
struct macb_platform_data *pdata;
+ struct device_node *np;
int err = -ENXIO, i;
/* Enable management port */
@@ -335,26 +337,51 @@ int macb_mii_init(struct macb *bp)
bp->mii_bus->parent = &bp->dev->dev;
pdata = bp->pdev->dev.platform_data;
- if (pdata)
- bp->mii_bus->phy_mask = pdata->phy_mask;
-
bp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
if (!bp->mii_bus->irq) {
err = -ENOMEM;
goto err_out_free_mdiobus;
}
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bp->mii_bus->irq[i] = PHY_POLL;
-
dev_set_drvdata(&bp->dev->dev, bp->mii_bus);
- if (mdiobus_register(bp->mii_bus))
+ np = bp->pdev->dev.of_node;
+ if (np) {
+ /* try dt phy registration */
+ err = of_mdiobus_register(bp->mii_bus, np);
+
+ /* fallback to standard phy registration if no phy were
+ found during dt phy registration */
+ if (!err && !phy_find_first(bp->mii_bus)) {
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ struct phy_device *phydev;
+
+ phydev = mdiobus_scan(bp->mii_bus, i);
+ if (IS_ERR(phydev)) {
+ err = PTR_ERR(phydev);
+ break;
+ }
+ }
+
+ if (err)
+ goto err_out_unregister_bus;
+ }
+ } else {
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ bp->mii_bus->irq[i] = PHY_POLL;
+
+ if (pdata)
+ bp->mii_bus->phy_mask = pdata->phy_mask;
+
+ err = mdiobus_register(bp->mii_bus);
+ }
+
+ if (err)
goto err_out_free_mdio_irq;
- if (macb_mii_probe(bp->dev) != 0) {
+ err = macb_mii_probe(bp->dev);
+ if (err)
goto err_out_unregister_bus;
- }
return 0;
--
1.7.9.5
^ permalink raw reply related
* [PATCH v2 2/2] ARM: at91/dt: define phy available on sama5d3 mother board
From: Boris BREZILLON @ 2013-08-26 12:35 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
Ian Campbell, Russell King, Nicolas Ferre
Cc: devicetree, linux-arm-kernel, linux-kernel, netdev,
Boris BREZILLON
In-Reply-To: <1377520431-21807-1-git-send-email-b.brezillon@overkiz.com>
This patch describe the phy used on atmel sama5d3 mother board:
- phy address
- phy interrupt pin
Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
---
arch/arm/boot/dts/sama5d3xmb.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/sama5d3xmb.dtsi b/arch/arm/boot/dts/sama5d3xmb.dtsi
index 8a9e05d..e9521d5 100644
--- a/arch/arm/boot/dts/sama5d3xmb.dtsi
+++ b/arch/arm/boot/dts/sama5d3xmb.dtsi
@@ -81,6 +81,14 @@
macb1: ethernet@f802c000 {
phy-mode = "rmii";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ phy0: ethernet-phy@0 {
+ interrupt-parent = <&pioE>;
+ interrupts = <30 IRQ_TYPE_EDGE_FALLING>;
+ reg = <1>;
+ };
};
pinctrl@fffff200 {
--
1.7.9.5
^ permalink raw reply related
* [PATCH] net: mdio-sun4i: Convert to devm_* api
From: Jisheng Zhang @ 2013-08-26 13:11 UTC (permalink / raw)
To: maxime.ripard, davem, emilio; +Cc: netdev, linux-kernel, Jisheng Zhang
Use devm_ioremap_resource instead of of_iomap() and devm_kzalloc()
instead of kmalloc() to make cleanup paths simpler. This patch also
fixes the resource leak caused by missing corresponding iounamp()
of the of_iomap().
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
---
drivers/net/phy/mdio-sun4i.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c
index 7f25e49..18969b3 100644
--- a/drivers/net/phy/mdio-sun4i.c
+++ b/drivers/net/phy/mdio-sun4i.c
@@ -101,6 +101,7 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct mii_bus *bus;
struct sun4i_mdio_data *data;
+ struct resource *res;
int ret, i;
bus = mdiobus_alloc_size(sizeof(*data));
@@ -114,7 +115,8 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
bus->parent = &pdev->dev;
- bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
+ GFP_KERNEL);
if (!bus->irq) {
ret = -ENOMEM;
goto err_out_free_mdiobus;
@@ -124,10 +126,11 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
bus->irq[i] = PHY_POLL;
data = bus->priv;
- data->membase = of_iomap(np, 0);
- if (!data->membase) {
- ret = -ENOMEM;
- goto err_out_free_mdio_irq;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->membase)) {
+ ret = PTR_ERR(data->membase);
+ goto err_out_free_mdiobus;
}
data->regulator = devm_regulator_get(&pdev->dev, "phy");
@@ -139,7 +142,7 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
} else {
ret = regulator_enable(data->regulator);
if (ret)
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
}
ret = of_mdiobus_register(bus, np);
@@ -152,8 +155,6 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
err_out_disable_regulator:
regulator_disable(data->regulator);
-err_out_free_mdio_irq:
- kfree(bus->irq);
err_out_free_mdiobus:
mdiobus_free(bus);
return ret;
@@ -164,7 +165,6 @@ static int sun4i_mdio_remove(struct platform_device *pdev)
struct mii_bus *bus = platform_get_drvdata(pdev);
mdiobus_unregister(bus);
- kfree(bus->irq);
mdiobus_free(bus);
return 0;
--
1.8.4.rc3
^ permalink raw reply related
* Re: [PATCH v2 2/2] ARM: at91/dt: define phy available on sama5d3 mother board
From: Sergei Shtylyov @ 2013-08-26 13:21 UTC (permalink / raw)
To: Boris BREZILLON
Cc: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
Ian Campbell, Russell King, Nicolas Ferre, devicetree,
linux-arm-kernel, linux-kernel, netdev
In-Reply-To: <1377520540-21884-1-git-send-email-b.brezillon@overkiz.com>
Hello.
On 26-08-2013 16:35, Boris BREZILLON wrote:
> This patch describe the phy used on atmel sama5d3 mother board:
> - phy address
> - phy interrupt pin
> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
> ---
> arch/arm/boot/dts/sama5d3xmb.dtsi | 8 ++++++++
> 1 file changed, 8 insertions(+)
> diff --git a/arch/arm/boot/dts/sama5d3xmb.dtsi b/arch/arm/boot/dts/sama5d3xmb.dtsi
> index 8a9e05d..e9521d5 100644
> --- a/arch/arm/boot/dts/sama5d3xmb.dtsi
> +++ b/arch/arm/boot/dts/sama5d3xmb.dtsi
> @@ -81,6 +81,14 @@
>
> macb1: ethernet@f802c000 {
> phy-mode = "rmii";
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> + phy0: ethernet-phy@0 {
Address part of the node name doesn't match the "reg" property.
> + interrupt-parent = <&pioE>;
> + interrupts = <30 IRQ_TYPE_EDGE_FALLING>;
> + reg = <1>;
> + };
WBR, Sergei
^ permalink raw reply
* Re: [PATCH v2 2/2] ARM: at91/dt: define phy available on sama5d3 mother board
From: boris brezillon @ 2013-08-26 13:33 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
Ian Campbell, Russell King, Nicolas Ferre, devicetree,
linux-arm-kernel, linux-kernel, netdev
In-Reply-To: <521B5648.3020608@cogentembedded.com>
Hello Sergei,
On 26/08/2013 15:21, Sergei Shtylyov wrote:
> Hello.
>
> On 26-08-2013 16:35, Boris BREZILLON wrote:
>
>> This patch describe the phy used on atmel sama5d3 mother board:
>> - phy address
>> - phy interrupt pin
>
>> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
>> ---
>> arch/arm/boot/dts/sama5d3xmb.dtsi | 8 ++++++++
>> 1 file changed, 8 insertions(+)
>
>> diff --git a/arch/arm/boot/dts/sama5d3xmb.dtsi
>> b/arch/arm/boot/dts/sama5d3xmb.dtsi
>> index 8a9e05d..e9521d5 100644
>> --- a/arch/arm/boot/dts/sama5d3xmb.dtsi
>> +++ b/arch/arm/boot/dts/sama5d3xmb.dtsi
>> @@ -81,6 +81,14 @@
>>
>> macb1: ethernet@f802c000 {
>> phy-mode = "rmii";
>> +
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + phy0: ethernet-phy@0 {
>
> Address part of the node name doesn't match the "reg" property.
Indeed, I based my definition on arch/arc/boot/dts/angel4.dts where phy
is registered like this :
phy0: ethernet-phy@0 {
reg = <1>;
};
I think it's buggy there too, because I checked other dts files and they
all put the same address
after @ and in reg register.
I'll fix this fot the next version.
Thanks
Best Regards,
Boris
>
>> + interrupt-parent = <&pioE>;
>> + interrupts = <30 IRQ_TYPE_EDGE_FALLING>;
>> + reg = <1>;
>> + };
>
> WBR, Sergei
>
^ permalink raw reply
* Re: [systemd-devel] [PATCH] netns: unix: only allow to find out unix socket in same net namespace
From: Serge Hallyn @ 2013-08-26 13:53 UTC (permalink / raw)
To: Gao feng
Cc: James Bottomley, systemd-devel@lists.freedesktop.org,
libvir-list@redhat.com, netdev@vger.kernel.org, Linux Containers,
Kay Sievers, Eric W. Biederman, lxc-devel@lists.sourceforge.net,
davem@davemloft.net
In-Reply-To: <521ACCEF.4050101@cn.fujitsu.com>
Quoting Gao feng (gaofeng@cn.fujitsu.com):
> On 08/26/2013 11:19 AM, James Bottomley wrote:
> > On Mon, 2013-08-26 at 09:06 +0800, Gao feng wrote:
> >> On 08/26/2013 02:16 AM, James Bottomley wrote:
> >>> On Sun, 2013-08-25 at 19:37 +0200, Kay Sievers wrote:
> >>>> On Sun, Aug 25, 2013 at 7:16 PM, James Bottomley
> >>>> <jbottomley@parallels.com> wrote:
> >>>>> On Wed, 2013-08-21 at 11:51 +0200, Kay Sievers wrote:
> >>>>>> On Wed, Aug 21, 2013 at 9:22 AM, Gao feng <gaofeng@cn.fujitsu.com> wrote:
> >>>>>>> On 08/21/2013 03:06 PM, Eric W. Biederman wrote:
> >>>>>>
> >>>>>>>> I suspect libvirt should simply not share /run or any other normally
> >>>>>>>> writable directory with the host. Sharing /run /var/run or even /tmp
> >>>>>>>> seems extremely dubious if you want some kind of containment, and
> >>>>>>>> without strange things spilling through.
> >>>>>>
> >>>>>> Right, /run or /var cannot be shared. It's not only about sockets,
> >>>>>> many other things will also go really wrong that way.
> >>>>>
> >>>>> This is very narrow thinking about what a container might be and will
> >>>>> cause trouble as people start to create novel uses for containers in the
> >>>>> cloud if you try to impose this on our current infrastructure.
> >>>>>
> >>>>> One of the cgroup only container uses we see at Parallels (so no
> >>>>> separate filesystem and no net namespaces) is pure apache load balancer
> >>>>> type shared hosting. In this scenario, base apache is effectively
> >>>>> brought up in the host environment, but then spawned instances are
> >>>>> resource limited using cgroups according to what the customer has paid.
> >>>>> Obviously all apache instances are sharing /var and /run from the host
> >>>>> (mostly for logging and pid storage and static pages). The reason some
> >>>>> hosters do this is that it allows much higher density simple web serving
> >>>>> (either static pages from quota limited chroots or dynamic pages limited
> >>>>> by database space constraints) because each "instance" shares so much
> >>>>> from the host. The service is obviously much more basic than giving
> >>>>> each customer a container running apache, but it's much easier for the
> >>>>> hoster to administer and it serves the customer just as well for a large
> >>>>> cross section of use cases and for those it doesn't serve, the hoster
> >>>>> usually has separate container hosting (for a higher price, of course).
> >>>>
> >>>> The "container" as we talk about has it's own init, and no, it cannot
> >>>> share /var or /run.
> >>>
> >>> This is what we would call an IaaS container: bringing up init and
> >>> effectively a new OS inside a container is the closest containers come
> >>> to being like hypervisors. It's the most common use case of Parallels
> >>> containers in the field, so I'm certainly not telling you it's a bad
> >>> idea.
> >>>
> >>>> The stuff you talk about has nothing to do with that, it's not
> >>>> different from all services or a multi-instantiated service on the
> >>>> host sharing the same /run and /var.
> >>>
> >>> I gave you one example: a really simplistic one. A more sophisticated
> >>> example is a PaaS or SaaS container where you bring the OS up in the
> >>> host but spawn a particular application into its own container (this is
> >>> essentially similar to what Docker does). Often in this case, you do
> >>> add separate mount and network namespaces to make the application
> >>> isolated and migrateable with its own IP address. The reason you share
> >>> init and most of the OS from the host is for elasticity and density,
> >>> which are fast becoming a holy grail type quest of cloud orchestration
> >>> systems: if you don't have to bring up the OS from init and you can just
> >>> start the application from a C/R image (orders of magnitude smaller than
> >>> a full system image) and slap on the necessary namespaces as you clone
> >>> it, you have something that comes online in miliseconds which is a feat
> >>> no hypervisor based virtualisation can match.
> >>>
> >>> I'm not saying don't pursue the IaaS case, it's definitely useful ...
> >>> I'm just saying it would be a serious mistake to think that's the only
> >>> use case for containers and we certainly shouldn't adjust Linux to serve
> >>> only that use case.
> >>>
> >>
> >> The feature you said above VS contianer-reboot-host bug, I prefer to
> >> fix
> >> the bug.
> >
> > What bug?
> >
> >> and this feature can be achieved even container unshares /run
> >> directory
> >> with host by default, for libvirt, user can set the container
> >> configuration to
> >> make the container shares the /run directory with host.
> >>
> >> I would like to say, the reboot from container bug is more urgent and
> >> need
> >> to be fixed.
> >
> > Are you talking about the old bug where trying to reboot an lxc
> > container from within it would reboot the entire system?
>
> Yes, we are discussing this problem in this whole thread.
>
> If so, OpenVZ
> > has never suffered from that problem and I thought it was fixed
> > upstream. I've not tested lxc tools, but the latest vzctl from the
> > openvz website will bring up a container on the vanilla 3.9 kernel
> > (provided you have USER_NS compiled in) can also be used to reboot the
> > container, so I see no reason it wouldn't work for lxc as well.
> >
>
> I'm using libvirt lxc not lxc-tools.
> Not all of users enable user namespace, I trust these container management
> tools can have right/proper setting which inhibit this reboot-problem occur.
> but I don't think this reboot-problem won't happen in any configuration.
On any recent kernel, reboot syscall from inside a non-init pid-ns will
not reboot the host. If from within a non-init pid-ns you are managing
to reboot the host, then you have a problem with how userspace is set
up. The container is being allowed to request init on the host to
do the reboot - ie by sharing /dev/initctl inode with the host, or by
being in same net namespace as upstart on the host.
The fact that it's possible to create such containers is not a bug.
(On older kernels, you have to drop CAP_SYS_BOOT to prevent use of
reboot system call, as all lxc-like programs did.)
-serge
^ permalink raw reply
* [PATCH net-next] net: sctp: reorder sctp_globals to reduce cacheline usage
From: Daniel Borkmann @ 2013-08-26 14:34 UTC (permalink / raw)
To: davem; +Cc: netdev, linux-sctp
Reduce cacheline usage from 2 to 1 cacheline for sctp_globals structure. By
reordering elements, we can close gaps and simply achieve the following:
Current situation:
/* size: 80, cachelines: 2, members: 10 */
/* sum members: 57, holes: 4, sum holes: 16 */
/* padding: 7 */
/* last cacheline: 16 bytes */
Afterwards:
/* size: 64, cachelines: 1, members: 10 */
/* padding: 7 */
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
---
include/net/sctp/structs.h | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 422db6c..2174d8d 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -113,29 +113,27 @@ struct sctp_hashbucket {
/* The SCTP globals structure. */
extern struct sctp_globals {
- /* The following variables are implementation specific. */
-
- /* Default initialization values to be applied to new associations. */
- __u16 max_instreams;
- __u16 max_outstreams;
-
/* This is a list of groups of functions for each address
* family that we support.
*/
struct list_head address_families;
/* This is the hash of all endpoints. */
- int ep_hashsize;
struct sctp_hashbucket *ep_hashtable;
-
/* This is the hash of all associations. */
- int assoc_hashsize;
struct sctp_hashbucket *assoc_hashtable;
-
/* This is the sctp port control hash. */
- int port_hashsize;
struct sctp_bind_hashbucket *port_hashtable;
+ /* Sizes of above hashtables. */
+ int ep_hashsize;
+ int assoc_hashsize;
+ int port_hashsize;
+
+ /* Default initialization values to be applied to new associations. */
+ __u16 max_instreams;
+ __u16 max_outstreams;
+
/* Flag to indicate whether computing and verifying checksum
* is disabled. */
bool checksum_disable;
--
1.7.11.7
^ permalink raw reply related
* Re: [PATCH net-next] net: sctp: reorder sctp_globals to reduce cacheline usage
From: Neil Horman @ 2013-08-26 15:29 UTC (permalink / raw)
To: Daniel Borkmann; +Cc: davem, netdev, linux-sctp
In-Reply-To: <1377527640-22447-1-git-send-email-dborkman@redhat.com>
On Mon, Aug 26, 2013 at 04:34:00PM +0200, Daniel Borkmann wrote:
> Reduce cacheline usage from 2 to 1 cacheline for sctp_globals structure. By
> reordering elements, we can close gaps and simply achieve the following:
>
> Current situation:
> /* size: 80, cachelines: 2, members: 10 */
> /* sum members: 57, holes: 4, sum holes: 16 */
> /* padding: 7 */
> /* last cacheline: 16 bytes */
>
> Afterwards:
> /* size: 64, cachelines: 1, members: 10 */
> /* padding: 7 */
>
> Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
^ permalink raw reply
* [PATCH net-next 2/8] bonding: use netdev_upper list in bond_vlan_used
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev; +Cc: Veaceslav Falico, Jay Vosburgh, Andy Gospodarek
In-Reply-To: <1377534533-6944-1-git-send-email-vfalico@redhat.com>
Convert bond_vlan_used() to traverse the upper device list to see if we
have any vlans above us. It's protected by rcu, and in case we are holding
rtnl_lock we should call vlan_uses_dev() instead - it's faster.
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
drivers/net/bonding/bonding.h | 14 +++++++++++++-
1 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 4bf52d5..5ebb3d8 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -267,9 +267,21 @@ struct bonding {
#endif /* CONFIG_DEBUG_FS */
};
+/* if we hold rtnl_lock() - call vlan_uses_dev() */
static inline bool bond_vlan_used(struct bonding *bond)
{
- return !list_empty(&bond->vlan_list);
+ struct netdev_upper *upper;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(upper, &bond->dev->upper_dev_list, list) {
+ if (upper->dev->priv_flags & IFF_802_1Q_VLAN) {
+ rcu_read_unlock();
+ return true;
+ }
+ }
+ rcu_read_unlock();
+
+ return false;
}
#define bond_slave_get_rcu(dev) \
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 3/8] bonding: make bond_arp_send_all use upper device list
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev; +Cc: Veaceslav Falico, Jay Vosburgh, Andy Gospodarek
In-Reply-To: <1377534533-6944-1-git-send-email-vfalico@redhat.com>
Currently, bond_arp_send_all() is aware only of vlans, which breaks
configurations like bond <- bridge (or any other 'upper' device) with IP
(which is quite a common scenario for virt setups).
To fix this we convert the bond_arp_send_all() to first verify if the rt
device is the bond itself, and if not - to go through its list of upper
devices to see if any of them match the route device for the target. If the
match is a vlan device - we also save its vlan_id and tag it in
bond_arp_send().
Also, clean the function a bit to be more readable.
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
drivers/net/bonding/bond_main.c | 84 ++++++++++++++------------------------
1 files changed, 31 insertions(+), 53 deletions(-)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 7407e65..f8927b3 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2444,30 +2444,15 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_
static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
{
- int i, vlan_id;
- __be32 *targets = bond->params.arp_targets;
- struct vlan_entry *vlan;
- struct net_device *vlan_dev = NULL;
+ struct netdev_upper *upper;
struct rtable *rt;
+ __be32 *targets = bond->params.arp_targets, addr;
+ int i, vlan_id;
- for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
- __be32 addr;
- if (!targets[i])
- break;
+ for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) {
pr_debug("basa: target %pI4\n", &targets[i]);
- if (!bond_vlan_used(bond)) {
- pr_debug("basa: empty vlan: arp_send\n");
- addr = bond_confirm_addr(bond->dev, targets[i], 0);
- bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
- addr, 0);
- continue;
- }
- /*
- * If VLANs are configured, we do a route lookup to
- * determine which VLAN interface would be used, so we
- * can tag the ARP with the proper VLAN tag.
- */
+ /* Find out through which dev should the packet go */
rt = ip_route_output(dev_net(bond->dev), targets[i], 0,
RTO_ONLINK, 0);
if (IS_ERR(rt)) {
@@ -2478,47 +2463,40 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
continue;
}
- /*
- * This target is not on a VLAN
- */
- if (rt->dst.dev == bond->dev) {
- ip_rt_put(rt);
- pr_debug("basa: rtdev == bond->dev: arp_send\n");
- addr = bond_confirm_addr(bond->dev, targets[i], 0);
- bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
- addr, 0);
- continue;
- }
-
vlan_id = 0;
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- rcu_read_lock();
- vlan_dev = __vlan_find_dev_deep(bond->dev,
- htons(ETH_P_8021Q),
- vlan->vlan_id);
- rcu_read_unlock();
- if (vlan_dev == rt->dst.dev) {
- vlan_id = vlan->vlan_id;
- pr_debug("basa: vlan match on %s %d\n",
- vlan_dev->name, vlan_id);
- break;
- }
- }
- if (vlan_id && vlan_dev) {
- ip_rt_put(rt);
- addr = bond_confirm_addr(vlan_dev, targets[i], 0);
- bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
- addr, vlan_id);
- continue;
+ /* bond device itself */
+ if (rt->dst.dev == bond->dev)
+ goto found;
+
+ /* search for upper device, like vlan/bridge/team/etc */
+ rcu_read_lock();
+ list_for_each_entry_rcu(upper, &bond->dev->upper_dev_list,
+ list) {
+ if (upper->dev == rt->dst.dev) {
+ /* if it's a vlan - get its VID */
+ if (is_vlan_dev(rt->dst.dev))
+ vlan_id = vlan_dev_vlan_id(rt->dst.dev);
+
+ rcu_read_unlock();
+ goto found;
+ }
}
+ rcu_read_unlock();
- if (net_ratelimit()) {
+ /* Not our device - skip */
+ if (net_ratelimit())
pr_warning("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
bond->dev->name, &targets[i],
rt->dst.dev ? rt->dst.dev->name : "NULL");
- }
ip_rt_put(rt);
+ continue;
+
+found:
+ addr = bond_confirm_addr(rt->dst.dev, targets[i], 0);
+ ip_rt_put(rt);
+ bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
+ addr, vlan_id);
}
}
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 1/8] net: move netdev_upper to netdevice.h
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev
Cc: Veaceslav Falico, David S. Miller, Eric Dumazet, Jiri Pirko,
Alexander Duyck, Cong Wang
In-Reply-To: <1377534533-6944-1-git-send-email-vfalico@redhat.com>
So that any device can work with it to see its upper/master devices. It is
rcu'd and rtnl_lock protected, so one should either hold the rtnl_lock() or
to use the _rcu() functions for it.
CC: "David S. Miller" <davem@davemloft.net>
CC: Eric Dumazet <edumazet@google.com>
CC: Jiri Pirko <jiri@resnulli.us>
CC: Alexander Duyck <alexander.h.duyck@intel.com>
CC: Cong Wang <amwang@redhat.com>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
include/linux/netdevice.h | 8 ++++++++
net/core/dev.c | 8 --------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 077363d..8cc7f43 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2764,6 +2764,14 @@ extern int netdev_tstamp_prequeue;
extern int weight_p;
extern int bpf_jit_enable;
+struct netdev_upper {
+ struct net_device *dev;
+ bool master;
+ struct list_head list;
+ struct rcu_head rcu;
+ struct list_head search_list;
+};
+
extern bool netdev_has_upper_dev(struct net_device *dev,
struct net_device *upper_dev);
extern bool netdev_has_any_upper_dev(struct net_device *dev);
diff --git a/net/core/dev.c b/net/core/dev.c
index 1ed2b66..f58e82a 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4367,14 +4367,6 @@ softnet_break:
goto out;
}
-struct netdev_upper {
- struct net_device *dev;
- bool master;
- struct list_head list;
- struct rcu_head rcu;
- struct list_head search_list;
-};
-
static void __append_search_uppers(struct list_head *search_list,
struct net_device *dev)
{
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 4/8] bonding: convert bond_has_this_ip() to use upper devices
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev; +Cc: Veaceslav Falico, Jay Vosburgh, Andy Gospodarek
In-Reply-To: <1377534533-6944-1-git-send-email-vfalico@redhat.com>
Currently, bond_has_this_ip() is aware only of vlan upper devices, and thus
will return false if the address is associated with the upper bridge or any
other device, and thus will break the arp logic.
Fix this by using the upper device list. For every upper device we verify
if the address associated with it is our address, and if yes - return true.
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
drivers/net/bonding/bond_main.c | 24 ++++++++++++------------
1 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index f8927b3..f3787a2 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2392,24 +2392,24 @@ re_arm:
}
}
-static int bond_has_this_ip(struct bonding *bond, __be32 ip)
+static bool bond_has_this_ip(struct bonding *bond, __be32 ip)
{
- struct vlan_entry *vlan;
- struct net_device *vlan_dev;
+ struct netdev_upper *upper;
+ bool ret = false;
if (ip == bond_confirm_addr(bond->dev, 0, ip))
- return 1;
+ return true;
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- rcu_read_lock();
- vlan_dev = __vlan_find_dev_deep(bond->dev, htons(ETH_P_8021Q),
- vlan->vlan_id);
- rcu_read_unlock();
- if (vlan_dev && ip == bond_confirm_addr(vlan_dev, 0, ip))
- return 1;
+ rcu_read_lock();
+ list_for_each_entry_rcu(upper, &bond->dev->upper_dev_list, list) {
+ if (ip == bond_confirm_addr(upper->dev, 0, ip)) {
+ ret = true;
+ break;
+ }
}
+ rcu_read_unlock();
- return 0;
+ return ret;
}
/*
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 0/8] bonding: remove vlan special handling
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev
Cc: Jay Vosburgh, Andy Gospodarek, David S. Miller, Eric Dumazet,
Jiri Pirko, Alexander Duyck, Cong Wang, Veaceslav Falico
The aim of this patchset is to remove bondings' own vlan handling as much
as possible and replace it with the netdev upper device functionality.
This is achieved by exporting the netdev_upper structure and thus permiting
bonding to work directly with its upper devices. The only non-bonding
change is the exporting of netdev_upper, and the only special treatment of
vlans left is in the rlb mode.
This patchset solves several issues with bonding, simplifies it overall,
RCUify further and exports netdev_upper for any other users which might
also want to get rid of its own vlan_lists.
I'm testing it continuously currently, no issues found, will update on
anything.
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
CC: "David S. Miller" <davem@davemloft.net>
CC: Eric Dumazet <edumazet@google.com>
CC: Jiri Pirko <jiri@resnulli.us>
CC: Alexander Duyck <alexander.h.duyck@intel.com>
CC: Cong Wang <amwang@redhat.com>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
drivers/net/bonding/bond_alb.c | 74 ++++++------
drivers/net/bonding/bond_alb.h | 2 -
drivers/net/bonding/bond_main.c | 243 ++++++++-------------------------------
drivers/net/bonding/bonding.h | 20 ++-
include/linux/netdevice.h | 8 ++
net/core/dev.c | 8 --
6 files changed, 103 insertions(+), 252 deletions(-)
^ permalink raw reply
* [PATCH net-next 5/8] bonding: use vlan_uses_dev() in __bond_release_one()
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev; +Cc: Veaceslav Falico, Jay Vosburgh, Andy Gospodarek
In-Reply-To: <1377534533-6944-1-git-send-email-vfalico@redhat.com>
We always hold the rtnl_lock() in __bond_release_one(), so use
vlan_uses_dev() instead of bond_vlan_used().
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
drivers/net/bonding/bond_main.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index f3787a2..9af37e6 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1954,7 +1954,7 @@ static int __bond_release_one(struct net_device *bond_dev,
bond_set_carrier(bond);
eth_hw_addr_random(bond_dev);
- if (bond_vlan_used(bond)) {
+ if (vlan_uses_dev(bond_dev)) {
pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
bond_dev->name, bond_dev->name);
pr_warning("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs'.\n",
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 6/8] bonding: split alb_send_learning_packets()
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev; +Cc: Veaceslav Falico, Jay Vosburgh, Andy Gospodarek
In-Reply-To: <1377534533-6944-1-git-send-email-vfalico@redhat.com>
Create alb_send_lp_vid(), which will handle the skb/lp creation, vlan
tagging and sending, and use it in alb_send_learning_packets().
This way all the logic remains in alb_send_learning_packets(), which
becomes a lot more cleaner and easier to understand.
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
drivers/net/bonding/bond_alb.c | 59 +++++++++++++++++++++++----------------
1 files changed, 35 insertions(+), 24 deletions(-)
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 3a5db7b..3ca3c85 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -971,35 +971,53 @@ static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
/*********************** tlb/rlb shared functions *********************/
-static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
+static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[],
+ u16 vid)
{
- struct bonding *bond = bond_get_bond_by_slave(slave);
struct learning_pkt pkt;
+ struct sk_buff *skb;
int size = sizeof(struct learning_pkt);
- int i;
+ char *data;
memset(&pkt, 0, size);
memcpy(pkt.mac_dst, mac_addr, ETH_ALEN);
memcpy(pkt.mac_src, mac_addr, ETH_ALEN);
pkt.type = cpu_to_be16(ETH_P_LOOP);
- for (i = 0; i < MAX_LP_BURST; i++) {
- struct sk_buff *skb;
- char *data;
+ skb = dev_alloc_skb(size);
+ if (!skb)
+ return;
- skb = dev_alloc_skb(size);
+ data = skb_put(skb, size);
+ memcpy(data, &pkt, size);
+
+ skb_reset_mac_header(skb);
+ skb->network_header = skb->mac_header + ETH_HLEN;
+ skb->protocol = pkt.type;
+ skb->priority = TC_PRIO_CONTROL;
+ skb->dev = slave->dev;
+
+ if (vid) {
+ skb = vlan_put_tag(skb, htons(ETH_P_8021Q), vid);
if (!skb) {
+ pr_err("%s: Error: failed to insert VLAN tag\n",
+ slave->bond->dev->name);
return;
}
+ }
- data = skb_put(skb, size);
- memcpy(data, &pkt, size);
+ dev_queue_xmit(skb);
+}
- skb_reset_mac_header(skb);
- skb->network_header = skb->mac_header + ETH_HLEN;
- skb->protocol = pkt.type;
- skb->priority = TC_PRIO_CONTROL;
- skb->dev = slave->dev;
+
+static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
+{
+ struct bonding *bond = bond_get_bond_by_slave(slave);
+ u16 vlan_id;
+ int i;
+
+ for (i = 0; i < MAX_LP_BURST; i++) {
+ vlan_id = 0;
if (bond_vlan_used(bond)) {
struct vlan_entry *vlan;
@@ -1008,20 +1026,13 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
bond->alb_info.current_alb_vlan);
bond->alb_info.current_alb_vlan = vlan;
- if (!vlan) {
- kfree_skb(skb);
+ if (!vlan)
continue;
- }
- skb = vlan_put_tag(skb, htons(ETH_P_8021Q), vlan->vlan_id);
- if (!skb) {
- pr_err("%s: Error: failed to insert VLAN tag\n",
- bond->dev->name);
- continue;
- }
+ vlan_id = vlan->vlan_id;
}
- dev_queue_xmit(skb);
+ alb_send_lp_vid(slave, mac_addr, vlan_id);
}
}
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 7/8] bonding: make alb_send_learning_packets() use upper dev list
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev; +Cc: Veaceslav Falico, Jay Vosburgh, Andy Gospodarek
In-Reply-To: <1377534533-6944-1-git-send-email-vfalico@redhat.com>
Currently, if there are vlans on top of bond, alb_send_learning_packets()
will never send LPs from the bond itself (i.e. untagged), which might leave
untagged clients unupdated.
Also, the 'circular vlan' logic (i.e. update only MAX_LP_BURST vlans at a
time, and save the last vlan for the next update) is really suboptimal - in
case of lots of vlans it will take a lot of time to update every vlan. It
is also never called in any hot path and sends only a few small packets -
thus the optimization by itself is useless.
So remove the whole current_alb_vlan/MAX_LP_BURST logic from
alb_send_learning_packets(). Instead, we'll first send a packet untagged
and then traverse the upper dev list, sending a tagged packet for each vlan
found. Also, remove the MAX_LP_BURST define - we already don't need it.
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
drivers/net/bonding/bond_alb.c | 28 ++++++++++------------------
drivers/net/bonding/bond_alb.h | 1 -
2 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 3ca3c85..7b654a0 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1013,27 +1013,19 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[],
static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
{
struct bonding *bond = bond_get_bond_by_slave(slave);
- u16 vlan_id;
- int i;
-
- for (i = 0; i < MAX_LP_BURST; i++) {
- vlan_id = 0;
-
- if (bond_vlan_used(bond)) {
- struct vlan_entry *vlan;
+ struct netdev_upper *upper;
- vlan = bond_next_vlan(bond,
- bond->alb_info.current_alb_vlan);
-
- bond->alb_info.current_alb_vlan = vlan;
- if (!vlan)
- continue;
-
- vlan_id = vlan->vlan_id;
- }
+ /* send untagged */
+ alb_send_lp_vid(slave, mac_addr, 0);
- alb_send_lp_vid(slave, mac_addr, vlan_id);
+ /* loop through vlans and send one packet for each */
+ rcu_read_lock();
+ list_for_each_entry_rcu(upper, &bond->dev->upper_dev_list, list) {
+ if (upper->dev->priv_flags & IFF_802_1Q_VLAN)
+ alb_send_lp_vid(slave, mac_addr,
+ vlan_dev_vlan_id(upper->dev));
}
+ rcu_read_unlock();
}
static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h
index e7a5b8b..e139172 100644
--- a/drivers/net/bonding/bond_alb.h
+++ b/drivers/net/bonding/bond_alb.h
@@ -53,7 +53,6 @@ struct slave;
#define TLB_NULL_INDEX 0xffffffff
-#define MAX_LP_BURST 3
/* rlb defs */
#define RLB_HASH_TABLE_SIZE 256
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 8/8] bonding: remove vlan_list/current_alb_vlan
From: Veaceslav Falico @ 2013-08-26 16:28 UTC (permalink / raw)
To: netdev; +Cc: Veaceslav Falico, Jay Vosburgh, Andy Gospodarek
In-Reply-To: <1377534533-6944-1-git-send-email-vfalico@redhat.com>
Currently there are no real users of vlan_list/current_alb_vlan, only the
helpers which maintain them, so remove them.
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
---
drivers/net/bonding/bond_alb.c | 5 --
drivers/net/bonding/bond_alb.h | 1 -
drivers/net/bonding/bond_main.c | 133 +--------------------------------------
drivers/net/bonding/bonding.h | 6 --
4 files changed, 2 insertions(+), 143 deletions(-)
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 7b654a0..98bb7fc 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1762,11 +1762,6 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
void bond_alb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
{
- if (bond->alb_info.current_alb_vlan &&
- (bond->alb_info.current_alb_vlan->vlan_id == vlan_id)) {
- bond->alb_info.current_alb_vlan = NULL;
- }
-
if (bond->alb_info.rlb_enabled) {
rlb_clear_vlan(bond, vlan_id);
}
diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h
index e139172..e02c9c5 100644
--- a/drivers/net/bonding/bond_alb.h
+++ b/drivers/net/bonding/bond_alb.h
@@ -169,7 +169,6 @@ struct alb_bond_info {
* rx traffic should be
* rebalanced
*/
- struct vlan_entry *current_alb_vlan;
};
int bond_alb_initialize(struct bonding *bond, int rlb_enabled);
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 9af37e6..4c9ec08 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -283,116 +283,6 @@ const char *bond_mode_name(int mode)
/*---------------------------------- VLAN -----------------------------------*/
/**
- * bond_add_vlan - add a new vlan id on bond
- * @bond: bond that got the notification
- * @vlan_id: the vlan id to add
- *
- * Returns -ENOMEM if allocation failed.
- */
-static int bond_add_vlan(struct bonding *bond, unsigned short vlan_id)
-{
- struct vlan_entry *vlan;
-
- pr_debug("bond: %s, vlan id %d\n",
- (bond ? bond->dev->name : "None"), vlan_id);
-
- vlan = kzalloc(sizeof(struct vlan_entry), GFP_KERNEL);
- if (!vlan)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&vlan->vlan_list);
- vlan->vlan_id = vlan_id;
-
- write_lock_bh(&bond->lock);
-
- list_add_tail(&vlan->vlan_list, &bond->vlan_list);
-
- write_unlock_bh(&bond->lock);
-
- pr_debug("added VLAN ID %d on bond %s\n", vlan_id, bond->dev->name);
-
- return 0;
-}
-
-/**
- * bond_del_vlan - delete a vlan id from bond
- * @bond: bond that got the notification
- * @vlan_id: the vlan id to delete
- *
- * returns -ENODEV if @vlan_id was not found in @bond.
- */
-static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
-{
- struct vlan_entry *vlan;
- int res = -ENODEV;
-
- pr_debug("bond: %s, vlan id %d\n", bond->dev->name, vlan_id);
-
- block_netpoll_tx();
- write_lock_bh(&bond->lock);
-
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- if (vlan->vlan_id == vlan_id) {
- list_del(&vlan->vlan_list);
-
- if (bond_is_lb(bond))
- bond_alb_clear_vlan(bond, vlan_id);
-
- pr_debug("removed VLAN ID %d from bond %s\n",
- vlan_id, bond->dev->name);
-
- kfree(vlan);
-
- res = 0;
- goto out;
- }
- }
-
- pr_debug("couldn't find VLAN ID %d in bond %s\n",
- vlan_id, bond->dev->name);
-
-out:
- write_unlock_bh(&bond->lock);
- unblock_netpoll_tx();
- return res;
-}
-
-/**
- * bond_next_vlan - safely skip to the next item in the vlans list.
- * @bond: the bond we're working on
- * @curr: item we're advancing from
- *
- * Returns %NULL if list is empty, bond->next_vlan if @curr is %NULL,
- * or @curr->next otherwise (even if it is @curr itself again).
- *
- * Caller must hold bond->lock
- */
-struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr)
-{
- struct vlan_entry *next, *last;
-
- if (list_empty(&bond->vlan_list))
- return NULL;
-
- if (!curr) {
- next = list_entry(bond->vlan_list.next,
- struct vlan_entry, vlan_list);
- } else {
- last = list_entry(bond->vlan_list.prev,
- struct vlan_entry, vlan_list);
- if (last == curr) {
- next = list_entry(bond->vlan_list.next,
- struct vlan_entry, vlan_list);
- } else {
- next = list_entry(curr->vlan_list.next,
- struct vlan_entry, vlan_list);
- }
- }
-
- return next;
-}
-
-/**
* bond_dev_queue_xmit - Prepare skb for xmit.
*
* @bond: bond device that got this skb for tx.
@@ -451,13 +341,6 @@ static int bond_vlan_rx_add_vid(struct net_device *bond_dev,
goto unwind;
}
- res = bond_add_vlan(bond, vid);
- if (res) {
- pr_err("%s: Error: Failed to add vlan id %d\n",
- bond_dev->name, vid);
- goto unwind;
- }
-
return 0;
unwind:
@@ -478,17 +361,12 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev,
{
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
- int res;
bond_for_each_slave(bond, slave)
vlan_vid_del(slave->dev, proto, vid);
- res = bond_del_vlan(bond, vid);
- if (res) {
- pr_err("%s: Error: Failed to remove vlan id %d\n",
- bond_dev->name, vid);
- return res;
- }
+ if (bond_is_lb(bond))
+ bond_alb_clear_vlan(bond, vid);
return 0;
}
@@ -4120,7 +3998,6 @@ static void bond_setup(struct net_device *bond_dev)
/* Initialize pointers */
bond->dev = bond_dev;
- INIT_LIST_HEAD(&bond->vlan_list);
/* Initialize the device entry points */
ether_setup(bond_dev);
@@ -4173,7 +4050,6 @@ static void bond_uninit(struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave, *tmp_slave;
- struct vlan_entry *vlan, *tmp;
bond_netpoll_cleanup(bond_dev);
@@ -4185,11 +4061,6 @@ static void bond_uninit(struct net_device *bond_dev)
list_del(&bond->bond_list);
bond_debug_unregister(bond);
-
- list_for_each_entry_safe(vlan, tmp, &bond->vlan_list, vlan_list) {
- list_del(&vlan->vlan_list);
- kfree(vlan);
- }
}
/*------------------------- Module initialization ---------------------------*/
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 5ebb3d8..1675440 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -185,11 +185,6 @@ struct bond_parm_tbl {
#define BOND_MAX_MODENAME_LEN 20
-struct vlan_entry {
- struct list_head vlan_list;
- unsigned short vlan_id;
-};
-
struct slave {
struct net_device *dev; /* first - useful for panic debug */
struct list_head list;
@@ -254,7 +249,6 @@ struct bonding {
struct ad_bond_info ad_info;
struct alb_bond_info alb_info;
struct bond_params params;
- struct list_head vlan_list;
struct workqueue_struct *wq;
struct delayed_work mii_work;
struct delayed_work arp_work;
--
1.7.1
^ permalink raw reply related
* Re: [PATCH RFC 2/2] ipv6: Add support for IPsec virtual tunnel interfaces
From: Dan Williams @ 2013-08-26 16:37 UTC (permalink / raw)
To: Steffen Klassert; +Cc: netdev
In-Reply-To: <20130819062730.GP26773@secunet.com>
On Mon, 2013-08-19 at 08:27 +0200, Steffen Klassert wrote:
> This patch adds IPv6 support for IPsec virtual tunnel interfaces
> (vti). IPsec virtual tunnel interfaces provide a routable interface
> for IPsec tunnel endpoints.
Are new ioctls for this kind of thing kosher? Or should it be using
netlink instead?
Dan
> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
> ---
> net/ipv6/Kconfig | 11 +
> net/ipv6/Makefile | 1 +
> net/ipv6/ip6_vti.c | 1092 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1104 insertions(+)
> create mode 100644 net/ipv6/ip6_vti.c
>
> diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
> index 11b13ea..e1a8d90 100644
> --- a/net/ipv6/Kconfig
> +++ b/net/ipv6/Kconfig
> @@ -153,6 +153,17 @@ config INET6_XFRM_MODE_ROUTEOPTIMIZATION
> ---help---
> Support for MIPv6 route optimization mode.
>
> +config IPV6_VTI
> +tristate "Virtual (secure) IPv6: tunneling"
> + select IPV6_TUNNEL
> + depends on INET6_XFRM_MODE_TUNNEL
> + ---help---
> + Tunneling means encapsulating data of one protocol type within
> + another protocol and sending it over a channel that understands the
> + encapsulating protocol. This can be used with xfrm mode tunnel to give
> + the notion of a secure tunnel for IPSEC and then use routing protocol
> + on top.
> +
> config IPV6_SIT
> tristate "IPv6: IPv6-in-IPv4 tunnel (SIT driver)"
> select INET_TUNNEL
> diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
> index 470a9c0..17bb830 100644
> --- a/net/ipv6/Makefile
> +++ b/net/ipv6/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o
> obj-$(CONFIG_IPV6_MIP6) += mip6.o
> obj-$(CONFIG_NETFILTER) += netfilter/
>
> +obj-$(CONFIG_IPV6_VTI) += ip6_vti.o
> obj-$(CONFIG_IPV6_SIT) += sit.o
> obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
> obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
> diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
> new file mode 100644
> index 0000000..fb0b6b0
> --- /dev/null
> +++ b/net/ipv6/ip6_vti.c
> @@ -0,0 +1,1092 @@
> +/*
> + * IPv6 virtual tunneling interface
> + *
> + * Copyright (C) 2013 secunet Security Networks AG
> + *
> + * Author:
> + * Steffen Klassert <steffen.klassert@secunet.com>
> + *
> + * Based on:
> + * net/ipv6/ip6_tunnel.c
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/capability.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/sockios.h>
> +#include <linux/icmp.h>
> +#include <linux/if.h>
> +#include <linux/in.h>
> +#include <linux/ip.h>
> +#include <linux/if_tunnel.h>
> +#include <linux/net.h>
> +#include <linux/in6.h>
> +#include <linux/netdevice.h>
> +#include <linux/if_arp.h>
> +#include <linux/icmpv6.h>
> +#include <linux/init.h>
> +#include <linux/route.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/netfilter_ipv6.h>
> +#include <linux/slab.h>
> +#include <linux/hash.h>
> +
> +#include <asm/uaccess.h>
> +#include <linux/atomic.h>
> +
> +#include <net/icmp.h>
> +#include <net/ip.h>
> +#include <net/ip_tunnels.h>
> +#include <net/ipv6.h>
> +#include <net/ip6_route.h>
> +#include <net/addrconf.h>
> +#include <net/ip6_tunnel.h>
> +#include <net/xfrm.h>
> +#include <net/net_namespace.h>
> +#include <net/netns/generic.h>
> +
> +#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
> +
> +#define HASH_SIZE_SHIFT 5
> +#define HASH_SIZE (1 << HASH_SIZE_SHIFT)
> +
> +static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2)
> +{
> + u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2);
> +
> + return hash_32(hash, HASH_SIZE_SHIFT);
> +}
> +
> +static int vti6_dev_init(struct net_device *dev);
> +static void vti6_dev_setup(struct net_device *dev);
> +static struct rtnl_link_ops vti6_link_ops __read_mostly;
> +
> +static int vti6_net_id __read_mostly;
> +struct vti6_net {
> + /* the IPv6 tunnel fallback device */
> + struct net_device *fb_tnl_dev;
> + /* lists for storing tunnels in use */
> + struct ip6_tnl __rcu *tnls_r_l[HASH_SIZE];
> + struct ip6_tnl __rcu *tnls_wc[1];
> + struct ip6_tnl __rcu **tnls[2];
> +};
> +
> +static struct net_device_stats *vti6_get_stats(struct net_device *dev)
> +{
> + struct pcpu_tstats sum = { 0 };
> + int i;
> +
> + for_each_possible_cpu(i) {
> + const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
> +
> + sum.rx_packets += tstats->rx_packets;
> + sum.rx_bytes += tstats->rx_bytes;
> + sum.tx_packets += tstats->tx_packets;
> + sum.tx_bytes += tstats->tx_bytes;
> + }
> + dev->stats.rx_packets = sum.rx_packets;
> + dev->stats.rx_bytes = sum.rx_bytes;
> + dev->stats.tx_packets = sum.tx_packets;
> + dev->stats.tx_bytes = sum.tx_bytes;
> + return &dev->stats;
> +}
> +
> +#define for_each_vti6_tunnel_rcu(start) \
> + for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
> +
> +/**
> + * vti6_tnl_lookup - fetch tunnel matching the end-point addresses
> + * @net: network namespace
> + * @remote: the address of the tunnel exit-point
> + * @local: the address of the tunnel entry-point
> + *
> + * Return:
> + * tunnel matching given end-points if found,
> + * else fallback tunnel if its device is up,
> + * else %NULL
> + **/
> +static struct ip6_tnl *
> +vti6_tnl_lookup(struct net *net, const struct in6_addr *remote,
> + const struct in6_addr *local)
> +{
> + unsigned int hash = HASH(remote, local);
> + struct ip6_tnl *t;
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> +
> + for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
> + if (ipv6_addr_equal(local, &t->parms.laddr) &&
> + ipv6_addr_equal(remote, &t->parms.raddr) &&
> + (t->dev->flags & IFF_UP))
> + return t;
> + }
> + t = rcu_dereference(ip6n->tnls_wc[0]);
> + if (t && (t->dev->flags & IFF_UP))
> + return t;
> +
> + return NULL;
> +}
> +
> +/**
> + * vti6_tnl_bucket - get head of list matching given tunnel parameters
> + * @p: parameters containing tunnel end-points
> + *
> + * Description:
> + * vti6_tnl_bucket() returns the head of the list matching the
> + * &struct in6_addr entries laddr and raddr in @p.
> + *
> + * Return: head of IPv6 tunnel list
> + **/
> +static struct ip6_tnl __rcu **
> +vti6_tnl_bucket(struct vti6_net *ip6n, const struct __ip6_tnl_parm *p)
> +{
> + const struct in6_addr *remote = &p->raddr;
> + const struct in6_addr *local = &p->laddr;
> + unsigned int h = 0;
> + int prio = 0;
> +
> + if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
> + prio = 1;
> + h = HASH(remote, local);
> + }
> + return &ip6n->tnls[prio][h];
> +}
> +
> +static void
> +vti6_tnl_link(struct vti6_net *ip6n, struct ip6_tnl *t)
> +{
> + struct ip6_tnl __rcu **tp = vti6_tnl_bucket(ip6n, &t->parms);
> +
> + rcu_assign_pointer(t->next , rtnl_dereference(*tp));
> + rcu_assign_pointer(*tp, t);
> +}
> +
> +static void
> +vti6_tnl_unlink(struct vti6_net *ip6n, struct ip6_tnl *t)
> +{
> + struct ip6_tnl __rcu **tp;
> + struct ip6_tnl *iter;
> +
> + for (tp = vti6_tnl_bucket(ip6n, &t->parms);
> + (iter = rtnl_dereference(*tp)) != NULL;
> + tp = &iter->next) {
> + if (t == iter) {
> + rcu_assign_pointer(*tp, t->next);
> + break;
> + }
> + }
> +}
> +
> +static void vti6_dev_free(struct net_device *dev)
> +{
> + free_percpu(dev->tstats);
> + free_netdev(dev);
> +}
> +
> +static int vti6_tnl_create2(struct net_device *dev)
> +{
> + struct ip6_tnl *t = netdev_priv(dev);
> + struct net *net = dev_net(dev);
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> + int err;
> +
> + err = vti6_dev_init(dev);
> + if (err < 0)
> + goto out;
> +
> + err = register_netdevice(dev);
> + if (err < 0)
> + goto out;
> +
> + strcpy(t->parms.name, dev->name);
> + dev->rtnl_link_ops = &vti6_link_ops;
> +
> + dev_hold(dev);
> + vti6_tnl_link(ip6n, t);
> +
> + return 0;
> +
> +out:
> + return err;
> +}
> +static struct ip6_tnl *vti6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
> +{
> + struct net_device *dev;
> + struct ip6_tnl *t;
> + char name[IFNAMSIZ];
> + int err;
> +
> + if (p->name[0])
> + strlcpy(name, p->name, IFNAMSIZ);
> + else
> + sprintf(name, "ip6_vti%%d");
> +
> + dev = alloc_netdev(sizeof (*t), name, vti6_dev_setup);
> + if (dev == NULL)
> + goto failed;
> +
> + dev_net_set(dev, net);
> +
> + t = netdev_priv(dev);
> + t->parms = *p;
> + t->net = dev_net(dev);
> +
> + err = vti6_tnl_create2(dev);
> + if (err < 0)
> + goto failed_free;
> +
> + return t;
> +
> +failed_free:
> + vti6_dev_free(dev);
> +failed:
> + return NULL;
> +}
> +
> +/**
> + * vti6_locate - find or create tunnel matching given parameters
> + * @net: network namespace
> + * @p: tunnel parameters
> + * @create: != 0 if allowed to create new tunnel if no match found
> + *
> + * Description:
> + * vti6_locate() first tries to locate an existing tunnel
> + * based on @parms. If this is unsuccessful, but @create is set a new
> + * tunnel device is created and registered for use.
> + *
> + * Return:
> + * matching tunnel or NULL
> + **/
> +static struct ip6_tnl *vti6_locate(struct net *net, struct __ip6_tnl_parm *p, int create)
> +{
> + const struct in6_addr *remote = &p->raddr;
> + const struct in6_addr *local = &p->laddr;
> + struct ip6_tnl __rcu **tp;
> + struct ip6_tnl *t;
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> +
> + for (tp = vti6_tnl_bucket(ip6n, p);
> + (t = rtnl_dereference(*tp)) != NULL;
> + tp = &t->next) {
> + if (ipv6_addr_equal(local, &t->parms.laddr) &&
> + ipv6_addr_equal(remote, &t->parms.raddr))
> + return t;
> + }
> + if (!create)
> + return NULL;
> + return vti6_tnl_create(net, p);
> +}
> +
> +/**
> + * vti6_dev_uninit - tunnel device uninitializer
> + * @dev: the device to be destroyed
> + *
> + * Description:
> + * vti6_dev_uninit() removes tunnel from its list
> + **/
> +static void
> +vti6_dev_uninit(struct net_device *dev)
> +{
> + struct ip6_tnl *t = netdev_priv(dev);
> + struct net *net = dev_net(dev);
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> +
> + if (dev == ip6n->fb_tnl_dev)
> + RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);
> + else
> + vti6_tnl_unlink(ip6n, t);
> + ip6_tnl_dst_reset(t);
> + dev_put(dev);
> +}
> +
> +/**
> + * vti6_tnl_err - tunnel error handler
> + *
> + * Description:
> + * vti6_err() handle errors in the tunnel.
> + **/
> +static int
> +vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
> + u8 type, u8 code, int offset, __be32 info)
> +{
> + struct net *net = dev_net(skb->dev);
> + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) skb->data;
> + struct ip6_tnl *t;
> + int err = -ENOENT;
> +
> + rcu_read_lock();
> + if ((t = vti6_tnl_lookup(net, &ipv6h->daddr,
> + &ipv6h->saddr)) == NULL)
> + goto out;
> +
> + if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0)
> + goto out;
> +
> + err = 0;
> +
> + switch (type) {
> + case ICMPV6_DEST_UNREACH:
> + case ICMPV6_TIME_EXCEED:
> + case ICMPV6_PARAMPROB:
> + break;
> + case ICMPV6_PKT_TOOBIG:
> + ip6_update_pmtu(skb, net, info, 0, 0);
> + }
> +
> +
> +out:
> + rcu_read_unlock();
> + return err;
> +}
> +
> +static int vti6_rcv(struct sk_buff *skb)
> +{
> + struct ip6_tnl *t;
> + const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
> +
> + rcu_read_lock();
> +
> + if ((t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr,
> + &ipv6h->daddr)) != NULL) {
> + struct pcpu_tstats *tstats;
> +
> + if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) {
> + rcu_read_unlock();
> + goto discard;
> + }
> +
> + if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
> + rcu_read_unlock();
> + return 0;
> + }
> +
> + if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) {
> + t->dev->stats.rx_dropped++;
> + rcu_read_unlock();
> + goto discard;
> + }
> +
> + tstats = this_cpu_ptr(t->dev->tstats);
> + tstats->rx_packets++;
> + tstats->rx_bytes += skb->len;
> +
> + skb->mark = 0;
> + secpath_reset(skb);
> + skb->dev = t->dev;
> +
> + rcu_read_unlock();
> + return 0;
> + }
> + rcu_read_unlock();
> + return 1;
> +
> +discard:
> + kfree_skb(skb);
> + return 0;
> +}
> +
> +/**
> + * vti6_addr_conflict - compare packet addresses to tunnel's own
> + * @t: the outgoing tunnel device
> + * @hdr: IPv6 header from the incoming packet
> + *
> + * Description:
> + * Avoid trivial tunneling loop by checking that tunnel exit-point
> + * doesn't match source of incoming packet.
> + *
> + * Return:
> + * 1 if conflict,
> + * 0 else
> + **/
> +static inline bool
> +vti6_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
> +{
> + return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
> +}
> +
> +/**
> + * vti6_xmit - send a packet
> + * @skb: the outgoing socket buffer
> + * @dev: the outgoing tunnel device
> + **/
> +static int vti6_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct net *net = dev_net(dev);
> + struct ip6_tnl *t = netdev_priv(dev);
> + struct net_device_stats *stats = &t->dev->stats;
> + struct dst_entry *dst = NULL, *ndst = NULL;
> + struct flowi6 fl6;
> + struct ipv6hdr *ipv6h = ipv6_hdr(skb);
> + struct net_device *tdev;
> + int err = -1;
> +
> + if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
> + !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h))
> + return err;
> +
> + dst = ip6_tnl_dst_check(t);
> + if (!dst) {
> + memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
> +
> + ndst = ip6_route_output(net, NULL, &fl6);
> +
> + if (ndst->error)
> + goto tx_err_link_failure;
> + ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(&fl6), NULL, 0);
> + if (IS_ERR(ndst)) {
> + err = PTR_ERR(ndst);
> + ndst = NULL;
> + goto tx_err_link_failure;
> + }
> + dst = ndst;
> + }
> +
> + if (!dst->xfrm || dst->xfrm->props.mode != XFRM_MODE_TUNNEL)
> + goto tx_err_link_failure;
> +
> + tdev = dst->dev;
> +
> + if (tdev == dev) {
> + stats->collisions++;
> + net_warn_ratelimited("%s: Local routing loop detected!\n",
> + t->parms.name);
> + goto tx_err_dst_release;
> + }
> +
> +
> + skb_dst_drop(skb);
> + skb_dst_set_noref(skb, dst);
> +
> + ip6tunnel_xmit(skb, dev);
> + if (ndst)
> + ip6_tnl_dst_store(t, ndst);
> + return 0;
> +tx_err_link_failure:
> + stats->tx_carrier_errors++;
> + dst_link_failure(skb);
> +tx_err_dst_release:
> + dst_release(ndst);
> + return err;
> +}
> +
> +static netdev_tx_t
> +vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct ip6_tnl *t = netdev_priv(dev);
> + struct net_device_stats *stats = &t->dev->stats;
> + int ret;
> +
> + switch (skb->protocol) {
> + case htons(ETH_P_IPV6):
> + ret = vti6_xmit(skb, dev);
> + break;
> + default:
> + goto tx_err;
> + }
> +
> + if (ret < 0)
> + goto tx_err;
> +
> + return NETDEV_TX_OK;
> +
> +tx_err:
> + stats->tx_errors++;
> + stats->tx_dropped++;
> + kfree_skb(skb);
> + return NETDEV_TX_OK;
> +}
> +
> +static void vti6_link_config(struct ip6_tnl *t)
> +{
> + struct net_device *dev = t->dev;
> + struct __ip6_tnl_parm *p = &t->parms;
> + struct flowi6 *fl6 = &t->fl.u.ip6;
> +
> + memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
> + memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
> +
> + /* Set up flowi template */
> + fl6->saddr = p->laddr;
> + fl6->daddr = p->raddr;
> + fl6->flowi6_oif = p->link;
> + fl6->flowi6_mark = p->i_key;
> + fl6->flowi6_proto = p->proto;
> + fl6->flowlabel = 0;
> +
> + p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET);
> + p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
> +
> + if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV)
> + dev->flags |= IFF_POINTOPOINT;
> + else
> + dev->flags &= ~IFF_POINTOPOINT;
> +
> + dev->iflink = p->link;
> +
> + if (p->flags & IP6_TNL_F_CAP_XMIT) {
> + int strict = (ipv6_addr_type(&p->raddr) &
> + (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
> +
> + struct rt6_info *rt = rt6_lookup(dev_net(dev),
> + &p->raddr, &p->laddr,
> + p->link, strict);
> +
> + if (rt == NULL)
> + return;
> +
> + if (rt->dst.dev) {
> + dev->hard_header_len = rt->dst.dev->hard_header_len +
> + sizeof (struct ipv6hdr);
> +
> + dev->mtu = rt->dst.dev->mtu - sizeof (struct ipv6hdr);
> +
> + if (dev->mtu < IPV6_MIN_MTU)
> + dev->mtu = IPV6_MIN_MTU;
> + }
> + ip6_rt_put(rt);
> + }
> +}
> +
> +/**
> + * vti6_tnl_change - update the tunnel parameters
> + * @t: tunnel to be changed
> + * @p: tunnel configuration parameters
> + *
> + * Description:
> + * vti6_tnl_change() updates the tunnel parameters
> + **/
> +static int
> +vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
> +{
> + t->parms.laddr = p->laddr;
> + t->parms.raddr = p->raddr;
> + t->parms.link = p->link;
> + t->parms.i_key = p->i_key;
> + t->parms.o_key = p->o_key;
> + t->parms.proto = p->proto;
> + ip6_tnl_dst_reset(t);
> + vti6_link_config(t);
> + return 0;
> +}
> +
> +static int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
> +{
> + struct net *net = dev_net(t->dev);
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> + int err;
> +
> + vti6_tnl_unlink(ip6n, t);
> + synchronize_net();
> + err = vti6_tnl_change(t, p);
> + vti6_tnl_link(ip6n, t);
> + netdev_state_change(t->dev);
> + return err;
> +}
> +
> +static void
> +vti6_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm2 *u)
> +{
> + p->laddr = u->laddr;
> + p->raddr = u->raddr;
> + p->link = u->link;
> + p->i_key = u->i_key;
> + p->o_key = u->o_key;
> +
> + memcpy(p->name, u->name, sizeof(u->name));
> +}
> +
> +static void
> +vti6_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p)
> +{
> + u->laddr = p->laddr;
> + u->raddr = p->raddr;
> + u->link = p->link;
> + u->i_key = p->i_key;
> + u->o_key = p->o_key;
> + memcpy(u->name, p->name, sizeof(u->name));
> +}
> +
> +/**
> + * vti6_tnl_ioctl - configure vti6 tunnels from userspace
> + * @dev: virtual device associated with tunnel
> + * @ifr: parameters passed from userspace
> + * @cmd: command to be performed
> + *
> + * Description:
> + * vti6_ioctl() is used for managing IPv6 tunnels
> + * from userspace.
> + *
> + * The possible commands are the following:
> + * %SIOCGETTUNNEL: get tunnel parameters for device
> + * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters
> + * %SIOCCHGTUNNEL: change tunnel parameters to those given
> + * %SIOCDELTUNNEL: delete tunnel
> + *
> + * The fallback device "ip6tnl0", created during module
> + * initialization, can be used for creating other tunnel devices.
> + *
> + * Return:
> + * 0 on success,
> + * %-EFAULT if unable to copy data to or from userspace,
> + * %-EPERM if current process hasn't %CAP_NET_ADMIN set
> + * %-EINVAL if passed tunnel parameters are invalid,
> + * %-EEXIST if changing a tunnel's parameters would cause a conflict
> + * %-ENODEV if attempting to change or delete a nonexisting device
> + **/
> +static int
> +vti6_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
> +{
> + int err = 0;
> + struct ip6_tnl_parm2 p;
> + struct __ip6_tnl_parm p1;
> + struct ip6_tnl *t = NULL;
> + struct net *net = dev_net(dev);
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> +
> + switch (cmd) {
> + case SIOCGETTUNNEL:
> + if (dev == ip6n->fb_tnl_dev) {
> + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) {
> + err = -EFAULT;
> + break;
> + }
> + vti6_parm_from_user(&p1, &p);
> + t = vti6_locate(net, &p1, 0);
> + } else {
> + memset(&p, 0, sizeof(p));
> + }
> + if (t == NULL)
> + t = netdev_priv(dev);
> + vti6_parm_to_user(&p, &t->parms);
> + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) {
> + err = -EFAULT;
> + }
> + break;
> + case SIOCADDTUNNEL:
> + case SIOCCHGTUNNEL:
> + err = -EPERM;
> + if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
> + break;
> + err = -EFAULT;
> + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
> + break;
> + err = -EINVAL;
> + if (p.proto != IPPROTO_IPV6 && p.proto != 0)
> + break;
> + vti6_parm_from_user(&p1, &p);
> + t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL);
> + if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
> + if (t != NULL) {
> + if (t->dev != dev) {
> + err = -EEXIST;
> + break;
> + }
> + } else
> + t = netdev_priv(dev);
> +
> + err = vti6_update(t, &p1);
> + }
> + if (t) {
> + err = 0;
> + vti6_parm_to_user(&p, &t->parms);
> + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
> + err = -EFAULT;
> +
> + } else
> + err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
> + break;
> + case SIOCDELTUNNEL:
> + err = -EPERM;
> + if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
> + break;
> +
> + if (dev == ip6n->fb_tnl_dev) {
> + err = -EFAULT;
> + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
> + break;
> + err = -ENOENT;
> + vti6_parm_from_user(&p1, &p);
> + t = vti6_locate(net, &p1, 0);
> + if (t == NULL)
> + break;
> + err = -EPERM;
> + if (t->dev == ip6n->fb_tnl_dev)
> + break;
> + dev = t->dev;
> + }
> + err = 0;
> + unregister_netdevice(dev);
> + break;
> + default:
> + err = -EINVAL;
> + }
> + return err;
> +}
> +
> +/**
> + * vti6_tnl_change_mtu - change mtu manually for tunnel device
> + * @dev: virtual device associated with tunnel
> + * @new_mtu: the new mtu
> + *
> + * Return:
> + * 0 on success,
> + * %-EINVAL if mtu too small
> + **/
> +static int vti6_change_mtu(struct net_device *dev, int new_mtu)
> +{
> + if (new_mtu < IPV6_MIN_MTU) {
> + return -EINVAL;
> + }
> + dev->mtu = new_mtu;
> + return 0;
> +}
> +
> +static const struct net_device_ops vti6_netdev_ops = {
> + .ndo_uninit = vti6_dev_uninit,
> + .ndo_start_xmit = vti6_tnl_xmit,
> + .ndo_do_ioctl = vti6_ioctl,
> + .ndo_change_mtu = vti6_change_mtu,
> + .ndo_get_stats = vti6_get_stats,
> +};
> +
> +/**
> + * vti6_dev_setup - setup virtual tunnel device
> + * @dev: virtual device associated with tunnel
> + *
> + * Description:
> + * Initialize function pointers and device parameters
> + **/
> +static void vti6_dev_setup(struct net_device *dev)
> +{
> + struct ip6_tnl *t;
> +
> + dev->netdev_ops = &vti6_netdev_ops;
> + dev->destructor = vti6_dev_free;
> +
> + dev->type = ARPHRD_TUNNEL6;
> + dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr);
> + dev->mtu = ETH_DATA_LEN;
> + t = netdev_priv(dev);
> + dev->flags |= IFF_NOARP;
> + dev->addr_len = sizeof(struct in6_addr);
> + dev->features |= NETIF_F_NETNS_LOCAL;
> + dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
> +}
> +
> +/**
> + * vti6_dev_init_gen - general initializer for all tunnel devices
> + * @dev: virtual device associated with tunnel
> + **/
> +static inline int vti6_dev_init_gen(struct net_device *dev)
> +{
> + struct ip6_tnl *t = netdev_priv(dev);
> +
> + t->dev = dev;
> + t->net = dev_net(dev);
> + dev->tstats = alloc_percpu(struct pcpu_tstats);
> + if (!dev->tstats)
> + return -ENOMEM;
> + return 0;
> +}
> +
> +/**
> + * vti6_dev_init - initializer for all non fallback tunnel devices
> + * @dev: virtual device associated with tunnel
> + **/
> +static int vti6_dev_init(struct net_device *dev)
> +{
> + struct ip6_tnl *t = netdev_priv(dev);
> + int err = vti6_dev_init_gen(dev);
> +
> + if (err)
> + return err;
> + vti6_link_config(t);
> + return 0;
> +}
> +
> +/**
> + * vti6_fb_tnl_dev_init - initializer for fallback tunnel device
> + * @dev: fallback device
> + *
> + * Return: 0
> + **/
> +static int __net_init vti6_fb_tnl_dev_init(struct net_device *dev)
> +{
> + struct ip6_tnl *t = netdev_priv(dev);
> + struct net *net = dev_net(dev);
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> + int err = vti6_dev_init_gen(dev);
> +
> + if (err)
> + return err;
> +
> + t->parms.proto = IPPROTO_IPV6;
> + dev_hold(dev);
> +
> + vti6_link_config(t);
> +
> + rcu_assign_pointer(ip6n->tnls_wc[0], t);
> + return 0;
> +}
> +
> +static int vti6_validate(struct nlattr *tb[], struct nlattr *data[])
> +{
> + return 0;
> +}
> +
> +static void vti6_netlink_parms(struct nlattr *data[],
> + struct __ip6_tnl_parm *parms)
> +{
> + memset(parms, 0, sizeof(*parms));
> +
> + if (!data)
> + return;
> +
> + if (data[IFLA_VTI_LINK])
> + parms->link = nla_get_u32(data[IFLA_VTI_LINK]);
> +
> + if (data[IFLA_VTI_LOCAL])
> + nla_memcpy(&parms->laddr, data[IFLA_VTI_LOCAL],
> + sizeof(struct in6_addr));
> +
> + if (data[IFLA_VTI_REMOTE])
> + nla_memcpy(&parms->raddr, data[IFLA_VTI_REMOTE],
> + sizeof(struct in6_addr));
> +
> + if (data[IFLA_VTI_IKEY])
> + parms->i_key = nla_get_u32(data[IFLA_VTI_IKEY]);
> +
> + if (data[IFLA_VTI_OKEY])
> + parms->o_key = nla_get_u32(data[IFLA_VTI_OKEY]);
> +}
> +
> +static int vti6_newlink(struct net *src_net, struct net_device *dev,
> + struct nlattr *tb[], struct nlattr *data[])
> +{
> + struct net *net = dev_net(dev);
> + struct ip6_tnl *nt;
> +
> + nt = netdev_priv(dev);
> + vti6_netlink_parms(data, &nt->parms);
> +
> + nt->parms.proto = IPPROTO_IPV6;
> +
> + if (vti6_locate(net, &nt->parms, 0))
> + return -EEXIST;
> +
> + return vti6_tnl_create2(dev);
> +}
> +
> +static int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
> + struct nlattr *data[])
> +{
> + struct ip6_tnl *t;
> + struct __ip6_tnl_parm p;
> + struct net *net = dev_net(dev);
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> +
> + if (dev == ip6n->fb_tnl_dev)
> + return -EINVAL;
> +
> + vti6_netlink_parms(data, &p);
> +
> + t = vti6_locate(net, &p, 0);
> +
> + if (t) {
> + if (t->dev != dev)
> + return -EEXIST;
> + } else
> + t = netdev_priv(dev);
> +
> + return vti6_update(t, &p);
> +}
> +
> +static size_t vti6_get_size(const struct net_device *dev)
> +{
> + return
> + /* IFLA_VTI_LINK */
> + nla_total_size(4) +
> + /* IFLA_VTI_LOCAL */
> + nla_total_size(sizeof(struct in6_addr)) +
> + /* IFLA_VTI_REMOTE */
> + nla_total_size(sizeof(struct in6_addr)) +
> + /* IFLA_VTI_IKEY */
> + nla_total_size(4) +
> + /* IFLA_VTI_OKEY */
> + nla_total_size(4) +
> + 0;
> +}
> +
> +static int vti6_fill_info(struct sk_buff *skb, const struct net_device *dev)
> +{
> + struct ip6_tnl *tunnel = netdev_priv(dev);
> + struct __ip6_tnl_parm *parm = &tunnel->parms;
> +
> + if (nla_put_u32(skb, IFLA_VTI_LINK, parm->link) ||
> + nla_put(skb, IFLA_VTI_LOCAL, sizeof(struct in6_addr),
> + &parm->laddr) ||
> + nla_put(skb, IFLA_VTI_REMOTE, sizeof(struct in6_addr),
> + &parm->raddr) ||
> + nla_put_u32(skb, IFLA_VTI_IKEY, parm->i_key) ||
> + nla_put_u32(skb, IFLA_VTI_OKEY, parm->o_key))
> + goto nla_put_failure;
> + return 0;
> +
> +nla_put_failure:
> + return -EMSGSIZE;
> +}
> +
> +static const struct nla_policy vti6_policy[IFLA_VTI_MAX + 1] = {
> + [IFLA_VTI_LINK] = { .type = NLA_U32 },
> + [IFLA_VTI_LOCAL] = { .len = sizeof(struct in6_addr) },
> + [IFLA_VTI_REMOTE] = { .len = sizeof(struct in6_addr) },
> + [IFLA_VTI_IKEY] = { .type = NLA_U32 },
> + [IFLA_VTI_OKEY] = { .type = NLA_U32 },
> +};
> +
> +static struct rtnl_link_ops vti6_link_ops __read_mostly = {
> + .kind = "vti6",
> + .maxtype = IFLA_VTI_MAX,
> + .policy = vti6_policy,
> + .priv_size = sizeof(struct ip6_tnl),
> + .setup = vti6_dev_setup,
> + .validate = vti6_validate,
> + .newlink = vti6_newlink,
> + .changelink = vti6_changelink,
> + .get_size = vti6_get_size,
> + .fill_info = vti6_fill_info,
> +};
> +
> +static struct xfrm6_tunnel vti6_handler __read_mostly = {
> + .handler = vti6_rcv,
> + .err_handler = vti6_err,
> + .priority = 1,
> +};
> +
> +static void __net_exit vti6_destroy_tunnels(struct vti6_net *ip6n)
> +{
> + int h;
> + struct ip6_tnl *t;
> + LIST_HEAD(list);
> +
> + for (h = 0; h < HASH_SIZE; h++) {
> + t = rtnl_dereference(ip6n->tnls_r_l[h]);
> + while (t != NULL) {
> + unregister_netdevice_queue(t->dev, &list);
> + t = rtnl_dereference(t->next);
> + }
> + }
> +
> + t = rtnl_dereference(ip6n->tnls_wc[0]);
> + unregister_netdevice_queue(t->dev, &list);
> + unregister_netdevice_many(&list);
> +}
> +
> +static int __net_init vti6_init_net(struct net *net)
> +{
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> + struct ip6_tnl *t = NULL;
> + int err;
> +
> + ip6n->tnls[0] = ip6n->tnls_wc;
> + ip6n->tnls[1] = ip6n->tnls_r_l;
> +
> + err = -ENOMEM;
> + ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6_vti0",
> + vti6_dev_setup);
> +
> + if (!ip6n->fb_tnl_dev)
> + goto err_alloc_dev;
> + dev_net_set(ip6n->fb_tnl_dev, net);
> +
> + err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
> + if (err < 0)
> + goto err_register;
> +
> + err = register_netdev(ip6n->fb_tnl_dev);
> + if (err < 0)
> + goto err_register;
> +
> + t = netdev_priv(ip6n->fb_tnl_dev);
> +
> + strcpy(t->parms.name, ip6n->fb_tnl_dev->name);
> + return 0;
> +
> +err_register:
> + vti6_dev_free(ip6n->fb_tnl_dev);
> +err_alloc_dev:
> + return err;
> +}
> +
> +static void __net_exit vti6_exit_net(struct net *net)
> +{
> + struct vti6_net *ip6n = net_generic(net, vti6_net_id);
> +
> + rtnl_lock();
> + vti6_destroy_tunnels(ip6n);
> + rtnl_unlock();
> +}
> +
> +static struct pernet_operations vti6_net_ops = {
> + .init = vti6_init_net,
> + .exit = vti6_exit_net,
> + .id = &vti6_net_id,
> + .size = sizeof(struct vti6_net),
> +};
> +
> +/**
> + * vti6_tunnel_init - register protocol and reserve needed resources
> + *
> + * Return: 0 on success
> + **/
> +static int __init vti6_tunnel_init(void)
> +{
> + int err;
> +
> + err = register_pernet_device(&vti6_net_ops);
> + if (err < 0)
> + goto out_pernet;
> +
> + err = xfrm6_mode_tunnel_input_register(&vti6_handler);
> + if (err < 0) {
> + pr_err("%s: can't register vti6\n", __func__);
> + goto out;
> + }
> + err = rtnl_link_register(&vti6_link_ops);
> + if (err < 0)
> + goto rtnl_link_failed;
> +
> + return 0;
> +
> +rtnl_link_failed:
> + xfrm6_mode_tunnel_input_deregister(&vti6_handler);
> +out:
> + unregister_pernet_device(&vti6_net_ops);
> +out_pernet:
> + return err;
> +}
> +
> +/**
> + * vti6_tunnel_cleanup - free resources and unregister protocol
> + **/
> +static void __exit vti6_tunnel_cleanup(void)
> +{
> + rtnl_link_unregister(&vti6_link_ops);
> + if (xfrm6_mode_tunnel_input_deregister(&vti6_handler))
> + pr_info("%s: can't deregister vti6\n", __func__);
> +
> + unregister_pernet_device(&vti6_net_ops);
> +}
> +
> +module_init(vti6_tunnel_init);
> +module_exit(vti6_tunnel_cleanup);
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS_RTNL_LINK("vti6");
> +MODULE_ALIAS_NETDEV("ip6_vti0");
> +MODULE_AUTHOR("Steffen Klassert");
> +MODULE_DESCRIPTION("IPv6 virtual tunnel interface");
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox