NetBSD Problem Report #58869

From www@netbsd.org  Tue Dec  3 14:39:03 2024
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
	 client-signature RSA-PSS (2048 bits) client-digest SHA256)
	(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 1642E1A9238
	for <gnats-bugs@gnats.NetBSD.org>; Tue,  3 Dec 2024 14:39:03 +0000 (UTC)
Message-Id: <20241203143901.EECF11A9246@mollari.NetBSD.org>
Date: Tue,  3 Dec 2024 14:39:01 +0000 (UTC)
From: campbell+netbsd@mumble.net
Reply-To: campbell+netbsd@mumble.net
To: gnats-bugs@NetBSD.org
Subject: ipmi(4) holds sc_cmd_mtx across copyout, needed for wdog tickle
X-Send-Pr-Version: www-1.0

>Number:         58869
>Category:       kern
>Synopsis:       ipmi(4) holds sc_cmd_mtx across copyout, needed for wdog tickle
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Dec 03 14:40:00 +0000 2024
>Last-Modified:  Wed Dec 04 15:30:06 +0000 2024
>Originator:     Taylor R Campbell
>Release:        current, 10, 9, ...
>Organization:
The IpmiWdog Foundation
>Environment:
>Description:
The mutex struct ipmi_softc::sc_cmd_mtx is needed by the watchdog tickle path, so it should not be held across arbitrary sleeps.

But it is held across malloc/copyin/copyout, which can sleep arbitrarily long for paging.
>How-To-Repeat:
code inspection, using ipmi watchdog on heavily loaded system with swapping
>Fix:
Narrow the scope of sc_cmd_mtx in ipmi_ioctl to ipmi_sendcmd/recvcmd, not malloc/copyin/copyout.

>Audit-Trail:
From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Cc: 
Subject: Re: kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for wdog tickle
Date: Tue, 3 Dec 2024 14:51:38 +0000

 > Fix:
 > Narrow the scope of sc_cmd_mtx in ipmi_ioctl to
 > ipmi_sendcmd/recvcmd, not malloc/copyin/copyout.

 Oops -- it's not quite that simple because there's also a longer-term
 lock, sc_mode.  Need to work around that too.

 This is a bit troublesome because in the /dev/ipmi0 ioctl interface,
 when userland invokes ioctl(IPMICTL_SEND_COMMAND) it blocks watchdog
 tickles until userland later invokes ioctl(IPMICTL_RECEIVE_MSG).

 It seems to me either

 (a) there should just be a combined ioctl for send/recv (but I have no
     idea how widespread this ioctl interface is, maybe it's used by
     important applications like sysutils/ipmitool so we can't just
     remove IPMICTL_SEND_COMMAND and IPMICTL_RECEIVE_MSG); or

 (b) IPMICTL_SEND_COMMAND should do ipmi_recvmsg and save it in a
     buffer for the next IPMICTL_RECEIVE_MSG; or

 (c) if ipmi_recvmsg might take longer than IPMICTL_SEND_COMMAND is
     supposed to wait, it should be deferred to an asynchronous thread
     so the watchdog tickle is only held up by the ipmi(4)
     responsiveness and not by arbitrary userland dawdling.

From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Cc: Michael van Elst <mlelstv@NetBSD.org>,
	Edgar =?iso-8859-1?B?RnXf?= <ef@math.uni-bonn.de>,
	Hauke Fath <hf@spg.tu-darmstadt.de>
Subject: Re: kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for wdog tickle
Date: Tue, 3 Dec 2024 17:52:25 +0000

 This is a multi-part message in MIME format.
 --=_w7ACFWG5hulsG3bhLmupmr3o79Ol6y3k

 The attached patch series attempts to address this as proposed (with
 some other cleanups along the way).

 Also attached is a single giant diff with all the changes combined.

 (I don't have time to test this myself so I don't plan to commit it
 at the moment unless someone else wants to test it.)

 This is not likely to affect Edgar's get_sdr_partial error at boot.
 What it does prevent, if it works, is spurious watchdog reset because
 tickling has been held up, or delayed envstat(8) updates, while an
 ipmitool process is swapping in or out.

 --=_w7ACFWG5hulsG3bhLmupmr3o79Ol6y3k
 Content-Type: text/plain; charset="ISO-8859-1"; name="pr58869-ipmilocking"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="pr58869-ipmilocking.patch"

 # HG changeset patch
 # User Taylor R Campbell <riastradh@NetBSD.org>
 # Date 1733238903 0
 #      Tue Dec 03 15:15:03 2024 +0000
 # Branch trunk
 # Node ID a9fd9dbfe4d7587b99b818176e451b7950052e64
 # Parent  eab5d84e73c8103a6990693dd75b2cdba777472b
 # EXP-Topic riastradh-pr56568-ipmidelay
 ipmi(4): Omit needless condvar/mutex for sleep.

 Just use kpause instead.

 Cleanup in preparation for:

 PR kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for
 wdog tickle

 diff --git a/sys/dev/ipmi.c b/sys/dev/ipmi.c
 --- a/sys/dev/ipmi.c
 +++ b/sys/dev/ipmi.c
 @@ -355,9 +355,7 @@ bmc_io_wait_sleep(struct ipmi_softc *sc,
  		v =3D bmc_read(sc, offset);
  		if ((v & mask) =3D=3D value)
  			return v;
 -		mutex_enter(&sc->sc_sleep_mtx);
 -		cv_timedwait(&sc->sc_cmd_sleep, &sc->sc_sleep_mtx, 1);
 -		mutex_exit(&sc->sc_sleep_mtx);
 +		kpause("ipmicmd", /*intr*/false, /*timo*/1, /*mtx*/NULL);
  	}
  	return -1;
  }
 @@ -1096,9 +1094,7 @@ ipmi_delay(struct ipmi_softc *sc, int ms
  		delay(ms * 1000);
  		return;
  	}
 -	mutex_enter(&sc->sc_sleep_mtx);
 -	cv_timedwait(&sc->sc_cmd_sleep, &sc->sc_sleep_mtx, mstohz(ms));
 -	mutex_exit(&sc->sc_sleep_mtx);
 +	kpause("ipmicmd", /*intr*/false, /*timo*/mstohz(ms), /*mtx*/NULL);
  }
 =20
  /* Read a partial SDR entry */
 @@ -1967,12 +1963,10 @@ ipmi_match(device_t parent, cfdata_t cf,
  	sc.sc_if->probe(&sc);
 =20
  	mutex_init(&sc.sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 -	cv_init(&sc.sc_cmd_sleep, "ipmimtch");
 =20
  	if (ipmi_get_device_id(&sc, NULL) =3D=3D 0)
  		rv =3D 1;
 =20
 -	cv_destroy(&sc.sc_cmd_sleep);
  	mutex_destroy(&sc.sc_cmd_mtx);
  	ipmi_unmap_regs(&sc);
 =20
 @@ -2150,8 +2144,6 @@ ipmi_attach(device_t parent, device_t se
 =20
  	/* lock around read_sensor so that no one messes with the bmc regs */
  	mutex_init(&sc->sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 -	mutex_init(&sc->sc_sleep_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 -	cv_init(&sc->sc_cmd_sleep, "ipmicmd");
 =20
  	mutex_init(&sc->sc_poll_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
  	cv_init(&sc->sc_poll_cv, "ipmipoll");
 @@ -2215,8 +2207,6 @@ ipmi_detach(device_t self, int flags)
  	cv_destroy(&sc->sc_mode_cv);
  	cv_destroy(&sc->sc_poll_cv);
  	mutex_destroy(&sc->sc_poll_mtx);
 -	cv_destroy(&sc->sc_cmd_sleep);
 -	mutex_destroy(&sc->sc_sleep_mtx);
  	mutex_destroy(&sc->sc_cmd_mtx);
 =20
  	return 0;
 diff --git a/sys/dev/ipmivar.h b/sys/dev/ipmivar.h
 --- a/sys/dev/ipmivar.h
 +++ b/sys/dev/ipmivar.h
 @@ -95,8 +95,6 @@ struct ipmi_softc {
  	kcondvar_t		sc_mode_cv;
 =20
  	kmutex_t		sc_cmd_mtx;
 -	kmutex_t		sc_sleep_mtx;
 -	kcondvar_t		sc_cmd_sleep;
 =20
  	struct ipmi_bmc_args	*sc_iowait_args;
 =20
 # HG changeset patch
 # User Taylor R Campbell <riastradh@NetBSD.org>
 # Date 1733239308 0
 #      Tue Dec 03 15:21:48 2024 +0000
 # Branch trunk
 # Node ID acc525c4da4e26d67e193c33d922799554fed543
 # Parent  a9fd9dbfe4d7587b99b818176e451b7950052e64
 # EXP-Topic riastradh-pr56568-ipmidelay
 sys/ipmi.h: Tidy header file.

 Need <sys/ioccom.h> for _IOWR/_IOW/_IOR.  Nix trailing whitespace.

 Cleanup in preparation for:

 PR kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for
 wdog tickle

 diff --git a/sys/sys/ipmi.h b/sys/sys/ipmi.h
 --- a/sys/sys/ipmi.h
 +++ b/sys/sys/ipmi.h
 @@ -32,6 +32,8 @@
  #ifndef _SYS_IPMI_H_
  #define	_SYS_IPMI_H_
 =20
 +#include <sys/ioccom.h>
 +
  #define	IPMI_MAX_ADDR_SIZE		0x20
  #define IPMI_MAX_RX			1024
  #define	IPMI_BMC_SLAVE_ADDR		0x20
 @@ -99,7 +101,7 @@ struct ipmi_ipmb_addr {
  #define IPMICTL_SET_GETS_EVENTS_CMD     _IOW('i', 16, int)
  #define IPMICTL_SET_MY_ADDRESS_CMD      _IOW('i', 17, unsigned int)
  #define IPMICTL_GET_MY_ADDRESS_CMD      _IOR('i', 18, unsigned int)
 -#define IPMICTL_SET_MY_LUN_CMD          _IOW('i', 19, unsigned int)=20
 +#define IPMICTL_SET_MY_LUN_CMD          _IOW('i', 19, unsigned int)
  #define IPMICTL_GET_MY_LUN_CMD          _IOR('i', 20, unsigned int)
 =20
  #endif /* !_SYS_IPMI_H_ */
 # HG changeset patch
 # User Taylor R Campbell <riastradh@NetBSD.org>
 # Date 1733239370 0
 #      Tue Dec 03 15:22:50 2024 +0000
 # Branch trunk
 # Node ID 4393453be9263ecde5a6fa6ac64c76b1bfc5490e
 # Parent  acc525c4da4e26d67e193c33d922799554fed543
 # EXP-Topic riastradh-pr56568-ipmidelay
 ipmivar.h: Tidy includes and use a named constant, not a comment.

 Cleanup in preparation for:

 PR kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for
 wdog tickle

 diff --git a/sys/dev/ipmivar.h b/sys/dev/ipmivar.h
 --- a/sys/dev/ipmivar.h
 +++ b/sys/dev/ipmivar.h
 @@ -24,17 +24,20 @@
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY W=
 AY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
 - *
   */
 =20
 -#include <sys/mutex.h>
 -#include <sys/condvar.h>
 -
 -#include <dev/sysmon/sysmonvar.h>
 -
  #ifndef _IPMIVAR_H_
  #define _IPMIVAR_H_
 =20
 +#include <sys/types.h>
 +
 +#include <sys/bus.h>
 +#include <sys/condvar.h>
 +#include <sys/ipmi.h>
 +#include <sys/mutex.h>
 +
 +#include <dev/sysmon/sysmonvar.h>
 +
  #define IPMI_IF_KCS		1
  #define IPMI_IF_SMIC		2
  #define IPMI_IF_BT		3
 @@ -107,7 +110,7 @@ struct ipmi_softc {
  	envsys_data_t		*sc_sensor;
  	int 		sc_nsensors; /* total number of sensors */
 =20
 -	char		sc_buf[1024 + 3]; /* IPMI_MAX_RX + 3 */
 +	char		sc_buf[IPMI_MAX_RX + 3];
  	bool		sc_buf_rsvd;
 =20
  	/* request busy */
 # HG changeset patch
 # User Taylor R Campbell <riastradh@NetBSD.org>
 # Date 1733242069 0
 #      Tue Dec 03 16:07:49 2024 +0000
 # Branch trunk
 # Node ID 9e9ab94042dd69884b43d2a67b12adf8013f7972
 # Parent  4393453be9263ecde5a6fa6ac64c76b1bfc5490e
 # EXP-Topic riastradh-pr56568-ipmidelay
 ipmi(4): Nix trailing whitespace.

 No functional change intended.

 diff --git a/sys/dev/ipmi.c b/sys/dev/ipmi.c
 --- a/sys/dev/ipmi.c
 +++ b/sys/dev/ipmi.c
 @@ -1298,7 +1298,7 @@ static int64_t fixexp_a[] =3D {
  	0x0000000080000000ll /* 1.0/2.0 */,
  	0x000000002aaaaaabll /* 1.0/6.0 */,
  	0x000000000aaaaaabll /* 1.0/24.0 */,
 -	0x0000000002222222ll /* 1.0/120.0 */,=20
 +	0x0000000002222222ll /* 1.0/120.0 */,
  	0x00000000005b05b0ll /* 1.0/720.0 */,
  	0x00000000000d00d0ll /* 1.0/5040.0 */,
  	0x000000000001a01all /* 1.0/40320.0 */
 @@ -1316,7 +1316,7 @@ fixmul(int64_t x, int64_t y)
  		x =3D -x;
  		neg =3D !neg;
  	}
 -	if (y < 0) {=20
 +	if (y < 0) {
  		y =3D -y;
  		neg =3D !neg;
  	}
 @@ -1803,7 +1803,7 @@ add_child_sensors(struct ipmi_softc *sc,
  	char			*e;
  	struct ipmi_sensor	*psensor;
  	struct sdrtype1		*s1 =3D (struct sdrtype1 *)psdr;
 -=09
 +
  	typ =3D ipmi_sensor_type(sensor_type, ext_type, entity);
  	if (typ =3D=3D -1) {
  		dbg_printf(5, "Unknown sensor type:%#.2x et:%#.2x sn:%#.2x "
 @@ -2509,7 +2509,7 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
  		error =3D EOPNOTSUPP;
  		break;
  	default:
 -		error =3D ENODEV;=09
 +		error =3D ENODEV;
  		break;
  	}
 =20
 # HG changeset patch
 # User Taylor R Campbell <riastradh@NetBSD.org>
 # Date 1733245714 0
 #      Tue Dec 03 17:08:34 2024 +0000
 # Branch trunk
 # Node ID 702bc3da2819f628a6d103b38eaa0e3cbfa40593
 # Parent  9e9ab94042dd69884b43d2a67b12adf8013f7972
 # EXP-Topic riastradh-pr56568-ipmidelay
 ipmi(4): Avoid conflict between ioctl and watchdog/sensor thread.

 1. On ioctl IPMICTL_SEND_COMMAND, immediately read a message into a
    user buffer that will persist until IPMICTL_RECEIVE_MSG.  (But
    wait for any concurrent IPMICTL_RECEIVE_MSG to finish first so the
    buffer is available.)

 2. On ioctl IPMICTL_RECEIVE_MSG or IPMICTL_RECEIVE_MSG_TRUNC, mark
    the user buffer busy and read out of it with copyout -- without
    holding a mutex.

 This way, the watchdog/sensor thread can issue commands while
 IPMICTL_SEND_COMMAND and IPMICTL_RECEIVE_MSG are waiting for
 copyin/copyout or malloc -- which may be indefinite waits I/O to swap
 over the network, for example -- so it doesn't block tickling the
 watchdog or updating sensors for envstat.

 PR kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for
 wdog tickle

 diff --git a/sys/dev/ipmi.c b/sys/dev/ipmi.c
 --- a/sys/dev/ipmi.c
 +++ b/sys/dev/ipmi.c
 @@ -986,6 +986,8 @@ ipmi_sendcmd(struct ipmi_softc *sc, int=20
  	uint8_t	*buf;
  	int		rc =3D -1;
 =20
 +	KASSERT(mutex_owned(&sc->sc_cmd_mtx));
 +
  	dbg_printf(50, "%s: rssa=3D%#.2x nfln=3D%#.2x cmd=3D%#.2x len=3D%#.2x\n",
  	    __func__, rssa, NETFN_LUN(netfn, rslun), cmd, txlen);
  	dbg_dump(10, __func__, txlen, data);
 @@ -1054,6 +1056,8 @@ ipmi_recvcmd(struct ipmi_softc *sc, int=20
  	uint8_t	*buf, rc =3D 0;
  	int		rawlen;
 =20
 +	KASSERT(mutex_owned(&sc->sc_cmd_mtx));
 +
  	/* Need three extra bytes: netfn/cmd/ccode + data */
  	buf =3D ipmi_buf_acquire(sc, maxlen + 3);
  	if (buf =3D=3D NULL) {
 @@ -1090,6 +1094,9 @@ ipmi_recvcmd(struct ipmi_softc *sc, int=20
  static void
  ipmi_delay(struct ipmi_softc *sc, int ms)
  {
 +
 +	KASSERT(mutex_owned(&sc->sc_cmd_mtx));
 +
  	if (cold) {
  		delay(ms * 1000);
  		return;
 @@ -2110,21 +2117,12 @@ ipmi_thread(void *cookie)
  	ipmi_enabled =3D 1;
 =20
  	mutex_enter(&sc->sc_poll_mtx);
 -	sc->sc_thread_ready =3D true;
 -	cv_broadcast(&sc->sc_mode_cv);
  	while (sc->sc_thread_running) {
 -		while (sc->sc_mode =3D=3D IPMI_MODE_COMMAND)
 -			cv_wait(&sc->sc_mode_cv, &sc->sc_poll_mtx);
 -		sc->sc_mode =3D IPMI_MODE_ENVSYS;
 -
  		if (sc->sc_tickle_due) {
  			ipmi_dotickle(sc);
  			sc->sc_tickle_due =3D false;
  		}
  		ipmi_refresh_sensors(sc);
 -
 -		sc->sc_mode =3D IPMI_MODE_IDLE;
 -		cv_broadcast(&sc->sc_mode_cv);
  		cv_timedwait(&sc->sc_poll_cv, &sc->sc_poll_mtx,
  		    SENSOR_REFRESH_RATE);
  	}
 @@ -2144,10 +2142,10 @@ ipmi_attach(device_t parent, device_t se
 =20
  	/* lock around read_sensor so that no one messes with the bmc regs */
  	mutex_init(&sc->sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 +	cv_init(&sc->sc_userbuf_cv, "ipmiuser");
 =20
  	mutex_init(&sc->sc_poll_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
  	cv_init(&sc->sc_poll_cv, "ipmipoll");
 -	cv_init(&sc->sc_mode_cv, "ipmimode");
 =20
  	if (kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL, ipmi_thread, self,
  	    &sc->sc_kthread, "%s", device_xname(self)) !=3D 0) {
 @@ -2204,9 +2202,9 @@ ipmi_detach(device_t self, int flags)
 =20
  	ipmi_unmap_regs(sc);
 =20
 -	cv_destroy(&sc->sc_mode_cv);
  	cv_destroy(&sc->sc_poll_cv);
  	mutex_destroy(&sc->sc_poll_mtx);
 +	cv_destroy(&sc->sc_userbuf_cv);
  	mutex_destroy(&sc->sc_cmd_mtx);
 =20
  	return 0;
 @@ -2255,15 +2253,6 @@ ipmi_watchdog_setmode(struct sysmon_wdog
  	else
  		sc->sc_wdog.smw_period =3D smwdog->smw_period;
 =20
 -	/* Wait until the device is initialized */
 -	rc =3D 0;
 -	mutex_enter(&sc->sc_poll_mtx);
 -	while (sc->sc_thread_ready)
 -		rc =3D cv_wait_sig(&sc->sc_mode_cv, &sc->sc_poll_mtx);
 -	mutex_exit(&sc->sc_poll_mtx);
 -	if (rc)
 -		return rc;
 -
  	mutex_enter(&sc->sc_cmd_mtx);
  	/* see if we can properly task to the watchdog */
  	rc =3D ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
 @@ -2362,12 +2351,12 @@ ipmi_close(dev_t dev, int flag, int fmt,
  	if ((sc =3D device_lookup_private(&ipmi_cd, unit)) =3D=3D NULL)
  		return (ENXIO);
 =20
 -	mutex_enter(&sc->sc_poll_mtx);
 -	if (sc->sc_mode =3D=3D IPMI_MODE_COMMAND) {
 -		sc->sc_mode =3D IPMI_MODE_IDLE;
 -		cv_broadcast(&sc->sc_mode_cv);
 -	}
 -	mutex_exit(&sc->sc_poll_mtx);
 +	mutex_enter(&sc->sc_cmd_mtx);
 +	KASSERT(!sc->sc_userbuf_busy);
 +	memset(sc->sc_userbuf, 0, sizeof(sc->sc_userbuf)); /* paranoia */
 +	sc->sc_userbuf_len =3D 0;
 +	mutex_exit(&sc->sc_cmd_mtx);
 +
  	return 0;
  }
 =20
 @@ -2387,62 +2376,68 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
 =20
  	switch (cmd) {
  	case IPMICTL_SEND_COMMAND:
 -		mutex_enter(&sc->sc_poll_mtx);
 -		while (sc->sc_mode =3D=3D IPMI_MODE_ENVSYS) {
 -			error =3D cv_wait_sig(&sc->sc_mode_cv, &sc->sc_poll_mtx);
 -			if (error =3D=3D EINTR) {
 -				mutex_exit(&sc->sc_poll_mtx);
 -				return error;
 -			}
 -		}
 -		sc->sc_mode =3D IPMI_MODE_COMMAND;
 -		mutex_exit(&sc->sc_poll_mtx);
 -		break;
 -	}
 -
 -	mutex_enter(&sc->sc_cmd_mtx);
 -
 -	switch (cmd) {
 -	case IPMICTL_SEND_COMMAND:
  		req =3D data;
 -		buf =3D malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK);
 -
  		len =3D req->msg.data_len;
  		if (len < 0 || len > IPMI_MAX_RX) {
  			error =3D EINVAL;
  			break;
  		}
 =20
 -		/* clear pending result */
 -		if (sc->sc_sent)
 -			(void)ipmi_recvcmd(sc, IPMI_MAX_RX, &len, buf);
 -
  		/* XXX */
  		error =3D copyin(req->addr, &addr, sizeof(addr));
  		if (error)
  			break;
 =20
 +		buf =3D malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK);
  		error =3D copyin(req->msg.data, buf, len);
  		if (error)
  			break;
 =20
 -		/* save for receive */
 +		/*
 +		 * Acquire the lock to serialize ipmi_sendcmd/recvcmd
 +		 * and access to the saved command response state.
 +		 */
 +		mutex_enter(&sc->sc_cmd_mtx);
 +
 +		/*
 +		 * Wait for any concurrent copyouts of the user buffer
 +		 * in IPMICTL_RECEIVE_MSG to complete before we can
 +		 * reuse the user buffer.
 +		 */
 +		while (sc->sc_userbuf_busy) {
 +			error =3D cv_wait_sig(&sc->sc_userbuf_cv,
 +			    &sc->sc_cmd_mtx);
 +			if (error)
 +				break;
 +		}
 +		if (error) {
 +			mutex_exit(&sc->sc_cmd_mtx);
 +			break;
 +		}
 +
 +		/*
 +		 * Send the command and receive the response into the
 +		 * user buffer.  Record the rest of the parameters to
 +		 * pass back from IPMICTL_RECEIVE_MSG.
 +		 */
 +		if (ipmi_sendcmd(sc, BMC_SA, 0, req->msg.netfn,
 +		    req->msg.cmd, len, buf)) {
 +			error =3D EIO;
 +			mutex_exit(&sc->sc_cmd_mtx);
 +			break;
 +		}
 +		sc->sc_error =3D ipmi_recvcmd(sc, IPMI_MAX_RX,
 +		    &sc->sc_userbuf_len, sc->sc_userbuf);
  		sc->sc_msgid =3D req->msgid;
  		sc->sc_netfn =3D req->msg.netfn;
  		sc->sc_cmd =3D req->msg.cmd;
 -
 -		if (ipmi_sendcmd(sc, BMC_SA, 0, req->msg.netfn,
 -		    req->msg.cmd, len, buf)) {
 -			error =3D EIO;
 -			break;
 -		}
  		sc->sc_sent =3D true;
 +
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_RECEIVE_MSG_TRUNC:
  	case IPMICTL_RECEIVE_MSG:
  		recv =3D data;
 -		buf =3D malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK);
 -
  		if (recv->msg.data_len < 1) {
  			error =3D EINVAL;
  			break;
 @@ -2453,24 +2448,36 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
  		if (error)
  			break;
 =20
 +		mutex_enter(&sc->sc_cmd_mtx);
 +
 +		/*
 +		 * Wait for any concurrent copyouts of the user buffer
 +		 * in IPMICTL_RECEIVE_MSG to complete.
 +		 */
 +		while (sc->sc_userbuf_busy) {
 +			error =3D cv_wait_sig(&sc->sc_userbuf_cv,
 +			    &sc->sc_cmd_mtx);
 +			if (error)
 +				break;
 +		}
 +		if (error) {
 +			mutex_exit(&sc->sc_cmd_mtx);
 +			break;
 +		}
 =20
  		if (!sc->sc_sent) {
  			error =3D EIO;
 +			mutex_exit(&sc->sc_cmd_mtx);
  			break;
  		}
 -
 -		len =3D 0;
 -		error =3D ipmi_recvcmd(sc, IPMI_MAX_RX, &len, buf);
 -		if (error < 0) {
 -			error =3D EIO;
 -			break;
 -		}
 -		ccode =3D (unsigned char)error;
  		sc->sc_sent =3D false;
 +		len =3D sc->sc_userbuf_len;
 +		ccode =3D (unsigned char)sc->sc_error;
 =20
  		if (len > recv->msg.data_len - 1) {
  			if (cmd =3D=3D IPMICTL_RECEIVE_MSG) {
  				error =3D EMSGSIZE;
 +				mutex_exit(&sc->sc_cmd_mtx);
  				break;
  			}
  			len =3D recv->msg.data_len - 1;
 @@ -2484,23 +2491,54 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
  		recv->msg.cmd =3D sc->sc_cmd;
  		recv->msg.data_len =3D len+1;
 =20
 +		/*
 +		 * Mark the user buffer busy and drop sc_cmd_mtx so we
 +		 * can copyout without blocking watchdog tickle or
 +		 * sensor updates.
 +		 */
 +		sc->sc_userbuf_busy =3D true;
 +		mutex_exit(&sc->sc_cmd_mtx);
 +
  		error =3D copyout(&addr, recv->addr, sizeof(addr));
  		if (error =3D=3D 0)
  			error =3D copyout(&ccode, recv->msg.data, 1);
  		if (error =3D=3D 0)
 -			error =3D copyout(buf, recv->msg.data+1, len);
 +			error =3D copyout(sc->sc_userbuf, recv->msg.data+1, len);
 +
 +		/*
 +		 * Copyout done, release the user buffer and wake any
 +		 * processes waiting to send commands.
 +		 *
 +		 * (Broadcast, not signal -- if there are two processes
 +		 * waiting in IPMICTL_SEND_COMMAND, and we only wake
 +		 * one which _doesn't_ proceed to IPMICTL_RECEIVE_MSG,
 +		 * the other will wait indefinitely.)
 +		 */
 +		mutex_enter(&sc->sc_cmd_mtx);
 +		KASSERT(sc->sc_userbuf_busy);
 +		sc->sc_userbuf_busy =3D false;
 +		cv_broadcast(&sc->sc_userbuf_cv);
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_SET_MY_ADDRESS_CMD:
 +		mutex_enter(&sc->sc_cmd_mtx);
  		sc->sc_address =3D *(int *)data;
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_GET_MY_ADDRESS_CMD:
 +		mutex_enter(&sc->sc_cmd_mtx);
  		*(int *)data =3D sc->sc_address;
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_SET_MY_LUN_CMD:
 +		mutex_enter(&sc->sc_cmd_mtx);
  		sc->sc_lun =3D *(int *)data & 0x3;
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_GET_MY_LUN_CMD:
 +		mutex_enter(&sc->sc_cmd_mtx);
  		*(int *)data =3D sc->sc_lun;
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_SET_GETS_EVENTS_CMD:
  		break;
 @@ -2515,19 +2553,6 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
 =20
  	if (buf)
  		free(buf, M_DEVBUF);
 -
 -	mutex_exit(&sc->sc_cmd_mtx);
 -
 -	switch (cmd) {
 -	case IPMICTL_RECEIVE_MSG:
 -	case IPMICTL_RECEIVE_MSG_TRUNC:
 -		mutex_enter(&sc->sc_poll_mtx);
 -		sc->sc_mode =3D IPMI_MODE_IDLE;
 -		cv_broadcast(&sc->sc_mode_cv);
 -		mutex_exit(&sc->sc_poll_mtx);
 -		break;
 -	}
 -
  	return error;
  }
 =20
 diff --git a/sys/dev/ipmivar.h b/sys/dev/ipmivar.h
 --- a/sys/dev/ipmivar.h
 +++ b/sys/dev/ipmivar.h
 @@ -95,7 +95,6 @@ struct ipmi_softc {
 =20
  	kmutex_t		sc_poll_mtx;
  	kcondvar_t		sc_poll_cv;
 -	kcondvar_t		sc_mode_cv;
 =20
  	kmutex_t		sc_cmd_mtx;
 =20
 @@ -103,7 +102,6 @@ struct ipmi_softc {
 =20
  	struct ipmi_sensor	*current_sensor;
  	bool			sc_thread_running;
 -	bool			sc_thread_ready;
  	bool			sc_tickle_due;
  	struct sysmon_wdog	sc_wdog;
  	struct sysmon_envsys	*sc_envsys;
 @@ -113,12 +111,13 @@ struct ipmi_softc {
  	char		sc_buf[IPMI_MAX_RX + 3];
  	bool		sc_buf_rsvd;
 =20
 -	/* request busy */
 -	int		sc_mode;
 -#define IPMI_MODE_IDLE		0
 -#define IPMI_MODE_COMMAND	1
 -#define IPMI_MODE_ENVSYS	2
 +	/* user command response buffer */
 +	kcondvar_t	sc_userbuf_cv;
 +	int		sc_userbuf_len;
 +	bool		sc_userbuf_busy;
  	bool		sc_sent;
 +	unsigned char	sc_error;
 +	char		sc_userbuf[IPMI_MAX_RX + 3];
 =20
  	/* dummy */
  	int		sc_address;

 --=_w7ACFWG5hulsG3bhLmupmr3o79Ol6y3k
 Content-Type: text/plain; charset="ISO-8859-1"; name="pr58869-ipmilocking"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="pr58869-ipmilocking.diff"

 diff -r eab5d84e73c8 -r 702bc3da2819 sys/dev/ipmi.c
 --- a/sys/dev/ipmi.c	Tue Dec 03 00:13:12 2024 +0000
 +++ b/sys/dev/ipmi.c	Tue Dec 03 17:08:34 2024 +0000
 @@ -355,9 +355,7 @@ bmc_io_wait_sleep(struct ipmi_softc *sc,
  		v =3D bmc_read(sc, offset);
  		if ((v & mask) =3D=3D value)
  			return v;
 -		mutex_enter(&sc->sc_sleep_mtx);
 -		cv_timedwait(&sc->sc_cmd_sleep, &sc->sc_sleep_mtx, 1);
 -		mutex_exit(&sc->sc_sleep_mtx);
 +		kpause("ipmicmd", /*intr*/false, /*timo*/1, /*mtx*/NULL);
  	}
  	return -1;
  }
 @@ -988,6 +986,8 @@ ipmi_sendcmd(struct ipmi_softc *sc, int=20
  	uint8_t	*buf;
  	int		rc =3D -1;
 =20
 +	KASSERT(mutex_owned(&sc->sc_cmd_mtx));
 +
  	dbg_printf(50, "%s: rssa=3D%#.2x nfln=3D%#.2x cmd=3D%#.2x len=3D%#.2x\n",
  	    __func__, rssa, NETFN_LUN(netfn, rslun), cmd, txlen);
  	dbg_dump(10, __func__, txlen, data);
 @@ -1056,6 +1056,8 @@ ipmi_recvcmd(struct ipmi_softc *sc, int=20
  	uint8_t	*buf, rc =3D 0;
  	int		rawlen;
 =20
 +	KASSERT(mutex_owned(&sc->sc_cmd_mtx));
 +
  	/* Need three extra bytes: netfn/cmd/ccode + data */
  	buf =3D ipmi_buf_acquire(sc, maxlen + 3);
  	if (buf =3D=3D NULL) {
 @@ -1092,13 +1094,14 @@ ipmi_recvcmd(struct ipmi_softc *sc, int=20
  static void
  ipmi_delay(struct ipmi_softc *sc, int ms)
  {
 +
 +	KASSERT(mutex_owned(&sc->sc_cmd_mtx));
 +
  	if (cold) {
  		delay(ms * 1000);
  		return;
  	}
 -	mutex_enter(&sc->sc_sleep_mtx);
 -	cv_timedwait(&sc->sc_cmd_sleep, &sc->sc_sleep_mtx, mstohz(ms));
 -	mutex_exit(&sc->sc_sleep_mtx);
 +	kpause("ipmicmd", /*intr*/false, /*timo*/mstohz(ms), /*mtx*/NULL);
  }
 =20
  /* Read a partial SDR entry */
 @@ -1302,7 +1305,7 @@ static int64_t fixexp_a[] =3D {
  	0x0000000080000000ll /* 1.0/2.0 */,
  	0x000000002aaaaaabll /* 1.0/6.0 */,
  	0x000000000aaaaaabll /* 1.0/24.0 */,
 -	0x0000000002222222ll /* 1.0/120.0 */,=20
 +	0x0000000002222222ll /* 1.0/120.0 */,
  	0x00000000005b05b0ll /* 1.0/720.0 */,
  	0x00000000000d00d0ll /* 1.0/5040.0 */,
  	0x000000000001a01all /* 1.0/40320.0 */
 @@ -1320,7 +1323,7 @@ fixmul(int64_t x, int64_t y)
  		x =3D -x;
  		neg =3D !neg;
  	}
 -	if (y < 0) {=20
 +	if (y < 0) {
  		y =3D -y;
  		neg =3D !neg;
  	}
 @@ -1807,7 +1810,7 @@ add_child_sensors(struct ipmi_softc *sc,
  	char			*e;
  	struct ipmi_sensor	*psensor;
  	struct sdrtype1		*s1 =3D (struct sdrtype1 *)psdr;
 -=09
 +
  	typ =3D ipmi_sensor_type(sensor_type, ext_type, entity);
  	if (typ =3D=3D -1) {
  		dbg_printf(5, "Unknown sensor type:%#.2x et:%#.2x sn:%#.2x "
 @@ -1967,12 +1970,10 @@ ipmi_match(device_t parent, cfdata_t cf,
  	sc.sc_if->probe(&sc);
 =20
  	mutex_init(&sc.sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 -	cv_init(&sc.sc_cmd_sleep, "ipmimtch");
 =20
  	if (ipmi_get_device_id(&sc, NULL) =3D=3D 0)
  		rv =3D 1;
 =20
 -	cv_destroy(&sc.sc_cmd_sleep);
  	mutex_destroy(&sc.sc_cmd_mtx);
  	ipmi_unmap_regs(&sc);
 =20
 @@ -2116,21 +2117,12 @@ ipmi_thread(void *cookie)
  	ipmi_enabled =3D 1;
 =20
  	mutex_enter(&sc->sc_poll_mtx);
 -	sc->sc_thread_ready =3D true;
 -	cv_broadcast(&sc->sc_mode_cv);
  	while (sc->sc_thread_running) {
 -		while (sc->sc_mode =3D=3D IPMI_MODE_COMMAND)
 -			cv_wait(&sc->sc_mode_cv, &sc->sc_poll_mtx);
 -		sc->sc_mode =3D IPMI_MODE_ENVSYS;
 -
  		if (sc->sc_tickle_due) {
  			ipmi_dotickle(sc);
  			sc->sc_tickle_due =3D false;
  		}
  		ipmi_refresh_sensors(sc);
 -
 -		sc->sc_mode =3D IPMI_MODE_IDLE;
 -		cv_broadcast(&sc->sc_mode_cv);
  		cv_timedwait(&sc->sc_poll_cv, &sc->sc_poll_mtx,
  		    SENSOR_REFRESH_RATE);
  	}
 @@ -2150,12 +2142,10 @@ ipmi_attach(device_t parent, device_t se
 =20
  	/* lock around read_sensor so that no one messes with the bmc regs */
  	mutex_init(&sc->sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 -	mutex_init(&sc->sc_sleep_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
 -	cv_init(&sc->sc_cmd_sleep, "ipmicmd");
 +	cv_init(&sc->sc_userbuf_cv, "ipmiuser");
 =20
  	mutex_init(&sc->sc_poll_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
  	cv_init(&sc->sc_poll_cv, "ipmipoll");
 -	cv_init(&sc->sc_mode_cv, "ipmimode");
 =20
  	if (kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL, ipmi_thread, self,
  	    &sc->sc_kthread, "%s", device_xname(self)) !=3D 0) {
 @@ -2212,11 +2202,9 @@ ipmi_detach(device_t self, int flags)
 =20
  	ipmi_unmap_regs(sc);
 =20
 -	cv_destroy(&sc->sc_mode_cv);
  	cv_destroy(&sc->sc_poll_cv);
  	mutex_destroy(&sc->sc_poll_mtx);
 -	cv_destroy(&sc->sc_cmd_sleep);
 -	mutex_destroy(&sc->sc_sleep_mtx);
 +	cv_destroy(&sc->sc_userbuf_cv);
  	mutex_destroy(&sc->sc_cmd_mtx);
 =20
  	return 0;
 @@ -2265,15 +2253,6 @@ ipmi_watchdog_setmode(struct sysmon_wdog
  	else
  		sc->sc_wdog.smw_period =3D smwdog->smw_period;
 =20
 -	/* Wait until the device is initialized */
 -	rc =3D 0;
 -	mutex_enter(&sc->sc_poll_mtx);
 -	while (sc->sc_thread_ready)
 -		rc =3D cv_wait_sig(&sc->sc_mode_cv, &sc->sc_poll_mtx);
 -	mutex_exit(&sc->sc_poll_mtx);
 -	if (rc)
 -		return rc;
 -
  	mutex_enter(&sc->sc_cmd_mtx);
  	/* see if we can properly task to the watchdog */
  	rc =3D ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
 @@ -2372,12 +2351,12 @@ ipmi_close(dev_t dev, int flag, int fmt,
  	if ((sc =3D device_lookup_private(&ipmi_cd, unit)) =3D=3D NULL)
  		return (ENXIO);
 =20
 -	mutex_enter(&sc->sc_poll_mtx);
 -	if (sc->sc_mode =3D=3D IPMI_MODE_COMMAND) {
 -		sc->sc_mode =3D IPMI_MODE_IDLE;
 -		cv_broadcast(&sc->sc_mode_cv);
 -	}
 -	mutex_exit(&sc->sc_poll_mtx);
 +	mutex_enter(&sc->sc_cmd_mtx);
 +	KASSERT(!sc->sc_userbuf_busy);
 +	memset(sc->sc_userbuf, 0, sizeof(sc->sc_userbuf)); /* paranoia */
 +	sc->sc_userbuf_len =3D 0;
 +	mutex_exit(&sc->sc_cmd_mtx);
 +
  	return 0;
  }
 =20
 @@ -2397,62 +2376,68 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
 =20
  	switch (cmd) {
  	case IPMICTL_SEND_COMMAND:
 -		mutex_enter(&sc->sc_poll_mtx);
 -		while (sc->sc_mode =3D=3D IPMI_MODE_ENVSYS) {
 -			error =3D cv_wait_sig(&sc->sc_mode_cv, &sc->sc_poll_mtx);
 -			if (error =3D=3D EINTR) {
 -				mutex_exit(&sc->sc_poll_mtx);
 -				return error;
 -			}
 -		}
 -		sc->sc_mode =3D IPMI_MODE_COMMAND;
 -		mutex_exit(&sc->sc_poll_mtx);
 -		break;
 -	}
 -
 -	mutex_enter(&sc->sc_cmd_mtx);
 -
 -	switch (cmd) {
 -	case IPMICTL_SEND_COMMAND:
  		req =3D data;
 -		buf =3D malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK);
 -
  		len =3D req->msg.data_len;
  		if (len < 0 || len > IPMI_MAX_RX) {
  			error =3D EINVAL;
  			break;
  		}
 =20
 -		/* clear pending result */
 -		if (sc->sc_sent)
 -			(void)ipmi_recvcmd(sc, IPMI_MAX_RX, &len, buf);
 -
  		/* XXX */
  		error =3D copyin(req->addr, &addr, sizeof(addr));
  		if (error)
  			break;
 =20
 +		buf =3D malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK);
  		error =3D copyin(req->msg.data, buf, len);
  		if (error)
  			break;
 =20
 -		/* save for receive */
 +		/*
 +		 * Acquire the lock to serialize ipmi_sendcmd/recvcmd
 +		 * and access to the saved command response state.
 +		 */
 +		mutex_enter(&sc->sc_cmd_mtx);
 +
 +		/*
 +		 * Wait for any concurrent copyouts of the user buffer
 +		 * in IPMICTL_RECEIVE_MSG to complete before we can
 +		 * reuse the user buffer.
 +		 */
 +		while (sc->sc_userbuf_busy) {
 +			error =3D cv_wait_sig(&sc->sc_userbuf_cv,
 +			    &sc->sc_cmd_mtx);
 +			if (error)
 +				break;
 +		}
 +		if (error) {
 +			mutex_exit(&sc->sc_cmd_mtx);
 +			break;
 +		}
 +
 +		/*
 +		 * Send the command and receive the response into the
 +		 * user buffer.  Record the rest of the parameters to
 +		 * pass back from IPMICTL_RECEIVE_MSG.
 +		 */
 +		if (ipmi_sendcmd(sc, BMC_SA, 0, req->msg.netfn,
 +		    req->msg.cmd, len, buf)) {
 +			error =3D EIO;
 +			mutex_exit(&sc->sc_cmd_mtx);
 +			break;
 +		}
 +		sc->sc_error =3D ipmi_recvcmd(sc, IPMI_MAX_RX,
 +		    &sc->sc_userbuf_len, sc->sc_userbuf);
  		sc->sc_msgid =3D req->msgid;
  		sc->sc_netfn =3D req->msg.netfn;
  		sc->sc_cmd =3D req->msg.cmd;
 -
 -		if (ipmi_sendcmd(sc, BMC_SA, 0, req->msg.netfn,
 -		    req->msg.cmd, len, buf)) {
 -			error =3D EIO;
 -			break;
 -		}
  		sc->sc_sent =3D true;
 +
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_RECEIVE_MSG_TRUNC:
  	case IPMICTL_RECEIVE_MSG:
  		recv =3D data;
 -		buf =3D malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK);
 -
  		if (recv->msg.data_len < 1) {
  			error =3D EINVAL;
  			break;
 @@ -2463,24 +2448,36 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
  		if (error)
  			break;
 =20
 +		mutex_enter(&sc->sc_cmd_mtx);
 +
 +		/*
 +		 * Wait for any concurrent copyouts of the user buffer
 +		 * in IPMICTL_RECEIVE_MSG to complete.
 +		 */
 +		while (sc->sc_userbuf_busy) {
 +			error =3D cv_wait_sig(&sc->sc_userbuf_cv,
 +			    &sc->sc_cmd_mtx);
 +			if (error)
 +				break;
 +		}
 +		if (error) {
 +			mutex_exit(&sc->sc_cmd_mtx);
 +			break;
 +		}
 =20
  		if (!sc->sc_sent) {
  			error =3D EIO;
 +			mutex_exit(&sc->sc_cmd_mtx);
  			break;
  		}
 -
 -		len =3D 0;
 -		error =3D ipmi_recvcmd(sc, IPMI_MAX_RX, &len, buf);
 -		if (error < 0) {
 -			error =3D EIO;
 -			break;
 -		}
 -		ccode =3D (unsigned char)error;
  		sc->sc_sent =3D false;
 +		len =3D sc->sc_userbuf_len;
 +		ccode =3D (unsigned char)sc->sc_error;
 =20
  		if (len > recv->msg.data_len - 1) {
  			if (cmd =3D=3D IPMICTL_RECEIVE_MSG) {
  				error =3D EMSGSIZE;
 +				mutex_exit(&sc->sc_cmd_mtx);
  				break;
  			}
  			len =3D recv->msg.data_len - 1;
 @@ -2494,23 +2491,54 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
  		recv->msg.cmd =3D sc->sc_cmd;
  		recv->msg.data_len =3D len+1;
 =20
 +		/*
 +		 * Mark the user buffer busy and drop sc_cmd_mtx so we
 +		 * can copyout without blocking watchdog tickle or
 +		 * sensor updates.
 +		 */
 +		sc->sc_userbuf_busy =3D true;
 +		mutex_exit(&sc->sc_cmd_mtx);
 +
  		error =3D copyout(&addr, recv->addr, sizeof(addr));
  		if (error =3D=3D 0)
  			error =3D copyout(&ccode, recv->msg.data, 1);
  		if (error =3D=3D 0)
 -			error =3D copyout(buf, recv->msg.data+1, len);
 +			error =3D copyout(sc->sc_userbuf, recv->msg.data+1, len);
 +
 +		/*
 +		 * Copyout done, release the user buffer and wake any
 +		 * processes waiting to send commands.
 +		 *
 +		 * (Broadcast, not signal -- if there are two processes
 +		 * waiting in IPMICTL_SEND_COMMAND, and we only wake
 +		 * one which _doesn't_ proceed to IPMICTL_RECEIVE_MSG,
 +		 * the other will wait indefinitely.)
 +		 */
 +		mutex_enter(&sc->sc_cmd_mtx);
 +		KASSERT(sc->sc_userbuf_busy);
 +		sc->sc_userbuf_busy =3D false;
 +		cv_broadcast(&sc->sc_userbuf_cv);
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_SET_MY_ADDRESS_CMD:
 +		mutex_enter(&sc->sc_cmd_mtx);
  		sc->sc_address =3D *(int *)data;
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_GET_MY_ADDRESS_CMD:
 +		mutex_enter(&sc->sc_cmd_mtx);
  		*(int *)data =3D sc->sc_address;
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_SET_MY_LUN_CMD:
 +		mutex_enter(&sc->sc_cmd_mtx);
  		sc->sc_lun =3D *(int *)data & 0x3;
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_GET_MY_LUN_CMD:
 +		mutex_enter(&sc->sc_cmd_mtx);
  		*(int *)data =3D sc->sc_lun;
 +		mutex_exit(&sc->sc_cmd_mtx);
  		break;
  	case IPMICTL_SET_GETS_EVENTS_CMD:
  		break;
 @@ -2519,25 +2547,12 @@ ipmi_ioctl(dev_t dev, u_long cmd, void *
  		error =3D EOPNOTSUPP;
  		break;
  	default:
 -		error =3D ENODEV;=09
 +		error =3D ENODEV;
  		break;
  	}
 =20
  	if (buf)
  		free(buf, M_DEVBUF);
 -
 -	mutex_exit(&sc->sc_cmd_mtx);
 -
 -	switch (cmd) {
 -	case IPMICTL_RECEIVE_MSG:
 -	case IPMICTL_RECEIVE_MSG_TRUNC:
 -		mutex_enter(&sc->sc_poll_mtx);
 -		sc->sc_mode =3D IPMI_MODE_IDLE;
 -		cv_broadcast(&sc->sc_mode_cv);
 -		mutex_exit(&sc->sc_poll_mtx);
 -		break;
 -	}
 -
  	return error;
  }
 =20
 diff -r eab5d84e73c8 -r 702bc3da2819 sys/dev/ipmivar.h
 --- a/sys/dev/ipmivar.h	Tue Dec 03 00:13:12 2024 +0000
 +++ b/sys/dev/ipmivar.h	Tue Dec 03 17:08:34 2024 +0000
 @@ -24,17 +24,20 @@
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY W=
 AY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
 - *
   */
 =20
 -#include <sys/mutex.h>
 -#include <sys/condvar.h>
 -
 -#include <dev/sysmon/sysmonvar.h>
 -
  #ifndef _IPMIVAR_H_
  #define _IPMIVAR_H_
 =20
 +#include <sys/types.h>
 +
 +#include <sys/bus.h>
 +#include <sys/condvar.h>
 +#include <sys/ipmi.h>
 +#include <sys/mutex.h>
 +
 +#include <dev/sysmon/sysmonvar.h>
 +
  #define IPMI_IF_KCS		1
  #define IPMI_IF_SMIC		2
  #define IPMI_IF_BT		3
 @@ -92,32 +95,29 @@ struct ipmi_softc {
 =20
  	kmutex_t		sc_poll_mtx;
  	kcondvar_t		sc_poll_cv;
 -	kcondvar_t		sc_mode_cv;
 =20
  	kmutex_t		sc_cmd_mtx;
 -	kmutex_t		sc_sleep_mtx;
 -	kcondvar_t		sc_cmd_sleep;
 =20
  	struct ipmi_bmc_args	*sc_iowait_args;
 =20
  	struct ipmi_sensor	*current_sensor;
  	bool			sc_thread_running;
 -	bool			sc_thread_ready;
  	bool			sc_tickle_due;
  	struct sysmon_wdog	sc_wdog;
  	struct sysmon_envsys	*sc_envsys;
  	envsys_data_t		*sc_sensor;
  	int 		sc_nsensors; /* total number of sensors */
 =20
 -	char		sc_buf[1024 + 3]; /* IPMI_MAX_RX + 3 */
 +	char		sc_buf[IPMI_MAX_RX + 3];
  	bool		sc_buf_rsvd;
 =20
 -	/* request busy */
 -	int		sc_mode;
 -#define IPMI_MODE_IDLE		0
 -#define IPMI_MODE_COMMAND	1
 -#define IPMI_MODE_ENVSYS	2
 +	/* user command response buffer */
 +	kcondvar_t	sc_userbuf_cv;
 +	int		sc_userbuf_len;
 +	bool		sc_userbuf_busy;
  	bool		sc_sent;
 +	unsigned char	sc_error;
 +	char		sc_userbuf[IPMI_MAX_RX + 3];
 =20
  	/* dummy */
  	int		sc_address;
 diff -r eab5d84e73c8 -r 702bc3da2819 sys/sys/ipmi.h
 --- a/sys/sys/ipmi.h	Tue Dec 03 00:13:12 2024 +0000
 +++ b/sys/sys/ipmi.h	Tue Dec 03 17:08:34 2024 +0000
 @@ -32,6 +32,8 @@
  #ifndef _SYS_IPMI_H_
  #define	_SYS_IPMI_H_
 =20
 +#include <sys/ioccom.h>
 +
  #define	IPMI_MAX_ADDR_SIZE		0x20
  #define IPMI_MAX_RX			1024
  #define	IPMI_BMC_SLAVE_ADDR		0x20
 @@ -99,7 +101,7 @@ struct ipmi_ipmb_addr {
  #define IPMICTL_SET_GETS_EVENTS_CMD     _IOW('i', 16, int)
  #define IPMICTL_SET_MY_ADDRESS_CMD      _IOW('i', 17, unsigned int)
  #define IPMICTL_GET_MY_ADDRESS_CMD      _IOR('i', 18, unsigned int)
 -#define IPMICTL_SET_MY_LUN_CMD          _IOW('i', 19, unsigned int)=20
 +#define IPMICTL_SET_MY_LUN_CMD          _IOW('i', 19, unsigned int)
  #define IPMICTL_GET_MY_LUN_CMD          _IOR('i', 20, unsigned int)
 =20
  #endif /* !_SYS_IPMI_H_ */

 --=_w7ACFWG5hulsG3bhLmupmr3o79Ol6y3k--

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/58869 CVS commit: src/sys/dev
Date: Wed, 4 Dec 2024 15:25:12 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Wed Dec  4 15:25:12 UTC 2024

 Modified Files:
 	src/sys/dev: ipmi.c ipmivar.h

 Log Message:
 ipmi(4): Omit needless condvar/mutex for sleep.

 Just use kpause instead.

 Cleanup in preparation for:

 PR kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for
 wdog tickle


 To generate a diff of this commit:
 cvs rdiff -u -r1.12 -r1.13 src/sys/dev/ipmi.c
 cvs rdiff -u -r1.4 -r1.5 src/sys/dev/ipmivar.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/58869 CVS commit: src/sys/sys
Date: Wed, 4 Dec 2024 15:25:30 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Wed Dec  4 15:25:30 UTC 2024

 Modified Files:
 	src/sys/sys: ipmi.h

 Log Message:
 sys/ipmi.h: Tidy header file.

 Need <sys/ioccom.h> for _IOWR/_IOW/_IOR.  Nix trailing whitespace.

 Cleanup in preparation for:

 PR kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for
 wdog tickle


 To generate a diff of this commit:
 cvs rdiff -u -r1.1 -r1.2 src/sys/sys/ipmi.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/58869 CVS commit: src/sys/dev
Date: Wed, 4 Dec 2024 15:25:45 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Wed Dec  4 15:25:45 UTC 2024

 Modified Files:
 	src/sys/dev: ipmivar.h

 Log Message:
 ipmivar.h: Tidy includes and use a named constant, not a comment.

 Cleanup in preparation for:

 PR kern/58869: ipmi(4) holds sc_cmd_mtx across copyout, needed for
 wdog tickle


 To generate a diff of this commit:
 cvs rdiff -u -r1.5 -r1.6 src/sys/dev/ipmivar.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.49 2026/05/14 01:52:41 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.