xref: /openbsd-src/sys/scsi/mpath.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
1*0f9e9ec2Sjsg /*	$OpenBSD: mpath.c,v 1.58 2024/05/13 01:15:53 jsg Exp $ */
281efa114Sdlg 
381efa114Sdlg /*
481efa114Sdlg  * Copyright (c) 2009 David Gwynne <dlg@openbsd.org>
581efa114Sdlg  *
681efa114Sdlg  * Permission to use, copy, modify, and distribute this software for any
781efa114Sdlg  * purpose with or without fee is hereby granted, provided that the above
881efa114Sdlg  * copyright notice and this permission notice appear in all copies.
981efa114Sdlg  *
1081efa114Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1181efa114Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1281efa114Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1381efa114Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1481efa114Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1581efa114Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1681efa114Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1781efa114Sdlg  */
1881efa114Sdlg 
1981efa114Sdlg #include <sys/param.h>
2081efa114Sdlg #include <sys/systm.h>
2181efa114Sdlg #include <sys/kernel.h>
2281efa114Sdlg #include <sys/malloc.h>
2381efa114Sdlg #include <sys/device.h>
2481efa114Sdlg #include <sys/conf.h>
2581efa114Sdlg #include <sys/queue.h>
2681efa114Sdlg #include <sys/rwlock.h>
2781efa114Sdlg #include <sys/ioctl.h>
2881efa114Sdlg 
2981efa114Sdlg #include <scsi/scsi_all.h>
3081efa114Sdlg #include <scsi/scsiconf.h>
317e24ec3eSdlg #include <scsi/mpathvar.h>
3281efa114Sdlg 
3381efa114Sdlg #define MPATH_BUSWIDTH 256
3481efa114Sdlg 
3581efa114Sdlg int		mpath_match(struct device *, void *, void *);
3681efa114Sdlg void		mpath_attach(struct device *, struct device *, void *);
3781efa114Sdlg 
3881efa114Sdlg TAILQ_HEAD(mpath_paths, mpath_path);
3981efa114Sdlg 
40bba3b8d8Sdlg struct mpath_group {
41bba3b8d8Sdlg 	TAILQ_ENTRY(mpath_group) g_entry;
42bba3b8d8Sdlg 	struct mpath_paths	 g_paths;
435a08a2c9Sdlg 	struct mpath_dev	*g_dev;
44bba3b8d8Sdlg 	u_int			 g_id;
45bba3b8d8Sdlg };
46bba3b8d8Sdlg TAILQ_HEAD(mpath_groups, mpath_group);
47bba3b8d8Sdlg 
487e24ec3eSdlg struct mpath_dev {
497e24ec3eSdlg 	struct mutex		 d_mtx;
507e24ec3eSdlg 
51ecf9d073Sdlg 	struct scsi_xfer_list	 d_xfers;
527e24ec3eSdlg 	struct mpath_path	*d_next_path;
537e24ec3eSdlg 
545a08a2c9Sdlg 	struct mpath_groups	 d_groups;
555a08a2c9Sdlg 
565a08a2c9Sdlg 	struct mpath_group	*d_failover_iter;
575a08a2c9Sdlg 	struct timeout		 d_failover_tmo;
585a08a2c9Sdlg 	u_int			 d_failover;
595a08a2c9Sdlg 
60c32a5b15Sdlg 	const struct mpath_ops	*d_ops;
617e24ec3eSdlg 	struct devid		*d_id;
6281efa114Sdlg };
6381efa114Sdlg 
6481efa114Sdlg struct mpath_softc {
6581efa114Sdlg 	struct device		sc_dev;
6681efa114Sdlg 	struct scsibus_softc	*sc_scsibus;
6707fd7809Sdlg 	struct mpath_dev	*sc_devs[MPATH_BUSWIDTH];
6881efa114Sdlg };
697e24ec3eSdlg #define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
7081efa114Sdlg 
7181efa114Sdlg struct mpath_softc	*mpath;
7281efa114Sdlg 
739eaf72d1Smpi const struct cfattach mpath_ca = {
7481efa114Sdlg 	sizeof(struct mpath_softc),
7581efa114Sdlg 	mpath_match,
7681efa114Sdlg 	mpath_attach
7781efa114Sdlg };
7881efa114Sdlg 
7981efa114Sdlg struct cfdriver mpath_cd = {
8081efa114Sdlg 	NULL,
8181efa114Sdlg 	"mpath",
8281efa114Sdlg 	DV_DULL
8381efa114Sdlg };
8481efa114Sdlg 
85bae03be6Skrw void		mpath_cmd(struct scsi_xfer *);
86a33776e9Sdlg void		mpath_minphys(struct buf *, struct scsi_link *);
8781efa114Sdlg int		mpath_probe(struct scsi_link *);
8881efa114Sdlg 
898f5a0873Sdlg struct mpath_path *mpath_next_path(struct mpath_dev *);
90864c175eSdlg void		mpath_done(struct scsi_xfer *);
91864c175eSdlg 
925a08a2c9Sdlg void		mpath_failover(struct mpath_dev *);
935a08a2c9Sdlg void		mpath_failover_start(void *);
945a08a2c9Sdlg void		mpath_failover_check(struct mpath_dev *);
955a08a2c9Sdlg 
96a454aff3Snaddy const struct scsi_adapter mpath_switch = {
9721ceeee0Skrw 	mpath_cmd, NULL, mpath_probe, NULL, NULL
9881efa114Sdlg };
9981efa114Sdlg 
10081efa114Sdlg void		mpath_xs_stuffup(struct scsi_xfer *);
10181efa114Sdlg 
10281efa114Sdlg int
mpath_match(struct device * parent,void * match,void * aux)10381efa114Sdlg mpath_match(struct device *parent, void *match, void *aux)
10481efa114Sdlg {
10581efa114Sdlg 	return (1);
10681efa114Sdlg }
10781efa114Sdlg 
10881efa114Sdlg void
mpath_attach(struct device * parent,struct device * self,void * aux)10981efa114Sdlg mpath_attach(struct device *parent, struct device *self, void *aux)
11081efa114Sdlg {
11181efa114Sdlg 	struct mpath_softc		*sc = (struct mpath_softc *)self;
11281efa114Sdlg 	struct scsibus_attach_args	saa;
11381efa114Sdlg 
11481efa114Sdlg 	mpath = sc;
11581efa114Sdlg 
11681efa114Sdlg 	printf("\n");
11781efa114Sdlg 
118ead808c4Skrw 	saa.saa_adapter = &mpath_switch;
119ead808c4Skrw 	saa.saa_adapter_softc = sc;
120ead808c4Skrw 	saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
121ead808c4Skrw 	saa.saa_adapter_buswidth = MPATH_BUSWIDTH;
122ead808c4Skrw 	saa.saa_luns = 1;
123e5eae15dSkrw 	saa.saa_openings = 1024; /* XXX magical */
124e5eae15dSkrw 	saa.saa_pool = NULL;
125e5eae15dSkrw 	saa.saa_quirks = saa.saa_flags = 0;
126e5eae15dSkrw 	saa.saa_wwpn = saa.saa_wwnn = 0;
12781efa114Sdlg 
12881efa114Sdlg 	sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
12981efa114Sdlg 	    &saa, scsiprint);
13081efa114Sdlg }
13181efa114Sdlg 
13281efa114Sdlg void
mpath_xs_stuffup(struct scsi_xfer * xs)13381efa114Sdlg mpath_xs_stuffup(struct scsi_xfer *xs)
13481efa114Sdlg {
13581efa114Sdlg 	xs->error = XS_DRIVER_STUFFUP;
13681efa114Sdlg 	scsi_done(xs);
13781efa114Sdlg }
13881efa114Sdlg 
13981efa114Sdlg int
mpath_probe(struct scsi_link * link)14081efa114Sdlg mpath_probe(struct scsi_link *link)
14181efa114Sdlg {
14267c3123bSkrw 	struct mpath_softc *sc = link->bus->sb_adapter_softc;
14307fd7809Sdlg 	struct mpath_dev *d = sc->sc_devs[link->target];
144864c175eSdlg 
1457e24ec3eSdlg 	if (link->lun != 0 || d == NULL)
14681efa114Sdlg 		return (ENXIO);
14781efa114Sdlg 
1487e24ec3eSdlg 	link->id = devid_copy(d->d_id);
149864c175eSdlg 
15081efa114Sdlg 	return (0);
15181efa114Sdlg }
15281efa114Sdlg 
1537e24ec3eSdlg struct mpath_path *
mpath_next_path(struct mpath_dev * d)1548f5a0873Sdlg mpath_next_path(struct mpath_dev *d)
1557e24ec3eSdlg {
156bba3b8d8Sdlg 	struct mpath_group *g;
1577e24ec3eSdlg 	struct mpath_path *p;
1587e24ec3eSdlg 
1598f5a0873Sdlg #ifdef DIAGNOSTIC
1607e24ec3eSdlg 	if (d == NULL)
1617e24ec3eSdlg 		panic("%s: d is NULL", __func__);
162364ebb70Skrw #endif /* DIAGNOSTIC */
1637e24ec3eSdlg 
1647e24ec3eSdlg 	p = d->d_next_path;
165daa9e138Sdlg 	if (p != NULL) {
1667e24ec3eSdlg 		d->d_next_path = TAILQ_NEXT(p, p_entry);
167daa9e138Sdlg 		if (d->d_next_path == NULL &&
168daa9e138Sdlg 		    (g = TAILQ_FIRST(&d->d_groups)) != NULL)
169bba3b8d8Sdlg 			d->d_next_path = TAILQ_FIRST(&g->g_paths);
170bba3b8d8Sdlg 	}
1717e24ec3eSdlg 
1727e24ec3eSdlg 	return (p);
1737e24ec3eSdlg }
1747e24ec3eSdlg 
175bae03be6Skrw void
mpath_cmd(struct scsi_xfer * xs)17681efa114Sdlg mpath_cmd(struct scsi_xfer *xs)
17781efa114Sdlg {
17881efa114Sdlg 	struct scsi_link *link = xs->sc_link;
17967c3123bSkrw 	struct mpath_softc *sc = link->bus->sb_adapter_softc;
18007fd7809Sdlg 	struct mpath_dev *d = sc->sc_devs[link->target];
1817e24ec3eSdlg 	struct mpath_path *p;
182864c175eSdlg 	struct scsi_xfer *mxs;
18381efa114Sdlg 
1847e24ec3eSdlg #ifdef DIAGNOSTIC
1857e24ec3eSdlg 	if (d == NULL)
1866ba149ccSphessler 		panic("mpath_cmd issued against nonexistent device");
187364ebb70Skrw #endif /* DIAGNOSTIC */
1887e24ec3eSdlg 
1897e24ec3eSdlg 	if (ISSET(xs->flags, SCSI_POLL)) {
1907e24ec3eSdlg 		mtx_enter(&d->d_mtx);
1918f5a0873Sdlg 		p = mpath_next_path(d);
1927e24ec3eSdlg 		mtx_leave(&d->d_mtx);
1937e24ec3eSdlg 		if (p == NULL) {
19481efa114Sdlg 			mpath_xs_stuffup(xs);
195bae03be6Skrw 			return;
19681efa114Sdlg 		}
19781efa114Sdlg 
1987e24ec3eSdlg 		mxs = scsi_xs_get(p->p_link, xs->flags);
199864c175eSdlg 		if (mxs == NULL) {
200864c175eSdlg 			mpath_xs_stuffup(xs);
201bae03be6Skrw 			return;
20281efa114Sdlg 		}
20381efa114Sdlg 
204664c6166Skrw 		memcpy(&mxs->cmd, &xs->cmd, xs->cmdlen);
205864c175eSdlg 		mxs->cmdlen = xs->cmdlen;
206864c175eSdlg 		mxs->data = xs->data;
207864c175eSdlg 		mxs->datalen = xs->datalen;
208864c175eSdlg 		mxs->retries = xs->retries;
209864c175eSdlg 		mxs->timeout = xs->timeout;
21033c23a4bSmatthew 		mxs->bp = xs->bp;
211864c175eSdlg 
2127e24ec3eSdlg 		scsi_xs_sync(mxs);
2137e24ec3eSdlg 
2147e24ec3eSdlg 		xs->error = mxs->error;
2157e24ec3eSdlg 		xs->status = mxs->status;
2167e24ec3eSdlg 		xs->resid = mxs->resid;
2177e24ec3eSdlg 
2187e24ec3eSdlg 		memcpy(&xs->sense, &mxs->sense, sizeof(xs->sense));
2197e24ec3eSdlg 
2207e24ec3eSdlg 		scsi_xs_put(mxs);
2217e24ec3eSdlg 		scsi_done(xs);
2227e24ec3eSdlg 		return;
2237e24ec3eSdlg 	}
2247e24ec3eSdlg 
2257e24ec3eSdlg 	mtx_enter(&d->d_mtx);
226ecf9d073Sdlg 	SIMPLEQ_INSERT_TAIL(&d->d_xfers, xs, xfer_list);
2278f5a0873Sdlg 	p = mpath_next_path(d);
2287e24ec3eSdlg 	mtx_leave(&d->d_mtx);
2297e24ec3eSdlg 
2307e24ec3eSdlg 	if (p != NULL)
2317e24ec3eSdlg 		scsi_xsh_add(&p->p_xsh);
2327e24ec3eSdlg }
2337e24ec3eSdlg 
2347e24ec3eSdlg void
mpath_start(struct mpath_path * p,struct scsi_xfer * mxs)2357e24ec3eSdlg mpath_start(struct mpath_path *p, struct scsi_xfer *mxs)
2367e24ec3eSdlg {
2375a08a2c9Sdlg 	struct mpath_dev *d = p->p_group->g_dev;
2387e24ec3eSdlg 	struct scsi_xfer *xs;
2397e24ec3eSdlg 	int addxsh = 0;
2407e24ec3eSdlg 
2417e24ec3eSdlg 	if (ISSET(p->p_link->state, SDEV_S_DYING) || d == NULL)
2427e24ec3eSdlg 		goto fail;
2437e24ec3eSdlg 
2447e24ec3eSdlg 	mtx_enter(&d->d_mtx);
245ecf9d073Sdlg 	xs = SIMPLEQ_FIRST(&d->d_xfers);
246ecf9d073Sdlg 	if (xs != NULL) {
247ecf9d073Sdlg 		SIMPLEQ_REMOVE_HEAD(&d->d_xfers, xfer_list);
248ecf9d073Sdlg 		if (!SIMPLEQ_EMPTY(&d->d_xfers))
2497e24ec3eSdlg 			addxsh = 1;
2507e24ec3eSdlg 	}
2517e24ec3eSdlg 	mtx_leave(&d->d_mtx);
2527e24ec3eSdlg 
253ecf9d073Sdlg 	if (xs == NULL)
2547e24ec3eSdlg 		goto fail;
2557e24ec3eSdlg 
256664c6166Skrw 	memcpy(&mxs->cmd, &xs->cmd, xs->cmdlen);
2577e24ec3eSdlg 	mxs->cmdlen = xs->cmdlen;
2587e24ec3eSdlg 	mxs->data = xs->data;
2597e24ec3eSdlg 	mxs->datalen = xs->datalen;
2607e24ec3eSdlg 	mxs->retries = xs->retries;
2617e24ec3eSdlg 	mxs->timeout = xs->timeout;
2627e24ec3eSdlg 	mxs->bp = xs->bp;
2637e24ec3eSdlg 	mxs->flags = xs->flags;
2647e24ec3eSdlg 
265864c175eSdlg 	mxs->cookie = xs;
266864c175eSdlg 	mxs->done = mpath_done;
267864c175eSdlg 
268864c175eSdlg 	scsi_xs_exec(mxs);
2697e24ec3eSdlg 
2707e24ec3eSdlg 	if (addxsh)
2717e24ec3eSdlg 		scsi_xsh_add(&p->p_xsh);
2727e24ec3eSdlg 
2737e24ec3eSdlg 	return;
2747e24ec3eSdlg fail:
2757e24ec3eSdlg 	scsi_xs_put(mxs);
276864c175eSdlg }
277864c175eSdlg 
278864c175eSdlg void
mpath_done(struct scsi_xfer * mxs)279864c175eSdlg mpath_done(struct scsi_xfer *mxs)
280864c175eSdlg {
281864c175eSdlg 	struct scsi_xfer *xs = mxs->cookie;
2827e24ec3eSdlg 	struct scsi_link *link = xs->sc_link;
28367c3123bSkrw 	struct mpath_softc *sc = link->bus->sb_adapter_softc;
28407fd7809Sdlg 	struct mpath_dev *d = sc->sc_devs[link->target];
2857e24ec3eSdlg 	struct mpath_path *p;
2867e24ec3eSdlg 
287c32a5b15Sdlg 	switch (mxs->error) {
288c32a5b15Sdlg 	case XS_SELTIMEOUT: /* physical path is gone, try the next */
289c32a5b15Sdlg 	case XS_RESET:
2907e24ec3eSdlg 		mtx_enter(&d->d_mtx);
291ecf9d073Sdlg 		SIMPLEQ_INSERT_HEAD(&d->d_xfers, xs, xfer_list);
2928f5a0873Sdlg 		p = mpath_next_path(d);
2937e24ec3eSdlg 		mtx_leave(&d->d_mtx);
2947e24ec3eSdlg 
2957e24ec3eSdlg 		scsi_xs_put(mxs);
2967e24ec3eSdlg 
2977e24ec3eSdlg 		if (p != NULL)
2987e24ec3eSdlg 			scsi_xsh_add(&p->p_xsh);
2997e24ec3eSdlg 		return;
3005a08a2c9Sdlg 	case XS_SENSE:
3015a08a2c9Sdlg 		switch (d->d_ops->op_checksense(mxs)) {
3025a08a2c9Sdlg 		case MPATH_SENSE_FAILOVER:
3035a08a2c9Sdlg 			mtx_enter(&d->d_mtx);
3045a08a2c9Sdlg 			SIMPLEQ_INSERT_HEAD(&d->d_xfers, xs, xfer_list);
3058f5a0873Sdlg 			p = mpath_next_path(d);
3065a08a2c9Sdlg 			mtx_leave(&d->d_mtx);
3075a08a2c9Sdlg 
3085a08a2c9Sdlg 			scsi_xs_put(mxs);
3095a08a2c9Sdlg 
3105a08a2c9Sdlg 			mpath_failover(d);
3115a08a2c9Sdlg 			return;
3125a08a2c9Sdlg 		case MPATH_SENSE_DECLINED:
3135a08a2c9Sdlg 			break;
3145a08a2c9Sdlg #ifdef DIAGNOSTIC
3155a08a2c9Sdlg 		default:
3165a08a2c9Sdlg 			panic("unexpected return from checksense");
317364ebb70Skrw #endif /* DIAGNOSTIC */
3185a08a2c9Sdlg 		}
3195a08a2c9Sdlg 		break;
3207e24ec3eSdlg 	}
321864c175eSdlg 
322864c175eSdlg 	xs->error = mxs->error;
323864c175eSdlg 	xs->status = mxs->status;
324864c175eSdlg 	xs->resid = mxs->resid;
325864c175eSdlg 
326864c175eSdlg 	memcpy(&xs->sense, &mxs->sense, sizeof(xs->sense));
327864c175eSdlg 
328864c175eSdlg 	scsi_xs_put(mxs);
329864c175eSdlg 
33081efa114Sdlg 	scsi_done(xs);
33181efa114Sdlg }
33281efa114Sdlg 
333a33776e9Sdlg void
mpath_failover(struct mpath_dev * d)3345a08a2c9Sdlg mpath_failover(struct mpath_dev *d)
3355a08a2c9Sdlg {
336ed271c9dSdlg 	if (!scsi_pending_start(&d->d_mtx, &d->d_failover))
3375a08a2c9Sdlg 		return;
3385a08a2c9Sdlg 
3395a08a2c9Sdlg 	mpath_failover_start(d);
3405a08a2c9Sdlg }
3415a08a2c9Sdlg 
3425a08a2c9Sdlg void
mpath_failover_start(void * xd)3435a08a2c9Sdlg mpath_failover_start(void *xd)
3445a08a2c9Sdlg {
3455a08a2c9Sdlg 	struct mpath_dev *d = xd;
3465a08a2c9Sdlg 
3475a08a2c9Sdlg 	mtx_enter(&d->d_mtx);
3485a08a2c9Sdlg 	d->d_failover_iter = TAILQ_FIRST(&d->d_groups);
3495a08a2c9Sdlg 	mtx_leave(&d->d_mtx);
3505a08a2c9Sdlg 
3515a08a2c9Sdlg 	mpath_failover_check(d);
3525a08a2c9Sdlg }
3535a08a2c9Sdlg 
3545a08a2c9Sdlg void
mpath_failover_check(struct mpath_dev * d)3555a08a2c9Sdlg mpath_failover_check(struct mpath_dev *d)
3565a08a2c9Sdlg {
3575a08a2c9Sdlg 	struct mpath_group *g = d->d_failover_iter;
3585a08a2c9Sdlg 	struct mpath_path *p;
3595a08a2c9Sdlg 
3605a08a2c9Sdlg 	if (g == NULL)
3615a08a2c9Sdlg 		timeout_add_sec(&d->d_failover_tmo, 1);
3625a08a2c9Sdlg 	else {
3635a08a2c9Sdlg 		p = TAILQ_FIRST(&g->g_paths);
3645a08a2c9Sdlg 		d->d_ops->op_status(p->p_link);
3655a08a2c9Sdlg 	}
3665a08a2c9Sdlg }
3675a08a2c9Sdlg 
3685a08a2c9Sdlg void
mpath_path_status(struct mpath_path * p,int status)3695a08a2c9Sdlg mpath_path_status(struct mpath_path *p, int status)
3705a08a2c9Sdlg {
3715a08a2c9Sdlg 	struct mpath_group *g = p->p_group;
3725a08a2c9Sdlg 	struct mpath_dev *d = g->g_dev;
3735a08a2c9Sdlg 
3745a08a2c9Sdlg 	mtx_enter(&d->d_mtx);
3755a08a2c9Sdlg 	if (status == MPATH_S_ACTIVE) {
3765a08a2c9Sdlg 		TAILQ_REMOVE(&d->d_groups, g, g_entry);
3775a08a2c9Sdlg 		TAILQ_INSERT_HEAD(&d->d_groups, g, g_entry);
3785a08a2c9Sdlg 		d->d_next_path = p;
3795a08a2c9Sdlg 	} else
3805a08a2c9Sdlg 		d->d_failover_iter = TAILQ_NEXT(d->d_failover_iter, g_entry);
3815a08a2c9Sdlg 	mtx_leave(&d->d_mtx);
3825a08a2c9Sdlg 
3835a08a2c9Sdlg 	if (status == MPATH_S_ACTIVE) {
3845a08a2c9Sdlg 		scsi_xsh_add(&p->p_xsh);
385ed271c9dSdlg 		if (!scsi_pending_finish(&d->d_mtx, &d->d_failover))
3865a08a2c9Sdlg 			mpath_failover_start(d);
3875a08a2c9Sdlg 	} else
3885a08a2c9Sdlg 		mpath_failover_check(d);
3895a08a2c9Sdlg }
3905a08a2c9Sdlg 
3915a08a2c9Sdlg void
mpath_minphys(struct buf * bp,struct scsi_link * link)392a33776e9Sdlg mpath_minphys(struct buf *bp, struct scsi_link *link)
393a33776e9Sdlg {
39467c3123bSkrw 	struct mpath_softc *sc = link->bus->sb_adapter_softc;
39507fd7809Sdlg 	struct mpath_dev *d = sc->sc_devs[link->target];
396bba3b8d8Sdlg 	struct mpath_group *g;
397a33776e9Sdlg 	struct mpath_path *p;
398a33776e9Sdlg 
3997e24ec3eSdlg #ifdef DIAGNOSTIC
4007e24ec3eSdlg 	if (d == NULL)
4016ba149ccSphessler 		panic("mpath_minphys against nonexistent device");
402364ebb70Skrw #endif /* DIAGNOSTIC */
403a33776e9Sdlg 
404bba3b8d8Sdlg 	mtx_enter(&d->d_mtx);
405bba3b8d8Sdlg 	TAILQ_FOREACH(g, &d->d_groups, g_entry) {
406bba3b8d8Sdlg 		TAILQ_FOREACH(p, &g->g_paths, p_entry) {
407bba3b8d8Sdlg 			/* XXX crossing layers with mutex held */
40867c3123bSkrw 			if (p->p_link->bus->sb_adapter->dev_minphys != NULL)
40967c3123bSkrw 				p->p_link->bus->sb_adapter->dev_minphys(bp,
41067c3123bSkrw 				    p->p_link);
411a33776e9Sdlg 		}
412bba3b8d8Sdlg 	}
413bba3b8d8Sdlg 	mtx_leave(&d->d_mtx);
414bba3b8d8Sdlg }
415a33776e9Sdlg 
41681efa114Sdlg int
mpath_path_probe(struct scsi_link * link)4177e24ec3eSdlg mpath_path_probe(struct scsi_link *link)
41881efa114Sdlg {
41907fd7809Sdlg 	if (mpath == NULL)
42051fda23dSdlg 		return (ENXIO);
42151fda23dSdlg 
4227e24ec3eSdlg 	if (link->id == NULL)
4237e24ec3eSdlg 		return (EINVAL);
42481efa114Sdlg 
42512db79caSdlg 	if (ISSET(link->flags, SDEV_UMASS))
42612db79caSdlg 		return (EINVAL);
42712db79caSdlg 
42867c3123bSkrw 	if (mpath == link->bus->sb_adapter_softc)
42981efa114Sdlg 		return (ENXIO);
43081efa114Sdlg 
4317e24ec3eSdlg 	return (0);
43281efa114Sdlg }
43381efa114Sdlg 
4347e24ec3eSdlg int
mpath_path_attach(struct mpath_path * p,u_int g_id,const struct mpath_ops * ops)435bba3b8d8Sdlg mpath_path_attach(struct mpath_path *p, u_int g_id, const struct mpath_ops *ops)
4367e24ec3eSdlg {
43707fd7809Sdlg 	struct mpath_softc *sc = mpath;
4387e24ec3eSdlg 	struct scsi_link *link = p->p_link;
4397e24ec3eSdlg 	struct mpath_dev *d = NULL;
440bba3b8d8Sdlg 	struct mpath_group *g;
4417e24ec3eSdlg 	int newdev = 0, addxsh = 0;
4427e24ec3eSdlg 	int target;
4437e24ec3eSdlg 
4447e24ec3eSdlg #ifdef DIAGNOSTIC
4457e24ec3eSdlg 	if (p->p_link == NULL)
4467e24ec3eSdlg 		panic("mpath_path_attach: NULL link");
4475a08a2c9Sdlg 	if (p->p_group != NULL)
4485a08a2c9Sdlg 		panic("mpath_path_attach: group is not NULL");
449364ebb70Skrw #endif /* DIAGNOSTIC */
4507e24ec3eSdlg 
45181efa114Sdlg 	for (target = 0; target < MPATH_BUSWIDTH; target++) {
45207fd7809Sdlg 		if ((d = sc->sc_devs[target]) == NULL)
4537e24ec3eSdlg 			continue;
4547e24ec3eSdlg 
455c32a5b15Sdlg 		if (DEVID_CMP(d->d_id, link->id) && d->d_ops == ops)
4567e24ec3eSdlg 			break;
4577e24ec3eSdlg 
4587e24ec3eSdlg 		d = NULL;
4597e24ec3eSdlg 	}
4607e24ec3eSdlg 
4617e24ec3eSdlg 	if (d == NULL) {
4627e24ec3eSdlg 		for (target = 0; target < MPATH_BUSWIDTH; target++) {
46307fd7809Sdlg 			if (sc->sc_devs[target] == NULL)
46481efa114Sdlg 				break;
46581efa114Sdlg 		}
46681efa114Sdlg 		if (target >= MPATH_BUSWIDTH)
46781efa114Sdlg 			return (ENXIO);
46881efa114Sdlg 
469bba3b8d8Sdlg 		d = malloc(sizeof(*d), M_DEVBUF, M_WAITOK | M_CANFAIL | M_ZERO);
4707e24ec3eSdlg 		if (d == NULL)
4717e24ec3eSdlg 			return (ENOMEM);
47281efa114Sdlg 
4737e24ec3eSdlg 		mtx_init(&d->d_mtx, IPL_BIO);
474bba3b8d8Sdlg 		TAILQ_INIT(&d->d_groups);
475ecf9d073Sdlg 		SIMPLEQ_INIT(&d->d_xfers);
4767e24ec3eSdlg 		d->d_id = devid_copy(link->id);
477c32a5b15Sdlg 		d->d_ops = ops;
47881efa114Sdlg 
4795a08a2c9Sdlg 		timeout_set(&d->d_failover_tmo, mpath_failover_start, d);
4805a08a2c9Sdlg 
48107fd7809Sdlg 		sc->sc_devs[target] = d;
4827e24ec3eSdlg 		newdev = 1;
483864c175eSdlg 	} else {
484864c175eSdlg 		/*
485864c175eSdlg 		 * instead of carrying identical values in different devid
486864c175eSdlg 		 * instances, delete the new one and reference the old one in
487864c175eSdlg 		 * the new scsi_link.
488864c175eSdlg 		 */
489864c175eSdlg 		devid_free(link->id);
4907e24ec3eSdlg 		link->id = devid_copy(d->d_id);
49181efa114Sdlg 	}
49281efa114Sdlg 
493bba3b8d8Sdlg 	TAILQ_FOREACH(g, &d->d_groups, g_entry) {
494bba3b8d8Sdlg 		if (g->g_id == g_id)
495bba3b8d8Sdlg 			break;
496bba3b8d8Sdlg 	}
497bba3b8d8Sdlg 
498bba3b8d8Sdlg 	if (g == NULL) {
499bba3b8d8Sdlg 		g = malloc(sizeof(*g),  M_DEVBUF,
500bba3b8d8Sdlg 		    M_WAITOK | M_CANFAIL | M_ZERO);
501bba3b8d8Sdlg 		if (g == NULL) {
502d67ef76aSdlg 			if (newdev) {
503a67129dbStedu 				free(d, M_DEVBUF, sizeof(*d));
504d67ef76aSdlg 				sc->sc_devs[target] = NULL;
505d67ef76aSdlg 			}
506bba3b8d8Sdlg 
507bba3b8d8Sdlg 			return (ENOMEM);
508bba3b8d8Sdlg 		}
509bba3b8d8Sdlg 
510bba3b8d8Sdlg 		TAILQ_INIT(&g->g_paths);
5115a08a2c9Sdlg 		g->g_dev = d;
512bba3b8d8Sdlg 		g->g_id = g_id;
513bba3b8d8Sdlg 
5147e24ec3eSdlg 		mtx_enter(&d->d_mtx);
515bba3b8d8Sdlg 		TAILQ_INSERT_TAIL(&d->d_groups, g, g_entry);
516bba3b8d8Sdlg 		mtx_leave(&d->d_mtx);
517bba3b8d8Sdlg 	}
518bba3b8d8Sdlg 
5195a08a2c9Sdlg 	p->p_group = g;
520bba3b8d8Sdlg 
521bba3b8d8Sdlg 	mtx_enter(&d->d_mtx);
522bba3b8d8Sdlg 	TAILQ_INSERT_TAIL(&g->g_paths, p, p_entry);
523ecf9d073Sdlg 	if (!SIMPLEQ_EMPTY(&d->d_xfers))
5247e24ec3eSdlg 		addxsh = 1;
525bba3b8d8Sdlg 
5265a08a2c9Sdlg 	if (d->d_next_path == NULL)
5275a08a2c9Sdlg 		d->d_next_path = p;
5287e24ec3eSdlg 	mtx_leave(&d->d_mtx);
52981efa114Sdlg 
530bba3b8d8Sdlg 	if (newdev)
53181efa114Sdlg 		scsi_probe_target(mpath->sc_scsibus, target);
5327e24ec3eSdlg 	else if (addxsh)
5337e24ec3eSdlg 		scsi_xsh_add(&p->p_xsh);
53481efa114Sdlg 
53581efa114Sdlg 	return (0);
53681efa114Sdlg }
53781efa114Sdlg 
53881efa114Sdlg int
mpath_path_detach(struct mpath_path * p)5397e24ec3eSdlg mpath_path_detach(struct mpath_path *p)
54081efa114Sdlg {
5415a08a2c9Sdlg 	struct mpath_group *g = p->p_group;
542b4126379Sjsg 	struct mpath_dev *d;
5437e24ec3eSdlg 	struct mpath_path *np = NULL;
5447e24ec3eSdlg 
5457e24ec3eSdlg #ifdef DIAGNOSTIC
5465a08a2c9Sdlg 	if (g == NULL)
5476ba149ccSphessler 		panic("mpath: detaching a path from a nonexistent bus");
548364ebb70Skrw #endif /* DIAGNOSTIC */
549b4126379Sjsg 	d = g->g_dev;
5505a08a2c9Sdlg 	p->p_group = NULL;
5517e24ec3eSdlg 
5527e24ec3eSdlg 	mtx_enter(&d->d_mtx);
553bba3b8d8Sdlg 	TAILQ_REMOVE(&g->g_paths, p, p_entry);
554bba3b8d8Sdlg 	if (d->d_next_path == p)
555bba3b8d8Sdlg 		d->d_next_path = TAILQ_FIRST(&g->g_paths);
556bba3b8d8Sdlg 
5575a08a2c9Sdlg 	if (TAILQ_EMPTY(&g->g_paths))
558bba3b8d8Sdlg 		TAILQ_REMOVE(&d->d_groups, g, g_entry);
5595a08a2c9Sdlg 	else
5605a08a2c9Sdlg 		g = NULL;
561bba3b8d8Sdlg 
562ecf9d073Sdlg 	if (!SIMPLEQ_EMPTY(&d->d_xfers))
5637e24ec3eSdlg 		np = d->d_next_path;
5647e24ec3eSdlg 	mtx_leave(&d->d_mtx);
5657e24ec3eSdlg 
5665a08a2c9Sdlg 	if (g != NULL)
567a67129dbStedu 		free(g, M_DEVBUF, sizeof(*g));
5685a08a2c9Sdlg 
5697e24ec3eSdlg 	scsi_xsh_del(&p->p_xsh);
5707e24ec3eSdlg 
5715a08a2c9Sdlg 	if (np == NULL)
5725a08a2c9Sdlg 		mpath_failover(d);
5735a08a2c9Sdlg 	else
5747e24ec3eSdlg 		scsi_xsh_add(&np->p_xsh);
5757e24ec3eSdlg 
5767e24ec3eSdlg 	return (0);
5777e24ec3eSdlg }
5787e24ec3eSdlg 
5797e24ec3eSdlg struct device *
mpath_bootdv(struct device * dev)5807e24ec3eSdlg mpath_bootdv(struct device *dev)
5817e24ec3eSdlg {
58207fd7809Sdlg 	struct mpath_softc *sc = mpath;
5837e24ec3eSdlg 	struct mpath_dev *d;
584bba3b8d8Sdlg 	struct mpath_group *g;
5858142e516Sdlg 	struct mpath_path *p;
5868142e516Sdlg 	int target;
5878142e516Sdlg 
58807fd7809Sdlg 	if (sc == NULL)
5897e24ec3eSdlg 		return (dev);
5907e24ec3eSdlg 
5918142e516Sdlg 	for (target = 0; target < MPATH_BUSWIDTH; target++) {
59207fd7809Sdlg 		if ((d = sc->sc_devs[target]) == NULL)
5938142e516Sdlg 			continue;
5948142e516Sdlg 
595bba3b8d8Sdlg 		TAILQ_FOREACH(g, &d->d_groups, g_entry) {
596bba3b8d8Sdlg 			TAILQ_FOREACH(p, &g->g_paths, p_entry) {
5977e24ec3eSdlg 				if (p->p_link->device_softc == dev) {
5987e24ec3eSdlg 					return (scsi_get_link(mpath->sc_scsibus,
5997e24ec3eSdlg 					    target, 0)->device_softc);
6008142e516Sdlg 				}
60181efa114Sdlg 			}
6028142e516Sdlg 		}
603bba3b8d8Sdlg 	}
6048142e516Sdlg 
6057e24ec3eSdlg 	return (dev);
6068142e516Sdlg }
607