Similarly to the issue from the previous patch, neigh_timer_handler() also updates the neighbor separately from formatting and sending the netlink notification message. We have not seen reports to the effect of this causing trouble, but in theory, the same sort of issues could have come up: neigh_timer_handler() would make changes as necessary, but before formatting and sending a notification, is interrupted before sending by another thread, which makes a parallel change and sends its own message. The message send that is prompted by an earlier change thus contains information that does not reflect the change having been made. To solve this, the netlink notification needs to be in the same critical section that updates the neighbor. The critical section is ended by the neigh_probe() call which drops the lock before calling solicit. Stretching the critical section over the solicit call is problematic, because that can then involved all sorts of forwarding callbacks. Therefore, like in the previous patch, split the netlink notification away from the internal one and move it ahead of the probe call. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel --- Notes: v2: - Do not skip the notification from inside the atomic_read(&neigh->probes) >= neigh_max_probes(neigh) conditional. Instead set a flag, and goto out after the notification if the flag is set. - Move the __neigh_notify() call another block up above the NUD_IN_TIMER check. That belongs logically together with the (NUD_INCOMPLETE | NUD_PROBE) check afterwards, no sense to split the two conditionals with the notifier. net/core/neighbour.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 1d7489f50b21..e0897eb41c8d 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1104,6 +1104,7 @@ static void neigh_timer_handler(struct timer_list *t) { unsigned long now, next; struct neighbour *neigh = timer_container_of(neigh, t, timer); + bool skip_probe = false; unsigned int state; int notify = 0; @@ -1171,9 +1172,15 @@ static void neigh_timer_handler(struct timer_list *t) neigh_invalidate(neigh); } notify = 1; - goto out; + skip_probe = true; } + if (notify) + __neigh_notify(neigh, RTM_NEWNEIGH, 0, 0); + + if (skip_probe) + goto out; + if (neigh->nud_state & NUD_IN_TIMER) { if (time_before(next, jiffies + HZ/100)) next = jiffies + HZ/100; @@ -1187,10 +1194,8 @@ static void neigh_timer_handler(struct timer_list *t) write_unlock(&neigh->lock); } - if (notify) { - neigh_notify(neigh, RTM_NEWNEIGH, 0, 0); + if (notify) call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); - } trace_neigh_timer_handler(neigh, 0); -- 2.51.1