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 5c766d642b
"ipv4: introduce address lifetime"
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
05a324b9c5
Коммит
c988d1e8cb
|
@ -587,13 +587,16 @@ static void check_lifetime(struct work_struct *work)
|
||||||
{
|
{
|
||||||
unsigned long now, next, next_sec, next_sched;
|
unsigned long now, next, next_sec, next_sched;
|
||||||
struct in_ifaddr *ifa;
|
struct in_ifaddr *ifa;
|
||||||
|
struct hlist_node *n;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
now = jiffies;
|
now = jiffies;
|
||||||
next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
|
next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
for (i = 0; i < IN4_ADDR_HSIZE; i++) {
|
for (i = 0; i < IN4_ADDR_HSIZE; i++) {
|
||||||
|
bool change_needed = false;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
|
hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
|
||||||
unsigned long age;
|
unsigned long age;
|
||||||
|
|
||||||
|
@ -606,16 +609,7 @@ static void check_lifetime(struct work_struct *work)
|
||||||
|
|
||||||
if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
|
if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
|
||||||
age >= ifa->ifa_valid_lft) {
|
age >= ifa->ifa_valid_lft) {
|
||||||
struct in_ifaddr **ifap ;
|
change_needed = true;
|
||||||
|
|
||||||
rtnl_lock();
|
|
||||||
for (ifap = &ifa->ifa_dev->ifa_list;
|
|
||||||
*ifap != NULL; ifap = &ifa->ifa_next) {
|
|
||||||
if (*ifap == ifa)
|
|
||||||
inet_del_ifa(ifa->ifa_dev,
|
|
||||||
ifap, 1);
|
|
||||||
}
|
|
||||||
rtnl_unlock();
|
|
||||||
} else if (ifa->ifa_preferred_lft ==
|
} else if (ifa->ifa_preferred_lft ==
|
||||||
INFINITY_LIFE_TIME) {
|
INFINITY_LIFE_TIME) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -625,10 +619,8 @@ static void check_lifetime(struct work_struct *work)
|
||||||
next = ifa->ifa_tstamp +
|
next = ifa->ifa_tstamp +
|
||||||
ifa->ifa_valid_lft * HZ;
|
ifa->ifa_valid_lft * HZ;
|
||||||
|
|
||||||
if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) {
|
if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
|
||||||
ifa->ifa_flags |= IFA_F_DEPRECATED;
|
change_needed = true;
|
||||||
rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
|
|
||||||
}
|
|
||||||
} else if (time_before(ifa->ifa_tstamp +
|
} else if (time_before(ifa->ifa_tstamp +
|
||||||
ifa->ifa_preferred_lft * HZ,
|
ifa->ifa_preferred_lft * HZ,
|
||||||
next)) {
|
next)) {
|
||||||
|
@ -636,8 +628,42 @@ static void check_lifetime(struct work_struct *work)
|
||||||
ifa->ifa_preferred_lft * HZ;
|
ifa->ifa_preferred_lft * HZ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (!change_needed)
|
||||||
|
continue;
|
||||||
|
rtnl_lock();
|
||||||
|
hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
|
||||||
|
unsigned long age;
|
||||||
|
|
||||||
|
if (ifa->ifa_flags & IFA_F_PERMANENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* We try to batch several events at once. */
|
||||||
|
age = (now - ifa->ifa_tstamp +
|
||||||
|
ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
|
||||||
|
|
||||||
|
if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
|
||||||
|
age >= ifa->ifa_valid_lft) {
|
||||||
|
struct in_ifaddr **ifap;
|
||||||
|
|
||||||
|
for (ifap = &ifa->ifa_dev->ifa_list;
|
||||||
|
*ifap != NULL; ifap = &(*ifap)->ifa_next) {
|
||||||
|
if (*ifap == ifa) {
|
||||||
|
inet_del_ifa(ifa->ifa_dev,
|
||||||
|
ifap, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ifa->ifa_preferred_lft !=
|
||||||
|
INFINITY_LIFE_TIME &&
|
||||||
|
age >= ifa->ifa_preferred_lft &&
|
||||||
|
!(ifa->ifa_flags & IFA_F_DEPRECATED)) {
|
||||||
|
ifa->ifa_flags |= IFA_F_DEPRECATED;
|
||||||
|
rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rtnl_unlock();
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
next_sec = round_jiffies_up(next);
|
next_sec = round_jiffies_up(next);
|
||||||
next_sched = next;
|
next_sched = next;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче