Hi,
This patchset by Jianbo provides bug fixes for IPsec over bonding
driver.
It adds the missing xdo_dev_state_free API, and fixes "scheduling while
atomic" by using mutex lock instead.
Series generated against:
commit 14ab4792ee12 ("net/tcp: Disable TCP-AO static key after RCU grace period")
Regards,
Tariq
V3:
- Add RCU read lock/unlock for bond_ipsec_add_sa, bond_ipsec_del_sa and bond_ipsec_free_sa.
V2:
- Rebased on top of latest net branch.
- Squashed patch #2 into #1 per Hangbin comment.
- Addressed Hangbin's comments.
- Patch #3 (was #4): Addressed comments by Paolo.
Jianbo Liu (3):
bonding: implement xdo_dev_state_free and call it after deletion
bonding: extract the use of real_device into local variable
bonding: change ipsec_lock from spin lock to mutex
drivers/net/bonding/bond_main.c | 151 ++++++++++++++++++++------------
include/net/bonding.h | 2 +-
2 files changed, 98 insertions(+), 55 deletions(-)
--
2.44.0
From: Jianbo Liu <redacted>
Add this implementation for bonding, so hardware resources can be
freed after xfrm state is deleted.
And call it when deleting all SAs from old active real interface.
Fixes: 9a5605505d9c ("bonding: Add struct bond_ipesc to manage SA")
Signed-off-by: Jianbo Liu <redacted>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/bonding/bond_main.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
From: Jianbo Liu <redacted>
Add a local variable for slave->dev, to prepare for the lock change in
the next patch. There is no functionality change.
Fixes: 9a5605505d9c ("bonding: Add struct bond_ipesc to manage SA")
Signed-off-by: Jianbo Liu <redacted>
Reviewed-by: Cosmin Ratiu <redacted>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/bonding/bond_main.c | 58 +++++++++++++++++++--------------
1 file changed, 33 insertions(+), 25 deletions(-)
@@ -427,6 +427,7 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,structnetlink_ext_ack*extack){structnet_device*bond_dev=xs->xso.dev;+structnet_device*real_dev;structbond_ipsec*ipsec;structbonding*bond;structslave*slave;
@@ -443,9 +444,10 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,return-ENODEV;}-if(!slave->dev->xfrmdev_ops||-!slave->dev->xfrmdev_ops->xdo_dev_state_add||-netif_is_bond_master(slave->dev)){+real_dev=slave->dev;+if(!real_dev->xfrmdev_ops||+!real_dev->xfrmdev_ops->xdo_dev_state_add||+netif_is_bond_master(real_dev)){NL_SET_ERR_MSG_MOD(extack,"Slave does not support ipsec offload");rcu_read_unlock();return-EINVAL;
@@ -456,9 +458,9 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,rcu_read_unlock();return-ENOMEM;}-xs->xso.real_dev=slave->dev;-err=slave->dev->xfrmdev_ops->xdo_dev_state_add(xs,extack);+xs->xso.real_dev=real_dev;+err=real_dev->xfrmdev_ops->xdo_dev_state_add(xs,extack);if(!err){ipsec->xs=xs;INIT_LIST_HEAD(&ipsec->list);
@@ -475,6 +477,7 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,staticvoidbond_ipsec_add_sa_all(structbonding*bond){structnet_device*bond_dev=bond->dev;+structnet_device*real_dev;structbond_ipsec*ipsec;structslave*slave;
@@ -439,38 +439,33 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,rcu_read_lock();bond=netdev_priv(bond_dev);slave=rcu_dereference(bond->curr_active_slave);-if(!slave){-rcu_read_unlock();+real_dev=slave?slave->dev:NULL;+rcu_read_unlock();+if(!real_dev)return-ENODEV;-}-real_dev=slave->dev;if(!real_dev->xfrmdev_ops||!real_dev->xfrmdev_ops->xdo_dev_state_add||netif_is_bond_master(real_dev)){NL_SET_ERR_MSG_MOD(extack,"Slave does not support ipsec offload");-rcu_read_unlock();return-EINVAL;}-ipsec=kmalloc(sizeof(*ipsec),GFP_ATOMIC);-if(!ipsec){-rcu_read_unlock();+ipsec=kmalloc(sizeof(*ipsec),GFP_KERNEL);+if(!ipsec)return-ENOMEM;-}xs->xso.real_dev=real_dev;err=real_dev->xfrmdev_ops->xdo_dev_state_add(xs,extack);if(!err){ipsec->xs=xs;INIT_LIST_HEAD(&ipsec->list);-spin_lock_bh(&bond->ipsec_lock);+mutex_lock(&bond->ipsec_lock);list_add(&ipsec->list,&bond->ipsec_list);-spin_unlock_bh(&bond->ipsec_lock);+mutex_unlock(&bond->ipsec_lock);}else{kfree(ipsec);}-rcu_read_unlock();returnerr;}
@@ -481,35 +476,43 @@ static void bond_ipsec_add_sa_all(struct bonding *bond)structbond_ipsec*ipsec;structslave*slave;-rcu_read_lock();-slave=rcu_dereference(bond->curr_active_slave);-if(!slave)-gotoout;+slave=rtnl_dereference(bond->curr_active_slave);+real_dev=slave?slave->dev:NULL;+if(!real_dev)+return;-real_dev=slave->dev;+mutex_lock(&bond->ipsec_lock);if(!real_dev->xfrmdev_ops||!real_dev->xfrmdev_ops->xdo_dev_state_add||netif_is_bond_master(real_dev)){-spin_lock_bh(&bond->ipsec_lock);if(!list_empty(&bond->ipsec_list))slave_warn(bond_dev,real_dev,"%s: no slave xdo_dev_state_add\n",__func__);-spin_unlock_bh(&bond->ipsec_lock);gotoout;}-spin_lock_bh(&bond->ipsec_lock);list_for_each_entry(ipsec,&bond->ipsec_list,list){+structnet_device*dev=ipsec->xs->xso.real_dev;++/* If new state is added before ipsec_lock acquired */+if(dev){+if(dev==real_dev)+continue;++dev->xfrmdev_ops->xdo_dev_state_delete(ipsec->xs);+if(dev->xfrmdev_ops->xdo_dev_state_free)+dev->xfrmdev_ops->xdo_dev_state_free(ipsec->xs);+}+ipsec->xs->xso.real_dev=real_dev;if(real_dev->xfrmdev_ops->xdo_dev_state_add(ipsec->xs,NULL)){slave_warn(bond_dev,real_dev,"%s: failed to add SA\n",__func__);ipsec->xs->xso.real_dev=NULL;}}-spin_unlock_bh(&bond->ipsec_lock);out:-rcu_read_unlock();+mutex_unlock(&bond->ipsec_lock);}/**
From: Hangbin Liu <hidden> Date: 2024-08-06 08:45:43
On Mon, Aug 05, 2024 at 08:03:55AM +0300, Tariq Toukan wrote:
quoted hunk
From: Jianbo Liu <redacted>
Add this implementation for bonding, so hardware resources can be
freed after xfrm state is deleted.
And call it when deleting all SAs from old active real interface.
Fixes: 9a5605505d9c ("bonding: Add struct bond_ipesc to manage SA")
Signed-off-by: Jianbo Liu <redacted>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/bonding/bond_main.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
From: Jianbo Liu <hidden> Date: 2024-08-06 09:09:51
On Tue, 2024-08-06 at 16:45 +0800, Hangbin Liu wrote:
On Mon, Aug 05, 2024 at 08:03:55AM +0300, Tariq Toukan wrote:
quoted
From: Jianbo Liu <redacted>
Add this implementation for bonding, so hardware resources can be
freed after xfrm state is deleted.
And call it when deleting all SAs from old active real interface.
Fixes: 9a5605505d9c ("bonding: Add struct bond_ipesc to manage SA")
Signed-off-by: Jianbo Liu <redacted>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/bonding/bond_main.c | 32
++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/net/bonding/bond_main.c
b/drivers/net/bonding/bond_main.c
index 1cd92c12e782..eb5e43860670 100644
From: Hangbin Liu <hidden> Date: 2024-08-07 10:11:29
On Mon, Aug 05, 2024 at 08:03:55AM +0300, Tariq Toukan wrote:
quoted hunk
From: Jianbo Liu <redacted>
Add this implementation for bonding, so hardware resources can be
freed after xfrm state is deleted.
And call it when deleting all SAs from old active real interface.
Fixes: 9a5605505d9c ("bonding: Add struct bond_ipesc to manage SA")
Signed-off-by: Jianbo Liu <redacted>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/bonding/bond_main.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
From: Hangbin Liu <hidden> Date: 2024-08-07 10:13:09
On Mon, Aug 05, 2024 at 08:03:56AM +0300, Tariq Toukan wrote:
quoted hunk
From: Jianbo Liu <redacted>
Add a local variable for slave->dev, to prepare for the lock change in
the next patch. There is no functionality change.
Fixes: 9a5605505d9c ("bonding: Add struct bond_ipesc to manage SA")
Signed-off-by: Jianbo Liu <redacted>
Reviewed-by: Cosmin Ratiu <redacted>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/bonding/bond_main.c | 58 +++++++++++++++++++--------------
1 file changed, 33 insertions(+), 25 deletions(-)
@@ -427,6 +427,7 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,structnetlink_ext_ack*extack){structnet_device*bond_dev=xs->xso.dev;+structnet_device*real_dev;structbond_ipsec*ipsec;structbonding*bond;structslave*slave;
@@ -443,9 +444,10 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,return-ENODEV;}-if(!slave->dev->xfrmdev_ops||-!slave->dev->xfrmdev_ops->xdo_dev_state_add||-netif_is_bond_master(slave->dev)){+real_dev=slave->dev;+if(!real_dev->xfrmdev_ops||+!real_dev->xfrmdev_ops->xdo_dev_state_add||+netif_is_bond_master(real_dev)){NL_SET_ERR_MSG_MOD(extack,"Slave does not support ipsec offload");rcu_read_unlock();return-EINVAL;
@@ -456,9 +458,9 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,rcu_read_unlock();return-ENOMEM;}-xs->xso.real_dev=slave->dev;-err=slave->dev->xfrmdev_ops->xdo_dev_state_add(xs,extack);+xs->xso.real_dev=real_dev;+err=real_dev->xfrmdev_ops->xdo_dev_state_add(xs,extack);if(!err){ipsec->xs=xs;INIT_LIST_HEAD(&ipsec->list);
@@ -475,6 +477,7 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,staticvoidbond_ipsec_add_sa_all(structbonding*bond){structnet_device*bond_dev=bond->dev;+structnet_device*real_dev;structbond_ipsec*ipsec;structslave*slave;
@@ -481,35 +476,43 @@ static void bond_ipsec_add_sa_all(struct bonding *bond)structbond_ipsec*ipsec;structslave*slave;-rcu_read_lock();-slave=rcu_dereference(bond->curr_active_slave);-if(!slave)-gotoout;+slave=rtnl_dereference(bond->curr_active_slave);+real_dev=slave?slave->dev:NULL;+if(!real_dev)+return;-real_dev=slave->dev;+mutex_lock(&bond->ipsec_lock);if(!real_dev->xfrmdev_ops||!real_dev->xfrmdev_ops->xdo_dev_state_add||netif_is_bond_master(real_dev)){-spin_lock_bh(&bond->ipsec_lock);if(!list_empty(&bond->ipsec_list))slave_warn(bond_dev,real_dev,"%s: no slave xdo_dev_state_add\n",__func__);-spin_unlock_bh(&bond->ipsec_lock);gotoout;}-spin_lock_bh(&bond->ipsec_lock);list_for_each_entry(ipsec,&bond->ipsec_list,list){+structnet_device*dev=ipsec->xs->xso.real_dev;++/* If new state is added before ipsec_lock acquired */+if(dev){+if(dev==real_dev)+continue;
Hi Jianbo,
Why we skip the deleting here if dev == real_dev? What if the state
is added again on the same slave? From the previous logic it looks we
don't check and do over write for the same device.
Thanks
Hangbin
bonding *bond)
struct bond_ipsec *ipsec;
struct slave *slave;
- rcu_read_lock();
- slave = rcu_dereference(bond->curr_active_slave);
- if (!slave)
- goto out;
+ slave = rtnl_dereference(bond->curr_active_slave);
+ real_dev = slave ? slave->dev : NULL;
+ if (!real_dev)
+ return;
- real_dev = slave->dev;
+ mutex_lock(&bond->ipsec_lock);
if (!real_dev->xfrmdev_ops ||
!real_dev->xfrmdev_ops->xdo_dev_state_add ||
netif_is_bond_master(real_dev)) {
- spin_lock_bh(&bond->ipsec_lock);
if (!list_empty(&bond->ipsec_list))
slave_warn(bond_dev, real_dev,
"%s: no slave
xdo_dev_state_add\n",
__func__);
- spin_unlock_bh(&bond->ipsec_lock);
goto out;
}
- spin_lock_bh(&bond->ipsec_lock);
list_for_each_entry(ipsec, &bond->ipsec_list, list) {
+ struct net_device *dev = ipsec->xs->xso.real_dev;
+
+ /* If new state is added before ipsec_lock acquired
*/
+ if (dev) {
+ if (dev == real_dev)
+ continue;
Hi Jianbo,
Why we skip the deleting here if dev == real_dev? What if the state
Here the bond active slave is updated. If dev == real_dev, the state
(should be newly added) is offloaded to new active, so no need to
delete and add back again.
is added again on the same slave? From the previous logic it looks we
Why is it added to the same slave? It's not the active one.
don't check and do over write for the same device.
Thanks
Hangbin
quoted
+ dev->xfrmdev_ops-
quoted
xdo_dev_state_delete(ipsec->xs);
+ if (dev->xfrmdev_ops->xdo_dev_state_free)
+ dev->xfrmdev_ops-
quoted
xdo_dev_state_free(ipsec->xs);
+ }
+
ipsec->xs->xso.real_dev = real_dev;
if (real_dev->xfrmdev_ops->xdo_dev_state_add(ipsec-
bonding *bond)
struct bond_ipsec *ipsec;
struct slave *slave;
- rcu_read_lock();
- slave = rcu_dereference(bond->curr_active_slave);
- if (!slave)
- goto out;
+ slave = rtnl_dereference(bond->curr_active_slave);
+ real_dev = slave ? slave->dev : NULL;
+ if (!real_dev)
+ return;
- real_dev = slave->dev;
+ mutex_lock(&bond->ipsec_lock);
if (!real_dev->xfrmdev_ops ||
!real_dev->xfrmdev_ops->xdo_dev_state_add ||
netif_is_bond_master(real_dev)) {
- spin_lock_bh(&bond->ipsec_lock);
if (!list_empty(&bond->ipsec_list))
slave_warn(bond_dev, real_dev,
"%s: no slave
xdo_dev_state_add\n",
__func__);
- spin_unlock_bh(&bond->ipsec_lock);
goto out;
}
- spin_lock_bh(&bond->ipsec_lock);
list_for_each_entry(ipsec, &bond->ipsec_list, list) {
+ struct net_device *dev = ipsec->xs->xso.real_dev;
+
+ /* If new state is added before ipsec_lock acquired
*/
+ if (dev) {
+ if (dev == real_dev)
+ continue;
Hi Jianbo,
Why we skip the deleting here if dev == real_dev? What if the state
Here the bond active slave is updated. If dev == real_dev, the state
(should be newly added) is offloaded to new active, so no need to
delete and add back again.
quoted
is added again on the same slave? From the previous logic it looks we
Why is it added to the same slave? It's not the active one.
OK, I got what you mean now. Thanks for the explaination.
Reviewed-by: Hangbin Liu <redacted>
can xs->xso.dev be NULL during the dev_free_state callback?
Shouldn't be NULL because bond_ipsec_del_sa is called before and xs is
removed from bond->ipsec_list, so xs->xso.dev is kept unless it's
cleared in dev's xdo_dev_state_delete callback.
What's holding onto real_dev once you drop the rcu lock here?
I think it should be xfrm state (and bond device).
Please explain it in the commit message in more certain terms.
Sorry, I don't understand. The real_dev is saved in xs->xso.real_dev,
and also bond's slave. It's straightforward. What else do I need to
explain?
Thanks!
Jianbo
What's holding onto real_dev once you drop the rcu lock here?
I think it should be xfrm state (and bond device).
Please explain it in the commit message in more certain terms.
Sorry, I don't understand. The real_dev is saved in xs->xso.real_dev,
and also bond's slave. It's straightforward. What else do I need to
explain?
I think Jakub means you need to make sure the real_dev is not freed during
xfrmdev_ops. See bond_ipsec_add_sa(). You unlock it too early and later
xfrmdev_ops is not protected.
Hangbin
What's holding onto real_dev once you drop the rcu lock
here?
I think it should be xfrm state (and bond device).
Please explain it in the commit message in more certain terms.
Sorry, I don't understand. The real_dev is saved in xs-
quoted
xso.real_dev,
and also bond's slave. It's straightforward. What else do I need to
explain?
I think Jakub means you need to make sure the real_dev is not freed
during
xfrmdev_ops. See bond_ipsec_add_sa(). You unlock it too early and
later
xfrmdev_ops is not protected.
This RCU lock is to protect the reading of curr_active_slave, which is
pointing to a big stuct - slave struct, so there is no error to get
real_dev from slave->dev.
Thanks!
Jianbo
What's holding onto real_dev once you drop the rcu lock
here?
I think it should be xfrm state (and bond device).
Please explain it in the commit message in more certain terms.
Sorry, I don't understand. The real_dev is saved in xs-
quoted
xso.real_dev,
and also bond's slave. It's straightforward. What else do I need to
explain?
I think Jakub means you need to make sure the real_dev is not freed
during
xfrmdev_ops. See bond_ipsec_add_sa(). You unlock it too early and
later
xfrmdev_ops is not protected.
This RCU lock is to protect the reading of curr_active_slave, which is
pointing to a big stuct - slave struct, so there is no error to get
real_dev from slave->dev.
It's not about getting real_dev from slave->dev. As Jakub said, What's holding
on real_dev once you drop the rcu lock?
Thanks
Hangbin
What's holding onto real_dev once you drop the rcu lock
here?
I think it should be xfrm state (and bond device).
Please explain it in the commit message in more certain
terms.
Sorry, I don't understand. The real_dev is saved in xs-
quoted
xso.real_dev,
and also bond's slave. It's straightforward. What else do I
need to
explain?
I think Jakub means you need to make sure the real_dev is not
freed
during
xfrmdev_ops. See bond_ipsec_add_sa(). You unlock it too early and
later
xfrmdev_ops is not protected.
This RCU lock is to protect the reading of curr_active_slave, which
is
pointing to a big stuct - slave struct, so there is no error to get
real_dev from slave->dev.
It's not about getting real_dev from slave->dev. As Jakub said,
What's holding
on real_dev once you drop the rcu lock?
As you mentioned the lock, I explained what's it used for, so we will
not mix basic concepts and make things complicated.
As for Jakub's question, I already answered. And I'm waiting for his
reply so I can better undestand how to modify if there is any.
Thanks!
Jianbo