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.
This commit is contained in:
thorpej 2020-04-18 15:56:26 +00:00
parent 39b0cf9180
commit 81b0afaa64
1 changed files with 14 additions and 3 deletions

View File

@ -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 <sys/cdefs.h>
__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)