Skip to content

Commit c988d1e

Browse files
jpirkodavem330
authored andcommitted
net: ipv4: fix schedule while atomic bug in check_lifetime()
move might_sleep operations out of the rcu_read_lock() section. Also fix iterating over ifa_dev->ifa_list Introduced by: commit 5c766d6 "ipv4: introduce address lifetime" Signed-off-by: Jiri Pirko <jiri@resnulli.us> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 05a324b commit c988d1e

File tree

1 file changed

+42
-16
lines changed

1 file changed

+42
-16
lines changed

net/ipv4/devinet.c

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -587,13 +587,16 @@ static void check_lifetime(struct work_struct *work)
587587
{
588588
unsigned long now, next, next_sec, next_sched;
589589
struct in_ifaddr *ifa;
590+
struct hlist_node *n;
590591
int i;
591592

592593
now = jiffies;
593594
next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
594595

595-
rcu_read_lock();
596596
for (i = 0; i < IN4_ADDR_HSIZE; i++) {
597+
bool change_needed = false;
598+
599+
rcu_read_lock();
597600
hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
598601
unsigned long age;
599602

@@ -606,16 +609,7 @@ static void check_lifetime(struct work_struct *work)
606609

607610
if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
608611
age >= ifa->ifa_valid_lft) {
609-
struct in_ifaddr **ifap ;
610-
611-
rtnl_lock();
612-
for (ifap = &ifa->ifa_dev->ifa_list;
613-
*ifap != NULL; ifap = &ifa->ifa_next) {
614-
if (*ifap == ifa)
615-
inet_del_ifa(ifa->ifa_dev,
616-
ifap, 1);
617-
}
618-
rtnl_unlock();
612+
change_needed = true;
619613
} else if (ifa->ifa_preferred_lft ==
620614
INFINITY_LIFE_TIME) {
621615
continue;
@@ -625,19 +619,51 @@ static void check_lifetime(struct work_struct *work)
625619
next = ifa->ifa_tstamp +
626620
ifa->ifa_valid_lft * HZ;
627621

628-
if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) {
629-
ifa->ifa_flags |= IFA_F_DEPRECATED;
630-
rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
631-
}
622+
if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
623+
change_needed = true;
632624
} else if (time_before(ifa->ifa_tstamp +
633625
ifa->ifa_preferred_lft * HZ,
634626
next)) {
635627
next = ifa->ifa_tstamp +
636628
ifa->ifa_preferred_lft * HZ;
637629
}
638630
}
631+
rcu_read_unlock();
632+
if (!change_needed)
633+
continue;
634+
rtnl_lock();
635+
hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
636+
unsigned long age;
637+
638+
if (ifa->ifa_flags & IFA_F_PERMANENT)
639+
continue;
640+
641+
/* We try to batch several events at once. */
642+
age = (now - ifa->ifa_tstamp +
643+
ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
644+
645+
if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
646+
age >= ifa->ifa_valid_lft) {
647+
struct in_ifaddr **ifap;
648+
649+
for (ifap = &ifa->ifa_dev->ifa_list;
650+
*ifap != NULL; ifap = &(*ifap)->ifa_next) {
651+
if (*ifap == ifa) {
652+
inet_del_ifa(ifa->ifa_dev,
653+
ifap, 1);
654+
break;
655+
}
656+
}
657+
} else if (ifa->ifa_preferred_lft !=
658+
INFINITY_LIFE_TIME &&
659+
age >= ifa->ifa_preferred_lft &&
660+
!(ifa->ifa_flags & IFA_F_DEPRECATED)) {
661+
ifa->ifa_flags |= IFA_F_DEPRECATED;
662+
rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
663+
}
664+
}
665+
rtnl_unlock();
639666
}
640-
rcu_read_unlock();
641667

642668
next_sec = round_jiffies_up(next);
643669
next_sched = next;

0 commit comments

Comments
 (0)