* Re: [PATCH] udp: Fix inverted NAPI_GRO_CB(skb)->flush test
From: Eric Dumazet @ 2014-09-11 5:13 UTC (permalink / raw)
To: Scott Wood; +Cc: netdev, davem, Tom Herbert
In-Reply-To: <1410402198-30811-1-git-send-email-scottwood@freescale.com>
On Wed, 2014-09-10 at 21:23 -0500, Scott Wood wrote:
> Commit 2abb7cdc0d ("udp: Add support for doing checksum unnecessary
> conversion") caused napi_gro_cb structs with the "flush" field zero to
> take the "udp_gro_receive" path rather than the "set flush to 1" path
> that they would previously take. As a result I saw booting from an NFS
> root hang shortly after starting userspace, with "server not
> responding" messages.
>
> This change to the handling of "flush == 0" packets appears to be
> incidental to the goal of adding new code in the case where
> skb_gro_checksum_validate_zero_check() returns zero. Based on that and
> the fact that it breaks things, I'm assuming that it is unintentional.
>
> Fixes: 2abb7cdc0d ("udp: Add support for doing checksum unnecessary conversion")
> Cc: Tom Herbert <therbert@google.com>
> Signed-off-by: Scott Wood <scottwood@freescale.com>
> ---
> net/ipv4/udp_offload.c | 2 +-
> net/ipv6/udp_offload.c | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
Good catch, thanks !
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* [PATCH net-next v3 2/2] bonding: Simplify the xmit function for modes that use xmit_hash
From: Mahesh Bandewar @ 2014-09-11 4:16 UTC (permalink / raw)
To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David Miller
Cc: netdev, Mahesh Bandewar, Eric Dumazet, Maciej Zenczykowski
Earlier change to use usable slave array for TLB mode had an additional
performance advantage. So extending the same logic to all other modes
that use xmit-hash for slave selection (viz 802.3AD, and XOR modes).
Also consolidating this with the earlier TLB change.
The main idea is to build the usable slaves array in the control path
and use that array for slave selection during xmit operation.
Measured performance in a setup with a bond of 4x1G NICs with 200
instances of netperf for the modes involved (3ad, xor, tlb)
cmd: netperf -t TCP_RR -H <TargetHost> -l 60 -s 5
Mode TPS-Before TPS-After
802.3ad : 468,694 493,101
TLB (lb=0): 392,583 392,965
XOR : 475,696 484,517
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
---
v1:
(a) If bond_update_slave_arr() fails to allocate memory, it will overwrite
the slave that need to be removed.
(b) Freeing of array will assign NULL (to handle bond->down to bond->up
transition gracefully.
(c) Change from pr_debug() to pr_err() if bond_update_slave_arr() returns
failure.
(d) XOR: bond_update_slave_arr() will consider mii-mon, arp-mon cases and
will populate the array even if these parameters are not used.
(e) 3AD: Should handle the ad_agg_selection_logic correctly.
v2:
(a) Removed rcu_read_{un}lock() calls from array manipulation code.
(b) Slave link-events now refresh array for all these modes.
(c) Moved free-array call from bond_close() to bond_uninit().
v3:
(a) Fixed null pointer dereference.
(b) Removed bond->lock lockdep dependency.
drivers/net/bonding/bond_3ad.c | 76 +++-----------------
drivers/net/bonding/bond_alb.c | 51 ++------------
drivers/net/bonding/bond_alb.h | 8 ---
drivers/net/bonding/bond_main.c | 152 ++++++++++++++++++++++++++++++++++++----
drivers/net/bonding/bonding.h | 8 +++
5 files changed, 164 insertions(+), 131 deletions(-)
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 5d27a6207384..516075f0a740 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -1579,6 +1579,8 @@ static void ad_agg_selection_logic(struct aggregator *agg)
__disable_port(port);
}
}
+ if (bond_update_slave_arr(bond, NULL))
+ pr_err("Failed to build slave-array for 3ad mode.\n");
}
/* if the selected aggregator is of join individuals
@@ -1717,6 +1719,8 @@ static void ad_enable_collecting_distributing(struct port *port)
port->actor_port_number,
port->aggregator->aggregator_identifier);
__enable_port(port);
+ if (bond_update_slave_arr(port->slave->bond, NULL))
+ pr_err("Failed to build slave-array for 3ad mode.\n");
}
}
@@ -1733,6 +1737,8 @@ static void ad_disable_collecting_distributing(struct port *port)
port->actor_port_number,
port->aggregator->aggregator_identifier);
__disable_port(port);
+ if (bond_update_slave_arr(port->slave->bond, NULL))
+ pr_err("Failed to build slave-array for 3ad mode.\n");
}
}
@@ -2311,6 +2317,9 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
*/
port->sm_vars |= AD_PORT_BEGIN;
+ if (bond_update_slave_arr(slave->bond, NULL))
+ pr_err("Failed to build slave-array for 3ad mode.\n");
+
__release_state_machine_lock(port);
}
@@ -2406,73 +2415,6 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
return ret;
}
-int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
-{
- struct bonding *bond = netdev_priv(dev);
- struct slave *slave, *first_ok_slave;
- struct aggregator *agg;
- struct ad_info ad_info;
- struct list_head *iter;
- int slaves_in_agg;
- int slave_agg_no;
- int agg_id;
-
- if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
- netdev_dbg(dev, "__bond_3ad_get_active_agg_info failed\n");
- goto err_free;
- }
-
- slaves_in_agg = ad_info.ports;
- agg_id = ad_info.aggregator_id;
-
- if (slaves_in_agg == 0) {
- netdev_dbg(dev, "active aggregator is empty\n");
- goto err_free;
- }
-
- slave_agg_no = bond_xmit_hash(bond, skb) % slaves_in_agg;
- first_ok_slave = NULL;
-
- bond_for_each_slave_rcu(bond, slave, iter) {
- agg = SLAVE_AD_INFO(slave)->port.aggregator;
- if (!agg || agg->aggregator_identifier != agg_id)
- continue;
-
- if (slave_agg_no >= 0) {
- if (!first_ok_slave && bond_slave_can_tx(slave))
- first_ok_slave = slave;
- slave_agg_no--;
- continue;
- }
-
- if (bond_slave_can_tx(slave)) {
- bond_dev_queue_xmit(bond, skb, slave->dev);
- goto out;
- }
- }
-
- if (slave_agg_no >= 0) {
- netdev_err(dev, "Couldn't find a slave to tx on for aggregator ID %d\n",
- agg_id);
- goto err_free;
- }
-
- /* we couldn't find any suitable slave after the agg_no, so use the
- * first suitable found, if found.
- */
- if (first_ok_slave)
- bond_dev_queue_xmit(bond, skb, first_ok_slave->dev);
- else
- goto err_free;
-
-out:
- return NETDEV_TX_OK;
-err_free:
- /* no suitable interface, frame not sent */
- dev_kfree_skb_any(skb);
- goto out;
-}
-
int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
struct slave *slave)
{
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 028496205f39..dbac0ceb17f6 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -200,7 +200,6 @@ static int tlb_initialize(struct bonding *bond)
static void tlb_deinitialize(struct bonding *bond)
{
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
- struct tlb_up_slave *arr;
_lock_tx_hashtbl_bh(bond);
@@ -208,10 +207,6 @@ static void tlb_deinitialize(struct bonding *bond)
bond_info->tx_hashtbl = NULL;
_unlock_tx_hashtbl_bh(bond);
-
- arr = rtnl_dereference(bond_info->slave_arr);
- if (arr)
- kfree_rcu(arr, rcu);
}
static long long compute_gap(struct slave *slave)
@@ -1409,39 +1404,9 @@ out:
return NETDEV_TX_OK;
}
-static int bond_tlb_update_slave_arr(struct bonding *bond,
- struct slave *skipslave)
-{
- struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
- struct slave *tx_slave;
- struct list_head *iter;
- struct tlb_up_slave *new_arr, *old_arr;
-
- new_arr = kzalloc(offsetof(struct tlb_up_slave, arr[bond->slave_cnt]),
- GFP_ATOMIC);
- if (!new_arr)
- return -ENOMEM;
-
- bond_for_each_slave(bond, tx_slave, iter) {
- if (!bond_slave_can_tx(tx_slave))
- continue;
- if (skipslave == tx_slave)
- continue;
- new_arr->arr[new_arr->count++] = tx_slave;
- }
-
- old_arr = rtnl_dereference(bond_info->slave_arr);
- rcu_assign_pointer(bond_info->slave_arr, new_arr);
- if (old_arr)
- kfree_rcu(old_arr, rcu);
-
- return 0;
-}
-
int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
- struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
struct ethhdr *eth_data;
struct slave *tx_slave = NULL;
u32 hash_index;
@@ -1462,12 +1427,14 @@ int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
hash_index & 0xFF,
skb->len);
} else {
- struct tlb_up_slave *slaves;
+ struct bond_up_slave *slaves;
+ unsigned int count;
- slaves = rcu_dereference(bond_info->slave_arr);
- if (slaves && slaves->count)
+ slaves = rcu_dereference(bond->slave_arr);
+ count = slaves ? slaves->count : 0;
+ if (count)
tx_slave = slaves->arr[hash_index %
- slaves->count];
+ count];
}
break;
}
@@ -1733,10 +1700,6 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave)
rlb_clear_slave(bond, slave);
}
- if (bond_is_nondyn_tlb(bond))
- if (bond_tlb_update_slave_arr(bond, slave))
- pr_err("Failed to build slave-array for TLB mode.\n");
-
}
/* Caller must hold bond lock for read */
@@ -1762,7 +1725,7 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
}
if (bond_is_nondyn_tlb(bond)) {
- if (bond_tlb_update_slave_arr(bond, NULL))
+ if (bond_update_slave_arr(bond, NULL))
pr_err("Failed to build slave-array for TLB mode.\n");
}
}
diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h
index aaeac61d03cf..5fc76c01636c 100644
--- a/drivers/net/bonding/bond_alb.h
+++ b/drivers/net/bonding/bond_alb.h
@@ -139,20 +139,12 @@ struct tlb_slave_info {
*/
};
-struct tlb_up_slave {
- unsigned int count;
- struct rcu_head rcu;
- struct slave *arr[0];
-};
-
struct alb_bond_info {
struct tlb_client_info *tx_hashtbl; /* Dynamically allocated */
spinlock_t tx_hashtbl_lock;
u32 unbalanced_load;
int tx_rebalance_counter;
int lp_counter;
- /* -------- non-dynamic tlb mode only ---------*/
- struct tlb_up_slave __rcu *slave_arr; /* Up slaves */
/* -------- rlb parameters -------- */
int rlb_enabled;
struct rlb_client_info *rx_hashtbl; /* Receive hash table */
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index b43b2df9e5d1..4412c458939d 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1700,6 +1700,10 @@ static int __bond_release_one(struct net_device *bond_dev,
write_unlock_bh(&bond->curr_slave_lock);
}
+ if (bond_mode_uses_xmit_hash(bond) &&
+ bond_update_slave_arr(bond, slave))
+ pr_err("Failed to build slave-array.\n");
+
netdev_info(bond_dev, "Releasing %s interface %s\n",
bond_is_active_slave(slave) ? "active" : "backup",
slave_dev->name);
@@ -2015,6 +2019,10 @@ static void bond_miimon_commit(struct bonding *bond)
bond_alb_handle_link_change(bond, slave,
BOND_LINK_UP);
+ if (BOND_MODE(bond) == BOND_MODE_XOR &&
+ bond_update_slave_arr(bond, NULL))
+ pr_err("Failed to build slave-array for XOR mode.\n");
+
if (!bond->curr_active_slave || slave == primary)
goto do_failover;
@@ -2042,6 +2050,10 @@ static void bond_miimon_commit(struct bonding *bond)
bond_alb_handle_link_change(bond, slave,
BOND_LINK_DOWN);
+ if (BOND_MODE(bond) == BOND_MODE_XOR &&
+ bond_update_slave_arr(bond, NULL))
+ pr_err("Failed to build slave-array for XOR mode.\n");
+
if (slave == rcu_access_pointer(bond->curr_active_slave))
goto do_failover;
@@ -2505,6 +2517,9 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
if (slave_state_changed) {
bond_slave_state_change(bond);
+ if (BOND_MODE(bond) == BOND_MODE_XOR &&
+ bond_update_slave_arr(bond, NULL))
+ pr_err("Failed to build slave-array for XOR mode.\n");
} else if (do_failover) {
/* the bond_select_active_slave must hold RTNL
* and curr_slave_lock for write.
@@ -2899,11 +2914,23 @@ static int bond_slave_netdev_event(unsigned long event,
if (old_duplex != slave->duplex)
bond_3ad_adapter_duplex_changed(slave);
}
+ /* Refresh slave-array if applicable!
+ * If the setuo does not use miimon or arpmon (mode-specific!),
+ * then these event will not cause the slave-array to be
+ * refreshed. This will cause xmit to use a slave that is not
+ * usable. Avoid such situation by refeshing the array at these
+ * events. If these (miimon/arpmon) parameters are configured
+ * then array gets refreshed twice and that should be fine!
+ */
+ if (bond_mode_uses_xmit_hash(bond) &&
+ bond_update_slave_arr(bond, NULL))
+ pr_err("Failed to build slave-array for XOR mode.\n");
break;
case NETDEV_DOWN:
- /*
- * ... Or is it this?
- */
+ /* Refresh slave-array if applicable! */
+ if (bond_mode_uses_xmit_hash(bond) &&
+ bond_update_slave_arr(bond, NULL))
+ pr_err("Failed to build slave-array for XOR mode.\n");
break;
case NETDEV_CHANGEMTU:
/*
@@ -3147,6 +3174,10 @@ static int bond_open(struct net_device *bond_dev)
bond_3ad_initiate_agg_selection(bond, 1);
}
+ if (bond_mode_uses_xmit_hash(bond) &&
+ bond_update_slave_arr(bond, NULL))
+ pr_err("Failed to build slave-array for XOR mode.\n");
+
return 0;
}
@@ -3654,15 +3685,106 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
return NETDEV_TX_OK;
}
-/* In bond_xmit_xor() , we determine the output device by using a pre-
- * determined xmit_hash_policy(), If the selected device is not enabled,
- * find the next active slave.
+/* Build the usable slaves array in control path for modes that use xmit-hash
+ * to determine the slave interface -
+ * (a) BOND_MODE_8023AD
+ * (b) BOND_MODE_XOR
+ * (c) BOND_MODE_TLB && tlb_dynamic_lb == 0
*/
-static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
+int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave)
{
- struct bonding *bond = netdev_priv(bond_dev);
+ struct slave *slave;
+ struct list_head *iter;
+ struct bond_up_slave *new_arr, *old_arr;
+ int slaves_in_agg;
+ int agg_id = 0;
+ int ret = 0;
+
+ new_arr = kzalloc(offsetof(struct bond_up_slave, arr[bond->slave_cnt]),
+ GFP_ATOMIC);
+ if (!new_arr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+ struct ad_info ad_info;
+
+ if (bond_3ad_get_active_agg_info(bond, &ad_info)) {
+ pr_debug("bond_3ad_get_active_agg_info failed\n");
+ kfree_rcu(new_arr, rcu);
+ ret = -EINVAL;
+ goto out;
+ }
+ slaves_in_agg = ad_info.ports;
+ agg_id = ad_info.aggregator_id;
+ }
+ bond_for_each_slave(bond, slave, iter) {
+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+ struct aggregator *agg;
- bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb) % bond->slave_cnt);
+ agg = SLAVE_AD_INFO(slave)->port.aggregator;
+ if (!agg || agg->aggregator_identifier != agg_id)
+ continue;
+ }
+ if (!bond_slave_can_tx(slave))
+ continue;
+ if (skipslave == slave)
+ continue;
+ new_arr->arr[new_arr->count++] = slave;
+ }
+
+ old_arr = rcu_dereference_protected(bond->slave_arr,
+ lockdep_rtnl_is_held() ||
+ lockdep_is_held(&bond->curr_slave_lock));
+ rcu_assign_pointer(bond->slave_arr, new_arr);
+ if (old_arr)
+ kfree_rcu(old_arr, rcu);
+
+out:
+ if (ret != 0 && skipslave) {
+ int idx;
+
+ /* Rare situation where caller has asked to skip a specific
+ * slave but allocation failed (most likely!). BTW this is
+ * only possible when the call is initiated from
+ * __bond_release_one(). In this sitation; overwrite the
+ * skipslave entry in the array with the last entry from the
+ * array to avoid a situation where the xmit path may choose
+ * this to-be-skipped slave to send a packet out.
+ */
+ old_arr = rtnl_dereference(bond->slave_arr);
+ for (idx = 0; idx < old_arr->count; idx++) {
+ if (skipslave == old_arr->arr[idx]) {
+ old_arr->arr[idx] =
+ old_arr->arr[old_arr->count-1];
+ old_arr->count--;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+/* Use this Xmit function for 3AD as well as XOR modes. The current
+ * usable slave array is formed in the control path. The xmit function
+ * just calculates hash and sends the packet out.
+ */
+int bond_3ad_xor_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct bonding *bond = netdev_priv(dev);
+ struct slave *slave;
+ struct bond_up_slave *slaves;
+ unsigned int count;
+
+ slaves = rcu_dereference(bond->slave_arr);
+ count = slaves ? slaves->count : 0;
+ if (count) {
+ slave = slaves->arr[bond_xmit_hash(bond, skb) % count];
+ bond_dev_queue_xmit(bond, skb, slave->dev);
+ } else {
+ dev_kfree_skb_any(skb);
+ atomic_long_inc(&dev->tx_dropped);
+ }
return NETDEV_TX_OK;
}
@@ -3764,12 +3886,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
return bond_xmit_roundrobin(skb, dev);
case BOND_MODE_ACTIVEBACKUP:
return bond_xmit_activebackup(skb, dev);
+ case BOND_MODE_8023AD:
case BOND_MODE_XOR:
- return bond_xmit_xor(skb, dev);
+ return bond_3ad_xor_xmit(skb, dev);
case BOND_MODE_BROADCAST:
return bond_xmit_broadcast(skb, dev);
- case BOND_MODE_8023AD:
- return bond_3ad_xmit_xor(skb, dev);
case BOND_MODE_ALB:
return bond_alb_xmit(skb, dev);
case BOND_MODE_TLB:
@@ -3947,6 +4068,7 @@ static void bond_uninit(struct net_device *bond_dev)
struct bonding *bond = netdev_priv(bond_dev);
struct list_head *iter;
struct slave *slave;
+ struct bond_up_slave *arr;
bond_netpoll_cleanup(bond_dev);
@@ -3955,6 +4077,12 @@ static void bond_uninit(struct net_device *bond_dev)
__bond_release_one(bond_dev, slave->dev, true);
netdev_info(bond_dev, "Released all slaves\n");
+ arr = rtnl_dereference(bond->slave_arr);
+ if (arr) {
+ kfree_rcu(arr, rcu);
+ RCU_INIT_POINTER(bond->slave_arr, NULL);
+ }
+
list_del(&bond->bond_list);
bond_debug_unregister(bond);
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 8375133dd347..78d6d3b7a780 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -177,6 +177,12 @@ struct slave {
struct kobject kobj;
};
+struct bond_up_slave {
+ unsigned int count;
+ struct rcu_head rcu;
+ struct slave *arr[0];
+};
+
/*
* Link pseudo-state only used internally by monitors
*/
@@ -193,6 +199,7 @@ struct bonding {
struct slave __rcu *curr_active_slave;
struct slave __rcu *current_arp_slave;
struct slave __rcu *primary_slave;
+ struct bond_up_slave __rcu *slave_arr; /* Array of usable slaves */
bool force_primary;
s32 slave_cnt; /* never change this value outside the attach/detach wrappers */
int (*recv_probe)(const struct sk_buff *, struct bonding *,
@@ -530,6 +537,7 @@ const char *bond_slave_link_status(s8 link);
struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
struct net_device *end_dev,
int level);
+int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave);
#ifdef CONFIG_PROC_FS
void bond_create_proc_entry(struct bonding *bond);
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH net-next v3 1/2] bonding: display xmit_hash_policy for non-dynamic-tlb mode
From: Mahesh Bandewar @ 2014-09-11 4:16 UTC (permalink / raw)
To: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David Miller
Cc: netdev, Mahesh Bandewar, Eric Dumazet, Maciej Zenczykowski
It's a trivial fix to display xmit_hash_policy for this new TLB mode
since it uses transmit-hash-poilicy as part of bonding-master info
(/proc/net/bonding/<bonding-interface).
Signed-off-by: Mahesh Bandewar <maheshb@google.com>
---
v1
Rebase
v2
Added bond_mode_uses_xmit_hash() inline function
v3
Rebase
drivers/net/bonding/bond_procfs.c | 3 +--
drivers/net/bonding/bonding.h | 7 +++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index bb09d0442aa8..a3948f8d1e53 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -73,8 +73,7 @@ static void bond_info_show_master(struct seq_file *seq)
seq_printf(seq, "\n");
- if (BOND_MODE(bond) == BOND_MODE_XOR ||
- BOND_MODE(bond) == BOND_MODE_8023AD) {
+ if (bond_mode_uses_xmit_hash(bond)) {
optval = bond_opt_get_val(BOND_OPT_XMIT_HASH,
bond->params.xmit_policy);
seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 78c461abaa09..8375133dd347 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -270,6 +270,13 @@ static inline bool bond_is_nondyn_tlb(const struct bonding *bond)
(bond->params.tlb_dynamic_lb == 0);
}
+static inline bool bond_mode_uses_xmit_hash(const struct bonding *bond)
+{
+ return (BOND_MODE(bond) == BOND_MODE_8023AD ||
+ BOND_MODE(bond) == BOND_MODE_XOR ||
+ bond_is_nondyn_tlb(bond));
+}
+
static inline bool bond_mode_uses_arp(int mode)
{
return mode != BOND_MODE_8023AD && mode != BOND_MODE_TLB &&
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* RE: [PATCH] zd1211rw: replace ZD_ASSERT with lockdep_assert_held()
From: Sharma, Sanjeev @ 2014-09-11 4:02 UTC (permalink / raw)
To: Johannes Berg
Cc: dsd@gentoo.org, kune@deine-taler.de, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <1408707084.4349.0.camel@jlt4.sipsolutions.net>
-----Original Message-----
From: Johannes Berg [mailto:johannes@sipsolutions.net]
Sent: Friday, August 22, 2014 5:01 PM
To: Sharma, Sanjeev
Cc: dsd@gentoo.org; kune@deine-taler.de; linville@tuxdriver.com; linux-wireless@vger.kernel.org; netdev@vger.kernel.org; linux-kernel@vger.kernel.org
Subject: Re: [PATCH] zd1211rw: replace ZD_ASSERT with lockdep_assert_held()
On Tue, 2014-08-19 at 06:39 +0000, Sharma, Sanjeev wrote:
> Ping for review the patch.
> Make sure it compiles ...
Any update on this patch ?
Regards
Sanjeev Sharma
^ permalink raw reply
* [PATCH] udp: Fix inverted NAPI_GRO_CB(skb)->flush test
From: Scott Wood @ 2014-09-11 2:23 UTC (permalink / raw)
To: netdev, davem, Tom Herbert; +Cc: Scott Wood
Commit 2abb7cdc0d ("udp: Add support for doing checksum unnecessary
conversion") caused napi_gro_cb structs with the "flush" field zero to
take the "udp_gro_receive" path rather than the "set flush to 1" path
that they would previously take. As a result I saw booting from an NFS
root hang shortly after starting userspace, with "server not
responding" messages.
This change to the handling of "flush == 0" packets appears to be
incidental to the goal of adding new code in the case where
skb_gro_checksum_validate_zero_check() returns zero. Based on that and
the fact that it breaks things, I'm assuming that it is unintentional.
Fixes: 2abb7cdc0d ("udp: Add support for doing checksum unnecessary conversion")
Cc: Tom Herbert <therbert@google.com>
Signed-off-by: Scott Wood <scottwood@freescale.com>
---
net/ipv4/udp_offload.c | 2 +-
net/ipv6/udp_offload.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 84e0e05..b8c845a 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -294,7 +294,7 @@ static struct sk_buff **udp4_gro_receive(struct sk_buff **head,
goto flush;
/* Don't bother verifying checksum if we're going to flush anyway. */
- if (!NAPI_GRO_CB(skb)->flush)
+ if (NAPI_GRO_CB(skb)->flush)
goto skip;
if (skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check,
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 89cb9a9..2907fb8 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -138,7 +138,7 @@ static struct sk_buff **udp6_gro_receive(struct sk_buff **head,
goto flush;
/* Don't bother verifying checksum if we're going to flush anyway. */
- if (!NAPI_GRO_CB(skb)->flush)
+ if (NAPI_GRO_CB(skb)->flush)
goto skip;
if (skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check,
--
1.9.1
^ permalink raw reply related
* [net-next v5 3/3] l2tp: Refactor l2tp core driver to make use of the common UDP tunnel functions
From: Andy Zhou @ 2014-09-11 3:29 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410406193-6185-1-git-send-email-azhou@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
---
net/l2tp/l2tp_core.c | 208 +++++++++++++++++++++++++++++++-------------------
1 file changed, 128 insertions(+), 80 deletions(-)
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 2aa2b6c..1f2bb8e 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -113,6 +113,11 @@ struct l2tp_net {
spinlock_t l2tp_session_hlist_lock;
};
+struct l2tp_udp_tunnel {
+ struct udp_tunnel_sock uts;
+ struct l2tp_tunnel tunnel;
+};
+
static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
@@ -1198,7 +1203,6 @@ static void l2tp_tunnel_destruct(struct sock *sk)
l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: closing...\n", tunnel->name);
-
/* Disable udp encapsulation */
switch (tunnel->encap) {
case L2TP_ENCAPTYPE_UDP:
@@ -1298,6 +1302,18 @@ static void l2tp_udp_encap_destroy(struct sock *sk)
}
}
+static void free_l2tp_udp_sock_rcu(struct rcu_head *rcu)
+{
+ struct l2tp_tunnel *tunnel;
+ struct l2tp_udp_tunnel *udp_tunnel;
+
+ tunnel = container_of(rcu, struct l2tp_tunnel, rcu);
+
+ udp_tunnel = container_of(tunnel, struct l2tp_udp_tunnel, tunnel);
+
+ udp_tunnel_sock_free(&udp_tunnel->uts);
+}
+
/* Really kill the tunnel.
* Come here only when all sessions have been cleared from the tunnel.
*/
@@ -1306,7 +1322,19 @@ static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel)
BUG_ON(atomic_read(&tunnel->ref_count) != 0);
BUG_ON(tunnel->sock != NULL);
l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: free...\n", tunnel->name);
- kfree_rcu(tunnel, rcu);
+
+ switch (tunnel->encap) {
+ case L2TP_ENCAPTYPE_UDP:
+ call_rcu(&tunnel->rcu, free_l2tp_udp_sock_rcu);
+ break;
+
+ case L2TP_ENCAPTYPE_IP:
+ kfree_rcu(tunnel, rcu);
+ break;
+
+ default:
+ BUG();
+ }
}
/* Workqueue tunnel deletion function */
@@ -1342,6 +1370,35 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
l2tp_tunnel_sock_put(sk);
}
+static void l2tp_tunnel_udp_port_conf(struct l2tp_tunnel_cfg *cfg,
+ struct udp_port_cfg *udp_conf)
+{
+ memset(udp_conf, 0, sizeof(*udp_conf));
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (cfg->local_ip6 && cfg->peer_ip6) {
+ udp_conf->family = AF_INET6;
+ memcpy(&udp_conf->local_ip6, cfg->local_ip6,
+ sizeof(udp_conf->local_ip6));
+ memcpy(&udp_conf->peer_ip6, cfg->peer_ip6,
+ sizeof(udp_conf->peer_ip6));
+ udp_conf->use_udp6_tx_checksums =
+ cfg->udp6_zero_tx_checksums;
+ udp_conf->use_udp6_rx_checksums =
+ cfg->udp6_zero_rx_checksums;
+ } else
+#endif
+ {
+ udp_conf->family = AF_INET;
+ udp_conf->local_ip = cfg->local_ip;
+ udp_conf->peer_ip = cfg->peer_ip;
+ udp_conf->use_udp_checksums = cfg->use_udp_checksums;
+ }
+
+ udp_conf->local_udp_port = htons(cfg->local_udp_port);
+ udp_conf->peer_udp_port = htons(cfg->peer_udp_port);
+}
+
/* Create a socket for the tunnel, if one isn't set up by
* userspace. This is used for static tunnels where there is no
* managing L2TP daemon.
@@ -1363,31 +1420,7 @@ static int l2tp_tunnel_sock_create(struct net *net,
switch (cfg->encap) {
case L2TP_ENCAPTYPE_UDP:
- memset(&udp_conf, 0, sizeof(udp_conf));
-
-#if IS_ENABLED(CONFIG_IPV6)
- if (cfg->local_ip6 && cfg->peer_ip6) {
- udp_conf.family = AF_INET6;
- memcpy(&udp_conf.local_ip6, cfg->local_ip6,
- sizeof(udp_conf.local_ip6));
- memcpy(&udp_conf.peer_ip6, cfg->peer_ip6,
- sizeof(udp_conf.peer_ip6));
- udp_conf.use_udp6_tx_checksums =
- cfg->udp6_zero_tx_checksums;
- udp_conf.use_udp6_rx_checksums =
- cfg->udp6_zero_rx_checksums;
- } else
-#endif
- {
- udp_conf.family = AF_INET;
- udp_conf.local_ip = cfg->local_ip;
- udp_conf.peer_ip = cfg->peer_ip;
- udp_conf.use_udp_checksums = cfg->use_udp_checksums;
- }
-
- udp_conf.local_udp_port = htons(cfg->local_udp_port);
- udp_conf.peer_udp_port = htons(cfg->peer_udp_port);
-
+ l2tp_tunnel_udp_port_conf(cfg, &udp_conf);
err = udp_sock_create(net, &udp_conf, &sock);
if (err < 0)
goto out;
@@ -1473,6 +1506,31 @@ out:
static struct lock_class_key l2tp_socket_class;
+static int l2tp_sk_sanity_check(struct sock *sk, enum l2tp_encap_type encap,
+ u32 tunnel_id, int fd)
+{
+ unsigned int expected_protocol;
+
+ switch (encap) {
+ case L2TP_ENCAPTYPE_UDP:
+ expected_protocol = IPPROTO_UDP;
+ break;
+ case L2TP_ENCAPTYPE_IP:
+ expected_protocol = IPPROTO_L2TP;
+ break;
+ default:
+ return -EPROTONOSUPPORT;
+ }
+
+ if (sk->sk_protocol != expected_protocol) {
+ pr_err("tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
+ tunnel_id, fd, sk->sk_protocol, expected_protocol);
+ return -EPROTONOSUPPORT;
+ }
+
+ return 0;
+}
+
int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp)
{
struct l2tp_tunnel *tunnel = NULL;
@@ -1480,7 +1538,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
struct socket *sock = NULL;
struct sock *sk = NULL;
struct l2tp_net *pn;
- enum l2tp_encap_type encap = L2TP_ENCAPTYPE_UDP;
+ enum l2tp_encap_type encap = cfg ? cfg->encap : L2TP_ENCAPTYPE_UDP;
/* Get the tunnel socket from the fd, which was opened by
* the userspace L2TP daemon. If not specified, create a
@@ -1488,9 +1546,11 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
*/
if (fd < 0) {
err = l2tp_tunnel_sock_create(net, tunnel_id, peer_tunnel_id,
- cfg, &sock);
+ cfg, &sock);
if (err < 0)
goto err;
+
+ sk = sock->sk;
} else {
sock = sockfd_lookup(fd, &err);
if (!sock) {
@@ -1500,58 +1560,66 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
goto err;
}
+ sk = sock->sk;
+
/* Reject namespace mismatches */
- if (!net_eq(sock_net(sock->sk), net)) {
+ if (!net_eq(sock_net(sk), net)) {
pr_err("tunl %u: netns mismatch\n", tunnel_id);
err = -EINVAL;
goto err;
}
- }
- sk = sock->sk;
+ /* Quick sanity checks */
+ err = l2tp_sk_sanity_check(sk, encap, tunnel_id, fd);
+ if (err)
+ goto err;
- if (cfg != NULL)
- encap = cfg->encap;
+ /* Check if this socket has already been prepped */
+ tunnel = l2tp_tunnel(sk);
+ if (tunnel != NULL) {
+ /* This socket has already been prepped */
+ err = -EBUSY;
+ goto err;
+ }
+ }
- /* Quick sanity checks */
switch (encap) {
- case L2TP_ENCAPTYPE_UDP:
- err = -EPROTONOSUPPORT;
- if (sk->sk_protocol != IPPROTO_UDP) {
- pr_err("tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
- tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP);
+ case L2TP_ENCAPTYPE_UDP: {
+ struct udp_tunnel_sock_cfg udp_cfg;
+ struct l2tp_udp_tunnel *udp_tunnel;
+ struct udp_tunnel_sock *uts;
+
+ udp_cfg.sock = sock;
+ udp_cfg.encap_type = UDP_ENCAP_L2TPINUDP;
+ udp_cfg.encap_rcv = l2tp_udp_encap_recv;
+ udp_cfg.encap_destroy = l2tp_udp_encap_destroy;
+
+ uts = create_udp_tunnel_sock(net, sizeof(*udp_tunnel),
+ &udp_cfg);
+ if (!uts) {
+ err = -ENOMEM;
goto err;
}
+
+ udp_tunnel = container_of(uts, struct l2tp_udp_tunnel, uts);
+ tunnel = &udp_tunnel->tunnel;
break;
+ }
case L2TP_ENCAPTYPE_IP:
- err = -EPROTONOSUPPORT;
- if (sk->sk_protocol != IPPROTO_L2TP) {
- pr_err("tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
- tunnel_id, fd, sk->sk_protocol, IPPROTO_L2TP);
+ tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
+ if (tunnel == NULL) {
+ err = -ENOMEM;
goto err;
}
- break;
}
- /* Check if this socket has already been prepped */
- tunnel = l2tp_tunnel(sk);
- if (tunnel != NULL) {
- /* This socket has already been prepped */
- err = -EBUSY;
- goto err;
- }
-
- tunnel = kzalloc(sizeof(struct l2tp_tunnel), GFP_KERNEL);
- if (tunnel == NULL) {
- err = -ENOMEM;
- goto err;
- }
+ rcu_assign_sk_user_data(sk, tunnel);
+ tunnel->encap = encap;
tunnel->version = version;
tunnel->tunnel_id = tunnel_id;
tunnel->peer_tunnel_id = peer_tunnel_id;
- tunnel->debug = L2TP_DEFAULT_DEBUG_FLAGS;
-
+ tunnel->debug = cfg ? cfg->debug : L2TP_DEFAULT_DEBUG_FLAGS;
tunnel->magic = L2TP_TUNNEL_MAGIC;
sprintf(&tunnel->name[0], "tunl %u", tunnel_id);
rwlock_init(&tunnel->hlist_lock);
@@ -1560,9 +1628,6 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
tunnel->l2tp_net = net;
pn = l2tp_pernet(net);
- if (cfg != NULL)
- tunnel->debug = cfg->debug;
-
#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
@@ -1581,23 +1646,6 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
}
#endif
- /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
- tunnel->encap = encap;
- if (encap == L2TP_ENCAPTYPE_UDP) {
- /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
- udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP;
- udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;
- udp_sk(sk)->encap_destroy = l2tp_udp_encap_destroy;
-#if IS_ENABLED(CONFIG_IPV6)
- if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
- udpv6_encap_enable();
- else
-#endif
- udp_encap_enable();
- }
-
- sk->sk_user_data = tunnel;
-
/* Hook on the tunnel socket destructor so that we can cleanup
* if the tunnel socket goes away.
*/
--
1.7.9.5
^ permalink raw reply related
* [net-next v5 2/3] vxlan: Refactor vxlan driver to make use of the common UDP tunnel functions.
From: Andy Zhou @ 2014-09-11 3:29 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410406193-6185-1-git-send-email-azhou@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
---
drivers/net/vxlan.c | 174 +++++++++++++++++------------------------
include/net/vxlan.h | 17 ++--
net/openvswitch/vport-vxlan.c | 6 +-
3 files changed, 84 insertions(+), 113 deletions(-)
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 53c3ec1..d915669 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -42,6 +42,7 @@
#include <net/netns/generic.h>
#include <net/vxlan.h>
#include <net/protocol.h>
+#include <net/udp_tunnel.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ipv6.h>
#include <net/addrconf.h>
@@ -280,7 +281,7 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, __be16 port)
struct vxlan_sock *vs;
hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
- if (inet_sk(vs->sock->sk)->inet_sport == port)
+ if (inet_sk(vs->uts.sock->sk)->inet_sport == port)
return vs;
}
return NULL;
@@ -636,7 +637,7 @@ static int vxlan_gro_complete(struct sk_buff *skb, int nhoff)
static void vxlan_notify_add_rx_port(struct vxlan_sock *vs)
{
struct net_device *dev;
- struct sock *sk = vs->sock->sk;
+ struct sock *sk = vs->uts.sock->sk;
struct net *net = sock_net(sk);
sa_family_t sa_family = sk->sk_family;
__be16 port = inet_sk(sk)->inet_sport;
@@ -661,7 +662,7 @@ static void vxlan_notify_add_rx_port(struct vxlan_sock *vs)
static void vxlan_notify_del_rx_port(struct vxlan_sock *vs)
{
struct net_device *dev;
- struct sock *sk = vs->sock->sk;
+ struct sock *sk = vs->uts.sock->sk;
struct net *net = sock_net(sk);
sa_family_t sa_family = sk->sk_family;
__be16 port = inet_sk(sk)->inet_sport;
@@ -1053,7 +1054,7 @@ static void vxlan_sock_hold(struct vxlan_sock *vs)
void vxlan_sock_release(struct vxlan_sock *vs)
{
- struct sock *sk = vs->sock->sk;
+ struct sock *sk = vs->uts.sock->sk;
struct net *net = sock_net(sk);
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
@@ -1062,7 +1063,6 @@ void vxlan_sock_release(struct vxlan_sock *vs)
spin_lock(&vn->sock_lock);
hlist_del_rcu(&vs->hlist);
- rcu_assign_sk_user_data(vs->sock->sk, NULL);
vxlan_notify_del_rx_port(vs);
spin_unlock(&vn->sock_lock);
@@ -1078,7 +1078,7 @@ static void vxlan_igmp_join(struct work_struct *work)
{
struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_join);
struct vxlan_sock *vs = vxlan->vn_sock;
- struct sock *sk = vs->sock->sk;
+ struct sock *sk = vs->uts.sock->sk;
union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
int ifindex = vxlan->default_dst.remote_ifindex;
@@ -1107,7 +1107,7 @@ static void vxlan_igmp_leave(struct work_struct *work)
{
struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_leave);
struct vxlan_sock *vs = vxlan->vn_sock;
- struct sock *sk = vs->sock->sk;
+ struct sock *sk = vs->uts.sock->sk;
union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
int ifindex = vxlan->default_dst.remote_ifindex;
@@ -1336,7 +1336,6 @@ out:
}
#if IS_ENABLED(CONFIG_IPV6)
-
static struct sk_buff *vxlan_na_create(struct sk_buff *request,
struct neighbour *n, bool isrouter)
{
@@ -1570,13 +1569,6 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
return false;
}
-static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
- bool udp_csum)
-{
- int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
- return iptunnel_handle_offloads(skb, udp_csum, type);
-}
-
#if IS_ENABLED(CONFIG_IPV6)
static int vxlan6_xmit_skb(struct vxlan_sock *vs,
struct dst_entry *dst, struct sk_buff *skb,
@@ -1585,13 +1577,12 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
__be16 src_port, __be16 dst_port, __be32 vni,
bool xnet)
{
- struct ipv6hdr *ip6h;
struct vxlanhdr *vxh;
- struct udphdr *uh;
int min_headroom;
int err;
+ bool udp_sum = !udp_get_no_check6_tx(vs->uts.sock->sk);
- skb = vxlan_handle_offloads(skb, !udp_get_no_check6_tx(vs->sock->sk));
+ skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb))
return -EINVAL;
@@ -1619,38 +1610,8 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
vxh->vx_flags = htonl(VXLAN_FLAGS);
vxh->vx_vni = vni;
- __skb_push(skb, sizeof(*uh));
- skb_reset_transport_header(skb);
- uh = udp_hdr(skb);
-
- uh->dest = dst_port;
- uh->source = src_port;
-
- uh->len = htons(skb->len);
-
- memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
- IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
- IPSKB_REROUTED);
- skb_dst_set(skb, dst);
-
- udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
- saddr, daddr, skb->len);
-
- __skb_push(skb, sizeof(*ip6h));
- skb_reset_network_header(skb);
- ip6h = ipv6_hdr(skb);
- ip6h->version = 6;
- ip6h->priority = prio;
- ip6h->flow_lbl[0] = 0;
- ip6h->flow_lbl[1] = 0;
- ip6h->flow_lbl[2] = 0;
- ip6h->payload_len = htons(skb->len);
- ip6h->nexthdr = IPPROTO_UDP;
- ip6h->hop_limit = ttl;
- ip6h->daddr = *daddr;
- ip6h->saddr = *saddr;
-
- ip6tunnel_xmit(skb, dev);
+ udp_tunnel6_xmit_skb(&vs->uts, dst, skb, dev, saddr, daddr, prio,
+ ttl, src_port, dst_port);
return 0;
}
#endif
@@ -1661,11 +1622,11 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
__be16 src_port, __be16 dst_port, __be32 vni, bool xnet)
{
struct vxlanhdr *vxh;
- struct udphdr *uh;
int min_headroom;
int err;
+ bool udp_sum = !vs->uts.sock->sk->sk_no_check_tx;
- skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
+ skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb))
return -EINVAL;
@@ -1691,20 +1652,8 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
vxh->vx_flags = htonl(VXLAN_FLAGS);
vxh->vx_vni = vni;
- __skb_push(skb, sizeof(*uh));
- skb_reset_transport_header(skb);
- uh = udp_hdr(skb);
-
- uh->dest = dst_port;
- uh->source = src_port;
-
- uh->len = htons(skb->len);
-
- udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
- src, dst, skb->len);
-
- return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
- tos, ttl, df, xnet);
+ return udp_tunnel_xmit_skb(&vs->uts, rt, skb, src, dst, tos,
+ ttl, df, src_port, dst_port, xnet);
}
EXPORT_SYMBOL_GPL(vxlan_xmit_skb);
@@ -1829,18 +1778,18 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
- err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb,
- fl4.saddr, dst->sin.sin_addr.s_addr,
- tos, ttl, df, src_port, dst_port,
- htonl(vni << 8),
- !net_eq(vxlan->net, dev_net(vxlan->dev)));
+ err = udp_tunnel_xmit_skb(&vxlan->vn_sock->uts, rt, skb,
+ fl4.saddr, dst->sin.sin_addr.s_addr,
+ tos, ttl, df, src_port, dst_port,
+ !net_eq(vxlan->net,
+ dev_net(vxlan->dev)));
if (err < 0)
goto rt_tx_error;
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
#if IS_ENABLED(CONFIG_IPV6)
} else {
- struct sock *sk = vxlan->vn_sock->sock->sk;
+ struct sock *sk = vxlan->vn_sock->uts.sock->sk;
struct dst_entry *ndst;
struct flowi6 fl6;
u32 flags;
@@ -2202,8 +2151,8 @@ void vxlan_get_rx_port(struct net_device *dev)
spin_lock(&vn->sock_lock);
for (i = 0; i < PORT_HASH_SIZE; ++i) {
hlist_for_each_entry_rcu(vs, &vn->sock_list[i], hlist) {
- port = inet_sk(vs->sock->sk)->inet_sport;
- sa_family = vs->sock->sk->sk_family;
+ port = inet_sk(vs->uts.sock->sk)->inet_sport;
+ sa_family = vs->uts.sock->sk->sk_family;
dev->netdev_ops->ndo_add_vxlan_port(dev, sa_family,
port);
}
@@ -2330,12 +2279,18 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
.get_link = ethtool_op_get_link,
};
+static void free_vxlan_sock_rcu(struct rcu_head *rcu)
+{
+ struct vxlan_sock *vs = container_of(rcu, struct vxlan_sock, rcu);
+
+ udp_tunnel_sock_free(&vs->uts);
+}
+
static void vxlan_del_work(struct work_struct *work)
{
struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work);
-
- sk_release_kernel(vs->sock->sk);
- kfree_rcu(vs, rcu);
+ udp_tunnel_sock_release(&vs->uts);
+ call_rcu(&vs->rcu, free_vxlan_sock_rcu);
}
static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
@@ -2375,39 +2330,60 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
return sock;
}
+static struct vxlan_sock *vxlan_create_tunnel_sock(struct net *net,
+ struct socket *sock)
+{
+ struct udp_tunnel_sock_cfg cfg;
+ struct udp_tunnel_sock *uts;
+ struct vxlan_sock *vs;
+
+ cfg.sock = sock;
+ cfg.encap_type = 1;
+ cfg.encap_rcv = vxlan_udp_encap_recv;
+ cfg.encap_destroy = NULL;
+
+ uts = create_udp_tunnel_sock(net, sizeof(*vs), &cfg);
+
+ if (IS_ERR(uts))
+ vs = ERR_CAST(uts);
+ else
+ vs = container_of(uts, struct vxlan_sock, uts);
+
+ return vs;
+}
+
/* Create new listen socket if needed */
static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
- vxlan_rcv_t *rcv, void *data,
+ vxlan_rcv_t rcv, void *data,
u32 flags)
{
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
struct vxlan_sock *vs;
struct socket *sock;
- struct sock *sk;
- unsigned int h;
bool ipv6 = !!(flags & VXLAN_F_IPV6);
+ unsigned int h;
- vs = kzalloc(sizeof(*vs), GFP_KERNEL);
- if (!vs)
- return ERR_PTR(-ENOMEM);
+ sock = vxlan_create_sock(net, ipv6, port, flags);
+ if (IS_ERR(sock))
+ return ERR_CAST(sock);
+
+ vs = vxlan_create_tunnel_sock(net, sock);
+ if (IS_ERR(vs))
+ return vs;
for (h = 0; h < VNI_HASH_SIZE; ++h)
INIT_HLIST_HEAD(&vs->vni_list[h]);
- INIT_WORK(&vs->del_work, vxlan_del_work);
+ spin_lock(&vn->sock_lock);
+ list_add(&vs->next, &vn->vxlan_list);
+ spin_unlock(&vn->sock_lock);
- sock = vxlan_create_sock(net, ipv6, port, flags);
- if (IS_ERR(sock)) {
- kfree(vs);
- return ERR_CAST(sock);
- }
+ INIT_WORK(&vs->del_work, vxlan_del_work);
- vs->sock = sock;
- sk = sock->sk;
atomic_set(&vs->refcnt, 1);
+
vs->rcv = rcv;
- vs->data = data;
- rcu_assign_sk_user_data(vs->sock->sk, vs);
+ vs->rcv_data = data;
/* Initialize the vxlan udp offloads structure */
vs->udp_offloads.port = port;
@@ -2419,21 +2395,11 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
vxlan_notify_add_rx_port(vs);
spin_unlock(&vn->sock_lock);
- /* Mark socket as an encapsulation socket. */
- udp_sk(sk)->encap_type = 1;
- udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
-#if IS_ENABLED(CONFIG_IPV6)
- if (ipv6)
- ipv6_stub->udpv6_encap_enable();
- else
-#endif
- udp_encap_enable();
-
return vs;
}
struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
- vxlan_rcv_t *rcv, void *data,
+ vxlan_rcv_t rcv, void *data,
bool no_share, u32 flags)
{
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index d5f59f3..b1dd547 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -4,23 +4,27 @@
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/udp.h>
+#include <net/udp_tunnel.h>
#define VNI_HASH_BITS 10
#define VNI_HASH_SIZE (1<<VNI_HASH_BITS)
struct vxlan_sock;
-typedef void (vxlan_rcv_t)(struct vxlan_sock *vh, struct sk_buff *skb, __be32 key);
-/* per UDP socket information */
+typedef void (*vxlan_rcv_t)(struct vxlan_sock *vs, struct sk_buff *skb,
+ __be32 key);
+
+/* per vxlan socket information */
struct vxlan_sock {
+ struct udp_tunnel_sock uts; /* Must be the first member */
struct hlist_node hlist;
- vxlan_rcv_t *rcv;
- void *data;
+ struct list_head next;
struct work_struct del_work;
- struct socket *sock;
struct rcu_head rcu;
struct hlist_head vni_list[VNI_HASH_SIZE];
atomic_t refcnt;
+ vxlan_rcv_t rcv;
+ void *rcv_data;
struct udp_offload udp_offloads;
};
@@ -35,7 +39,7 @@ struct vxlan_sock {
#define VXLAN_F_UDP_ZERO_CSUM6_RX 0x100
struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
- vxlan_rcv_t *rcv, void *data,
+ vxlan_rcv_t rcv, void *data,
bool no_share, u32 flags);
void vxlan_sock_release(struct vxlan_sock *vs);
@@ -57,4 +61,5 @@ static inline void vxlan_get_rx_port(struct net_device *netdev)
{
}
#endif
+
#endif
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index d8b7e24..7599efd 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -59,7 +59,7 @@ static inline struct vxlan_port *vxlan_vport(const struct vport *vport)
static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32 vx_vni)
{
struct ovs_key_ipv4_tunnel tun_key;
- struct vport *vport = vs->data;
+ struct vport *vport = vs->rcv_data;
struct iphdr *iph;
__be64 key;
@@ -74,7 +74,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32 vx_vni)
static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
{
struct vxlan_port *vxlan_port = vxlan_vport(vport);
- __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport;
+ __be16 dst_port = inet_sk(vxlan_port->vs->uts.sock->sk)->inet_sport;
if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port)))
return -EMSGSIZE;
@@ -139,7 +139,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
{
struct net *net = ovs_dp_get_net(vport->dp);
struct vxlan_port *vxlan_port = vxlan_vport(vport);
- __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport;
+ __be16 dst_port = inet_sk(vxlan_port->vs->uts.sock->sk)->inet_sport;
struct rtable *rt;
struct flowi4 fl;
__be16 src_port;
--
1.7.9.5
^ permalink raw reply related
* [net-next v5 1/3] udp-tunnel: Expand UDP tunnel APIs
From: Andy Zhou @ 2014-09-11 3:29 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410406193-6185-1-git-send-email-azhou@nicira.com>
Added common udp tunnel socket creation, and packet transmission APIs
API that can be used by other UDP based tunneling protocol
implementation.
Signed-off-by: Andy Zhou <azhou@nicira.com>
---
include/net/udp_tunnel.h | 73 +++++++++++++++++++++++++++
net/ipv4/Kconfig | 1 +
net/ipv4/udp_tunnel.c | 108 ++++++++++++++++++++++++++--------------
net/ipv6/Makefile | 1 +
net/ipv6/ip6_udp_tunnel.c | 121 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 268 insertions(+), 36 deletions(-)
create mode 100644 net/ipv6/ip6_udp_tunnel.c
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index ffd69cb..e9dcf83 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -1,6 +1,14 @@
#ifndef __NET_UDP_TUNNEL_H
#define __NET_UDP_TUNNEL_H
+#include <net/ip_tunnels.h>
+#include <net/udp.h>
+
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ipv6.h>
+#include <net/addrconf.h>
+#endif
+
struct udp_port_cfg {
u8 family;
@@ -29,4 +37,69 @@ struct udp_port_cfg {
int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
struct socket **sockp);
+#if IS_ENABLED(CONFIG_IPV6)
+int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+ struct socket **sockp);
+#else
+static inline int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+ struct socket **sockp)
+{
+ return 0;
+}
+#endif
+
+struct udp_tunnel_sock;
+
+typedef int (*udp_tunnel_encap_rcv_t)(struct sock *sk, struct sk_buff *skb);
+typedef void (*udp_tunnel_encap_destroy_t)(struct sock *sk);
+
+struct udp_tunnel_sock_cfg {
+ struct socket *sock; /* The socket UDP tunnel will attach to */
+ /* Used for setting up udp_sock fields, see udp.h for details */
+ __u8 encap_type;
+ udp_tunnel_encap_rcv_t encap_rcv;
+ udp_tunnel_encap_destroy_t encap_destroy;
+};
+
+struct udp_tunnel_sock {
+ struct socket *sock;
+};
+
+struct udp_tunnel_sock *create_udp_tunnel_sock(struct net *net, size_t size,
+ struct udp_tunnel_sock_cfg
+ *sock_cfg);
+
+int udp_tunnel_xmit_skb(struct udp_tunnel_sock *uts, struct rtable *rt,
+ struct sk_buff *skb, __be32 src, __be32 dst,
+ __u8 tos, __u8 ttl, __be16 df, __be16 src_port,
+ __be16 dst_port, bool xnet);
+
+#if IS_ENABLED(CONFIG_IPV6)
+int udp_tunnel6_xmit_skb(struct udp_tunnel_sock *uts, struct dst_entry *dst,
+ struct sk_buff *skb, struct net_device *dev,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ __u8 prio, __u8 ttl, __be16 src_port,
+ __be16 dst_port);
+#endif
+
+void udp_tunnel_sock_release(struct udp_tunnel_sock *uts);
+void udp_tunnel_sock_free(struct udp_tunnel_sock *uts);
+
+static inline struct sk_buff *udp_tunnel_handle_offloads(struct sk_buff *skb,
+ bool udp_csum)
+{
+ int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+
+ return iptunnel_handle_offloads(skb, udp_csum, type);
+}
+
+static inline void udp_tunnel_encap_enable(struct socket *sock)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sock->sk->sk_family == PF_INET6)
+ ipv6_stub->udpv6_encap_enable();
+ else
+#endif
+ udp_encap_enable();
+}
#endif
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index dbc10d8..cccb95f 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -308,6 +308,7 @@ config NET_IPVTI
on top.
config NET_UDP_TUNNEL
+ depends on (IPV6 || IPV6=n)
tristate
default n
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index 61ec1a6..d60c1a0 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -14,42 +14,9 @@ int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
int err = -EINVAL;
struct socket *sock = NULL;
-#if IS_ENABLED(CONFIG_IPV6)
if (cfg->family == AF_INET6) {
- struct sockaddr_in6 udp6_addr;
-
- err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
- if (err < 0)
- goto error;
-
- sk_change_net(sock->sk, net);
-
- udp6_addr.sin6_family = AF_INET6;
- memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
- sizeof(udp6_addr.sin6_addr));
- udp6_addr.sin6_port = cfg->local_udp_port;
- err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
- sizeof(udp6_addr));
- if (err < 0)
- goto error;
-
- if (cfg->peer_udp_port) {
- udp6_addr.sin6_family = AF_INET6;
- memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
- sizeof(udp6_addr.sin6_addr));
- udp6_addr.sin6_port = cfg->peer_udp_port;
- err = kernel_connect(sock,
- (struct sockaddr *)&udp6_addr,
- sizeof(udp6_addr), 0);
- }
- if (err < 0)
- goto error;
-
- udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
- udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
- } else
-#endif
- if (cfg->family == AF_INET) {
+ return udp_sock_create6(net, cfg, sockp);
+ } else if (cfg->family == AF_INET) {
struct sockaddr_in udp_addr;
err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock);
@@ -82,7 +49,6 @@ int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
return -EPFNOSUPPORT;
}
-
*sockp = sock;
return 0;
@@ -97,4 +63,74 @@ error:
}
EXPORT_SYMBOL(udp_sock_create);
+struct udp_tunnel_sock *
+create_udp_tunnel_sock(struct net *net, size_t size,
+ struct udp_tunnel_sock_cfg *cfg)
+{
+ struct udp_tunnel_sock *uts;
+ struct sock *sk;
+ struct socket *sock = cfg->sock;
+
+ uts = kzalloc(size, GFP_KERNEL);
+ if (!uts)
+ return ERR_PTR(-ENOMEM);
+
+ sk = sock->sk;
+
+ /* Disable multicast loopback */
+ inet_sk(sk)->mc_loop = 0;
+
+ rcu_assign_sk_user_data(sk, uts);
+
+ udp_sk(sk)->encap_type = cfg->encap_type;
+ udp_sk(sk)->encap_rcv = cfg->encap_rcv;
+ udp_sk(sk)->encap_destroy = cfg->encap_destroy;
+
+ uts->sock = sock;
+
+ udp_tunnel_encap_enable(sock);
+
+ return uts;
+}
+EXPORT_SYMBOL_GPL(create_udp_tunnel_sock);
+
+int udp_tunnel_xmit_skb(struct udp_tunnel_sock *uts, struct rtable *rt,
+ struct sk_buff *skb, __be32 src, __be32 dst,
+ __u8 tos, __u8 ttl, __be16 df, __be16 src_port,
+ __be16 dst_port, bool xnet)
+{
+ struct udphdr *uh;
+ struct socket *sock = uts->sock;
+
+ __skb_push(skb, sizeof(*uh));
+ skb_reset_transport_header(skb);
+ uh = udp_hdr(skb);
+
+ uh->dest = dst_port;
+ uh->source = src_port;
+ uh->len = htons(skb->len);
+
+ udp_set_csum(sock->sk->sk_no_check_tx, skb, src, dst, skb->len);
+
+ return iptunnel_xmit(sock->sk, rt, skb, src, dst, IPPROTO_UDP,
+ tos, ttl, df, xnet);
+}
+EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb);
+
+void udp_tunnel_sock_release(struct udp_tunnel_sock *uts)
+{
+ struct sock *sk = uts->sock->sk;
+
+ rcu_assign_sk_user_data(uts->sock->sk, NULL);
+ kernel_sock_shutdown(uts->sock, SHUT_RDWR);
+ sk_release_kernel(sk);
+}
+EXPORT_SYMBOL_GPL(udp_tunnel_sock_release);
+
+void udp_tunnel_sock_free(struct udp_tunnel_sock *uts)
+{
+ kfree(uts);
+}
+EXPORT_SYMBOL_GPL(udp_tunnel_sock_free);
+
MODULE_LICENSE("GPL");
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 2fe6836..45f830e 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION) += xfrm6_mode_ro.o
obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o
obj-$(CONFIG_IPV6_MIP6) += mip6.o
obj-$(CONFIG_NETFILTER) += netfilter/
+obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o
obj-$(CONFIG_IPV6_VTI) += ip6_vti.o
obj-$(CONFIG_IPV6_SIT) += sit.o
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
new file mode 100644
index 0000000..5109f46
--- /dev/null
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -0,0 +1,121 @@
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/in6.h>
+#include <net/udp.h>
+#include <net/udp_tunnel.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/ip6_tunnel.h>
+#include <net/ip6_checksum.h>
+
+int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+ struct socket **sockp)
+{
+ struct sockaddr_in6 udp6_addr;
+ int err = -EINVAL;
+ struct socket *sock = NULL;
+
+ err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
+ if (err < 0)
+ goto error;
+
+ sk_change_net(sock->sk, net);
+
+ udp6_addr.sin6_family = AF_INET6;
+ memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
+ sizeof(udp6_addr.sin6_addr));
+ udp6_addr.sin6_port = cfg->local_udp_port;
+ err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
+ sizeof(udp6_addr));
+ if (err < 0)
+ goto error;
+
+ if (cfg->peer_udp_port) {
+ udp6_addr.sin6_family = AF_INET6;
+ memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
+ sizeof(udp6_addr.sin6_addr));
+ udp6_addr.sin6_port = cfg->peer_udp_port;
+ err = kernel_connect(sock,
+ (struct sockaddr *)&udp6_addr,
+ sizeof(udp6_addr), 0);
+ }
+ if (err < 0)
+ goto error;
+
+ udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
+ udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
+
+ *sockp = sock;
+ return 0;
+
+error:
+ if (sock) {
+ kernel_sock_shutdown(sock, SHUT_RDWR);
+ sk_release_kernel(sock->sk);
+ }
+ *sockp = NULL;
+ return err;
+}
+EXPORT_SYMBOL_GPL(udp_sock_create6);
+
+int udp_tunnel6_xmit_skb(struct udp_tunnel_sock *uts, struct dst_entry *dst,
+ struct sk_buff *skb, struct net_device *dev,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ __u8 prio, __u8 ttl, __be16 src_port, __be16 dst_port)
+{
+ struct udphdr *uh;
+ struct ipv6hdr *ip6h;
+
+ __skb_push(skb, sizeof(*uh));
+ skb_reset_transport_header(skb);
+ uh = udp_hdr(skb);
+
+ uh->dest = dst_port;
+ uh->source = src_port;
+
+ uh->len = htons(skb->len);
+ uh->check = 0;
+
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+ IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED
+ | IPSKB_REROUTED);
+ skb_dst_set(skb, dst);
+
+ if (!skb_is_gso(skb) && !(dst->dev->features & NETIF_F_IPV6_CSUM)) {
+ __wsum csum = skb_checksum(skb, 0, skb->len, 0);
+
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ uh->check = csum_ipv6_magic(saddr, daddr, skb->len,
+ IPPROTO_UDP, csum);
+ if (uh->check == 0)
+ uh->check = CSUM_MANGLED_0;
+ } else {
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ uh->check = ~csum_ipv6_magic(saddr, daddr,
+ skb->len, IPPROTO_UDP, 0);
+ }
+
+ __skb_push(skb, sizeof(*ip6h));
+ skb_reset_network_header(skb);
+ ip6h = ipv6_hdr(skb);
+ ip6h->version = 6;
+ ip6h->priority = prio;
+ ip6h->flow_lbl[0] = 0;
+ ip6h->flow_lbl[1] = 0;
+ ip6h->flow_lbl[2] = 0;
+ ip6h->payload_len = htons(skb->len);
+ ip6h->nexthdr = IPPROTO_UDP;
+ ip6h->hop_limit = ttl;
+ ip6h->daddr = *daddr;
+ ip6h->saddr = *saddr;
+
+ ip6tunnel_xmit(skb, dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
--
1.7.9.5
^ permalink raw reply related
* [net-next v5 0/3] Refactor vxlan and l2tp to use common UDP tunnel APIs
From: Andy Zhou @ 2014-09-11 3:29 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
This patch series expend current UDP tunnel APIs and refactoring current
UDP tunnel based protocols to make use of the new APIs. The main motivation
is to reduce code duplication.
*** BLURB HERE ***
Andy Zhou (3):
udp-tunnel: Expand UDP tunnel APIs
vxlan: Refactor vxlan driver to make use of the common UDP tunnel
functions.
l2tp: Refactor l2tp core driver to make use of the common UDP tunnel
functions
drivers/net/vxlan.c | 174 ++++++++++++++--------------------
include/net/udp_tunnel.h | 73 +++++++++++++++
include/net/vxlan.h | 17 ++--
net/ipv4/Kconfig | 1 +
net/ipv4/udp_tunnel.c | 108 ++++++++++++++-------
net/ipv6/Makefile | 1 +
net/ipv6/ip6_udp_tunnel.c | 121 ++++++++++++++++++++++++
net/l2tp/l2tp_core.c | 208 +++++++++++++++++++++++++----------------
net/openvswitch/vport-vxlan.c | 6 +-
9 files changed, 480 insertions(+), 229 deletions(-)
create mode 100644 net/ipv6/ip6_udp_tunnel.c
--
1.7.9.5
^ permalink raw reply
* Re: [net-next PATCH v4 12/16] net: sched: rcu'ify cls_bpf
From: Eric Dumazet @ 2014-09-11 2:28 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910155141.2036.66048.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:51 -0700, John Fastabend wrote:
> This patch makes the cls_bpf classifier RCU safe. The tcf_lock
> was being used to protect a list of cls_bpf_prog now this list
> is RCU safe and updates occur with rcu_replace.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
...
> @@ -256,18 +254,19 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
> if (ret < 0)
> return ret;
>
> - if (prog != NULL) {
> - if (handle && prog->handle != handle)
> - return -EINVAL;
> - return cls_bpf_modify_existing(net, tp, prog, base, tb,
> - tca[TCA_RATE], ovr);
> - }
> -
> prog = kzalloc(sizeof(*prog), GFP_KERNEL);
> - if (prog == NULL)
> + if (!prog)
> return -ENOBUFS;
>
> tcf_exts_init(&prog->exts, TCA_BPF_ACT, TCA_BPF_POLICE);
> +
> + if (oldprog) {
> + if (handle && oldprog->handle != handle) {
> + ret = -EINVAL;
> + goto errout;
> + }
> + }
> +
> if (handle == 0)
> prog->handle = cls_bpf_grab_new_handle(tp, head);
> else
> @@ -281,15 +280,17 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
> if (ret < 0)
> goto errout;
>
> - tcf_tree_lock(tp);
> - list_add(&prog->link, &head->plist);
> - tcf_tree_unlock(tp);
> + if (oldprog) {
> + list_replace_rcu(&prog->link, &oldprog->link);
> + call_rcu(&oldprog->rcu, __cls_bpf_delete_prog);
> + } else {
> + list_add_rcu(&prog->link, &head->plist);
> + }
>
> *arg = (unsigned long) prog;
> -
> return 0;
> errout:
> - if (*arg == 0UL && prog)
> + if (prog)
> kfree(prog);
>
nit, you can directly call kfree(prog) even if prog == NULL
^ permalink raw reply
* linux-next: manual merge of the arm64 tree with the net tree
From: Stephen Rothwell @ 2014-09-11 2:16 UTC (permalink / raw)
To: Catalin Marinas, David Miller, netdev
Cc: linux-next, linux-kernel, Zi Shen Lim, Will Deacon
[-- Attachment #1: Type: text/plain, Size: 1389 bytes --]
Hi Catalin,
Today's linux-next merge of the arm64 tree got a conflict in
Documentation/networking/filter.txt between commit 1d7efe9dfaa6
("Documentation: filter: Add MIPS to architectures with BPF JIT") from
the net tree and commit e54bcde3d69d ("arm64: eBPF JIT compiler") from
the arm64 tree.
I fixed it up (see below) and can carry the fix as necessary (no action
is required).
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
diff --cc Documentation/networking/filter.txt
index d16f424c5e8d,1842d4f8ae13..000000000000
--- a/Documentation/networking/filter.txt
+++ b/Documentation/networking/filter.txt
@@@ -462,9 -462,9 +462,9 @@@ JIT compile
------------
The Linux kernel has a built-in BPF JIT compiler for x86_64, SPARC, PowerPC,
- ARM, MIPS and s390 and can be enabled through CONFIG_BPF_JIT. The JIT compiler
-ARM, ARM64 and s390 and can be enabled through CONFIG_BPF_JIT. The JIT compiler
--is transparently invoked for each attached filter from user space or for
--internal kernel users if it has been previously enabled by root:
++ARM, ARM64, MIPS and s390 and can be enabled through CONFIG_BPF_JIT. The JIT
++compiler is transparently invoked for each attached filter from user space or
++for internal kernel users if it has been previously enabled by root:
echo 1 > /proc/sys/net/core/bpf_jit_enable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [net-next PATCH v4 11/16] net: sched: rcu'ify cls_rsvp
From: Eric Dumazet @ 2014-09-11 1:30 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910155116.2036.68040.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:51 -0700, John Fastabend wrote:
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
> net/sched/cls_rsvp.h | 157 ++++++++++++++++++++++++++++----------------------
> 1 file changed, 89 insertions(+), 68 deletions(-)
>
> diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
> index 1020e23..afa508b 100644
> --- a/net/sched/cls_rsvp.h
> +++ b/net/sched/cls_rsvp.h
> @@ -70,31 +70,34 @@ struct rsvp_head {
> u32 tmap[256/32];
> u32 hgenerator;
> u8 tgenerator;
> - struct rsvp_session *ht[256];
> + struct rsvp_session __rcu *ht[256];
> + struct rcu_head rcu;
> };
>
> struct rsvp_session {
> - struct rsvp_session *next;
> - __be32 dst[RSVP_DST_LEN];
> - struct tc_rsvp_gpi dpi;
> - u8 protocol;
> - u8 tunnelid;
> + struct rsvp_session __rcu *next;
> + __be32 dst[RSVP_DST_LEN];
> + struct tc_rsvp_gpi dpi;
> + u8 protocol;
> + u8 tunnelid;
> /* 16 (src,sport) hash slots, and one wildcard source slot */
> - struct rsvp_filter *ht[16 + 1];
> + struct rsvp_filter __rcu *ht[16 + 1];
> + struct rcu_head rcu;
> };
>
>
> struct rsvp_filter {
> - struct rsvp_filter *next;
> - __be32 src[RSVP_DST_LEN];
> - struct tc_rsvp_gpi spi;
> - u8 tunnelhdr;
> + struct rsvp_filter __rcu *next;
> + __be32 src[RSVP_DST_LEN];
> + struct tc_rsvp_gpi spi;
> + u8 tunnelhdr;
>
> - struct tcf_result res;
> - struct tcf_exts exts;
> + struct tcf_result res;
> + struct tcf_exts exts;
>
> - u32 handle;
> - struct rsvp_session *sess;
> + u32 handle;
> + struct rsvp_session *sess;
> + struct rcu_head rcu;
> };
>
> static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
> @@ -128,7 +131,7 @@ static inline unsigned int hash_src(__be32 *src)
> static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
> struct tcf_result *res)
> {
> - struct rsvp_session **sht = ((struct rsvp_head *)tp->root)->ht;
> + struct rsvp_head *head = rcu_dereference_bh(tp->root);
> struct rsvp_session *s;
> struct rsvp_filter *f;
> unsigned int h1, h2;
> @@ -169,7 +172,8 @@ restart:
> h1 = hash_dst(dst, protocol, tunnelid);
> h2 = hash_src(src);
>
> - for (s = sht[h1]; s; s = s->next) {
> + for (s = rcu_dereference_bh(head->ht[h1]); s;
> + s = rcu_dereference_bh(s->next)) {
> if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
> protocol == s->protocol &&
> !(s->dpi.mask &
> @@ -181,7 +185,8 @@ restart:
> #endif
> tunnelid == s->tunnelid) {
>
> - for (f = s->ht[h2]; f; f = f->next) {
> + for (f = rcu_dereference_bh(s->ht[h2]); f;
> + f = rcu_dereference_bh(f->next)) {
> if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
> !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
> #if RSVP_DST_LEN == 4
> @@ -205,7 +210,8 @@ matched:
> }
>
> /* And wildcard bucket... */
> - for (f = s->ht[16]; f; f = f->next) {
> + for (f = rcu_dereference_bh(s->ht[16]); f;
> + f = rcu_dereference_bh(f->next)) {
> *res = f->res;
> RSVP_APPLY_RESULT();
> goto matched;
> @@ -218,7 +224,7 @@ matched:
>
> static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
> {
> - struct rsvp_session **sht = ((struct rsvp_head *)tp->root)->ht;
> + struct rsvp_head *head = rtnl_dereference(tp->root);
> struct rsvp_session *s;
> struct rsvp_filter *f;
> unsigned int h1 = handle & 0xFF;
> @@ -227,8 +233,10 @@ static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
> if (h2 > 16)
> return 0;
>
> - for (s = sht[h1]; s; s = s->next) {
> - for (f = s->ht[h2]; f; f = f->next) {
> + for (s = rtnl_dereference(head->ht[h1]); s;
> + s = rtnl_dereference(s->next)) {
> + for (f = rtnl_dereference(s->ht[h2]); f;
> + f = rtnl_dereference(f->next)) {
> if (f->handle == handle)
> return (unsigned long)f;
> }
> @@ -246,7 +254,7 @@ static int rsvp_init(struct tcf_proto *tp)
>
> data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
> if (data) {
> - tp->root = data;
> + rcu_assign_pointer(tp->root, data);
> return 0;
> }
> return -ENOBUFS;
> @@ -257,53 +265,55 @@ rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
> {
> tcf_unbind_filter(tp, &f->res);
> tcf_exts_destroy(tp, &f->exts);
> - kfree(f);
> + kfree_rcu(f, rcu);
> }
>
> static void rsvp_destroy(struct tcf_proto *tp)
> {
> - struct rsvp_head *data = xchg(&tp->root, NULL);
> - struct rsvp_session **sht;
> + struct rsvp_head *data = rtnl_dereference(tp->root);
> int h1, h2;
>
> if (data == NULL)
> return;
>
> - sht = data->ht;
> + RCU_INIT_POINTER(tp->root, NULL);
>
> for (h1 = 0; h1 < 256; h1++) {
> struct rsvp_session *s;
>
> - while ((s = sht[h1]) != NULL) {
> - sht[h1] = s->next;
> + while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
> + RCU_INIT_POINTER(data->ht[h1], s->next);
>
> for (h2 = 0; h2 <= 16; h2++) {
> struct rsvp_filter *f;
>
> - while ((f = s->ht[h2]) != NULL) {
> - s->ht[h2] = f->next;
> + while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
> + rcu_assign_pointer(s->ht[h2], f->next);
> rsvp_delete_filter(tp, f);
> }
> }
> - kfree(s);
> + kfree_rcu(s, rcu);
> }
> }
> - kfree(data);
> + RCU_INIT_POINTER(tp->root, NULL);
Strange, you already did the RCU_INIT_POINTER(tp->root, NULL) before the
for(h1 = 0; h1 < 256; h1++) loop
> + kfree_rcu(data, rcu);
> }
>
> static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
> {
> - struct rsvp_filter **fp, *f = (struct rsvp_filter *)arg;
> + struct rsvp_head *head = rtnl_dereference(tp->root);
> + struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg;
> + struct rsvp_filter __rcu **fp;
> unsigned int h = f->handle;
> - struct rsvp_session **sp;
> - struct rsvp_session *s = f->sess;
> + struct rsvp_session __rcu **sp;
> + struct rsvp_session *nsp, *s = f->sess;
> int i;
>
> - for (fp = &s->ht[(h >> 8) & 0xFF]; *fp; fp = &(*fp)->next) {
> - if (*fp == f) {
> - tcf_tree_lock(tp);
> + fp = &s->ht[(h >> 8) & 0xFF];
> + for (nfp = rtnl_dereference(*fp); nfp;
> + fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
> + if (nfp == f) {
> *fp = f->next;
It seems you do not follow your own convention here ?
RCU_INIT_POINTER(*fp, f->next);
> - tcf_tree_unlock(tp);
> rsvp_delete_filter(tp, f);
>
> /* Strip tree */
> @@ -313,14 +323,12 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
> return 0;
>
> /* OK, session has no flows */
> - for (sp = &((struct rsvp_head *)tp->root)->ht[h & 0xFF];
> - *sp; sp = &(*sp)->next) {
> - if (*sp == s) {
> - tcf_tree_lock(tp);
> + sp = &head->ht[h & 0xFF];
> + for (nsp = rtnl_dereference(*sp); nsp;
> + sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
> + if (nsp == s) {
> *sp = s->next;
Same remark here.
> - tcf_tree_unlock(tp);
> -
> - kfree(s);
> + kfree_rcu(s, rcu);
> return 0;
> }
> }
> @@ -333,7 +341,7 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
Thanks !
^ permalink raw reply
* Re: [net-next PATCH v4 10/16] net: sched: make cls_u32 lockless
From: Eric Dumazet @ 2014-09-11 1:26 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910155050.2036.26320.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:50 -0700, John Fastabend wrote:
> Make cls_u32 classifier safe to run without holding lock. This patch
> converts statistics that are kept in read section u32_classify into
> per cpu counters.
>
> This patch was tested with a tight u32 filter add/delete loop while
> generating traffic with pktgen. By running pktgen on vlan devices
> created on top of a physical device we can hit the qdisc layer
> correctly. For ingress qdisc's a loopback cable was used.
>
> for i in {1..100}; do
> q=`echo $i%8|bc`;
> echo -n "u32 tos: iteration $i on queue $q";
> tc filter add dev p3p2 parent $p prio $i u32 match ip tos 0x10 0xff \
> action skbedit queue_mapping $q;
> sleep 1;
> tc filter del dev p3p2 prio $i;
>
> echo -n "u32 tos hash table: iteration $i on queue $q";
> tc filter add dev p3p2 parent $p protocol ip prio $i handle 628: u32 divisor 1
> tc filter add dev p3p2 parent $p protocol ip prio $i u32 \
> match ip protocol 17 0xff link 628: offset at 0 mask 0xf00 shift 6 plus 0
> tc filter add dev p3p2 parent $p protocol ip prio $i u32 \
> ht 628:0 match ip tos 0x10 0xff action skbedit queue_mapping $q
> sleep 2;
> tc filter del dev p3p2 prio $i
> sleep 1;
> done
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Re: [net-next PATCH v4 09/16] net: sched: make cls_u32 per cpu
From: Eric Dumazet @ 2014-09-11 1:19 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910155026.2036.4648.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:50 -0700, John Fastabend wrote:
> This uses per cpu counters in cls_u32 in preparation
> to convert over to rcu.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Re: [net-next PATCH v4 08/16] net: sched: RCU cls_tcindex
From: Eric Dumazet @ 2014-09-11 1:17 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910154959.2036.57657.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:50 -0700, John Fastabend wrote:
> Make cls_tcindex RCU safe.
>
> This patch addds a new RCU routine rcu_dereference_bh_rtnl() to check
> caller either holds the rcu read lock or RTNL. This is needed to
> handle the case where tcindex_lookup() is being called in both cases.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Re: [net-next PATCH v4 07/16] net: sched: RCU cls_route
From: Eric Dumazet @ 2014-09-11 1:12 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910154930.2036.78269.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:49 -0700, John Fastabend wrote:
> RCUify the route classifier. For now however spinlock's are used to
> protect fastmap cache.
>
> The issue here is the fastmap may be read by one CPU while the
> cache is being updated by another. An array of pointers could be
> one possible solution.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Re: [net-next PATCH v4 06/16] net: sched: fw use RCU
From: Eric Dumazet @ 2014-09-11 1:03 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910154903.2036.86240.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:49 -0700, John Fastabend wrote:
> RCU'ify fw classifier.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
> net/sched/cls_fw.c | 111 ++++++++++++++++++++++++++++++++++++----------------
> 1 file changed, 77 insertions(+), 34 deletions(-)
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Re: [net-next PATCH v4 05/16] net: sched: cls_flow use RCU
From: Eric Dumazet @ 2014-09-11 0:58 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910154837.2036.83248.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:48 -0700, John Fastabend wrote:
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
> net/sched/cls_flow.c | 145 +++++++++++++++++++++++++++++---------------------
> 1 file changed, 84 insertions(+), 61 deletions(-)
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Rusty away 18th September -- 11th October
From: Rusty Russell @ 2014-09-11 0:56 UTC (permalink / raw)
To: Linus Torvalds; +Cc: netdev, Michael S. Tsirkin, LKML, virtualization
Hi all,
Probably won't read mail. Linus, I'll have pull requests early
next week; if there's anything needed I'm sure Michael Tsirkin can
handle it.
Cheers,
Rusty.
PS. England and Italy for a holiday; my daughter chose them (somehow I
forgot to inform her of the existence of Euro Disney...)
^ permalink raw reply
* Re: [net-next PATCH v4 02/16] net: rcu-ify tcf_proto
From: Eric Dumazet @ 2014-09-11 0:56 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910154723.2036.13857.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:47 -0700, John Fastabend wrote:
> rcu'ify tcf_proto this allows calling tc_classify() without holding
> any locks. Updaters are protected by RTNL.
>
> This patch prepares the core net_sched infrastracture for running
> the classifier/action chains without holding the qdisc lock however
> it does nothing to ensure cls_xxx and act_xxx types also work without
> locking. Additional patches are required to address the fall out.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
...
> diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c
> index ed30e43..4b52b70 100644
> --- a/net/sched/sch_choke.c
> +++ b/net/sched/sch_choke.c
> @@ -57,7 +57,7 @@ struct choke_sched_data {
>
> /* Variables */
> struct red_vars vars;
> - struct tcf_proto *filter_list;
> + struct tcf_proto __rcu *filter_list;
> struct {
> u32 prob_drop; /* Early probability drops */
> u32 prob_mark; /* Early probability marks */
> @@ -193,9 +193,11 @@ static bool choke_classify(struct sk_buff *skb,
> {
> struct choke_sched_data *q = qdisc_priv(sch);
> struct tcf_result res;
> + struct tcf_proto *fl;
> int result;
>
> - result = tc_classify(skb, q->filter_list, &res);
> + fl = rcu_dereference_bh(q->filter_list);
Hmm... please change the caller to pass fl.
Idea is to read q->filter_list once.
> + result = tc_classify(skb, fl, &res);
> if (result >= 0) {
> #ifdef CONFIG_NET_CLS_ACT
> switch (result) {
> @@ -244,12 +246,14 @@ static bool choke_match_random(const struct choke_sched_data *q,
> unsigned int *pidx)
> {
> struct sk_buff *oskb;
> + struct tcf_proto *fl;
>
> if (q->head == q->tail)
> return false;
>
> oskb = choke_peek_random(q, pidx);
> - if (q->filter_list)
> + fl = rcu_dereference_bh(q->filter_list);
You could use rcu_access_pointer() and not have this fl variable.
> + if (fl)
> return choke_get_classid(nskb) == choke_get_classid(oskb);
>
> return choke_match_flow(oskb, nskb);
> @@ -259,9 +263,11 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
> {
> struct choke_sched_data *q = qdisc_priv(sch);
> const struct red_parms *p = &q->parms;
> + struct tcf_proto *fl;
> int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
>
> - if (q->filter_list) {
> + fl = rcu_dereference_bh(q->filter_list);
> + if (fl) {
> /* If using external classifiers, get result and record it. */
> if (!choke_classify(skb, sch, &ret))
Here I think you should pass fl as an additional parameter to
choke_classify()
OR, just use rcu_access_pointer() here as you do not deref
q->filter_list here.
> goto other_drop; /* Packet was eaten by filter */
> @@ -554,7 +560,8 @@ static unsigned long choke_bind(struct Qdisc *sch, unsigned long parent,
> return 0;
> }
>
> -static struct tcf_proto **choke_find_tcf(struct Qdisc *sch, unsigned long cl)
> +static struct tcf_proto __rcu **choke_find_tcf(struct Qdisc *sch,
> + unsigned long cl)
> {
> struct choke_sched_data *q = qdisc_priv(sch);
>
remaining part seems fine.
^ permalink raw reply
* [PATCH 3/3] virtio_ring: unify direct/indirect code paths.
From: Rusty Russell @ 2014-09-11 0:47 UTC (permalink / raw)
To: linux-kernel; +Cc: Rusty Russell, netdev, Michael S. Tsirkin
In-Reply-To: <1410396458-1165-1-git-send-email-rusty@rustcorp.com.au>
virtqueue_add() populates the virtqueue descriptor table from the sgs
given. If it uses an indirect descriptor table, then it puts a single
descriptor in the descriptor table pointing to the kmalloc'ed indirect
table where the sg is populated.
Previously vring_add_indirect() did the allocation and the simple
linear layout. We replace that with alloc_indirect() which allocates
the indirect table then chains it like the normal descriptor table so
we can reuse the core logic.
This slows down pktgen by less than 1/2 a percent (which uses direct
descriptors), as well as vring_bench, but it's far neater.
vring_bench before:
1061485790-1104800648(1.08254e+09+/-6.6e+06)ns
vring_bench after:
1125610268-1183528965(1.14172e+09+/-8e+06)ns
pktgen before:
787781-796334(793165+/-2.4e+03)pps 365-369(367.5+/-1.2)Mb/sec (365530384-369498976(3.68028e+08+/-1.1e+06)bps) errors: 0
pktgen after:
779988-790404(786391+/-2.5e+03)pps 361-366(364.35+/-1.3)Mb/sec (361914432-366747456(3.64885e+08+/-1.2e+06)bps) errors: 0
Now, if we make force indirect descriptors by turning off any_header_sg
in virtio_net.c:
pktgen before:
713773-721062(718374+/-2.1e+03)pps 331-334(332.95+/-0.92)Mb/sec (331190672-334572768(3.33325e+08+/-9.6e+05)bps) errors: 0
pktgen after:
710542-719195(714898+/-2.4e+03)pps 329-333(331.15+/-1.1)Mb/sec (329691488-333706480(3.31713e+08+/-1.1e+06)bps) errors: 0
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
drivers/virtio/virtio_ring.c | 134 ++++++++++++++++++-------------------------
1 file changed, 55 insertions(+), 79 deletions(-)
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 374399c62080..6d2b5310c991 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -99,18 +99,10 @@ struct vring_virtqueue
#define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
-/* Set up an indirect table of descriptors and add it to the queue. */
-static inline int vring_add_indirect(struct vring_virtqueue *vq,
- struct scatterlist *sgs[],
- unsigned int total_sg,
- unsigned int out_sgs,
- unsigned int in_sgs,
- gfp_t gfp)
+static struct vring_desc *alloc_indirect(unsigned int total_sg, gfp_t gfp)
{
- struct vring_desc *desc;
- unsigned head;
- struct scatterlist *sg;
- int i, n;
+ struct vring_desc *desc;
+ unsigned int i;
/*
* We require lowmem mappings for the descriptors because
@@ -119,51 +111,13 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
*/
gfp &= ~(__GFP_HIGHMEM | __GFP_HIGH);
- desc = kmalloc(total_sg * sizeof(struct vring_desc), gfp);
- if (!desc)
- return -ENOMEM;
-
- /* Transfer entries from the sg lists into the indirect page */
- i = 0;
- for (n = 0; n < out_sgs; n++) {
- for (sg = sgs[n]; sg; sg = sg_next(sg)) {
- desc[i].flags = VRING_DESC_F_NEXT;
- desc[i].addr = sg_phys(sg);
- desc[i].len = sg->length;
- desc[i].next = i+1;
- i++;
- }
- }
- for (; n < (out_sgs + in_sgs); n++) {
- for (sg = sgs[n]; sg; sg = sg_next(sg)) {
- desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
- desc[i].addr = sg_phys(sg);
- desc[i].len = sg->length;
- desc[i].next = i+1;
- i++;
- }
- }
- BUG_ON(i != total_sg);
-
- /* Last one doesn't continue. */
- desc[i-1].flags &= ~VRING_DESC_F_NEXT;
- desc[i-1].next = 0;
-
- /* We're about to use a buffer */
- vq->vq.num_free--;
-
- /* Use a single buffer which doesn't continue */
- head = vq->free_head;
- vq->vring.desc[head].flags = VRING_DESC_F_INDIRECT;
- vq->vring.desc[head].addr = virt_to_phys(desc);
- /* kmemleak gives a false positive, as it's hidden by virt_to_phys */
- kmemleak_ignore(desc);
- vq->vring.desc[head].len = i * sizeof(struct vring_desc);
-
- /* Update free pointer */
- vq->free_head = vq->vring.desc[head].next;
+ desc = kmalloc(total_sg * sizeof(struct vring_desc), gfp);
+ if (!desc)
+ return NULL;
- return head;
+ for (i = 0; i < total_sg; i++)
+ desc[i].next = i+1;
+ return desc;
}
static inline int virtqueue_add(struct virtqueue *_vq,
@@ -176,8 +130,10 @@ static inline int virtqueue_add(struct virtqueue *_vq,
{
struct vring_virtqueue *vq = to_vvq(_vq);
struct scatterlist *sg;
- unsigned int i, n, avail, uninitialized_var(prev);
+ struct vring_desc *desc;
+ unsigned int i, n, avail, descs_used, uninitialized_var(prev);
int head;
+ bool indirect;
START_USE(vq);
@@ -201,21 +157,40 @@ static inline int virtqueue_add(struct virtqueue *_vq,
}
#endif
+ BUG_ON(total_sg > vq->vring.num);
+ BUG_ON(total_sg == 0);
+
+ head = vq->free_head;
+
/* If the host supports indirect descriptor tables, and we have multiple
* buffers, then go indirect. FIXME: tune this threshold */
- if (vq->indirect && total_sg > 1 && vq->vq.num_free) {
- head = vring_add_indirect(vq, sgs, total_sg,
- out_sgs, in_sgs, gfp);
- if (likely(head >= 0))
- goto add_head;
+ if (vq->indirect && total_sg > 1 && vq->vq.num_free)
+ desc = alloc_indirect(total_sg, gfp);
+ else
+ desc = NULL;
+
+ if (desc) {
+ /* Use a single buffer which doesn't continue */
+ vq->vring.desc[head].flags = VRING_DESC_F_INDIRECT;
+ vq->vring.desc[head].addr = virt_to_phys(desc);
+ /* avoid kmemleak false positive (hidden by virt_to_phys) */
+ kmemleak_ignore(desc);
+ vq->vring.desc[head].len = total_sg * sizeof(struct vring_desc);
+
+ /* Set up rest to use this indirect table. */
+ i = 0;
+ descs_used = 1;
+ indirect = true;
+ } else {
+ desc = vq->vring.desc;
+ i = head;
+ descs_used = total_sg;
+ indirect = false;
}
- BUG_ON(total_sg > vq->vring.num);
- BUG_ON(total_sg == 0);
-
- if (vq->vq.num_free < total_sg) {
+ if (vq->vq.num_free < descs_used) {
pr_debug("Can't add buf len %i - avail = %i\n",
- total_sg, vq->vq.num_free);
+ descs_used, vq->vq.num_free);
/* FIXME: for historical reasons, we force a notify here if
* there are outgoing parts to the buffer. Presumably the
* host should service the ring ASAP. */
@@ -226,34 +201,35 @@ static inline int virtqueue_add(struct virtqueue *_vq,
}
/* We're about to use some buffers from the free list. */
- vq->vq.num_free -= total_sg;
+ vq->vq.num_free -= descs_used;
- head = i = vq->free_head;
for (n = 0; n < out_sgs; n++) {
for (sg = sgs[n]; sg; sg = sg_next(sg)) {
- vq->vring.desc[i].flags = VRING_DESC_F_NEXT;
- vq->vring.desc[i].addr = sg_phys(sg);
- vq->vring.desc[i].len = sg->length;
+ desc[i].flags = VRING_DESC_F_NEXT;
+ desc[i].addr = sg_phys(sg);
+ desc[i].len = sg->length;
prev = i;
- i = vq->vring.desc[i].next;
+ i = desc[i].next;
}
}
for (; n < (out_sgs + in_sgs); n++) {
for (sg = sgs[n]; sg; sg = sg_next(sg)) {
- vq->vring.desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
- vq->vring.desc[i].addr = sg_phys(sg);
- vq->vring.desc[i].len = sg->length;
+ desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
+ desc[i].addr = sg_phys(sg);
+ desc[i].len = sg->length;
prev = i;
- i = vq->vring.desc[i].next;
+ i = desc[i].next;
}
}
/* Last one doesn't continue. */
- vq->vring.desc[prev].flags &= ~VRING_DESC_F_NEXT;
+ desc[prev].flags &= ~VRING_DESC_F_NEXT;
/* Update free pointer */
- vq->free_head = i;
+ if (indirect)
+ vq->free_head = vq->vring.desc[head].next;
+ else
+ vq->free_head = i;
-add_head:
/* Set token. */
vq->data[head] = data;
--
1.9.1
^ permalink raw reply related
* [PATCH 2/3] virtio_ring: assume sgs are always well-formed.
From: Rusty Russell @ 2014-09-11 0:47 UTC (permalink / raw)
To: linux-kernel; +Cc: Rusty Russell, netdev, Michael S. Tsirkin
In-Reply-To: <1410396458-1165-1-git-send-email-rusty@rustcorp.com.au>
We used to have several callers which just used arrays. They're
gone, so we can use sg_next() everywhere, simplifying the code.
On my laptop, this slowed down vring_bench by 15%:
vring_bench before:
936153354-967745359(9.44739e+08+/-6.1e+06)ns
vring_bench after:
1061485790-1104800648(1.08254e+09+/-6.6e+06)ns
However, a more realistic test using pktgen on a AMD FX(tm)-8320 saw
a few percent improvement:
pktgen before:
767390-792966(785159+/-6.5e+03)pps 356-367(363.75+/-2.9)Mb/sec (356068960-367936224(3.64314e+08+/-3e+06)bps) errors: 0
pktgen after:
787781-796334(793165+/-2.4e+03)pps 365-369(367.5+/-1.2)Mb/sec (365530384-369498976(3.68028e+08+/-1.1e+06)bps) errors: 0
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
drivers/virtio/virtio_ring.c | 68 +++++++++++++-------------------------------
1 file changed, 19 insertions(+), 49 deletions(-)
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 4d08f45a9c29..374399c62080 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -99,28 +99,10 @@ struct vring_virtqueue
#define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
-static inline struct scatterlist *sg_next_chained(struct scatterlist *sg,
- unsigned int *count)
-{
- return sg_next(sg);
-}
-
-static inline struct scatterlist *sg_next_arr(struct scatterlist *sg,
- unsigned int *count)
-{
- if (--(*count) == 0)
- return NULL;
- return sg + 1;
-}
-
/* Set up an indirect table of descriptors and add it to the queue. */
static inline int vring_add_indirect(struct vring_virtqueue *vq,
struct scatterlist *sgs[],
- struct scatterlist *(*next)
- (struct scatterlist *, unsigned int *),
unsigned int total_sg,
- unsigned int total_out,
- unsigned int total_in,
unsigned int out_sgs,
unsigned int in_sgs,
gfp_t gfp)
@@ -144,7 +126,7 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
/* Transfer entries from the sg lists into the indirect page */
i = 0;
for (n = 0; n < out_sgs; n++) {
- for (sg = sgs[n]; sg; sg = next(sg, &total_out)) {
+ for (sg = sgs[n]; sg; sg = sg_next(sg)) {
desc[i].flags = VRING_DESC_F_NEXT;
desc[i].addr = sg_phys(sg);
desc[i].len = sg->length;
@@ -153,7 +135,7 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
}
}
for (; n < (out_sgs + in_sgs); n++) {
- for (sg = sgs[n]; sg; sg = next(sg, &total_in)) {
+ for (sg = sgs[n]; sg; sg = sg_next(sg)) {
desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
desc[i].addr = sg_phys(sg);
desc[i].len = sg->length;
@@ -186,10 +168,7 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,
static inline int virtqueue_add(struct virtqueue *_vq,
struct scatterlist *sgs[],
- struct scatterlist *(*next)
- (struct scatterlist *, unsigned int *),
- unsigned int total_out,
- unsigned int total_in,
+ unsigned int total_sg,
unsigned int out_sgs,
unsigned int in_sgs,
void *data,
@@ -197,7 +176,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
{
struct vring_virtqueue *vq = to_vvq(_vq);
struct scatterlist *sg;
- unsigned int i, n, avail, uninitialized_var(prev), total_sg;
+ unsigned int i, n, avail, uninitialized_var(prev);
int head;
START_USE(vq);
@@ -222,13 +201,10 @@ static inline int virtqueue_add(struct virtqueue *_vq,
}
#endif
- total_sg = total_in + total_out;
-
/* If the host supports indirect descriptor tables, and we have multiple
* buffers, then go indirect. FIXME: tune this threshold */
if (vq->indirect && total_sg > 1 && vq->vq.num_free) {
- head = vring_add_indirect(vq, sgs, next, total_sg, total_out,
- total_in,
+ head = vring_add_indirect(vq, sgs, total_sg,
out_sgs, in_sgs, gfp);
if (likely(head >= 0))
goto add_head;
@@ -254,7 +230,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
head = i = vq->free_head;
for (n = 0; n < out_sgs; n++) {
- for (sg = sgs[n]; sg; sg = next(sg, &total_out)) {
+ for (sg = sgs[n]; sg; sg = sg_next(sg)) {
vq->vring.desc[i].flags = VRING_DESC_F_NEXT;
vq->vring.desc[i].addr = sg_phys(sg);
vq->vring.desc[i].len = sg->length;
@@ -263,7 +239,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
}
}
for (; n < (out_sgs + in_sgs); n++) {
- for (sg = sgs[n]; sg; sg = next(sg, &total_in)) {
+ for (sg = sgs[n]; sg; sg = sg_next(sg)) {
vq->vring.desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
vq->vring.desc[i].addr = sg_phys(sg);
vq->vring.desc[i].len = sg->length;
@@ -324,29 +300,23 @@ int virtqueue_add_sgs(struct virtqueue *_vq,
void *data,
gfp_t gfp)
{
- unsigned int i, total_out, total_in;
+ unsigned int i, total_sg = 0;
/* Count them first. */
- for (i = total_out = total_in = 0; i < out_sgs; i++) {
- struct scatterlist *sg;
- for (sg = sgs[i]; sg; sg = sg_next(sg))
- total_out++;
- }
- for (; i < out_sgs + in_sgs; i++) {
+ for (i = 0; i < out_sgs + in_sgs; i++) {
struct scatterlist *sg;
for (sg = sgs[i]; sg; sg = sg_next(sg))
- total_in++;
+ total_sg++;
}
- return virtqueue_add(_vq, sgs, sg_next_chained,
- total_out, total_in, out_sgs, in_sgs, data, gfp);
+ return virtqueue_add(_vq, sgs, total_sg, out_sgs, in_sgs, data, gfp);
}
EXPORT_SYMBOL_GPL(virtqueue_add_sgs);
/**
* virtqueue_add_outbuf - expose output buffers to other end
* @vq: the struct virtqueue we're talking about.
- * @sgs: array of scatterlists (need not be terminated!)
- * @num: the number of scatterlists readable by other side
+ * @sg: scatterlist (must be well-formed and terminated!)
+ * @num: the number of entries in @sg readable by other side
* @data: the token identifying the buffer.
* @gfp: how to do memory allocations (if necessary).
*
@@ -356,19 +326,19 @@ EXPORT_SYMBOL_GPL(virtqueue_add_sgs);
* Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
*/
int virtqueue_add_outbuf(struct virtqueue *vq,
- struct scatterlist sg[], unsigned int num,
+ struct scatterlist *sg, unsigned int num,
void *data,
gfp_t gfp)
{
- return virtqueue_add(vq, &sg, sg_next_arr, num, 0, 1, 0, data, gfp);
+ return virtqueue_add(vq, &sg, num, 1, 0, data, gfp);
}
EXPORT_SYMBOL_GPL(virtqueue_add_outbuf);
/**
* virtqueue_add_inbuf - expose input buffers to other end
* @vq: the struct virtqueue we're talking about.
- * @sgs: array of scatterlists (need not be terminated!)
- * @num: the number of scatterlists writable by other side
+ * @sg: scatterlist (must be well-formed and terminated!)
+ * @num: the number of entries in @sg writable by other side
* @data: the token identifying the buffer.
* @gfp: how to do memory allocations (if necessary).
*
@@ -378,11 +348,11 @@ EXPORT_SYMBOL_GPL(virtqueue_add_outbuf);
* Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
*/
int virtqueue_add_inbuf(struct virtqueue *vq,
- struct scatterlist sg[], unsigned int num,
+ struct scatterlist *sg, unsigned int num,
void *data,
gfp_t gfp)
{
- return virtqueue_add(vq, &sg, sg_next_arr, 0, num, 0, 1, data, gfp);
+ return virtqueue_add(vq, &sg, num, 0, 1, data, gfp);
}
EXPORT_SYMBOL_GPL(virtqueue_add_inbuf);
--
1.9.1
^ permalink raw reply related
* [PATCH 1/3] virtio_net: pass well-formed sgs to virtqueue_add_*()
From: Rusty Russell @ 2014-09-11 0:47 UTC (permalink / raw)
To: linux-kernel; +Cc: Rusty Russell, netdev, Michael S. Tsirkin
In-Reply-To: <1410396458-1165-1-git-send-email-rusty@rustcorp.com.au>
This is the only driver which doesn't hand virtqueue_add_inbuf and
virtqueue_add_outbuf a well-formed, well-terminated sg. Fix it,
so we can make virtio_add_* simpler.
pktgen results:
modprobe pktgen
echo 'add_device eth0' > /proc/net/pktgen/kpktgend_0
echo nowait 1 > /proc/net/pktgen/eth0
echo count 1000000 > /proc/net/pktgen/eth0
echo clone_skb 100000 > /proc/net/pktgen/eth0
echo dst_mac 4e:14:25:a9:30:ac > /proc/net/pktgen/eth0
echo dst 192.168.1.2 > /proc/net/pktgen/eth0
for i in `seq 20`; do echo start > /proc/net/pktgen/pgctrl; tail -n1 /proc/net/pktgen/eth0; done
Before:
746547-793084(786421+/-9.6e+03)pps 346-367(364.4+/-4.4)Mb/sec (346397808-367990976(3.649e+08+/-4.5e+06)bps) errors: 0
After:
767390-792966(785159+/-6.5e+03)pps 356-367(363.75+/-2.9)Mb/sec (356068960-367936224(3.64314e+08+/-3e+06)bps) errors: 0
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
drivers/net/virtio_net.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 59caa06f34a6..31cac511b3c3 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -546,8 +546,8 @@ static int add_recvbuf_small(struct receive_queue *rq, gfp_t gfp)
skb_put(skb, GOOD_PACKET_LEN);
hdr = skb_vnet_hdr(skb);
+ sg_init_table(rq->sg, MAX_SKB_FRAGS + 2);
sg_set_buf(rq->sg, &hdr->hdr, sizeof hdr->hdr);
-
skb_to_sgvec(skb, rq->sg + 1, 0, skb->len);
err = virtqueue_add_inbuf(rq->vq, rq->sg, 2, skb, gfp);
@@ -563,6 +563,8 @@ static int add_recvbuf_big(struct receive_queue *rq, gfp_t gfp)
char *p;
int i, err, offset;
+ sg_init_table(rq->sg, MAX_SKB_FRAGS + 2);
+
/* page in rq->sg[MAX_SKB_FRAGS + 1] is list tail */
for (i = MAX_SKB_FRAGS + 1; i > 1; --i) {
first = get_a_page(rq, gfp);
@@ -899,6 +901,7 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
if (vi->mergeable_rx_bufs)
hdr->mhdr.num_buffers = 0;
+ sg_init_table(sq->sg, MAX_SKB_FRAGS + 2);
if (can_push) {
__skb_push(skb, hdr_len);
num_sg = skb_to_sgvec(skb, sq->sg, 0, skb->len);
--
1.9.1
^ permalink raw reply related
* Re: [net-next PATCH v4 01/16] net: qdisc: use rcu prefix and silence sparse warnings
From: Eric Dumazet @ 2014-09-11 0:23 UTC (permalink / raw)
To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140910154659.2036.95037.stgit@nitbit.x32>
On Wed, 2014-09-10 at 08:47 -0700, John Fastabend wrote:
> Add __rcu notation to qdisc handling by doing this we can make
> smatch output more legible. And anyways some of the cases should
> be using rcu_dereference() see qdisc_all_tx_empty(),
> qdisc_tx_chainging(), and so on.
>
> Also *wake_queue() API is commonly called from driver timer routines
> without rcu lock or rtnl lock. So I added rcu_read_lock() blocks
> around netif_wake_subqueue and netif_tx_wake_queue.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
...
>
> @@ -416,10 +423,13 @@ static inline bool qdisc_all_tx_empty(const struct net_device *dev)
> static inline bool qdisc_tx_changing(const struct net_device *dev)
> {
> unsigned int i;
> +
> for (i = 0; i < dev->num_tx_queues; i++) {
> struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
> - if (txq->qdisc != txq->qdisc_sleeping)
> + if (rcu_access_pointer(txq->qdisc) != txq->qdisc_sleeping) {
> + rcu_read_unlock();
You forgot to remove this rcu_read_unlock();
> return true;
> + }
> }
> return false;
> }
> @@ -428,10 +438,13 @@ static inline bool qdisc_tx_changing(const struct net_device *dev)
> static inline bool qdisc_tx_is_noop(const struct net_device *dev)
> {
> unsigned int i;
> +
> for (i = 0; i < dev->num_tx_queues; i++) {
> struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
> - if (txq->qdisc != &noop_qdisc)
> + if (rcu_dereference(txq->qdisc) != &noop_qdisc) {
rcu_access_pointer()
> + rcu_read_unlock();
You forgot to remove this rcu_read_unlock();
> return false;
> + }
> }
> return true;
> }
^ permalink raw reply
* Re: [PATCH net-next v2 2/2] bonding: Simplify the xmit function for modes that use xmit_hash
From: Mahesh Bandewar @ 2014-09-11 0:12 UTC (permalink / raw)
To: Nikolay Aleksandrov
Cc: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David Miller,
netdev, Eric Dumazet, Maciej Zenczykowski
In-Reply-To: <54105025.9040802@redhat.com>
On Wed, Sep 10, 2014 at 6:20 AM, Nikolay Aleksandrov <nikolay@redhat.com> wrote:
> On 10/09/14 02:46, Mahesh Bandewar wrote:
>>
>> Earlier change to use usable slave array for TLB mode had an additional
>> performance advantage. So extending the same logic to all other modes
>> that use xmit-hash for slave selection (viz 802.3AD, and XOR modes).
>> Also consolidating this with the earlier TLB change.
>>
>> The main idea is to build the usable slaves array in the control path
>> and use that array for slave selection during xmit operation.
>>
>> Measured performance in a setup with a bond of 4x1G NICs with 200
>> instances of netperf for the modes involved (3ad, xor, tlb)
>> cmd: netperf -t TCP_RR -H <TargetHost> -l 60 -s 5
>>
>> Mode TPS-Before TPS-After
>>
>> 802.3ad : 468,694 493,101
>> TLB (lb=0): 392,583 392,965
>> XOR : 475,696 484,517
>>
>> Signed-off-by: Mahesh Bandewar <maheshb@google.com>
>> ---
>> v1:
>> (a) If bond_update_slave_arr() fails to allocate memory, it will
>> overwrite
>> the slave that need to be removed.
>> (b) Freeing of array will assign NULL (to handle bond->down to bond->up
>> transition gracefully.
>> (c) Change from pr_debug() to pr_err() if bond_update_slave_arr()
>> returns
>> failure.
>> (d) XOR: bond_update_slave_arr() will consider mii-mon, arp-mon cases
>> and
>> will populate the array even if these parameters are not used.
>> (e) 3AD: Should handle the ad_agg_selection_logic correctly.
>> v2:
>> (a) Removed rcu_read_{un}lock() calls from array manipulation code.
>> (b) Slave link-events now refresh array for all these modes.
>> (c) Moved free-array call from bond_close() to bond_uninit().
>>
>> drivers/net/bonding/bond_3ad.c | 76 +++----------------
>> drivers/net/bonding/bond_alb.c | 51 ++-----------
>> drivers/net/bonding/bond_alb.h | 8 --
>> drivers/net/bonding/bond_main.c | 158
>> +++++++++++++++++++++++++++++++++++++---
>> drivers/net/bonding/bonding.h | 8 ++
>> 5 files changed, 170 insertions(+), 131 deletions(-)
>>
> Hello Mahesh,
> First a question, if a bond device in XOR mode is up and we enslave a single
> slave how would it start transmitting ? Same question, if we are enslaving a
> second device then the array will be rebuild with only the first upon
> NETDEV_UP (of course all this is in the case miimon is 0).
> The NETDEV_UP upon enslave happens before the slave is linked in.
We definitely need to handle the NETDEV_UP event. If we do not then
the slave array stays stale when device is already enslaved and link
flaps. Now in case of enslaving the first and the second slave (the
case that you have mentioned), I do not see that behavior! If link
event happens before enslaving then the slave-array will not get
updated since the notifier call-chain (a) may not call bonding
notifier (b) even if it calls; the update_slave_arr() will miss it
since it will not be present in slaves list. I do not see that
happening.
OTOH bond_set_carrier() call is almost at the end of enslave() (which
is after slave is linked in) that might be triggering the NETDEV_UP
event which results in the array update.
> One side note, you can now simplify these patches and drop the checks for
> bond->lock since Dave applied my patches.
>
Yes, done.
> A few more important problems below.
>
>
>> diff --git a/drivers/net/bonding/bond_3ad.c
>> b/drivers/net/bonding/bond_3ad.c
>> index ee2c73a9de39..1549e718074a 100644
>> --- a/drivers/net/bonding/bond_3ad.c
>> +++ b/drivers/net/bonding/bond_3ad.c
>> @@ -1579,6 +1579,8 @@ static void ad_agg_selection_logic(struct aggregator
>> *agg)
>> __disable_port(port);
>> }
>> }
>> + if (bond_update_slave_arr(bond, NULL))
>> + pr_err("Failed to build slave-array for 3ad
>> mode.\n");
>> }
>>
>> /* if the selected aggregator is of join individuals
>> @@ -1717,6 +1719,8 @@ static void ad_enable_collecting_distributing(struct
>> port *port)
>> port->actor_port_number,
>> port->aggregator->aggregator_identifier);
>> __enable_port(port);
>> + if (bond_update_slave_arr(port->slave->bond, NULL))
>> + pr_err("Failed to build slave-array for 3ad
>> mode.\n");
>> }
>> }
>>
>> @@ -1733,6 +1737,8 @@ static void
>> ad_disable_collecting_distributing(struct port *port)
>> port->actor_port_number,
>> port->aggregator->aggregator_identifier);
>> __disable_port(port);
>> + if (bond_update_slave_arr(port->slave->bond, NULL))
>> + pr_err("Failed to build slave-array for 3ad
>> mode.\n");
>> }
>> }
>>
>> @@ -2311,6 +2317,9 @@ void bond_3ad_handle_link_change(struct slave
>> *slave, char link)
>> */
>> port->sm_vars |= AD_PORT_BEGIN;
>>
>> + if (bond_update_slave_arr(slave->bond, NULL))
>> + pr_err("Failed to build slave-array for 3ad mode.\n");
>> +
>> __release_state_machine_lock(port);
>> }
>>
>> @@ -2407,73 +2416,6 @@ int bond_3ad_get_active_agg_info(struct bonding
>> *bond, struct ad_info *ad_info)
>> return ret;
>> }
>>
>> -int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
>> -{
>> - struct bonding *bond = netdev_priv(dev);
>> - struct slave *slave, *first_ok_slave;
>> - struct aggregator *agg;
>> - struct ad_info ad_info;
>> - struct list_head *iter;
>> - int slaves_in_agg;
>> - int slave_agg_no;
>> - int agg_id;
>> -
>> - if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
>> - netdev_dbg(dev, "__bond_3ad_get_active_agg_info
>> failed\n");
>> - goto err_free;
>> - }
>> -
>> - slaves_in_agg = ad_info.ports;
>> - agg_id = ad_info.aggregator_id;
>> -
>> - if (slaves_in_agg == 0) {
>> - netdev_dbg(dev, "active aggregator is empty\n");
>> - goto err_free;
>> - }
>> -
>> - slave_agg_no = bond_xmit_hash(bond, skb) % slaves_in_agg;
>> - first_ok_slave = NULL;
>> -
>> - bond_for_each_slave_rcu(bond, slave, iter) {
>> - agg = SLAVE_AD_INFO(slave)->port.aggregator;
>> - if (!agg || agg->aggregator_identifier != agg_id)
>> - continue;
>> -
>> - if (slave_agg_no >= 0) {
>> - if (!first_ok_slave && bond_slave_can_tx(slave))
>> - first_ok_slave = slave;
>> - slave_agg_no--;
>> - continue;
>> - }
>> -
>> - if (bond_slave_can_tx(slave)) {
>> - bond_dev_queue_xmit(bond, skb, slave->dev);
>> - goto out;
>> - }
>> - }
>> -
>> - if (slave_agg_no >= 0) {
>> - netdev_err(dev, "Couldn't find a slave to tx on for
>> aggregator ID %d\n",
>> - agg_id);
>> - goto err_free;
>> - }
>> -
>> - /* we couldn't find any suitable slave after the agg_no, so use
>> the
>> - * first suitable found, if found.
>> - */
>> - if (first_ok_slave)
>> - bond_dev_queue_xmit(bond, skb, first_ok_slave->dev);
>> - else
>> - goto err_free;
>> -
>> -out:
>> - return NETDEV_TX_OK;
>> -err_free:
>> - /* no suitable interface, frame not sent */
>> - dev_kfree_skb_any(skb);
>> - goto out;
>> -}
>> -
>> int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding
>> *bond,
>> struct slave *slave)
>> {
>> diff --git a/drivers/net/bonding/bond_alb.c
>> b/drivers/net/bonding/bond_alb.c
>> index 73c21e233131..c44cabd8f2ba 100644
>> --- a/drivers/net/bonding/bond_alb.c
>> +++ b/drivers/net/bonding/bond_alb.c
>> @@ -200,7 +200,6 @@ static int tlb_initialize(struct bonding *bond)
>> static void tlb_deinitialize(struct bonding *bond)
>> {
>> struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
>> - struct tlb_up_slave *arr;
>>
>> _lock_tx_hashtbl_bh(bond);
>>
>> @@ -208,10 +207,6 @@ static void tlb_deinitialize(struct bonding *bond)
>> bond_info->tx_hashtbl = NULL;
>>
>> _unlock_tx_hashtbl_bh(bond);
>> -
>> - arr = rtnl_dereference(bond_info->slave_arr);
>> - if (arr)
>> - kfree_rcu(arr, rcu);
>> }
>>
>> static long long compute_gap(struct slave *slave)
>> @@ -1409,39 +1404,9 @@ out:
>> return NETDEV_TX_OK;
>> }
>>
>> -static int bond_tlb_update_slave_arr(struct bonding *bond,
>> - struct slave *skipslave)
>> -{
>> - struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
>> - struct slave *tx_slave;
>> - struct list_head *iter;
>> - struct tlb_up_slave *new_arr, *old_arr;
>> -
>> - new_arr = kzalloc(offsetof(struct tlb_up_slave,
>> arr[bond->slave_cnt]),
>> - GFP_ATOMIC);
>> - if (!new_arr)
>> - return -ENOMEM;
>> -
>> - bond_for_each_slave(bond, tx_slave, iter) {
>> - if (!bond_slave_can_tx(tx_slave))
>> - continue;
>> - if (skipslave == tx_slave)
>> - continue;
>> - new_arr->arr[new_arr->count++] = tx_slave;
>> - }
>> -
>> - old_arr = rtnl_dereference(bond_info->slave_arr);
>> - rcu_assign_pointer(bond_info->slave_arr, new_arr);
>> - if (old_arr)
>> - kfree_rcu(old_arr, rcu);
>> -
>> - return 0;
>> -}
>> -
>> int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
>> {
>> struct bonding *bond = netdev_priv(bond_dev);
>> - struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
>> struct ethhdr *eth_data;
>> struct slave *tx_slave = NULL;
>> u32 hash_index;
>> @@ -1462,12 +1427,14 @@ int bond_tlb_xmit(struct sk_buff *skb, struct
>> net_device *bond_dev)
>> hash_index &
>> 0xFF,
>> skb->len);
>> } else {
>> - struct tlb_up_slave *slaves;
>> + struct bond_up_slave *slaves;
>> + unsigned int count;
>>
>> - slaves =
>> rcu_dereference(bond_info->slave_arr);
>> - if (slaves && slaves->count)
>> + slaves = rcu_dereference(bond->slave_arr);
>> + count = slaves->count;
>
> ^^^^^^^^^^^^^^^
> What happens if slaves is still NULL ? We'll dereference a NULL pointer
> here.
> I'm beginning to think you don't test these submissions at all, I hit the
> obvious NULL pointer dereference immediately after trying to enslave a
> device in XOR mode with these patches applied.
>
I do test the code before sending it :)
I was using the following method to see the robustness of the change -
while true; do
echo -eth$(($RANDOM % 4 + 1)) >
/sys/class/net/eth0/bonding/slaves
sleep 1
echo +eth$(($RANDOM % 4 + 1)) >
/sys/class/net/eth0/bonding/slaves
sleep 1
done
while having some background traffic through the bond interface (BTW I
have a setup of 4 nics). It caught several issues but one things that
I missed was to start with no slaves and then add them one by one. My
setup always came up with 4 slaves and above script would change the
slave count randomly, but some how it never took it to zero. It looks
silly but this error could have been found if looked carefully, OTOH
this is functionally a big change and making changes to the collection
of hacks and coming out right first time is little difficult. So
please bare with me. On the positive note - because of you I learnt so
much about the XOR mode now! I definitely appreciate your inputs.
>
>> + if (slaves && count)
>> tx_slave = slaves->arr[hash_index
>> %
>> -
>> slaves->count];
>> + count];
>> }
>> break;
>> }
>> @@ -1733,10 +1700,6 @@ void bond_alb_deinit_slave(struct bonding *bond,
>> struct slave *slave)
>> rlb_clear_slave(bond, slave);
>> }
>>
>> - if (bond_is_nondyn_tlb(bond))
>> - if (bond_tlb_update_slave_arr(bond, slave))
>> - pr_err("Failed to build slave-array for TLB
>> mode.\n");
>> -
>> }
>>
>> /* Caller must hold bond lock for read */
>> @@ -1762,7 +1725,7 @@ void bond_alb_handle_link_change(struct bonding
>> *bond, struct slave *slave, char
>> }
>>
>> if (bond_is_nondyn_tlb(bond)) {
>> - if (bond_tlb_update_slave_arr(bond, NULL))
>> + if (bond_update_slave_arr(bond, NULL))
>> pr_err("Failed to build slave-array for TLB
>> mode.\n");
>> }
>> }
>> diff --git a/drivers/net/bonding/bond_alb.h
>> b/drivers/net/bonding/bond_alb.h
>> index aaeac61d03cf..5fc76c01636c 100644
>> --- a/drivers/net/bonding/bond_alb.h
>> +++ b/drivers/net/bonding/bond_alb.h
>> @@ -139,20 +139,12 @@ struct tlb_slave_info {
>> */
>> };
>>
>> -struct tlb_up_slave {
>> - unsigned int count;
>> - struct rcu_head rcu;
>> - struct slave *arr[0];
>> -};
>> -
>> struct alb_bond_info {
>> struct tlb_client_info *tx_hashtbl; /* Dynamically allocated */
>> spinlock_t tx_hashtbl_lock;
>> u32 unbalanced_load;
>> int tx_rebalance_counter;
>> int lp_counter;
>> - /* -------- non-dynamic tlb mode only ---------*/
>> - struct tlb_up_slave __rcu *slave_arr; /* Up slaves */
>> /* -------- rlb parameters -------- */
>> int rlb_enabled;
>> struct rlb_client_info *rx_hashtbl; /* Receive hash table */
>> diff --git a/drivers/net/bonding/bond_main.c
>> b/drivers/net/bonding/bond_main.c
>> index f0f5eab0fab1..456cda67383b 100644
>> --- a/drivers/net/bonding/bond_main.c
>> +++ b/drivers/net/bonding/bond_main.c
>> @@ -1693,6 +1693,10 @@ static int __bond_release_one(struct net_device
>> *bond_dev,
>> if (BOND_MODE(bond) == BOND_MODE_8023AD)
>> bond_3ad_unbind_slave(slave);
>>
>> + if (bond_mode_uses_xmit_hash(bond) &&
>> + bond_update_slave_arr(bond, slave))
>> + pr_err("Failed to build slave-array.\n");
>> +
>> write_unlock_bh(&bond->lock);
>>
>> netdev_info(bond_dev, "Releasing %s interface %s\n",
>> @@ -2009,6 +2013,10 @@ static void bond_miimon_commit(struct bonding
>> *bond)
>> bond_alb_handle_link_change(bond, slave,
>> BOND_LINK_UP);
>>
>> + if (BOND_MODE(bond) == BOND_MODE_XOR &&
>> + bond_update_slave_arr(bond, NULL))
>> + pr_err("Failed to build slave-array for
>> XOR mode.\n");
>> +
>> if (!bond->curr_active_slave ||
>> (slave == bond->primary_slave))
>> goto do_failover;
>> @@ -2037,6 +2045,10 @@ static void bond_miimon_commit(struct bonding
>> *bond)
>> bond_alb_handle_link_change(bond, slave,
>>
>> BOND_LINK_DOWN);
>>
>> + if (BOND_MODE(bond) == BOND_MODE_XOR &&
>> + bond_update_slave_arr(bond, NULL))
>> + pr_err("Failed to build slave-array for
>> XOR mode.\n");
>> +
>> if (slave ==
>> rcu_access_pointer(bond->curr_active_slave))
>> goto do_failover;
>>
>> @@ -2500,6 +2512,9 @@ static void bond_loadbalance_arp_mon(struct
>> work_struct *work)
>>
>> if (slave_state_changed) {
>> bond_slave_state_change(bond);
>> + if (BOND_MODE(bond) == BOND_MODE_XOR &&
>> + bond_update_slave_arr(bond, NULL))
>> + pr_err("Failed to build slave-array for
>> XOR mode.\n");
>> } else if (do_failover) {
>> /* the bond_select_active_slave must hold RTNL
>> * and curr_slave_lock for write.
>> @@ -2893,11 +2908,23 @@ static int bond_slave_netdev_event(unsigned long
>> event,
>> if (old_duplex != slave->duplex)
>> bond_3ad_adapter_duplex_changed(slave);
>> }
>> + /* Refresh slave-array if applicable!
>> + * If the setuo does not use miimon or arpmon
>> (mode-specific!),
>> + * then these event will not cause the slave-array to be
>> + * refreshed. This will cause xmit to use a slave that is
>> not
>> + * usable. Avoid such situation by refeshing the array at
>> these
>> + * events. If these (miimon/arpmon) parameters are
>> configured
>> + * then array gets refreshed twice and that should be
>> fine!
>> + */
>> + if (bond_mode_uses_xmit_hash(bond) &&
>> + bond_update_slave_arr(bond, NULL))
>> + pr_err("Failed to build slave-array for XOR
>> mode.\n");
>> break;
>> case NETDEV_DOWN:
>> - /*
>> - * ... Or is it this?
>> - */
>> + /* Refresh slave-array if applicable! */
>> + if (bond_mode_uses_xmit_hash(bond) &&
>> + bond_update_slave_arr(bond, NULL))
>> + pr_err("Failed to build slave-array for XOR
>> mode.\n");
>> break;
>> case NETDEV_CHANGEMTU:
>> /*
>> @@ -3143,6 +3170,10 @@ static int bond_open(struct net_device *bond_dev)
>> bond_3ad_initiate_agg_selection(bond, 1);
>> }
>>
>> + if (bond_mode_uses_xmit_hash(bond) &&
>> + bond_update_slave_arr(bond, NULL))
>> + pr_err("Failed to build slave-array for XOR mode.\n");
>> +
>> return 0;
>> }
>>
>> @@ -3684,15 +3715,112 @@ static int bond_xmit_activebackup(struct sk_buff
>> *skb, struct net_device *bond_d
>> return NETDEV_TX_OK;
>> }
>>
>> -/* In bond_xmit_xor() , we determine the output device by using a pre-
>> - * determined xmit_hash_policy(), If the selected device is not enabled,
>> - * find the next active slave.
>> +/* Build the usable slaves array in control path for modes that use
>> xmit-hash
>> + * to determine the slave interface -
>> + * (a) BOND_MODE_8023AD
>> + * (b) BOND_MODE_XOR
>> + * (c) BOND_MODE_TLB && tlb_dynamic_lb == 0
>> */
>> -static int bond_xmit_xor(struct sk_buff *skb, struct net_device
>> *bond_dev)
>> +int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave)
>> {
>> - struct bonding *bond = netdev_priv(bond_dev);
>> + struct slave *slave;
>> + struct list_head *iter;
>> + struct bond_up_slave *new_arr, *old_arr;
>> + int slaves_in_agg;
>> + int agg_id = 0;
>> + int ret = 0;
>> +
>> + if (!bond_has_slaves(bond))
>> + goto out;
>> +
>> + new_arr = kzalloc(offsetof(struct bond_up_slave,
>> arr[bond->slave_cnt]),
>> + GFP_ATOMIC);
>> + if (!new_arr) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> + if (BOND_MODE(bond) == BOND_MODE_8023AD) {
>> + struct ad_info ad_info;
>> +
>> + if (bond_3ad_get_active_agg_info(bond, &ad_info)) {
>> + pr_debug("bond_3ad_get_active_agg_info failed\n");
>> + kfree_rcu(new_arr, rcu);
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> + slaves_in_agg = ad_info.ports;
>> + agg_id = ad_info.aggregator_id;
>> + }
>> + bond_for_each_slave(bond, slave, iter) {
>> + if (BOND_MODE(bond) == BOND_MODE_8023AD) {
>> + struct aggregator *agg;
>> +
>> + agg = SLAVE_AD_INFO(slave)->port.aggregator;
>> + if (!agg || agg->aggregator_identifier != agg_id)
>> + continue;
>> + }
>> + if (!bond_slave_can_tx(slave))
>> + continue;
>> + if (skipslave == slave)
>> + continue;
>> + new_arr->arr[new_arr->count++] = slave;
>> + }
>>
>> - bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb) %
>> bond->slave_cnt);
>> + old_arr = rcu_dereference_protected(bond->slave_arr,
>> + lockdep_rtnl_is_held() ||
>> + lockdep_is_held(&bond->lock)
>> ||
>> +
>> lockdep_is_held(&bond->curr_slave_lock));
>> + rcu_assign_pointer(bond->slave_arr, new_arr);
>> + if (old_arr)
>> + kfree_rcu(old_arr, rcu);
>> +
>> +out:
>> + if (ret != 0 && skipslave) {
>> + int idx;
>> +
>> + /* Rare situation where caller has asked to skip a
>> specific
>> + * slave but allocation failed (most likely!). BTW this is
>> + * only possible when the call is initiated from
>> + * __bond_release_one(). In this sitation; overwrite the
>> + * skipslave entry in the array with the last entry from
>> the
>> + * array to avoid a situation where the xmit path may
>> choose
>> + * this to-be-skipped slave to send a packet out.
>> + */
>> + old_arr = rcu_dereference_protected(bond->slave_arr,
>> + lockdep_is_held(&bond->lock)
>> &&
>> + lockdep_rtnl_is_held());
>> + for (idx = 0; idx < old_arr->count; idx++) {
>> + if (skipslave == old_arr->arr[idx]) {
>> + old_arr->arr[idx] =
>> + old_arr->arr[old_arr->count-1];
>> + old_arr->count--;
>> + break;
>> + }
>> + }
>> + }
>> + return ret;
>> +}
>> +
>> +/* Use this Xmit function for 3AD as well as XOR modes. The current
>> + * usable slave array is formed in the control path. The xmit function
>> + * just calculates hash and sends the packet out.
>> + */
>> +int bond_3ad_xor_xmit(struct sk_buff *skb, struct net_device *dev)
>> +{
>> + struct bonding *bond = netdev_priv(dev);
>> + struct slave *slave;
>> + struct bond_up_slave *slaves;
>> + unsigned int count;
>> +
>> + slaves = rcu_dereference(bond->slave_arr);
>> + count = slaves->count;
>
> ^^^^^^^^^^^
> Same here, if slaves is NULL we'll dereference a NULL ptr.
>
>
>> + if (slaves && count) {
>> + slave = slaves->arr[bond_xmit_hash(bond, skb) % count];
>> + bond_dev_queue_xmit(bond, skb, slave->dev);
>> + } else {
>> + dev_kfree_skb_any(skb);
>> + atomic_long_inc(&dev->tx_dropped);
>> + }
>>
>> return NETDEV_TX_OK;
>> }
>> @@ -3794,12 +3922,11 @@ static netdev_tx_t __bond_start_xmit(struct
>> sk_buff *skb, struct net_device *dev
>> return bond_xmit_roundrobin(skb, dev);
>> case BOND_MODE_ACTIVEBACKUP:
>> return bond_xmit_activebackup(skb, dev);
>> + case BOND_MODE_8023AD:
>> case BOND_MODE_XOR:
>> - return bond_xmit_xor(skb, dev);
>> + return bond_3ad_xor_xmit(skb, dev);
>> case BOND_MODE_BROADCAST:
>> return bond_xmit_broadcast(skb, dev);
>> - case BOND_MODE_8023AD:
>> - return bond_3ad_xmit_xor(skb, dev);
>> case BOND_MODE_ALB:
>> return bond_alb_xmit(skb, dev);
>> case BOND_MODE_TLB:
>> @@ -3980,6 +4107,7 @@ static void bond_uninit(struct net_device *bond_dev)
>> struct bonding *bond = netdev_priv(bond_dev);
>> struct list_head *iter;
>> struct slave *slave;
>> + struct bond_up_slave *arr;
>>
>> bond_netpoll_cleanup(bond_dev);
>>
>> @@ -3988,6 +4116,12 @@ static void bond_uninit(struct net_device
>> *bond_dev)
>> __bond_release_one(bond_dev, slave->dev, true);
>> netdev_info(bond_dev, "Released all slaves\n");
>>
>> + arr = rtnl_dereference(bond->slave_arr);
>> + if (arr) {
>> + kfree_rcu(arr, rcu);
>> + RCU_INIT_POINTER(bond->slave_arr, NULL);
>> + }
>> +
>> list_del(&bond->bond_list);
>>
>> bond_debug_unregister(bond);
>> diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
>> index 64b36ba65b89..5642cccd4469 100644
>> --- a/drivers/net/bonding/bonding.h
>> +++ b/drivers/net/bonding/bonding.h
>> @@ -177,6 +177,12 @@ struct slave {
>> struct kobject kobj;
>> };
>>
>> +struct bond_up_slave {
>> + unsigned int count;
>> + struct rcu_head rcu;
>> + struct slave *arr[0];
>> +};
>> +
>> /*
>> * Link pseudo-state only used internally by monitors
>> */
>> @@ -196,6 +202,7 @@ struct bonding {
>> struct slave __rcu *curr_active_slave;
>> struct slave __rcu *current_arp_slave;
>> struct slave *primary_slave;
>> + struct bond_up_slave __rcu *slave_arr; /* Array of usable slaves
>> */
>> bool force_primary;
>> s32 slave_cnt; /* never change this value outside the
>> attach/detach wrappers */
>> int (*recv_probe)(const struct sk_buff *, struct bonding *,
>> @@ -534,6 +541,7 @@ const char *bond_slave_link_status(s8 link);
>> struct bond_vlan_tag *bond_verify_device_path(struct net_device
>> *start_dev,
>> struct net_device *end_dev,
>> int level);
>> +int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave);
>>
>> #ifdef CONFIG_PROC_FS
>> void bond_create_proc_entry(struct bonding *bond);
>>
>
^ 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