From 81b0afaa64e2eb29d91964ff6d0eed0028491010 Mon Sep 17 00:00:00 2001 From: thorpej Date: Sat, 18 Apr 2020 15:56:26 +0000 Subject: [PATCH] In _if_down(), release the link state change lock before calling workqueue_wait(). Add a comment explaining how the locking here works. PR kern/55018. --- sys/net/if.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/sys/net/if.c b/sys/net/if.c index d4d132fca80f..8e9e6d9f8e1d 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1,4 +1,4 @@ -/* $NetBSD: if.c,v 1.473 2020/02/21 00:26:23 joerg Exp $ */ +/* $NetBSD: if.c,v 1.474 2020/04/18 15:56:26 thorpej Exp $ */ /*- * Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc. @@ -90,7 +90,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.473 2020/02/21 00:26:23 joerg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.474 2020/04/18 15:56:26 thorpej Exp $"); #if defined(_KERNEL_OPT) #include "opt_inet.h" @@ -2519,11 +2519,22 @@ _if_down(struct ifnet *ifp) pserialize_read_exit(s); curlwp_bindx(bound); + /* + * Modification of if_link_cansched is serialized with the + * ifnet ioctl lock. + * + * The link state change lock is taken to synchronize with the + * read in if_link_state_change_work_schedule(). Once we set + * this to false, our if_link_work won't be scheduled. But + * we need to wait for our if_link_work to drain in case we + * lost that race. + */ IF_LINK_STATE_CHANGE_LOCK(ifp); ifp->if_link_cansched = false; - workqueue_wait(ifnet_link_state_wq, &ifp->if_link_work); IF_LINK_STATE_CHANGE_UNLOCK(ifp); + workqueue_wait(ifnet_link_state_wq, &ifp->if_link_work); + IFQ_PURGE(&ifp->if_snd); #if NCARP > 0 if (ifp->if_carp)