Skip to content

Commit 2d9a93b

Browse files
TaeheeYoodavem330
authored andcommitted
mld: convert from timer to delayed work
mcast.c has several timers for delaying works. Timer's expire handler is working under atomic context so it can't use sleepable things such as GFP_KERNEL, mutex, etc. In order to use sleepable APIs, it converts from timers to delayed work. But there are some critical sections, which is used by both process and BH context. So that it still uses spin_lock_bh() and rwlock. Suggested-by: Cong Wang <[email protected]> Signed-off-by: Taehee Yoo <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 6e27514 commit 2d9a93b

File tree

2 files changed

+83
-65
lines changed

2 files changed

+83
-65
lines changed

include/net/if_inet6.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ struct ifmcaddr6 {
120120
unsigned int mca_sfmode;
121121
unsigned char mca_crcount;
122122
unsigned long mca_sfcount[2];
123-
struct timer_list mca_timer;
123+
struct delayed_work mca_work;
124124
unsigned int mca_flags;
125125
int mca_users;
126126
refcount_t mca_refcnt;
@@ -179,9 +179,9 @@ struct inet6_dev {
179179
unsigned long mc_qri; /* Query Response Interval */
180180
unsigned long mc_maxdelay;
181181

182-
struct timer_list mc_gq_timer; /* general query timer */
183-
struct timer_list mc_ifc_timer; /* interface change timer */
184-
struct timer_list mc_dad_timer; /* dad complete mc timer */
182+
struct delayed_work mc_gq_work; /* general query work */
183+
struct delayed_work mc_ifc_work; /* interface change work */
184+
struct delayed_work mc_dad_work; /* dad complete mc work */
185185

186186
struct ifacaddr6 *ac_list;
187187
rwlock_t lock;

net/ipv6/mcast.c

Lines changed: 79 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
#include <linux/socket.h>
3030
#include <linux/sockios.h>
3131
#include <linux/jiffies.h>
32-
#include <linux/times.h>
3332
#include <linux/net.h>
3433
#include <linux/in.h>
3534
#include <linux/in6.h>
@@ -42,6 +41,7 @@
4241
#include <linux/slab.h>
4342
#include <linux/pkt_sched.h>
4443
#include <net/mld.h>
44+
#include <linux/workqueue.h>
4545

4646
#include <linux/netfilter.h>
4747
#include <linux/netfilter_ipv6.h>
@@ -67,14 +67,13 @@ static int __mld2_query_bugs[] __attribute__((__unused__)) = {
6767
BUILD_BUG_ON_ZERO(offsetof(struct mld2_grec, grec_mca) % 4)
6868
};
6969

70+
static struct workqueue_struct *mld_wq;
7071
static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;
7172

7273
static void igmp6_join_group(struct ifmcaddr6 *ma);
7374
static void igmp6_leave_group(struct ifmcaddr6 *ma);
74-
static void igmp6_timer_handler(struct timer_list *t);
75+
static void mld_mca_work(struct work_struct *work);
7576

76-
static void mld_gq_timer_expire(struct timer_list *t);
77-
static void mld_ifc_timer_expire(struct timer_list *t);
7877
static void mld_ifc_event(struct inet6_dev *idev);
7978
static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc);
8079
static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc);
@@ -713,7 +712,7 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc)
713712
igmp6_leave_group(mc);
714713

715714
spin_lock_bh(&mc->mca_lock);
716-
if (del_timer(&mc->mca_timer))
715+
if (cancel_delayed_work(&mc->mca_work))
717716
refcount_dec(&mc->mca_refcnt);
718717
spin_unlock_bh(&mc->mca_lock);
719718
}
@@ -854,7 +853,7 @@ static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
854853
if (!mc)
855854
return NULL;
856855

857-
timer_setup(&mc->mca_timer, igmp6_timer_handler, 0);
856+
INIT_DELAYED_WORK(&mc->mca_work, mld_mca_work);
858857

859858
mc->mca_addr = *addr;
860859
mc->idev = idev; /* reference taken by caller */
@@ -1027,48 +1026,48 @@ bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
10271026
return rv;
10281027
}
10291028

1030-
static void mld_gq_start_timer(struct inet6_dev *idev)
1029+
static void mld_gq_start_work(struct inet6_dev *idev)
10311030
{
10321031
unsigned long tv = prandom_u32() % idev->mc_maxdelay;
10331032

10341033
idev->mc_gq_running = 1;
1035-
if (!mod_timer(&idev->mc_gq_timer, jiffies+tv+2))
1034+
if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2))
10361035
in6_dev_hold(idev);
10371036
}
10381037

1039-
static void mld_gq_stop_timer(struct inet6_dev *idev)
1038+
static void mld_gq_stop_work(struct inet6_dev *idev)
10401039
{
10411040
idev->mc_gq_running = 0;
1042-
if (del_timer(&idev->mc_gq_timer))
1041+
if (cancel_delayed_work(&idev->mc_gq_work))
10431042
__in6_dev_put(idev);
10441043
}
10451044

1046-
static void mld_ifc_start_timer(struct inet6_dev *idev, unsigned long delay)
1045+
static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay)
10471046
{
10481047
unsigned long tv = prandom_u32() % delay;
10491048

1050-
if (!mod_timer(&idev->mc_ifc_timer, jiffies+tv+2))
1049+
if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2))
10511050
in6_dev_hold(idev);
10521051
}
10531052

1054-
static void mld_ifc_stop_timer(struct inet6_dev *idev)
1053+
static void mld_ifc_stop_work(struct inet6_dev *idev)
10551054
{
10561055
idev->mc_ifc_count = 0;
1057-
if (del_timer(&idev->mc_ifc_timer))
1056+
if (cancel_delayed_work(&idev->mc_ifc_work))
10581057
__in6_dev_put(idev);
10591058
}
10601059

1061-
static void mld_dad_start_timer(struct inet6_dev *idev, unsigned long delay)
1060+
static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay)
10621061
{
10631062
unsigned long tv = prandom_u32() % delay;
10641063

1065-
if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2))
1064+
if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2))
10661065
in6_dev_hold(idev);
10671066
}
10681067

1069-
static void mld_dad_stop_timer(struct inet6_dev *idev)
1068+
static void mld_dad_stop_work(struct inet6_dev *idev)
10701069
{
1071-
if (del_timer(&idev->mc_dad_timer))
1070+
if (cancel_delayed_work(&idev->mc_dad_work))
10721071
__in6_dev_put(idev);
10731072
}
10741073

@@ -1080,21 +1079,20 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
10801079
{
10811080
unsigned long delay = resptime;
10821081

1083-
/* Do not start timer for these addresses */
1082+
/* Do not start work for these addresses */
10841083
if (ipv6_addr_is_ll_all_nodes(&ma->mca_addr) ||
10851084
IPV6_ADDR_MC_SCOPE(&ma->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL)
10861085
return;
10871086

1088-
if (del_timer(&ma->mca_timer)) {
1087+
if (cancel_delayed_work(&ma->mca_work)) {
10891088
refcount_dec(&ma->mca_refcnt);
1090-
delay = ma->mca_timer.expires - jiffies;
1089+
delay = ma->mca_work.timer.expires - jiffies;
10911090
}
10921091

10931092
if (delay >= resptime)
10941093
delay = prandom_u32() % resptime;
10951094

1096-
ma->mca_timer.expires = jiffies + delay;
1097-
if (!mod_timer(&ma->mca_timer, jiffies + delay))
1095+
if (!mod_delayed_work(mld_wq, &ma->mca_work, delay))
10981096
refcount_inc(&ma->mca_refcnt);
10991097
ma->mca_flags |= MAF_TIMER_RUNNING;
11001098
}
@@ -1305,10 +1303,10 @@ static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld,
13051303
if (v1_query)
13061304
mld_set_v1_mode(idev);
13071305

1308-
/* cancel MLDv2 report timer */
1309-
mld_gq_stop_timer(idev);
1310-
/* cancel the interface change timer */
1311-
mld_ifc_stop_timer(idev);
1306+
/* cancel MLDv2 report work */
1307+
mld_gq_stop_work(idev);
1308+
/* cancel the interface change work */
1309+
mld_ifc_stop_work(idev);
13121310
/* clear deleted report items */
13131311
mld_clear_delrec(idev);
13141312

@@ -1398,7 +1396,7 @@ int igmp6_event_query(struct sk_buff *skb)
13981396
if (mlh2->mld2q_nsrcs)
13991397
return -EINVAL; /* no sources allowed */
14001398

1401-
mld_gq_start_timer(idev);
1399+
mld_gq_start_work(idev);
14021400
return 0;
14031401
}
14041402
/* mark sources to include, if group & source-specific */
@@ -1482,14 +1480,14 @@ int igmp6_event_report(struct sk_buff *skb)
14821480
return -ENODEV;
14831481

14841482
/*
1485-
* Cancel the timer for this group
1483+
* Cancel the work for this group
14861484
*/
14871485

14881486
read_lock_bh(&idev->lock);
14891487
for (ma = idev->mc_list; ma; ma = ma->next) {
14901488
if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) {
14911489
spin_lock(&ma->mca_lock);
1492-
if (del_timer(&ma->mca_timer))
1490+
if (cancel_delayed_work(&ma->mca_work))
14931491
refcount_dec(&ma->mca_refcnt);
14941492
ma->mca_flags &= ~(MAF_LAST_REPORTER|MAF_TIMER_RUNNING);
14951493
spin_unlock(&ma->mca_lock);
@@ -2103,21 +2101,23 @@ void ipv6_mc_dad_complete(struct inet6_dev *idev)
21032101
mld_send_initial_cr(idev);
21042102
idev->mc_dad_count--;
21052103
if (idev->mc_dad_count)
2106-
mld_dad_start_timer(idev,
2107-
unsolicited_report_interval(idev));
2104+
mld_dad_start_work(idev,
2105+
unsolicited_report_interval(idev));
21082106
}
21092107
}
21102108

2111-
static void mld_dad_timer_expire(struct timer_list *t)
2109+
static void mld_dad_work(struct work_struct *work)
21122110
{
2113-
struct inet6_dev *idev = from_timer(idev, t, mc_dad_timer);
2111+
struct inet6_dev *idev = container_of(to_delayed_work(work),
2112+
struct inet6_dev,
2113+
mc_dad_work);
21142114

21152115
mld_send_initial_cr(idev);
21162116
if (idev->mc_dad_count) {
21172117
idev->mc_dad_count--;
21182118
if (idev->mc_dad_count)
2119-
mld_dad_start_timer(idev,
2120-
unsolicited_report_interval(idev));
2119+
mld_dad_start_work(idev,
2120+
unsolicited_report_interval(idev));
21212121
}
21222122
in6_dev_put(idev);
21232123
}
@@ -2416,12 +2416,12 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
24162416
delay = prandom_u32() % unsolicited_report_interval(ma->idev);
24172417

24182418
spin_lock_bh(&ma->mca_lock);
2419-
if (del_timer(&ma->mca_timer)) {
2419+
if (cancel_delayed_work(&ma->mca_work)) {
24202420
refcount_dec(&ma->mca_refcnt);
2421-
delay = ma->mca_timer.expires - jiffies;
2421+
delay = ma->mca_work.timer.expires - jiffies;
24222422
}
24232423

2424-
if (!mod_timer(&ma->mca_timer, jiffies + delay))
2424+
if (!mod_delayed_work(mld_wq, &ma->mca_work, delay))
24252425
refcount_inc(&ma->mca_refcnt);
24262426
ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
24272427
spin_unlock_bh(&ma->mca_lock);
@@ -2458,25 +2458,29 @@ static void igmp6_leave_group(struct ifmcaddr6 *ma)
24582458
}
24592459
}
24602460

2461-
static void mld_gq_timer_expire(struct timer_list *t)
2461+
static void mld_gq_work(struct work_struct *work)
24622462
{
2463-
struct inet6_dev *idev = from_timer(idev, t, mc_gq_timer);
2463+
struct inet6_dev *idev = container_of(to_delayed_work(work),
2464+
struct inet6_dev,
2465+
mc_gq_work);
24642466

24652467
idev->mc_gq_running = 0;
24662468
mld_send_report(idev, NULL);
24672469
in6_dev_put(idev);
24682470
}
24692471

2470-
static void mld_ifc_timer_expire(struct timer_list *t)
2472+
static void mld_ifc_work(struct work_struct *work)
24712473
{
2472-
struct inet6_dev *idev = from_timer(idev, t, mc_ifc_timer);
2474+
struct inet6_dev *idev = container_of(to_delayed_work(work),
2475+
struct inet6_dev,
2476+
mc_ifc_work);
24732477

24742478
mld_send_cr(idev);
24752479
if (idev->mc_ifc_count) {
24762480
idev->mc_ifc_count--;
24772481
if (idev->mc_ifc_count)
2478-
mld_ifc_start_timer(idev,
2479-
unsolicited_report_interval(idev));
2482+
mld_ifc_start_work(idev,
2483+
unsolicited_report_interval(idev));
24802484
}
24812485
in6_dev_put(idev);
24822486
}
@@ -2486,22 +2490,23 @@ static void mld_ifc_event(struct inet6_dev *idev)
24862490
if (mld_in_v1_mode(idev))
24872491
return;
24882492
idev->mc_ifc_count = idev->mc_qrv;
2489-
mld_ifc_start_timer(idev, 1);
2493+
mld_ifc_start_work(idev, 1);
24902494
}
24912495

2492-
static void igmp6_timer_handler(struct timer_list *t)
2496+
static void mld_mca_work(struct work_struct *work)
24932497
{
2494-
struct ifmcaddr6 *ma = from_timer(ma, t, mca_timer);
2498+
struct ifmcaddr6 *ma = container_of(to_delayed_work(work),
2499+
struct ifmcaddr6, mca_work);
24952500

24962501
if (mld_in_v1_mode(ma->idev))
24972502
igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
24982503
else
24992504
mld_send_report(ma->idev, ma);
25002505

2501-
spin_lock(&ma->mca_lock);
2506+
spin_lock_bh(&ma->mca_lock);
25022507
ma->mca_flags |= MAF_LAST_REPORTER;
25032508
ma->mca_flags &= ~MAF_TIMER_RUNNING;
2504-
spin_unlock(&ma->mca_lock);
2509+
spin_unlock_bh(&ma->mca_lock);
25052510
ma_put(ma);
25062511
}
25072512

@@ -2537,12 +2542,12 @@ void ipv6_mc_down(struct inet6_dev *idev)
25372542
for (i = idev->mc_list; i; i = i->next)
25382543
igmp6_group_dropped(i);
25392544

2540-
/* Should stop timer after group drop. or we will
2541-
* start timer again in mld_ifc_event()
2545+
/* Should stop work after group drop. or we will
2546+
* start work again in mld_ifc_event()
25422547
*/
2543-
mld_ifc_stop_timer(idev);
2544-
mld_gq_stop_timer(idev);
2545-
mld_dad_stop_timer(idev);
2548+
mld_ifc_stop_work(idev);
2549+
mld_gq_stop_work(idev);
2550+
mld_dad_stop_work(idev);
25462551
read_unlock_bh(&idev->lock);
25472552
}
25482553

@@ -2579,11 +2584,11 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
25792584
write_lock_bh(&idev->lock);
25802585
spin_lock_init(&idev->mc_lock);
25812586
idev->mc_gq_running = 0;
2582-
timer_setup(&idev->mc_gq_timer, mld_gq_timer_expire, 0);
2587+
INIT_DELAYED_WORK(&idev->mc_gq_work, mld_gq_work);
25832588
idev->mc_tomb = NULL;
25842589
idev->mc_ifc_count = 0;
2585-
timer_setup(&idev->mc_ifc_timer, mld_ifc_timer_expire, 0);
2586-
timer_setup(&idev->mc_dad_timer, mld_dad_timer_expire, 0);
2590+
INIT_DELAYED_WORK(&idev->mc_ifc_work, mld_ifc_work);
2591+
INIT_DELAYED_WORK(&idev->mc_dad_work, mld_dad_work);
25872592
ipv6_mc_reset(idev);
25882593
write_unlock_bh(&idev->lock);
25892594
}
@@ -2596,7 +2601,7 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev)
25962601
{
25972602
struct ifmcaddr6 *i;
25982603

2599-
/* Deactivate timers */
2604+
/* Deactivate works */
26002605
ipv6_mc_down(idev);
26012606
mld_clear_delrec(idev);
26022607

@@ -2763,7 +2768,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
27632768
&im->mca_addr,
27642769
im->mca_users, im->mca_flags,
27652770
(im->mca_flags&MAF_TIMER_RUNNING) ?
2766-
jiffies_to_clock_t(im->mca_timer.expires-jiffies) : 0);
2771+
jiffies_to_clock_t(im->mca_work.timer.expires - jiffies) : 0);
27672772
return 0;
27682773
}
27692774

@@ -3002,7 +3007,19 @@ static struct pernet_operations igmp6_net_ops = {
30023007

30033008
int __init igmp6_init(void)
30043009
{
3005-
return register_pernet_subsys(&igmp6_net_ops);
3010+
int err;
3011+
3012+
err = register_pernet_subsys(&igmp6_net_ops);
3013+
if (err)
3014+
return err;
3015+
3016+
mld_wq = create_workqueue("mld");
3017+
if (!mld_wq) {
3018+
unregister_pernet_subsys(&igmp6_net_ops);
3019+
return -ENOMEM;
3020+
}
3021+
3022+
return err;
30063023
}
30073024

30083025
int __init igmp6_late_init(void)
@@ -3013,6 +3030,7 @@ int __init igmp6_late_init(void)
30133030
void igmp6_cleanup(void)
30143031
{
30153032
unregister_pernet_subsys(&igmp6_net_ops);
3033+
destroy_workqueue(mld_wq);
30163034
}
30173035

30183036
void igmp6_late_cleanup(void)

0 commit comments

Comments
 (0)