ipv4: igmp: use alarmtimer to prevent delayed reports
Subsystems (3)
PATCH 1/2 ipv4: igmp: use alarmtimer to prevent delayed reports
On receiving a IGMPv2/v3 query, based on max_delay set in the header a timer is started to send out a response after a random time within max_delay. If the system then moves into suspend state, Report is delayed until system wakes up.
Use a alarmtimer instead of using a timer. Alarmtimer will wake the system up from suspend to send out the IGMP report.
---
v2: use alarmtimer instead of wakelock.
---
If these changes are fine, I'll share similar patches for MLD and ARP.
--- include/linux/igmp.h | 7 ++++++- net/ipv4/igmp.c | 27 ++++++++++++++++----------- 2 files changed, 22 insertions(+), 12 deletions(-)
include/linux/igmp.h +6 -1
@@ -20,6 +20,9 @@#include <linux/in.h> #include <linux/refcount.h> #include <uapi/linux/igmp.h> +#ifdef CONFIG_IP_MULTICAST +#include <linux/alarmtimer.h> +#endif static inline struct igmphdr *igmp_hdr (const struct sk_buff *skb) {
@@ -83,7 +86,9 @@ struct ip_mc_list {struct ip_mc_list __rcu *next_rcu; };struct ip_mc_list __rcu *next_hash; -struct timer_list timer; +#ifdef CONFIG_IP_MULTICAST +struct alarm alarm; +#endif int users;refcount_t refcnt;spinlock_t lock;
net/ipv4/igmp.c +16 -11
@@ -199,7 +199,7 @@ static void ip_ma_put(struct ip_mc_list *im)static void igmp_stop_timer (struct ip_mc_list *im) {spin_lock_bh (&im->lock); -if (del_timer (&im->timer)) +if (alarm_cancel (&im->alarm))refcount_dec (&im->refcnt); im->tm_running =0 ; im->reporter =0 ;
@@ -210,11 +210,11 @@ static void igmp_stop_timer(struct ip_mc_list *im)/* It must be called with locked im->lock */ static void igmp_start_timer (struct ip_mc_list *im,int max_delay) { -int tv =prandom_u32 () % max_delay; +ktime_t expiry =jiffies_to_ktime (prandom_u32 () % max_delay +2 ); im->tm_running =1 ; -if (!mod_timer (&im->timer, jiffies+tv+2 )) -refcount_inc (&im->refcnt); +alarm_start_relative (&im->alarm, expiry); +refcount_inc (&im->refcnt); }static void igmp_gq_start_timer (struct in_device *in_dev)
@@ -241,11 +241,14 @@ static void igmp_ifc_start_timer(struct in_device *in_dev, int delay)static void igmp_mod_timer (struct ip_mc_list *im,int max_delay) { +ktime_t expiry; +spin_lock_bh (&im->lock); im->unsolicit_count =0 ; -if (del_timer (&im->timer)) { -if ((long )(im->timer.expires-jiffies) < max_delay) { -add_timer (&im->timer); + expiry =alarm_expires_remaining (&im->alarm); +if (alarm_cancel (&im->alarm)) { +if (ktime_to_jiffies (expiry) < max_delay) { +alarm_start_relative (&im->alarm, expiry); im->tm_running =1 ;spin_unlock_bh (&im->lock);return ;
@@ -812,9 +815,9 @@ static void igmp_ifc_event(struct in_device *in_dev) } -static void igmp_timer_expire (struct timer_list *t) +enum alarmtimer_restart igmp_timer_expire (struct alarm *alarm,ktime_t now) { -struct ip_mc_list *im =from_timer (im, t, timer); + struct ip_mc_list *im = container_of(alarm, struct ip_mc_list, alarm);struct in_device *in_dev = im->interface;spin_lock (&im->lock);
@@ -835,6 +838,8 @@ static void igmp_timer_expire(struct timer_list *t)igmp_send_report (in_dev, im,IGMPV3_HOST_MEMBERSHIP_REPORT );ip_ma_put (im); + +return ALARMTIMER_NORESTART ; }/* mark EXCLUDE-mode sources */
@@ -1413,7 +1418,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)refcount_set (&im->refcnt,1 );spin_lock_init (&im->lock);#ifdef CONFIG_IP_MULTICAST -timer_setup (&im->timer, igmp_timer_expire,0 ); +alarm_init (&im->alarm,ALARM_BOOTTIME , igmp_timer_expire); im->unsolicit_count = net->ipv4.sysctl_igmp_qrv;#endif
@@ -2811,7 +2816,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); } - delta = im->timer.expires - jiffies; + delta =ktime_to_jiffies (alarm_expires_remaining (&im->alarm)); seq_printf(seq, "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", im->multiaddr, im->users,
Signature · 1 line
1.9.1
PATCH 2/2 ktime: helpers to convert between ktime and jiffies
--- include/linux/ktime.h | 4 ++++ 1 file changed, 4 insertions(+)
include/linux/ktime.h +4 -0
@@ -96,6 +96,10 @@ static inline ktime_t timeval_to_ktime(struct timeval tv)/* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */ #define ktime_to_ns (kt) (kt) +/* ktime to jiffies and back */ +#define ktime_to_jiffies (kt) nsecs_to_jiffies(kt) +#define jiffies_to_ktime (j) jiffies_to_nsecs(j) +/** * ktime_compare - Compares two ktime_t variables for less, greater or equal * @cmp1: comparable1
Signature · 1 line
1.9.1
kbuild test robot
build botThank you for the patch! Yet something to improve:
Hi Tejaswi,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on net/master]
url: https://github.com/0day-ci/linux/commits/Tejaswi-Tanikella/ipv4-igmp-use-alarmtimer-to-prevent-delayed-reports/20180611-195615
config: x86_64-randconfig-x003-201823 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64 All errors (new ones prefixed by >>):
net/ipv4/igmp.c: In function 'igmp_mc_seq_show':
>> net/ipv4/igmp.c:2819:11: error: implicit declaration of function 'ktime_to_jiffies'; did you mean 'timeval_to_jiffies'? [-Werror=implicit-function-declaration]
delta = ktime_to_jiffies(alarm_expires_remaining(&im->alarm));
^~~~~~~~~~~~~~~~
timeval_to_jiffies
>> net/ipv4/igmp.c:2819:28: error: implicit declaration of function 'alarm_expires_remaining'; did you mean 'hrtimer_expires_remaining'? [-Werror=implicit-function-declaration]
delta = ktime_to_jiffies(alarm_expires_remaining(&im->alarm));
^~~~~~~~~~~~~~~~~~~~~~~
hrtimer_expires_remaining
>> net/ipv4/igmp.c:2819:55: error: 'struct ip_mc_list' has no member named 'alarm'
delta = ktime_to_jiffies(alarm_expires_remaining(&im->alarm));
^~cc1: some warnings being treated as errors
vim +2819 net/ipv4/igmp.c
2813
2814 if (rcu_access_pointer(state->in_dev->mc_list) == im) {
2815 seq_printf(seq, "%d\t%-10s: %5d %7s\n",
2816 state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier);
2817 }
2818
> 2819 delta = ktime_to_jiffies(alarm_expires_remaining(&im->alarm)); 2820 seq_printf(seq, 2821 "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", 2822 im->multiaddr, im->users, 2823 im->tm_running, 2824 im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, 2825 im->reporter);
2826 }
2827 return 0;
2828 }
2829
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
kbuild test robot
build botThank you for the patch! Yet something to improve:
Hi Tejaswi,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on net/master]
url: https://github.com/0day-ci/linux/commits/Tejaswi-Tanikella/ipv4-igmp-use-alarmtimer-to-prevent-delayed-reports/20180611-195615
config: i386-randconfig-x012-201823 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0 reproduce:
# save the attached .config to linux build tree
make ARCH=i386 Note: the linux-review/Tejaswi-Tanikella/ipv4-igmp-use-alarmtimer-to-prevent-delayed-reports/20180611-195615 HEAD 20987eecc3144eb22578baea09ff017ddcb45163 builds fine.
It only hurts bisectibility.
All errors (new ones prefixed by >>):
net//ipv4/igmp.c: In function 'igmp_start_timer':
>> net//ipv4/igmp.c:213:19: error: implicit declaration of function 'jiffies_to_ktime'; did you mean 'jiffies_to_timeval'? [-Werror=implicit-function-declaration]
ktime_t expiry = jiffies_to_ktime(prandom_u32() % max_delay + 2);
^~~~~~~~~~~~~~~~
jiffies_to_timeval
net//ipv4/igmp.c: In function 'igmp_mod_timer':
net//ipv4/igmp.c:250:7: error: implicit declaration of function 'ktime_to_jiffies'; did you mean 'timeval_to_jiffies'? [-Werror=implicit-function-declaration]
if (ktime_to_jiffies(expiry) < max_delay) {
^~~~~~~~~~~~~~~~
timeval_to_jiffiescc1: some warnings being treated as errors
vim +213 net//ipv4/igmp.c
209
210 /* It must be called with locked im->lock */
211 static void igmp_start_timer(struct ip_mc_list *im, int max_delay)
212 {
> 213 ktime_t expiry = jiffies_to_ktime(prandom_u32() % max_delay + 2);
214
215 im->tm_running = 1;
216 alarm_start_relative(&im->alarm, expiry);
217 refcount_inc(&im->refcnt);
218 }
219
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Quoted 7 lines · Tejaswi Tanikella
On receiving a IGMPv2/v3 query, based on
max_delayset in the header a timer is started to send out a response after a random time withinmax_delay. If the system then moves into suspend state, Report is delayed until system wakes up.Use a alarmtimer instead of using a timer. Alarmtimer will wake the system up from suspend to send out the IGMP report.
Hi Tejaswi
I think i must be missing something here. If we are suspended, we are not receiving multicast frames. If we are not receiving frames, why do we need to reply to the query?
Once we resume, i expect we will reply to the next query. You could optimise restarting the flow by immediately sending a membership report, same as when the setsockopt is used to join the group.
Andrew
Re: [PATCH 2/2] ktime: helpers to convert between ktime and jiffies
In reply to Tejaswi TanikellaQuoted 10 lines · Tejaswi Tanikella
Signed-off-by Tejaswi Tanikella <[email protected]>--- include/linux/ktime.h | 4 ++++ 1 file changed, 4 insertions(+)include/linux/ktime.h +0
-0
include/linux/ktime.h static inline ktime_t timeval_to_ktime(struct timeval tv) · line 101
Quoted 6 lines
include/linux/ktime.h +3
-0@@ -96,6 +96,10 @@ static inline ktime_t timeval_to_ktime(struct timeval tv)/* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */ #define ktime_to_ns (kt) (kt) +/* ktime to jiffies and back */ +#define ktime_to_jiffies (kt) nsecs_to_jiffies(kt) +#define jiffies_to_ktime (j) jiffies_to_nsecs(j)
Hi Tejaswi
You should also add some users of these new helpers.
Andrew
Quoted 15 lines · Andrew Lunn
Quoted 7 lines · Tejaswi Tanikella
On receiving a IGMPv2/v3 query, based on
max_delayset in the header a timer is started to send out a response after a random time withinmax_delay. If the system then moves into suspend state, Report is delayed until system wakes up.Use a alarmtimer instead of using a timer. Alarmtimer will wake the system up from suspend to send out the IGMP report.
Hi Tejaswi
I think i must be missing something here. If we are suspended, we are not receiving multicast frames. If we are not receiving frames, why do we need to reply to the query?
Quoted 5 lines
Once we resume, i expect we will reply to the next query. You could optimise restarting the flow by immediately sending a membership report, same as when the setsockopt is used to join the group.
Andrew
Hi Andrew,
I'll try to explain my scenario. This was observed on a arm64 device. An application registers for a mcast group, and just listens to mcast packets. The connection is setup and mcast packets are being forwarded by the router. Multicast packets are sent out every few minutes. Not a very busy connection.
After some time the router sends out a IGMPv2 query. The max delay in the header is set to 10s. The system starts a timer to send out the response at 9s. But the device suspends and wakes up after 60s. The response is sent out 50s late.
ftrace logs with boottime trace_clock and my igmp_logs:
4740693 kworker/0:3-395 [000] ..s2 4716.425695: igmp_log: skbaddr=ffffffc156fe6600 daddr=224.0.0.1, id=0, rc=4295217721 4740694 kworker/0:3-395 [000] d.s4 4716.425717: timer_start:timer=ffffffc217763140 function=igmp_timer_expire expires=4295218678 [timeout=957] flags=0x00000000
timer set for 9.57 seconds. 957 jiffies
< device suspends >
< wakes up after ~60s >
4781289 <idle>-0 [000] .ns2 4779.170886: timer_expire_entry: timer=ffffffc217763140 function=igmp_timer_expire now=4295218678 4781290 <idle>-0 [000] .ns2 4779.171045: igmp_log: skbaddr=ffffffc1559d0e00 daddr=227.226.228.225, id=0, rc=4295218678
Since the response was delayed, mcast packets are not forwarded by the router.
My changes use a alarmtimer, this will wake the system up if the timer expires.
Thanks,
Tejaswi
Re: [PATCH 2/2] ktime: helpers to convert between ktime and jiffies
In reply to Andrew LunnQuoted 20 lines · Andrew Lunn
Quoted 10 lines · Tejaswi Tanikella
Signed-off-by Tejaswi Tanikella <[email protected]>--- include/linux/ktime.h | 4 ++++ 1 file changed, 4 insertions(+)include/linux/ktime.h +0
-0include/linux/ktime.h static inline ktime_t timeval_to_ktime(struct timeval tv) · line 101
Quoted 6 lines
include/linux/ktime.h +3
-0@@ -96,6 +96,10 @@ static inline ktime_t timeval_to_ktime(struct timeval tv)/* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */ #define ktime_to_ns (kt) (kt) +/* ktime to jiffies and back */ +#define ktime_to_jiffies (kt) nsecs_to_jiffies(kt) +#define jiffies_to_ktime (j) jiffies_to_nsecs(j)Hi Tejaswi
Quoted 3 lines
You should also add some users of these new helpers.
Andrew
Hi Andrew,
I used them in the first patch.
Thanks,
Tejaswi
Quoted 18 lines
I'll try to explain my scenario. This was observed on a arm64 device. An application registers for a mcast group, and just listens to mcast packets. The connection is setup and mcast packets are being forwarded by the router. Multicast packets are sent out every few minutes. Not a very busy connection.
After some time the router sends out a IGMPv2 query. The max delay in the header is set to 10s. The system starts a timer to send out the response at 9s. But the device suspends and wakes up after 60s.
The response is sent out 50s late.
ftrace logs with boottime
trace_clockand myigmp_logs:4740693 kworker/0:3-395 [000] ..s2 4716.425695: igmp_log: skbaddr=ffffffc156fe6600 daddr=224.0.0.1, id=0, rc=4295217721 4740694 kworker/0:3-395 [000] d.s4 4716.425717: timer_start:timer=ffffffc217763140 function=igmp_timer_expire expires=4295218678 [timeout=957] flags=0x00000000timer set for 9.57 seconds. 957 jiffies
Quoted 8 lines
< device suspends >
< wakes up after ~60s >
4781289 <idle>-0 [000] .ns2 4779.170886: timer_expire_entry: timer=ffffffc217763140 function=igmp_timer_expire now=4295218678 4781290 <idle>-0 [000] .ns2 4779.171045: igmp_log: skbaddr=ffffffc1559d0e00 daddr=227.226.228.225, id=0, rc=4295218678Since the response was delayed, mcast packets are not forwarded by the router.
While it has been asleep, it has also been dropping any multicast traffic in the stream. So it does not really matter it has left the group. You were not receiving the packets anyway.
Thing about this from another angle. I have an NTP client running on my laptop, using multicast address 224.0.1.1. I suspend my laptop and walk away for two hours. When i come back, i find that 20 seconds after i suspended it, it resumed and send an group response message. And an hour later, since it was still running, the battery went flat.
It seems to me, the change you are proposing cannot be the default behaviour.
I actually think you need to be looking at some sort of WoL feature. You need the multicast stream data packets to wake you, and you also need to wake up the IGMP query message. And you need to wake up to send the group membership. Does your hardware have this sort of WoL support? You can then explicitly enable this WoL for your application.
Andrew
Re: [PATCH 2/2] ktime: helpers to convert between ktime and jiffies
In reply to Tejaswi TanikellaQuoted 21 lines · Tejaswi Tanikella
Quoted 20 lines · Andrew Lunn
Quoted 10 lines · Tejaswi Tanikella
Signed-off-by Tejaswi Tanikella <[email protected]>--- include/linux/ktime.h | 4 ++++ 1 file changed, 4 insertions(+)include/linux/ktime.h +0
-0include/linux/ktime.h static inline ktime_t timeval_to_ktime(struct timeval tv) · line 101
Quoted 6 lines
include/linux/ktime.h +3
-0@@ -96,6 +96,10 @@ static inline ktime_t timeval_to_ktime(struct timeval tv)/* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */ #define ktime_to_ns (kt) (kt) +/* ktime to jiffies and back */ +#define ktime_to_jiffies (kt) nsecs_to_jiffies(kt) +#define jiffies_to_ktime (j) jiffies_to_nsecs(j)Hi Tejaswi
Quoted 7 lines
Quoted 3 lines
You should also add some users of these new helpers.
Andrew
Hi Andrew,
I used them in the first patch.
Therefore you must swap the order of the patches in order not to break bisectability (that is, each commit must build on its own).
Thanks!--
Florian
Quoted 14 lines · Andrew Lunn
While it has been asleep, it has also been dropping any multicast traffic in the stream. So it does not really matter it has left the group. You were not receiving the packets anyway.
Thing about this from another angle. I have an NTP client running on my laptop, using multicast address 224.0.1.1. I suspend my laptop and walk away for two hours. When i come back, i find that 20 seconds after i suspended it, it resumed and send an group response message. And an hour later, since it was still running, the battery went flat.
It seems to me, the change you are proposing cannot be the default behaviour.
Quoted 7 lines
I actually think you need to be looking at some sort of WoL feature. You need the multicast stream data packets to wake you, and you also need to wake up the IGMP query message. And you need to wake up to send the group membership. Does your hardware have this sort of WoL support? You can then explicitly enable this WoL for your application.
Andrew
Thanks Andrew.
You are right, this should not be the default behaviour.
-Tejaswi