Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <xiyou.wangcong@gmail.com>
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Taehee Yoo and committed by
David S. Miller
2d9a93b4 6e275143

+83 -65
+4 -4
include/net/if_inet6.h
··· 120 120 unsigned int mca_sfmode; 121 121 unsigned char mca_crcount; 122 122 unsigned long mca_sfcount[2]; 123 - struct timer_list mca_timer; 123 + struct delayed_work mca_work; 124 124 unsigned int mca_flags; 125 125 int mca_users; 126 126 refcount_t mca_refcnt; ··· 179 179 unsigned long mc_qri; /* Query Response Interval */ 180 180 unsigned long mc_maxdelay; 181 181 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 */ 185 185 186 186 struct ifacaddr6 *ac_list; 187 187 rwlock_t lock;
+79 -61
net/ipv6/mcast.c
··· 29 29 #include <linux/socket.h> 30 30 #include <linux/sockios.h> 31 31 #include <linux/jiffies.h> 32 - #include <linux/times.h> 33 32 #include <linux/net.h> 34 33 #include <linux/in.h> 35 34 #include <linux/in6.h> ··· 41 42 #include <linux/slab.h> 42 43 #include <linux/pkt_sched.h> 43 44 #include <net/mld.h> 45 + #include <linux/workqueue.h> 44 46 45 47 #include <linux/netfilter.h> 46 48 #include <linux/netfilter_ipv6.h> ··· 67 67 BUILD_BUG_ON_ZERO(offsetof(struct mld2_grec, grec_mca) % 4) 68 68 }; 69 69 70 + static struct workqueue_struct *mld_wq; 70 71 static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT; 71 72 72 73 static void igmp6_join_group(struct ifmcaddr6 *ma); 73 74 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); 75 76 76 - static void mld_gq_timer_expire(struct timer_list *t); 77 - static void mld_ifc_timer_expire(struct timer_list *t); 78 77 static void mld_ifc_event(struct inet6_dev *idev); 79 78 static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc); 80 79 static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc); ··· 712 713 igmp6_leave_group(mc); 713 714 714 715 spin_lock_bh(&mc->mca_lock); 715 - if (del_timer(&mc->mca_timer)) 716 + if (cancel_delayed_work(&mc->mca_work)) 716 717 refcount_dec(&mc->mca_refcnt); 717 718 spin_unlock_bh(&mc->mca_lock); 718 719 } ··· 853 854 if (!mc) 854 855 return NULL; 855 856 856 - timer_setup(&mc->mca_timer, igmp6_timer_handler, 0); 857 + INIT_DELAYED_WORK(&mc->mca_work, mld_mca_work); 857 858 858 859 mc->mca_addr = *addr; 859 860 mc->idev = idev; /* reference taken by caller */ ··· 1026 1027 return rv; 1027 1028 } 1028 1029 1029 - static void mld_gq_start_timer(struct inet6_dev *idev) 1030 + static void mld_gq_start_work(struct inet6_dev *idev) 1030 1031 { 1031 1032 unsigned long tv = prandom_u32() % idev->mc_maxdelay; 1032 1033 1033 1034 idev->mc_gq_running = 1; 1034 - if (!mod_timer(&idev->mc_gq_timer, jiffies+tv+2)) 1035 + if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) 1035 1036 in6_dev_hold(idev); 1036 1037 } 1037 1038 1038 - static void mld_gq_stop_timer(struct inet6_dev *idev) 1039 + static void mld_gq_stop_work(struct inet6_dev *idev) 1039 1040 { 1040 1041 idev->mc_gq_running = 0; 1041 - if (del_timer(&idev->mc_gq_timer)) 1042 + if (cancel_delayed_work(&idev->mc_gq_work)) 1042 1043 __in6_dev_put(idev); 1043 1044 } 1044 1045 1045 - static void mld_ifc_start_timer(struct inet6_dev *idev, unsigned long delay) 1046 + static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay) 1046 1047 { 1047 1048 unsigned long tv = prandom_u32() % delay; 1048 1049 1049 - if (!mod_timer(&idev->mc_ifc_timer, jiffies+tv+2)) 1050 + if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) 1050 1051 in6_dev_hold(idev); 1051 1052 } 1052 1053 1053 - static void mld_ifc_stop_timer(struct inet6_dev *idev) 1054 + static void mld_ifc_stop_work(struct inet6_dev *idev) 1054 1055 { 1055 1056 idev->mc_ifc_count = 0; 1056 - if (del_timer(&idev->mc_ifc_timer)) 1057 + if (cancel_delayed_work(&idev->mc_ifc_work)) 1057 1058 __in6_dev_put(idev); 1058 1059 } 1059 1060 1060 - static void mld_dad_start_timer(struct inet6_dev *idev, unsigned long delay) 1061 + static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay) 1061 1062 { 1062 1063 unsigned long tv = prandom_u32() % delay; 1063 1064 1064 - if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2)) 1065 + if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) 1065 1066 in6_dev_hold(idev); 1066 1067 } 1067 1068 1068 - static void mld_dad_stop_timer(struct inet6_dev *idev) 1069 + static void mld_dad_stop_work(struct inet6_dev *idev) 1069 1070 { 1070 - if (del_timer(&idev->mc_dad_timer)) 1071 + if (cancel_delayed_work(&idev->mc_dad_work)) 1071 1072 __in6_dev_put(idev); 1072 1073 } 1073 1074 ··· 1079 1080 { 1080 1081 unsigned long delay = resptime; 1081 1082 1082 - /* Do not start timer for these addresses */ 1083 + /* Do not start work for these addresses */ 1083 1084 if (ipv6_addr_is_ll_all_nodes(&ma->mca_addr) || 1084 1085 IPV6_ADDR_MC_SCOPE(&ma->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) 1085 1086 return; 1086 1087 1087 - if (del_timer(&ma->mca_timer)) { 1088 + if (cancel_delayed_work(&ma->mca_work)) { 1088 1089 refcount_dec(&ma->mca_refcnt); 1089 - delay = ma->mca_timer.expires - jiffies; 1090 + delay = ma->mca_work.timer.expires - jiffies; 1090 1091 } 1091 1092 1092 1093 if (delay >= resptime) 1093 1094 delay = prandom_u32() % resptime; 1094 1095 1095 - ma->mca_timer.expires = jiffies + delay; 1096 - if (!mod_timer(&ma->mca_timer, jiffies + delay)) 1096 + if (!mod_delayed_work(mld_wq, &ma->mca_work, delay)) 1097 1097 refcount_inc(&ma->mca_refcnt); 1098 1098 ma->mca_flags |= MAF_TIMER_RUNNING; 1099 1099 } ··· 1303 1305 if (v1_query) 1304 1306 mld_set_v1_mode(idev); 1305 1307 1306 - /* cancel MLDv2 report timer */ 1307 - mld_gq_stop_timer(idev); 1308 - /* cancel the interface change timer */ 1309 - mld_ifc_stop_timer(idev); 1308 + /* cancel MLDv2 report work */ 1309 + mld_gq_stop_work(idev); 1310 + /* cancel the interface change work */ 1311 + mld_ifc_stop_work(idev); 1310 1312 /* clear deleted report items */ 1311 1313 mld_clear_delrec(idev); 1312 1314 ··· 1396 1398 if (mlh2->mld2q_nsrcs) 1397 1399 return -EINVAL; /* no sources allowed */ 1398 1400 1399 - mld_gq_start_timer(idev); 1401 + mld_gq_start_work(idev); 1400 1402 return 0; 1401 1403 } 1402 1404 /* mark sources to include, if group & source-specific */ ··· 1480 1482 return -ENODEV; 1481 1483 1482 1484 /* 1483 - * Cancel the timer for this group 1485 + * Cancel the work for this group 1484 1486 */ 1485 1487 1486 1488 read_lock_bh(&idev->lock); 1487 1489 for (ma = idev->mc_list; ma; ma = ma->next) { 1488 1490 if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) { 1489 1491 spin_lock(&ma->mca_lock); 1490 - if (del_timer(&ma->mca_timer)) 1492 + if (cancel_delayed_work(&ma->mca_work)) 1491 1493 refcount_dec(&ma->mca_refcnt); 1492 1494 ma->mca_flags &= ~(MAF_LAST_REPORTER|MAF_TIMER_RUNNING); 1493 1495 spin_unlock(&ma->mca_lock); ··· 2101 2103 mld_send_initial_cr(idev); 2102 2104 idev->mc_dad_count--; 2103 2105 if (idev->mc_dad_count) 2104 - mld_dad_start_timer(idev, 2105 - unsolicited_report_interval(idev)); 2106 + mld_dad_start_work(idev, 2107 + unsolicited_report_interval(idev)); 2106 2108 } 2107 2109 } 2108 2110 2109 - static void mld_dad_timer_expire(struct timer_list *t) 2111 + static void mld_dad_work(struct work_struct *work) 2110 2112 { 2111 - struct inet6_dev *idev = from_timer(idev, t, mc_dad_timer); 2113 + struct inet6_dev *idev = container_of(to_delayed_work(work), 2114 + struct inet6_dev, 2115 + mc_dad_work); 2112 2116 2113 2117 mld_send_initial_cr(idev); 2114 2118 if (idev->mc_dad_count) { 2115 2119 idev->mc_dad_count--; 2116 2120 if (idev->mc_dad_count) 2117 - mld_dad_start_timer(idev, 2118 - unsolicited_report_interval(idev)); 2121 + mld_dad_start_work(idev, 2122 + unsolicited_report_interval(idev)); 2119 2123 } 2120 2124 in6_dev_put(idev); 2121 2125 } ··· 2416 2416 delay = prandom_u32() % unsolicited_report_interval(ma->idev); 2417 2417 2418 2418 spin_lock_bh(&ma->mca_lock); 2419 - if (del_timer(&ma->mca_timer)) { 2419 + if (cancel_delayed_work(&ma->mca_work)) { 2420 2420 refcount_dec(&ma->mca_refcnt); 2421 - delay = ma->mca_timer.expires - jiffies; 2421 + delay = ma->mca_work.timer.expires - jiffies; 2422 2422 } 2423 2423 2424 - if (!mod_timer(&ma->mca_timer, jiffies + delay)) 2424 + if (!mod_delayed_work(mld_wq, &ma->mca_work, delay)) 2425 2425 refcount_inc(&ma->mca_refcnt); 2426 2426 ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER; 2427 2427 spin_unlock_bh(&ma->mca_lock); ··· 2458 2458 } 2459 2459 } 2460 2460 2461 - static void mld_gq_timer_expire(struct timer_list *t) 2461 + static void mld_gq_work(struct work_struct *work) 2462 2462 { 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); 2464 2466 2465 2467 idev->mc_gq_running = 0; 2466 2468 mld_send_report(idev, NULL); 2467 2469 in6_dev_put(idev); 2468 2470 } 2469 2471 2470 - static void mld_ifc_timer_expire(struct timer_list *t) 2472 + static void mld_ifc_work(struct work_struct *work) 2471 2473 { 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); 2473 2477 2474 2478 mld_send_cr(idev); 2475 2479 if (idev->mc_ifc_count) { 2476 2480 idev->mc_ifc_count--; 2477 2481 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)); 2480 2484 } 2481 2485 in6_dev_put(idev); 2482 2486 } ··· 2490 2486 if (mld_in_v1_mode(idev)) 2491 2487 return; 2492 2488 idev->mc_ifc_count = idev->mc_qrv; 2493 - mld_ifc_start_timer(idev, 1); 2489 + mld_ifc_start_work(idev, 1); 2494 2490 } 2495 2491 2496 - static void igmp6_timer_handler(struct timer_list *t) 2492 + static void mld_mca_work(struct work_struct *work) 2497 2493 { 2498 - struct ifmcaddr6 *ma = from_timer(ma, t, mca_timer); 2494 + struct ifmcaddr6 *ma = container_of(to_delayed_work(work), 2495 + struct ifmcaddr6, mca_work); 2499 2496 2500 2497 if (mld_in_v1_mode(ma->idev)) 2501 2498 igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); 2502 2499 else 2503 2500 mld_send_report(ma->idev, ma); 2504 2501 2505 - spin_lock(&ma->mca_lock); 2502 + spin_lock_bh(&ma->mca_lock); 2506 2503 ma->mca_flags |= MAF_LAST_REPORTER; 2507 2504 ma->mca_flags &= ~MAF_TIMER_RUNNING; 2508 - spin_unlock(&ma->mca_lock); 2505 + spin_unlock_bh(&ma->mca_lock); 2509 2506 ma_put(ma); 2510 2507 } 2511 2508 ··· 2542 2537 for (i = idev->mc_list; i; i = i->next) 2543 2538 igmp6_group_dropped(i); 2544 2539 2545 - /* Should stop timer after group drop. or we will 2546 - * start timer again in mld_ifc_event() 2540 + /* Should stop work after group drop. or we will 2541 + * start work again in mld_ifc_event() 2547 2542 */ 2548 - mld_ifc_stop_timer(idev); 2549 - mld_gq_stop_timer(idev); 2550 - mld_dad_stop_timer(idev); 2543 + mld_ifc_stop_work(idev); 2544 + mld_gq_stop_work(idev); 2545 + mld_dad_stop_work(idev); 2551 2546 read_unlock_bh(&idev->lock); 2552 2547 } 2553 2548 ··· 2584 2579 write_lock_bh(&idev->lock); 2585 2580 spin_lock_init(&idev->mc_lock); 2586 2581 idev->mc_gq_running = 0; 2587 - timer_setup(&idev->mc_gq_timer, mld_gq_timer_expire, 0); 2582 + INIT_DELAYED_WORK(&idev->mc_gq_work, mld_gq_work); 2588 2583 idev->mc_tomb = NULL; 2589 2584 idev->mc_ifc_count = 0; 2590 - timer_setup(&idev->mc_ifc_timer, mld_ifc_timer_expire, 0); 2591 - timer_setup(&idev->mc_dad_timer, mld_dad_timer_expire, 0); 2585 + INIT_DELAYED_WORK(&idev->mc_ifc_work, mld_ifc_work); 2586 + INIT_DELAYED_WORK(&idev->mc_dad_work, mld_dad_work); 2592 2587 ipv6_mc_reset(idev); 2593 2588 write_unlock_bh(&idev->lock); 2594 2589 } ··· 2601 2596 { 2602 2597 struct ifmcaddr6 *i; 2603 2598 2604 - /* Deactivate timers */ 2599 + /* Deactivate works */ 2605 2600 ipv6_mc_down(idev); 2606 2601 mld_clear_delrec(idev); 2607 2602 ··· 2768 2763 &im->mca_addr, 2769 2764 im->mca_users, im->mca_flags, 2770 2765 (im->mca_flags&MAF_TIMER_RUNNING) ? 2771 - jiffies_to_clock_t(im->mca_timer.expires-jiffies) : 0); 2766 + jiffies_to_clock_t(im->mca_work.timer.expires - jiffies) : 0); 2772 2767 return 0; 2773 2768 } 2774 2769 ··· 3007 3002 3008 3003 int __init igmp6_init(void) 3009 3004 { 3010 - return register_pernet_subsys(&igmp6_net_ops); 3005 + int err; 3006 + 3007 + err = register_pernet_subsys(&igmp6_net_ops); 3008 + if (err) 3009 + return err; 3010 + 3011 + mld_wq = create_workqueue("mld"); 3012 + if (!mld_wq) { 3013 + unregister_pernet_subsys(&igmp6_net_ops); 3014 + return -ENOMEM; 3015 + } 3016 + 3017 + return err; 3011 3018 } 3012 3019 3013 3020 int __init igmp6_late_init(void) ··· 3030 3013 void igmp6_cleanup(void) 3031 3014 { 3032 3015 unregister_pernet_subsys(&igmp6_net_ops); 3016 + destroy_workqueue(mld_wq); 3033 3017 } 3034 3018 3035 3019 void igmp6_late_cleanup(void)