Skip to content

Commit 2fecbf7

Browse files
committed
Merge branch 'fixes-for-ipsec-over-bonding'
Jianbo Liu says: ==================== Fixes for IPsec over bonding This patchset 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 c07ff85 ("netem: fix return value if duplicate enqueue fails") ==================== Link: https://patch.msgid.link/20240823031056.110999-1-jianbol@nvidia.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents 65a3cce + 2aeeef9 commit 2fecbf7

File tree

2 files changed

+106
-55
lines changed

2 files changed

+106
-55
lines changed

drivers/net/bonding/bond_main.c

Lines changed: 105 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,
427427
struct netlink_ext_ack *extack)
428428
{
429429
struct net_device *bond_dev = xs->xso.dev;
430+
struct net_device *real_dev;
431+
netdevice_tracker tracker;
430432
struct bond_ipsec *ipsec;
431433
struct bonding *bond;
432434
struct slave *slave;
@@ -438,74 +440,80 @@ static int bond_ipsec_add_sa(struct xfrm_state *xs,
438440
rcu_read_lock();
439441
bond = netdev_priv(bond_dev);
440442
slave = rcu_dereference(bond->curr_active_slave);
441-
if (!slave) {
442-
rcu_read_unlock();
443-
return -ENODEV;
443+
real_dev = slave ? slave->dev : NULL;
444+
netdev_hold(real_dev, &tracker, GFP_ATOMIC);
445+
rcu_read_unlock();
446+
if (!real_dev) {
447+
err = -ENODEV;
448+
goto out;
444449
}
445450

446-
if (!slave->dev->xfrmdev_ops ||
447-
!slave->dev->xfrmdev_ops->xdo_dev_state_add ||
448-
netif_is_bond_master(slave->dev)) {
451+
if (!real_dev->xfrmdev_ops ||
452+
!real_dev->xfrmdev_ops->xdo_dev_state_add ||
453+
netif_is_bond_master(real_dev)) {
449454
NL_SET_ERR_MSG_MOD(extack, "Slave does not support ipsec offload");
450-
rcu_read_unlock();
451-
return -EINVAL;
455+
err = -EINVAL;
456+
goto out;
452457
}
453458

454-
ipsec = kmalloc(sizeof(*ipsec), GFP_ATOMIC);
459+
ipsec = kmalloc(sizeof(*ipsec), GFP_KERNEL);
455460
if (!ipsec) {
456-
rcu_read_unlock();
457-
return -ENOMEM;
461+
err = -ENOMEM;
462+
goto out;
458463
}
459-
xs->xso.real_dev = slave->dev;
460464

461-
err = slave->dev->xfrmdev_ops->xdo_dev_state_add(xs, extack);
465+
xs->xso.real_dev = real_dev;
466+
err = real_dev->xfrmdev_ops->xdo_dev_state_add(xs, extack);
462467
if (!err) {
463468
ipsec->xs = xs;
464469
INIT_LIST_HEAD(&ipsec->list);
465-
spin_lock_bh(&bond->ipsec_lock);
470+
mutex_lock(&bond->ipsec_lock);
466471
list_add(&ipsec->list, &bond->ipsec_list);
467-
spin_unlock_bh(&bond->ipsec_lock);
472+
mutex_unlock(&bond->ipsec_lock);
468473
} else {
469474
kfree(ipsec);
470475
}
471-
rcu_read_unlock();
476+
out:
477+
netdev_put(real_dev, &tracker);
472478
return err;
473479
}
474480

475481
static void bond_ipsec_add_sa_all(struct bonding *bond)
476482
{
477483
struct net_device *bond_dev = bond->dev;
484+
struct net_device *real_dev;
478485
struct bond_ipsec *ipsec;
479486
struct slave *slave;
480487

481-
rcu_read_lock();
482-
slave = rcu_dereference(bond->curr_active_slave);
483-
if (!slave)
484-
goto out;
488+
slave = rtnl_dereference(bond->curr_active_slave);
489+
real_dev = slave ? slave->dev : NULL;
490+
if (!real_dev)
491+
return;
485492

486-
if (!slave->dev->xfrmdev_ops ||
487-
!slave->dev->xfrmdev_ops->xdo_dev_state_add ||
488-
netif_is_bond_master(slave->dev)) {
489-
spin_lock_bh(&bond->ipsec_lock);
493+
mutex_lock(&bond->ipsec_lock);
494+
if (!real_dev->xfrmdev_ops ||
495+
!real_dev->xfrmdev_ops->xdo_dev_state_add ||
496+
netif_is_bond_master(real_dev)) {
490497
if (!list_empty(&bond->ipsec_list))
491-
slave_warn(bond_dev, slave->dev,
498+
slave_warn(bond_dev, real_dev,
492499
"%s: no slave xdo_dev_state_add\n",
493500
__func__);
494-
spin_unlock_bh(&bond->ipsec_lock);
495501
goto out;
496502
}
497503

498-
spin_lock_bh(&bond->ipsec_lock);
499504
list_for_each_entry(ipsec, &bond->ipsec_list, list) {
500-
ipsec->xs->xso.real_dev = slave->dev;
501-
if (slave->dev->xfrmdev_ops->xdo_dev_state_add(ipsec->xs, NULL)) {
502-
slave_warn(bond_dev, slave->dev, "%s: failed to add SA\n", __func__);
505+
/* If new state is added before ipsec_lock acquired */
506+
if (ipsec->xs->xso.real_dev == real_dev)
507+
continue;
508+
509+
ipsec->xs->xso.real_dev = real_dev;
510+
if (real_dev->xfrmdev_ops->xdo_dev_state_add(ipsec->xs, NULL)) {
511+
slave_warn(bond_dev, real_dev, "%s: failed to add SA\n", __func__);
503512
ipsec->xs->xso.real_dev = NULL;
504513
}
505514
}
506-
spin_unlock_bh(&bond->ipsec_lock);
507515
out:
508-
rcu_read_unlock();
516+
mutex_unlock(&bond->ipsec_lock);
509517
}
510518

511519
/**
@@ -515,6 +523,8 @@ static void bond_ipsec_add_sa_all(struct bonding *bond)
515523
static void bond_ipsec_del_sa(struct xfrm_state *xs)
516524
{
517525
struct net_device *bond_dev = xs->xso.dev;
526+
struct net_device *real_dev;
527+
netdevice_tracker tracker;
518528
struct bond_ipsec *ipsec;
519529
struct bonding *bond;
520530
struct slave *slave;
@@ -525,66 +535,102 @@ static void bond_ipsec_del_sa(struct xfrm_state *xs)
525535
rcu_read_lock();
526536
bond = netdev_priv(bond_dev);
527537
slave = rcu_dereference(bond->curr_active_slave);
538+
real_dev = slave ? slave->dev : NULL;
539+
netdev_hold(real_dev, &tracker, GFP_ATOMIC);
540+
rcu_read_unlock();
528541

529542
if (!slave)
530543
goto out;
531544

532545
if (!xs->xso.real_dev)
533546
goto out;
534547

535-
WARN_ON(xs->xso.real_dev != slave->dev);
548+
WARN_ON(xs->xso.real_dev != real_dev);
536549

537-
if (!slave->dev->xfrmdev_ops ||
538-
!slave->dev->xfrmdev_ops->xdo_dev_state_delete ||
539-
netif_is_bond_master(slave->dev)) {
540-
slave_warn(bond_dev, slave->dev, "%s: no slave xdo_dev_state_delete\n", __func__);
550+
if (!real_dev->xfrmdev_ops ||
551+
!real_dev->xfrmdev_ops->xdo_dev_state_delete ||
552+
netif_is_bond_master(real_dev)) {
553+
slave_warn(bond_dev, real_dev, "%s: no slave xdo_dev_state_delete\n", __func__);
541554
goto out;
542555
}
543556

544-
slave->dev->xfrmdev_ops->xdo_dev_state_delete(xs);
557+
real_dev->xfrmdev_ops->xdo_dev_state_delete(xs);
545558
out:
546-
spin_lock_bh(&bond->ipsec_lock);
559+
netdev_put(real_dev, &tracker);
560+
mutex_lock(&bond->ipsec_lock);
547561
list_for_each_entry(ipsec, &bond->ipsec_list, list) {
548562
if (ipsec->xs == xs) {
549563
list_del(&ipsec->list);
550564
kfree(ipsec);
551565
break;
552566
}
553567
}
554-
spin_unlock_bh(&bond->ipsec_lock);
555-
rcu_read_unlock();
568+
mutex_unlock(&bond->ipsec_lock);
556569
}
557570

558571
static void bond_ipsec_del_sa_all(struct bonding *bond)
559572
{
560573
struct net_device *bond_dev = bond->dev;
574+
struct net_device *real_dev;
561575
struct bond_ipsec *ipsec;
562576
struct slave *slave;
563577

564-
rcu_read_lock();
565-
slave = rcu_dereference(bond->curr_active_slave);
566-
if (!slave) {
567-
rcu_read_unlock();
578+
slave = rtnl_dereference(bond->curr_active_slave);
579+
real_dev = slave ? slave->dev : NULL;
580+
if (!real_dev)
568581
return;
569-
}
570582

571-
spin_lock_bh(&bond->ipsec_lock);
583+
mutex_lock(&bond->ipsec_lock);
572584
list_for_each_entry(ipsec, &bond->ipsec_list, list) {
573585
if (!ipsec->xs->xso.real_dev)
574586
continue;
575587

576-
if (!slave->dev->xfrmdev_ops ||
577-
!slave->dev->xfrmdev_ops->xdo_dev_state_delete ||
578-
netif_is_bond_master(slave->dev)) {
579-
slave_warn(bond_dev, slave->dev,
588+
if (!real_dev->xfrmdev_ops ||
589+
!real_dev->xfrmdev_ops->xdo_dev_state_delete ||
590+
netif_is_bond_master(real_dev)) {
591+
slave_warn(bond_dev, real_dev,
580592
"%s: no slave xdo_dev_state_delete\n",
581593
__func__);
582594
} else {
583-
slave->dev->xfrmdev_ops->xdo_dev_state_delete(ipsec->xs);
595+
real_dev->xfrmdev_ops->xdo_dev_state_delete(ipsec->xs);
596+
if (real_dev->xfrmdev_ops->xdo_dev_state_free)
597+
real_dev->xfrmdev_ops->xdo_dev_state_free(ipsec->xs);
584598
}
585599
}
586-
spin_unlock_bh(&bond->ipsec_lock);
600+
mutex_unlock(&bond->ipsec_lock);
601+
}
602+
603+
static void bond_ipsec_free_sa(struct xfrm_state *xs)
604+
{
605+
struct net_device *bond_dev = xs->xso.dev;
606+
struct net_device *real_dev;
607+
netdevice_tracker tracker;
608+
struct bonding *bond;
609+
struct slave *slave;
610+
611+
if (!bond_dev)
612+
return;
613+
614+
rcu_read_lock();
615+
bond = netdev_priv(bond_dev);
616+
slave = rcu_dereference(bond->curr_active_slave);
617+
real_dev = slave ? slave->dev : NULL;
618+
netdev_hold(real_dev, &tracker, GFP_ATOMIC);
587619
rcu_read_unlock();
620+
621+
if (!slave)
622+
goto out;
623+
624+
if (!xs->xso.real_dev)
625+
goto out;
626+
627+
WARN_ON(xs->xso.real_dev != real_dev);
628+
629+
if (real_dev && real_dev->xfrmdev_ops &&
630+
real_dev->xfrmdev_ops->xdo_dev_state_free)
631+
real_dev->xfrmdev_ops->xdo_dev_state_free(xs);
632+
out:
633+
netdev_put(real_dev, &tracker);
588634
}
589635

590636
/**
@@ -627,6 +673,7 @@ static bool bond_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs)
627673
static const struct xfrmdev_ops bond_xfrmdev_ops = {
628674
.xdo_dev_state_add = bond_ipsec_add_sa,
629675
.xdo_dev_state_delete = bond_ipsec_del_sa,
676+
.xdo_dev_state_free = bond_ipsec_free_sa,
630677
.xdo_dev_offload_ok = bond_ipsec_offload_ok,
631678
};
632679
#endif /* CONFIG_XFRM_OFFLOAD */
@@ -5877,7 +5924,7 @@ void bond_setup(struct net_device *bond_dev)
58775924
/* set up xfrm device ops (only supported in active-backup right now) */
58785925
bond_dev->xfrmdev_ops = &bond_xfrmdev_ops;
58795926
INIT_LIST_HEAD(&bond->ipsec_list);
5880-
spin_lock_init(&bond->ipsec_lock);
5927+
mutex_init(&bond->ipsec_lock);
58815928
#endif /* CONFIG_XFRM_OFFLOAD */
58825929

58835930
/* don't acquire bond device's netif_tx_lock when transmitting */
@@ -5926,6 +5973,10 @@ static void bond_uninit(struct net_device *bond_dev)
59265973
__bond_release_one(bond_dev, slave->dev, true, true);
59275974
netdev_info(bond_dev, "Released all slaves\n");
59285975

5976+
#ifdef CONFIG_XFRM_OFFLOAD
5977+
mutex_destroy(&bond->ipsec_lock);
5978+
#endif /* CONFIG_XFRM_OFFLOAD */
5979+
59295980
bond_set_slave_arr(bond, NULL, NULL);
59305981

59315982
list_del_rcu(&bond->bond_list);

include/net/bonding.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ struct bonding {
260260
#ifdef CONFIG_XFRM_OFFLOAD
261261
struct list_head ipsec_list;
262262
/* protecting ipsec_list */
263-
spinlock_t ipsec_lock;
263+
struct mutex ipsec_lock;
264264
#endif /* CONFIG_XFRM_OFFLOAD */
265265
struct bpf_prog *xdp_prog;
266266
};

0 commit comments

Comments
 (0)