xref: /titanic_52/usr/src/uts/common/io/blkdev/blkdev.c (revision ecee5a1fb22b5ba5e3b9bf76dde2c12d7c15632a)
13f7d54a6SGarrett D'Amore /*
23f7d54a6SGarrett D'Amore  * CDDL HEADER START
33f7d54a6SGarrett D'Amore  *
43f7d54a6SGarrett D'Amore  * The contents of this file are subject to the terms of the
53f7d54a6SGarrett D'Amore  * Common Development and Distribution License (the "License").
63f7d54a6SGarrett D'Amore  * You may not use this file except in compliance with the License.
73f7d54a6SGarrett D'Amore  *
83f7d54a6SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93f7d54a6SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
103f7d54a6SGarrett D'Amore  * See the License for the specific language governing permissions
113f7d54a6SGarrett D'Amore  * and limitations under the License.
123f7d54a6SGarrett D'Amore  *
133f7d54a6SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
143f7d54a6SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153f7d54a6SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
163f7d54a6SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
173f7d54a6SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
183f7d54a6SGarrett D'Amore  *
193f7d54a6SGarrett D'Amore  * CDDL HEADER END
203f7d54a6SGarrett D'Amore  */
213f7d54a6SGarrett D'Amore /*
223f7d54a6SGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23cd21e7c5SGarrett D'Amore  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
24679ac156SAlexey Zaytsev  * Copyright 2012 Alexey Zaytsev <alexey.zaytsev@gmail.com> All rights reserved.
251a3a6deeSHans Rosenfeld  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
2610624986SYouzhong Yang  * Copyright 2017 The MathWorks, Inc.  All rights reserved.
2786e3bca6SGarrett D'Amore  */
2886e3bca6SGarrett D'Amore 
293f7d54a6SGarrett D'Amore #include <sys/types.h>
303f7d54a6SGarrett D'Amore #include <sys/ksynch.h>
313f7d54a6SGarrett D'Amore #include <sys/kmem.h>
323f7d54a6SGarrett D'Amore #include <sys/file.h>
333f7d54a6SGarrett D'Amore #include <sys/errno.h>
343f7d54a6SGarrett D'Amore #include <sys/open.h>
353f7d54a6SGarrett D'Amore #include <sys/buf.h>
363f7d54a6SGarrett D'Amore #include <sys/uio.h>
373f7d54a6SGarrett D'Amore #include <sys/aio_req.h>
383f7d54a6SGarrett D'Amore #include <sys/cred.h>
393f7d54a6SGarrett D'Amore #include <sys/modctl.h>
403f7d54a6SGarrett D'Amore #include <sys/cmlb.h>
413f7d54a6SGarrett D'Amore #include <sys/conf.h>
423f7d54a6SGarrett D'Amore #include <sys/devops.h>
433f7d54a6SGarrett D'Amore #include <sys/list.h>
443f7d54a6SGarrett D'Amore #include <sys/sysmacros.h>
453f7d54a6SGarrett D'Amore #include <sys/dkio.h>
463f7d54a6SGarrett D'Amore #include <sys/vtoc.h>
473f7d54a6SGarrett D'Amore #include <sys/scsi/scsi.h>	/* for DTYPE_DIRECT */
483f7d54a6SGarrett D'Amore #include <sys/kstat.h>
493f7d54a6SGarrett D'Amore #include <sys/fs/dv_node.h>
503f7d54a6SGarrett D'Amore #include <sys/ddi.h>
513f7d54a6SGarrett D'Amore #include <sys/sunddi.h>
523f7d54a6SGarrett D'Amore #include <sys/note.h>
533f7d54a6SGarrett D'Amore #include <sys/blkdev.h>
54510a6847SHans Rosenfeld #include <sys/scsi/impl/inquiry.h>
553f7d54a6SGarrett D'Amore 
563f7d54a6SGarrett D'Amore #define	BD_MAXPART	64
573f7d54a6SGarrett D'Amore #define	BDINST(dev)	(getminor(dev) / BD_MAXPART)
583f7d54a6SGarrett D'Amore #define	BDPART(dev)	(getminor(dev) % BD_MAXPART)
593f7d54a6SGarrett D'Amore 
603f7d54a6SGarrett D'Amore typedef struct bd bd_t;
613f7d54a6SGarrett D'Amore typedef struct bd_xfer_impl bd_xfer_impl_t;
623f7d54a6SGarrett D'Amore 
633f7d54a6SGarrett D'Amore struct bd {
643f7d54a6SGarrett D'Amore 	void		*d_private;
653f7d54a6SGarrett D'Amore 	dev_info_t	*d_dip;
663f7d54a6SGarrett D'Amore 	kmutex_t	d_ocmutex;
673f7d54a6SGarrett D'Amore 	kmutex_t	d_iomutex;
68bef9e21aSHans Rosenfeld 	kmutex_t	*d_errmutex;
693f7d54a6SGarrett D'Amore 	kmutex_t	d_statemutex;
703f7d54a6SGarrett D'Amore 	kcondvar_t	d_statecv;
713f7d54a6SGarrett D'Amore 	enum dkio_state	d_state;
723f7d54a6SGarrett D'Amore 	cmlb_handle_t	d_cmlbh;
733f7d54a6SGarrett D'Amore 	unsigned	d_open_lyr[BD_MAXPART];	/* open count */
743f7d54a6SGarrett D'Amore 	uint64_t	d_open_excl;	/* bit mask indexed by partition */
753f7d54a6SGarrett D'Amore 	uint64_t	d_open_reg[OTYPCNT];		/* bit mask */
763f7d54a6SGarrett D'Amore 
773f7d54a6SGarrett D'Amore 	uint32_t	d_qsize;
783f7d54a6SGarrett D'Amore 	uint32_t	d_qactive;
793f7d54a6SGarrett D'Amore 	uint32_t	d_maxxfer;
803f7d54a6SGarrett D'Amore 	uint32_t	d_blkshift;
8132ce6b81SHans Rosenfeld 	uint32_t	d_pblkshift;
823f7d54a6SGarrett D'Amore 	uint64_t	d_numblks;
833f7d54a6SGarrett D'Amore 	ddi_devid_t	d_devid;
843f7d54a6SGarrett D'Amore 
853f7d54a6SGarrett D'Amore 	kmem_cache_t	*d_cache;
863f7d54a6SGarrett D'Amore 	list_t		d_runq;
873f7d54a6SGarrett D'Amore 	list_t		d_waitq;
883f7d54a6SGarrett D'Amore 	kstat_t		*d_ksp;
893f7d54a6SGarrett D'Amore 	kstat_io_t	*d_kiop;
90bef9e21aSHans Rosenfeld 	kstat_t		*d_errstats;
91bef9e21aSHans Rosenfeld 	struct bd_errstats *d_kerr;
923f7d54a6SGarrett D'Amore 
933f7d54a6SGarrett D'Amore 	boolean_t	d_rdonly;
9459d8f100SGarrett D'Amore 	boolean_t	d_ssd;
953f7d54a6SGarrett D'Amore 	boolean_t	d_removable;
963f7d54a6SGarrett D'Amore 	boolean_t	d_hotpluggable;
973f7d54a6SGarrett D'Amore 	boolean_t	d_use_dma;
983f7d54a6SGarrett D'Amore 
993f7d54a6SGarrett D'Amore 	ddi_dma_attr_t	d_dma;
1003f7d54a6SGarrett D'Amore 	bd_ops_t	d_ops;
1013f7d54a6SGarrett D'Amore 	bd_handle_t	d_handle;
1023f7d54a6SGarrett D'Amore };
1033f7d54a6SGarrett D'Amore 
1043f7d54a6SGarrett D'Amore struct bd_handle {
1053f7d54a6SGarrett D'Amore 	bd_ops_t	h_ops;
1063f7d54a6SGarrett D'Amore 	ddi_dma_attr_t	*h_dma;
1073f7d54a6SGarrett D'Amore 	dev_info_t	*h_parent;
1083f7d54a6SGarrett D'Amore 	dev_info_t	*h_child;
1093f7d54a6SGarrett D'Amore 	void		*h_private;
1103f7d54a6SGarrett D'Amore 	bd_t		*h_bd;
1113f7d54a6SGarrett D'Amore 	char		*h_name;
112fa27e351SHans Rosenfeld 	char		h_addr[30];	/* enough for w%0.16x,%X */
1133f7d54a6SGarrett D'Amore };
1143f7d54a6SGarrett D'Amore 
1153f7d54a6SGarrett D'Amore struct bd_xfer_impl {
1163f7d54a6SGarrett D'Amore 	bd_xfer_t	i_public;
1173f7d54a6SGarrett D'Amore 	list_node_t	i_linkage;
1183f7d54a6SGarrett D'Amore 	bd_t		*i_bd;
1193f7d54a6SGarrett D'Amore 	buf_t		*i_bp;
1203f7d54a6SGarrett D'Amore 	uint_t		i_num_win;
1213f7d54a6SGarrett D'Amore 	uint_t		i_cur_win;
1223f7d54a6SGarrett D'Amore 	off_t		i_offset;
1233f7d54a6SGarrett D'Amore 	int		(*i_func)(void *, bd_xfer_t *);
1243f7d54a6SGarrett D'Amore 	uint32_t	i_blkshift;
1253f7d54a6SGarrett D'Amore 	size_t		i_len;
1263f7d54a6SGarrett D'Amore 	size_t		i_resid;
1273f7d54a6SGarrett D'Amore };
1283f7d54a6SGarrett D'Amore 
1293f7d54a6SGarrett D'Amore #define	i_dmah		i_public.x_dmah
1303f7d54a6SGarrett D'Amore #define	i_dmac		i_public.x_dmac
1313f7d54a6SGarrett D'Amore #define	i_ndmac		i_public.x_ndmac
1323f7d54a6SGarrett D'Amore #define	i_kaddr		i_public.x_kaddr
1333f7d54a6SGarrett D'Amore #define	i_nblks		i_public.x_nblks
1343f7d54a6SGarrett D'Amore #define	i_blkno		i_public.x_blkno
13586e3bca6SGarrett D'Amore #define	i_flags		i_public.x_flags
1363f7d54a6SGarrett D'Amore 
1373f7d54a6SGarrett D'Amore 
1383f7d54a6SGarrett D'Amore /*
1393f7d54a6SGarrett D'Amore  * Private prototypes.
1403f7d54a6SGarrett D'Amore  */
1413f7d54a6SGarrett D'Amore 
142510a6847SHans Rosenfeld static void bd_prop_update_inqstring(dev_info_t *, char *, char *, size_t);
143510a6847SHans Rosenfeld static void bd_create_inquiry_props(dev_info_t *, bd_drive_t *);
144bef9e21aSHans Rosenfeld static void bd_create_errstats(bd_t *, int, bd_drive_t *);
145bef9e21aSHans Rosenfeld static void bd_errstats_setstr(kstat_named_t *, char *, size_t, char *);
146bef9e21aSHans Rosenfeld static void bd_init_errstats(bd_t *, bd_drive_t *);
147510a6847SHans Rosenfeld 
1483f7d54a6SGarrett D'Amore static int bd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1493f7d54a6SGarrett D'Amore static int bd_attach(dev_info_t *, ddi_attach_cmd_t);
1503f7d54a6SGarrett D'Amore static int bd_detach(dev_info_t *, ddi_detach_cmd_t);
1513f7d54a6SGarrett D'Amore 
1523f7d54a6SGarrett D'Amore static int bd_open(dev_t *, int, int, cred_t *);
1533f7d54a6SGarrett D'Amore static int bd_close(dev_t, int, int, cred_t *);
1543f7d54a6SGarrett D'Amore static int bd_strategy(struct buf *);
1553f7d54a6SGarrett D'Amore static int bd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
15686e3bca6SGarrett D'Amore static int bd_dump(dev_t, caddr_t, daddr_t, int);
1573f7d54a6SGarrett D'Amore static int bd_read(dev_t, struct uio *, cred_t *);
1583f7d54a6SGarrett D'Amore static int bd_write(dev_t, struct uio *, cred_t *);
1593f7d54a6SGarrett D'Amore static int bd_aread(dev_t, struct aio_req *, cred_t *);
1603f7d54a6SGarrett D'Amore static int bd_awrite(dev_t, struct aio_req *, cred_t *);
1613f7d54a6SGarrett D'Amore static int bd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
1623f7d54a6SGarrett D'Amore     caddr_t, int *);
1633f7d54a6SGarrett D'Amore 
1643f7d54a6SGarrett D'Amore static int bd_tg_rdwr(dev_info_t *, uchar_t, void *, diskaddr_t, size_t,
1653f7d54a6SGarrett D'Amore     void *);
1663f7d54a6SGarrett D'Amore static int bd_tg_getinfo(dev_info_t *, int, void *, void *);
1673f7d54a6SGarrett D'Amore static int bd_xfer_ctor(void *, void *, int);
1683f7d54a6SGarrett D'Amore static void bd_xfer_dtor(void *, void *);
1693f7d54a6SGarrett D'Amore static void bd_sched(bd_t *);
1703f7d54a6SGarrett D'Amore static void bd_submit(bd_t *, bd_xfer_impl_t *);
1713f7d54a6SGarrett D'Amore static void bd_runq_exit(bd_xfer_impl_t *, int);
1723f7d54a6SGarrett D'Amore static void bd_update_state(bd_t *);
1733f7d54a6SGarrett D'Amore static int bd_check_state(bd_t *, enum dkio_state *);
1743f7d54a6SGarrett D'Amore static int bd_flush_write_cache(bd_t *, struct dk_callback *);
17510624986SYouzhong Yang static int bd_check_uio(dev_t, struct uio *);
1763f7d54a6SGarrett D'Amore 
1773f7d54a6SGarrett D'Amore struct cmlb_tg_ops bd_tg_ops = {
1783f7d54a6SGarrett D'Amore 	TG_DK_OPS_VERSION_1,
1793f7d54a6SGarrett D'Amore 	bd_tg_rdwr,
1803f7d54a6SGarrett D'Amore 	bd_tg_getinfo,
1813f7d54a6SGarrett D'Amore };
1823f7d54a6SGarrett D'Amore 
1833f7d54a6SGarrett D'Amore static struct cb_ops bd_cb_ops = {
1843f7d54a6SGarrett D'Amore 	bd_open, 		/* open */
1853f7d54a6SGarrett D'Amore 	bd_close, 		/* close */
1863f7d54a6SGarrett D'Amore 	bd_strategy, 		/* strategy */
1873f7d54a6SGarrett D'Amore 	nodev, 			/* print */
18886e3bca6SGarrett D'Amore 	bd_dump,		/* dump */
1893f7d54a6SGarrett D'Amore 	bd_read, 		/* read */
1903f7d54a6SGarrett D'Amore 	bd_write, 		/* write */
1913f7d54a6SGarrett D'Amore 	bd_ioctl, 		/* ioctl */
1923f7d54a6SGarrett D'Amore 	nodev, 			/* devmap */
1933f7d54a6SGarrett D'Amore 	nodev, 			/* mmap */
1943f7d54a6SGarrett D'Amore 	nodev, 			/* segmap */
1953f7d54a6SGarrett D'Amore 	nochpoll, 		/* poll */
1963f7d54a6SGarrett D'Amore 	bd_prop_op, 		/* cb_prop_op */
1973f7d54a6SGarrett D'Amore 	0, 			/* streamtab  */
1983f7d54a6SGarrett D'Amore 	D_64BIT | D_MP,		/* Driver comaptibility flag */
1993f7d54a6SGarrett D'Amore 	CB_REV,			/* cb_rev */
2003f7d54a6SGarrett D'Amore 	bd_aread,		/* async read */
2013f7d54a6SGarrett D'Amore 	bd_awrite		/* async write */
2023f7d54a6SGarrett D'Amore };
2033f7d54a6SGarrett D'Amore 
2043f7d54a6SGarrett D'Amore struct dev_ops bd_dev_ops = {
2053f7d54a6SGarrett D'Amore 	DEVO_REV, 		/* devo_rev, */
2063f7d54a6SGarrett D'Amore 	0, 			/* refcnt  */
2073f7d54a6SGarrett D'Amore 	bd_getinfo,		/* getinfo */
2083f7d54a6SGarrett D'Amore 	nulldev, 		/* identify */
2093f7d54a6SGarrett D'Amore 	nulldev, 		/* probe */
2103f7d54a6SGarrett D'Amore 	bd_attach, 		/* attach */
2113f7d54a6SGarrett D'Amore 	bd_detach,		/* detach */
2123f7d54a6SGarrett D'Amore 	nodev, 			/* reset */
2133f7d54a6SGarrett D'Amore 	&bd_cb_ops, 		/* driver operations */
2143f7d54a6SGarrett D'Amore 	NULL,			/* bus operations */
2153f7d54a6SGarrett D'Amore 	NULL,			/* power */
2163f7d54a6SGarrett D'Amore 	ddi_quiesce_not_needed,	/* quiesce */
2173f7d54a6SGarrett D'Amore };
2183f7d54a6SGarrett D'Amore 
2193f7d54a6SGarrett D'Amore static struct modldrv modldrv = {
2203f7d54a6SGarrett D'Amore 	&mod_driverops,
2213f7d54a6SGarrett D'Amore 	"Generic Block Device",
2223f7d54a6SGarrett D'Amore 	&bd_dev_ops,
2233f7d54a6SGarrett D'Amore };
2243f7d54a6SGarrett D'Amore 
2253f7d54a6SGarrett D'Amore static struct modlinkage modlinkage = {
2263f7d54a6SGarrett D'Amore 	MODREV_1, { &modldrv, NULL }
2273f7d54a6SGarrett D'Amore };
2283f7d54a6SGarrett D'Amore 
2293f7d54a6SGarrett D'Amore static void *bd_state;
2303f7d54a6SGarrett D'Amore static krwlock_t bd_lock;
2313f7d54a6SGarrett D'Amore 
2323f7d54a6SGarrett D'Amore int
2333f7d54a6SGarrett D'Amore _init(void)
2343f7d54a6SGarrett D'Amore {
2353f7d54a6SGarrett D'Amore 	int	rv;
2363f7d54a6SGarrett D'Amore 
2373f7d54a6SGarrett D'Amore 	rv = ddi_soft_state_init(&bd_state, sizeof (struct bd), 2);
2383f7d54a6SGarrett D'Amore 	if (rv != DDI_SUCCESS) {
2393f7d54a6SGarrett D'Amore 		return (rv);
2403f7d54a6SGarrett D'Amore 	}
2413f7d54a6SGarrett D'Amore 	rw_init(&bd_lock, NULL, RW_DRIVER, NULL);
2423f7d54a6SGarrett D'Amore 	rv = mod_install(&modlinkage);
2433f7d54a6SGarrett D'Amore 	if (rv != DDI_SUCCESS) {
2443f7d54a6SGarrett D'Amore 		rw_destroy(&bd_lock);
2453f7d54a6SGarrett D'Amore 		ddi_soft_state_fini(&bd_state);
2463f7d54a6SGarrett D'Amore 	}
2473f7d54a6SGarrett D'Amore 	return (rv);
2483f7d54a6SGarrett D'Amore }
2493f7d54a6SGarrett D'Amore 
2503f7d54a6SGarrett D'Amore int
2513f7d54a6SGarrett D'Amore _fini(void)
2523f7d54a6SGarrett D'Amore {
2533f7d54a6SGarrett D'Amore 	int	rv;
2543f7d54a6SGarrett D'Amore 
2553f7d54a6SGarrett D'Amore 	rv = mod_remove(&modlinkage);
2563f7d54a6SGarrett D'Amore 	if (rv == DDI_SUCCESS) {
2573f7d54a6SGarrett D'Amore 		rw_destroy(&bd_lock);
2583f7d54a6SGarrett D'Amore 		ddi_soft_state_fini(&bd_state);
2593f7d54a6SGarrett D'Amore 	}
2603f7d54a6SGarrett D'Amore 	return (rv);
2613f7d54a6SGarrett D'Amore }
2623f7d54a6SGarrett D'Amore 
2633f7d54a6SGarrett D'Amore int
2643f7d54a6SGarrett D'Amore _info(struct modinfo *modinfop)
2653f7d54a6SGarrett D'Amore {
2663f7d54a6SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
2673f7d54a6SGarrett D'Amore }
2683f7d54a6SGarrett D'Amore 
2693f7d54a6SGarrett D'Amore static int
2703f7d54a6SGarrett D'Amore bd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2713f7d54a6SGarrett D'Amore {
2723f7d54a6SGarrett D'Amore 	bd_t	*bd;
2733f7d54a6SGarrett D'Amore 	minor_t	inst;
2743f7d54a6SGarrett D'Amore 
2753f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(dip));
2763f7d54a6SGarrett D'Amore 
2773f7d54a6SGarrett D'Amore 	inst = BDINST((dev_t)arg);
2783f7d54a6SGarrett D'Amore 
2793f7d54a6SGarrett D'Amore 	switch (cmd) {
2803f7d54a6SGarrett D'Amore 	case DDI_INFO_DEVT2DEVINFO:
2813f7d54a6SGarrett D'Amore 		bd = ddi_get_soft_state(bd_state, inst);
2823f7d54a6SGarrett D'Amore 		if (bd == NULL) {
2833f7d54a6SGarrett D'Amore 			return (DDI_FAILURE);
2843f7d54a6SGarrett D'Amore 		}
2853f7d54a6SGarrett D'Amore 		*resultp = (void *)bd->d_dip;
2863f7d54a6SGarrett D'Amore 		break;
2873f7d54a6SGarrett D'Amore 
2883f7d54a6SGarrett D'Amore 	case DDI_INFO_DEVT2INSTANCE:
2893f7d54a6SGarrett D'Amore 		*resultp = (void *)(intptr_t)inst;
2903f7d54a6SGarrett D'Amore 		break;
2913f7d54a6SGarrett D'Amore 
2923f7d54a6SGarrett D'Amore 	default:
2933f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
2943f7d54a6SGarrett D'Amore 	}
2953f7d54a6SGarrett D'Amore 	return (DDI_SUCCESS);
2963f7d54a6SGarrett D'Amore }
2973f7d54a6SGarrett D'Amore 
298510a6847SHans Rosenfeld static void
299510a6847SHans Rosenfeld bd_prop_update_inqstring(dev_info_t *dip, char *name, char *data, size_t len)
300510a6847SHans Rosenfeld {
301510a6847SHans Rosenfeld 	int	ilen;
302510a6847SHans Rosenfeld 	char	*data_string;
303510a6847SHans Rosenfeld 
304510a6847SHans Rosenfeld 	ilen = scsi_ascii_inquiry_len(data, len);
305510a6847SHans Rosenfeld 	ASSERT3U(ilen, <=, len);
306510a6847SHans Rosenfeld 	if (ilen <= 0)
307510a6847SHans Rosenfeld 		return;
308510a6847SHans Rosenfeld 	/* ensure null termination */
309510a6847SHans Rosenfeld 	data_string = kmem_zalloc(ilen + 1, KM_SLEEP);
310510a6847SHans Rosenfeld 	bcopy(data, data_string, ilen);
311510a6847SHans Rosenfeld 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, name, data_string);
312510a6847SHans Rosenfeld 	kmem_free(data_string, ilen + 1);
313510a6847SHans Rosenfeld }
314510a6847SHans Rosenfeld 
315510a6847SHans Rosenfeld static void
316510a6847SHans Rosenfeld bd_create_inquiry_props(dev_info_t *dip, bd_drive_t *drive)
317510a6847SHans Rosenfeld {
318510a6847SHans Rosenfeld 	if (drive->d_vendor_len > 0)
319510a6847SHans Rosenfeld 		bd_prop_update_inqstring(dip, INQUIRY_VENDOR_ID,
320510a6847SHans Rosenfeld 		    drive->d_vendor, drive->d_vendor_len);
321510a6847SHans Rosenfeld 
322510a6847SHans Rosenfeld 	if (drive->d_product_len > 0)
323510a6847SHans Rosenfeld 		bd_prop_update_inqstring(dip, INQUIRY_PRODUCT_ID,
324510a6847SHans Rosenfeld 		    drive->d_product, drive->d_product_len);
325510a6847SHans Rosenfeld 
326510a6847SHans Rosenfeld 	if (drive->d_serial_len > 0)
327510a6847SHans Rosenfeld 		bd_prop_update_inqstring(dip, INQUIRY_SERIAL_NO,
328510a6847SHans Rosenfeld 		    drive->d_serial, drive->d_serial_len);
329510a6847SHans Rosenfeld 
330510a6847SHans Rosenfeld 	if (drive->d_revision_len > 0)
331510a6847SHans Rosenfeld 		bd_prop_update_inqstring(dip, INQUIRY_REVISION_ID,
332510a6847SHans Rosenfeld 		    drive->d_revision, drive->d_revision_len);
333510a6847SHans Rosenfeld }
334510a6847SHans Rosenfeld 
335bef9e21aSHans Rosenfeld static void
336bef9e21aSHans Rosenfeld bd_create_errstats(bd_t *bd, int inst, bd_drive_t *drive)
337bef9e21aSHans Rosenfeld {
338bef9e21aSHans Rosenfeld 	char	ks_module[KSTAT_STRLEN];
339bef9e21aSHans Rosenfeld 	char	ks_name[KSTAT_STRLEN];
340bef9e21aSHans Rosenfeld 	int	ndata = sizeof (struct bd_errstats) / sizeof (kstat_named_t);
341bef9e21aSHans Rosenfeld 
342bef9e21aSHans Rosenfeld 	if (bd->d_errstats != NULL)
343bef9e21aSHans Rosenfeld 		return;
344bef9e21aSHans Rosenfeld 
345bef9e21aSHans Rosenfeld 	(void) snprintf(ks_module, sizeof (ks_module), "%serr",
346bef9e21aSHans Rosenfeld 	    ddi_driver_name(bd->d_dip));
347bef9e21aSHans Rosenfeld 	(void) snprintf(ks_name, sizeof (ks_name), "%s%d,err",
348bef9e21aSHans Rosenfeld 	    ddi_driver_name(bd->d_dip), inst);
349bef9e21aSHans Rosenfeld 
350bef9e21aSHans Rosenfeld 	bd->d_errstats = kstat_create(ks_module, inst, ks_name, "device_error",
351bef9e21aSHans Rosenfeld 	    KSTAT_TYPE_NAMED, ndata, KSTAT_FLAG_PERSISTENT);
352bef9e21aSHans Rosenfeld 
353bef9e21aSHans Rosenfeld 	if (bd->d_errstats == NULL) {
354bef9e21aSHans Rosenfeld 		/*
355bef9e21aSHans Rosenfeld 		 * Even if we cannot create the kstat, we create a
356bef9e21aSHans Rosenfeld 		 * scratch kstat.  The reason for this is to ensure
357bef9e21aSHans Rosenfeld 		 * that we can update the kstat all of the time,
358bef9e21aSHans Rosenfeld 		 * without adding an extra branch instruction.
359bef9e21aSHans Rosenfeld 		 */
360bef9e21aSHans Rosenfeld 		bd->d_kerr = kmem_zalloc(sizeof (struct bd_errstats),
361bef9e21aSHans Rosenfeld 		    KM_SLEEP);
362bef9e21aSHans Rosenfeld 		bd->d_errmutex = kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
363bef9e21aSHans Rosenfeld 		mutex_init(bd->d_errmutex, NULL, MUTEX_DRIVER, NULL);
364bef9e21aSHans Rosenfeld 	} else {
365bef9e21aSHans Rosenfeld 		if (bd->d_errstats->ks_lock == NULL) {
366bef9e21aSHans Rosenfeld 			bd->d_errstats->ks_lock = kmem_zalloc(sizeof (kmutex_t),
367bef9e21aSHans Rosenfeld 			    KM_SLEEP);
368bef9e21aSHans Rosenfeld 			mutex_init(bd->d_errstats->ks_lock, NULL, MUTEX_DRIVER,
369bef9e21aSHans Rosenfeld 			    NULL);
370bef9e21aSHans Rosenfeld 		}
371bef9e21aSHans Rosenfeld 
372bef9e21aSHans Rosenfeld 		bd->d_errmutex = bd->d_errstats->ks_lock;
373bef9e21aSHans Rosenfeld 		bd->d_kerr = (struct bd_errstats *)bd->d_errstats->ks_data;
374bef9e21aSHans Rosenfeld 	}
375bef9e21aSHans Rosenfeld 
376bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_softerrs,	"Soft Errors",
377bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
378bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_harderrs,	"Hard Errors",
379bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
380bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_transerrs,	"Transport Errors",
381bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
382bef9e21aSHans Rosenfeld 
383bef9e21aSHans Rosenfeld 	if (drive->d_model_len > 0) {
384bef9e21aSHans Rosenfeld 		kstat_named_init(&bd->d_kerr->bd_model,	"Model",
385bef9e21aSHans Rosenfeld 		    KSTAT_DATA_STRING);
386bef9e21aSHans Rosenfeld 	} else {
387bef9e21aSHans Rosenfeld 		kstat_named_init(&bd->d_kerr->bd_vid,	"Vendor",
388bef9e21aSHans Rosenfeld 		    KSTAT_DATA_STRING);
389bef9e21aSHans Rosenfeld 		kstat_named_init(&bd->d_kerr->bd_pid,	"Product",
390bef9e21aSHans Rosenfeld 		    KSTAT_DATA_STRING);
391bef9e21aSHans Rosenfeld 	}
392bef9e21aSHans Rosenfeld 
393bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_revision,	"Revision",
394bef9e21aSHans Rosenfeld 	    KSTAT_DATA_STRING);
395bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_serial,	"Serial No",
396bef9e21aSHans Rosenfeld 	    KSTAT_DATA_STRING);
397bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_capacity,	"Size",
398bef9e21aSHans Rosenfeld 	    KSTAT_DATA_ULONGLONG);
399bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_media_err,	"Media Error",
400bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
401bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_ntrdy_err,	"Device Not Ready",
402bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
403bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_nodev_err,	"No Device",
404bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
405bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_recov_err,	"Recoverable",
406bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
407bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_illrq_err,	"Illegal Request",
408bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
409bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_pfa_err,
410bef9e21aSHans Rosenfeld 	    "Predictive Failure Analysis", KSTAT_DATA_UINT32);
411bef9e21aSHans Rosenfeld 
412bef9e21aSHans Rosenfeld 	bd->d_errstats->ks_private = bd;
413bef9e21aSHans Rosenfeld 
414bef9e21aSHans Rosenfeld 	kstat_install(bd->d_errstats);
415bef9e21aSHans Rosenfeld }
416bef9e21aSHans Rosenfeld 
417bef9e21aSHans Rosenfeld static void
418bef9e21aSHans Rosenfeld bd_errstats_setstr(kstat_named_t *k, char *str, size_t len, char *alt)
419bef9e21aSHans Rosenfeld {
420bef9e21aSHans Rosenfeld 	char	*tmp;
421bef9e21aSHans Rosenfeld 
422bef9e21aSHans Rosenfeld 	if (KSTAT_NAMED_STR_PTR(k) == NULL) {
423bef9e21aSHans Rosenfeld 		if (len > 0) {
424bef9e21aSHans Rosenfeld 			tmp = kmem_alloc(len + 1, KM_SLEEP);
4251a3a6deeSHans Rosenfeld 			(void) strlcpy(tmp, str, len + 1);
426bef9e21aSHans Rosenfeld 		} else {
427bef9e21aSHans Rosenfeld 			tmp = alt;
428bef9e21aSHans Rosenfeld 		}
429bef9e21aSHans Rosenfeld 
430bef9e21aSHans Rosenfeld 		kstat_named_setstr(k, tmp);
431bef9e21aSHans Rosenfeld 	}
432bef9e21aSHans Rosenfeld }
433bef9e21aSHans Rosenfeld 
434bef9e21aSHans Rosenfeld static void
435bef9e21aSHans Rosenfeld bd_init_errstats(bd_t *bd, bd_drive_t *drive)
436bef9e21aSHans Rosenfeld {
437bef9e21aSHans Rosenfeld 	struct bd_errstats	*est = bd->d_kerr;
438bef9e21aSHans Rosenfeld 
439bef9e21aSHans Rosenfeld 	mutex_enter(bd->d_errmutex);
440bef9e21aSHans Rosenfeld 
441bef9e21aSHans Rosenfeld 	if (drive->d_model_len > 0 &&
442bef9e21aSHans Rosenfeld 	    KSTAT_NAMED_STR_PTR(&est->bd_model) == NULL) {
443bef9e21aSHans Rosenfeld 		bd_errstats_setstr(&est->bd_model, drive->d_model,
444bef9e21aSHans Rosenfeld 		    drive->d_model_len, NULL);
445bef9e21aSHans Rosenfeld 	} else {
446bef9e21aSHans Rosenfeld 		bd_errstats_setstr(&est->bd_vid, drive->d_vendor,
447bef9e21aSHans Rosenfeld 		    drive->d_vendor_len, "Unknown ");
448bef9e21aSHans Rosenfeld 		bd_errstats_setstr(&est->bd_pid, drive->d_product,
449bef9e21aSHans Rosenfeld 		    drive->d_product_len, "Unknown         ");
450bef9e21aSHans Rosenfeld 	}
451bef9e21aSHans Rosenfeld 
452bef9e21aSHans Rosenfeld 	bd_errstats_setstr(&est->bd_revision, drive->d_revision,
453bef9e21aSHans Rosenfeld 	    drive->d_revision_len, "0001");
454bef9e21aSHans Rosenfeld 	bd_errstats_setstr(&est->bd_serial, drive->d_serial,
455bef9e21aSHans Rosenfeld 	    drive->d_serial_len, "0               ");
456bef9e21aSHans Rosenfeld 
457bef9e21aSHans Rosenfeld 	mutex_exit(bd->d_errmutex);
458bef9e21aSHans Rosenfeld }
459bef9e21aSHans Rosenfeld 
4603f7d54a6SGarrett D'Amore static int
4613f7d54a6SGarrett D'Amore bd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4623f7d54a6SGarrett D'Amore {
4633f7d54a6SGarrett D'Amore 	int		inst;
4643f7d54a6SGarrett D'Amore 	bd_handle_t	hdl;
4653f7d54a6SGarrett D'Amore 	bd_t		*bd;
4663f7d54a6SGarrett D'Amore 	bd_drive_t	drive;
4673f7d54a6SGarrett D'Amore 	int		rv;
4683f7d54a6SGarrett D'Amore 	char		name[16];
4693f7d54a6SGarrett D'Amore 	char		kcache[32];
4703f7d54a6SGarrett D'Amore 
4713f7d54a6SGarrett D'Amore 	switch (cmd) {
4723f7d54a6SGarrett D'Amore 	case DDI_ATTACH:
4733f7d54a6SGarrett D'Amore 		break;
4743f7d54a6SGarrett D'Amore 	case DDI_RESUME:
4753f7d54a6SGarrett D'Amore 		/* We don't do anything native for suspend/resume */
4763f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
4773f7d54a6SGarrett D'Amore 	default:
4783f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
4793f7d54a6SGarrett D'Amore 	}
4803f7d54a6SGarrett D'Amore 
4813f7d54a6SGarrett D'Amore 	inst = ddi_get_instance(dip);
4823f7d54a6SGarrett D'Amore 	hdl = ddi_get_parent_data(dip);
4833f7d54a6SGarrett D'Amore 
4843f7d54a6SGarrett D'Amore 	(void) snprintf(name, sizeof (name), "%s%d",
4853f7d54a6SGarrett D'Amore 	    ddi_driver_name(dip), ddi_get_instance(dip));
4863f7d54a6SGarrett D'Amore 	(void) snprintf(kcache, sizeof (kcache), "%s_xfer", name);
4873f7d54a6SGarrett D'Amore 
4883f7d54a6SGarrett D'Amore 	if (hdl == NULL) {
4893f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s: missing parent data!", name);
4903f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
4913f7d54a6SGarrett D'Amore 	}
4923f7d54a6SGarrett D'Amore 
4933f7d54a6SGarrett D'Amore 	if (ddi_soft_state_zalloc(bd_state, inst) != DDI_SUCCESS) {
4943f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s: unable to zalloc soft state!", name);
4953f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
4963f7d54a6SGarrett D'Amore 	}
4973f7d54a6SGarrett D'Amore 	bd = ddi_get_soft_state(bd_state, inst);
4983f7d54a6SGarrett D'Amore 
4993f7d54a6SGarrett D'Amore 	if (hdl->h_dma) {
5003f7d54a6SGarrett D'Amore 		bd->d_dma = *(hdl->h_dma);
5013f7d54a6SGarrett D'Amore 		bd->d_dma.dma_attr_granular =
5023f7d54a6SGarrett D'Amore 		    max(DEV_BSIZE, bd->d_dma.dma_attr_granular);
5033f7d54a6SGarrett D'Amore 		bd->d_use_dma = B_TRUE;
5043f7d54a6SGarrett D'Amore 
5053f7d54a6SGarrett D'Amore 		if (bd->d_maxxfer &&
5063f7d54a6SGarrett D'Amore 		    (bd->d_maxxfer != bd->d_dma.dma_attr_maxxfer)) {
5073f7d54a6SGarrett D'Amore 			cmn_err(CE_WARN,
5083f7d54a6SGarrett D'Amore 			    "%s: inconsistent maximum transfer size!",
5093f7d54a6SGarrett D'Amore 			    name);
5103f7d54a6SGarrett D'Amore 			/* We force it */
5113f7d54a6SGarrett D'Amore 			bd->d_maxxfer = bd->d_dma.dma_attr_maxxfer;
5123f7d54a6SGarrett D'Amore 		} else {
5133f7d54a6SGarrett D'Amore 			bd->d_maxxfer = bd->d_dma.dma_attr_maxxfer;
5143f7d54a6SGarrett D'Amore 		}
5153f7d54a6SGarrett D'Amore 	} else {
5163f7d54a6SGarrett D'Amore 		bd->d_use_dma = B_FALSE;
5173f7d54a6SGarrett D'Amore 		if (bd->d_maxxfer == 0) {
5183f7d54a6SGarrett D'Amore 			bd->d_maxxfer = 1024 * 1024;
5193f7d54a6SGarrett D'Amore 		}
5203f7d54a6SGarrett D'Amore 	}
5213f7d54a6SGarrett D'Amore 	bd->d_ops = hdl->h_ops;
5223f7d54a6SGarrett D'Amore 	bd->d_private = hdl->h_private;
5233f7d54a6SGarrett D'Amore 	bd->d_blkshift = 9;	/* 512 bytes, to start */
5243f7d54a6SGarrett D'Amore 
5253f7d54a6SGarrett D'Amore 	if (bd->d_maxxfer % DEV_BSIZE) {
5263f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s: maximum transfer misaligned!", name);
5273f7d54a6SGarrett D'Amore 		bd->d_maxxfer &= ~(DEV_BSIZE - 1);
5283f7d54a6SGarrett D'Amore 	}
5293f7d54a6SGarrett D'Amore 	if (bd->d_maxxfer < DEV_BSIZE) {
5303f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s: maximum transfer size too small!", name);
5313f7d54a6SGarrett D'Amore 		ddi_soft_state_free(bd_state, inst);
5323f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
5333f7d54a6SGarrett D'Amore 	}
5343f7d54a6SGarrett D'Amore 
5353f7d54a6SGarrett D'Amore 	bd->d_dip = dip;
5363f7d54a6SGarrett D'Amore 	bd->d_handle = hdl;
5373f7d54a6SGarrett D'Amore 	hdl->h_bd = bd;
5383f7d54a6SGarrett D'Amore 	ddi_set_driver_private(dip, bd);
5393f7d54a6SGarrett D'Amore 
5403f7d54a6SGarrett D'Amore 	mutex_init(&bd->d_iomutex, NULL, MUTEX_DRIVER, NULL);
5413f7d54a6SGarrett D'Amore 	mutex_init(&bd->d_ocmutex, NULL, MUTEX_DRIVER, NULL);
5423f7d54a6SGarrett D'Amore 	mutex_init(&bd->d_statemutex, NULL, MUTEX_DRIVER, NULL);
5433f7d54a6SGarrett D'Amore 	cv_init(&bd->d_statecv, NULL, CV_DRIVER, NULL);
5443f7d54a6SGarrett D'Amore 
5453f7d54a6SGarrett D'Amore 	list_create(&bd->d_waitq, sizeof (bd_xfer_impl_t),
5463f7d54a6SGarrett D'Amore 	    offsetof(struct bd_xfer_impl, i_linkage));
5473f7d54a6SGarrett D'Amore 	list_create(&bd->d_runq, sizeof (bd_xfer_impl_t),
5483f7d54a6SGarrett D'Amore 	    offsetof(struct bd_xfer_impl, i_linkage));
5493f7d54a6SGarrett D'Amore 
5503f7d54a6SGarrett D'Amore 	bd->d_cache = kmem_cache_create(kcache, sizeof (bd_xfer_impl_t), 8,
5513f7d54a6SGarrett D'Amore 	    bd_xfer_ctor, bd_xfer_dtor, NULL, bd, NULL, 0);
5523f7d54a6SGarrett D'Amore 
5533f7d54a6SGarrett D'Amore 	bd->d_ksp = kstat_create(ddi_driver_name(dip), inst, NULL, "disk",
5543f7d54a6SGarrett D'Amore 	    KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
5553f7d54a6SGarrett D'Amore 	if (bd->d_ksp != NULL) {
5563f7d54a6SGarrett D'Amore 		bd->d_ksp->ks_lock = &bd->d_iomutex;
5573f7d54a6SGarrett D'Amore 		kstat_install(bd->d_ksp);
5583f7d54a6SGarrett D'Amore 		bd->d_kiop = bd->d_ksp->ks_data;
5593f7d54a6SGarrett D'Amore 	} else {
5603f7d54a6SGarrett D'Amore 		/*
5613f7d54a6SGarrett D'Amore 		 * Even if we cannot create the kstat, we create a
5623f7d54a6SGarrett D'Amore 		 * scratch kstat.  The reason for this is to ensure
5633f7d54a6SGarrett D'Amore 		 * that we can update the kstat all of the time,
5643f7d54a6SGarrett D'Amore 		 * without adding an extra branch instruction.
5653f7d54a6SGarrett D'Amore 		 */
5663f7d54a6SGarrett D'Amore 		bd->d_kiop = kmem_zalloc(sizeof (kstat_io_t), KM_SLEEP);
5673f7d54a6SGarrett D'Amore 	}
5683f7d54a6SGarrett D'Amore 
5693f7d54a6SGarrett D'Amore 	cmlb_alloc_handle(&bd->d_cmlbh);
5703f7d54a6SGarrett D'Amore 
5713f7d54a6SGarrett D'Amore 	bd->d_state = DKIO_NONE;
5723f7d54a6SGarrett D'Amore 
5733f7d54a6SGarrett D'Amore 	bzero(&drive, sizeof (drive));
5743f7d54a6SGarrett D'Amore 	bd->d_ops.o_drive_info(bd->d_private, &drive);
5753f7d54a6SGarrett D'Amore 	bd->d_qsize = drive.d_qsize;
5763f7d54a6SGarrett D'Amore 	bd->d_removable = drive.d_removable;
5773f7d54a6SGarrett D'Amore 	bd->d_hotpluggable = drive.d_hotpluggable;
5783f7d54a6SGarrett D'Amore 
579bc220884SAlexey Zaytsev 	if (drive.d_maxxfer && drive.d_maxxfer < bd->d_maxxfer)
580bc220884SAlexey Zaytsev 		bd->d_maxxfer = drive.d_maxxfer;
581bc220884SAlexey Zaytsev 
582510a6847SHans Rosenfeld 	bd_create_inquiry_props(dip, &drive);
583bc220884SAlexey Zaytsev 
584bef9e21aSHans Rosenfeld 	bd_create_errstats(bd, inst, &drive);
585bef9e21aSHans Rosenfeld 	bd_init_errstats(bd, &drive);
586bef9e21aSHans Rosenfeld 	bd_update_state(bd);
587bef9e21aSHans Rosenfeld 
5883f7d54a6SGarrett D'Amore 	rv = cmlb_attach(dip, &bd_tg_ops, DTYPE_DIRECT,
5893f7d54a6SGarrett D'Amore 	    bd->d_removable, bd->d_hotpluggable,
590fa27e351SHans Rosenfeld 	    /*LINTED: E_BAD_PTR_CAST_ALIGN*/
591fa27e351SHans Rosenfeld 	    *(uint64_t *)drive.d_eui64 != 0 ? DDI_NT_BLOCK_BLKDEV :
5923f7d54a6SGarrett D'Amore 	    drive.d_lun >= 0 ? DDI_NT_BLOCK_CHAN : DDI_NT_BLOCK,
59386e3bca6SGarrett D'Amore 	    CMLB_FAKE_LABEL_ONE_PARTITION, bd->d_cmlbh, 0);
5943f7d54a6SGarrett D'Amore 	if (rv != 0) {
5953f7d54a6SGarrett D'Amore 		cmlb_free_handle(&bd->d_cmlbh);
5963f7d54a6SGarrett D'Amore 		kmem_cache_destroy(bd->d_cache);
5973f7d54a6SGarrett D'Amore 		mutex_destroy(&bd->d_iomutex);
5983f7d54a6SGarrett D'Amore 		mutex_destroy(&bd->d_ocmutex);
5993f7d54a6SGarrett D'Amore 		mutex_destroy(&bd->d_statemutex);
6003f7d54a6SGarrett D'Amore 		cv_destroy(&bd->d_statecv);
6013f7d54a6SGarrett D'Amore 		list_destroy(&bd->d_waitq);
6023f7d54a6SGarrett D'Amore 		list_destroy(&bd->d_runq);
6033f7d54a6SGarrett D'Amore 		if (bd->d_ksp != NULL) {
6043f7d54a6SGarrett D'Amore 			kstat_delete(bd->d_ksp);
6053f7d54a6SGarrett D'Amore 			bd->d_ksp = NULL;
6063f7d54a6SGarrett D'Amore 		} else {
6073f7d54a6SGarrett D'Amore 			kmem_free(bd->d_kiop, sizeof (kstat_io_t));
6083f7d54a6SGarrett D'Amore 		}
6093f7d54a6SGarrett D'Amore 		ddi_soft_state_free(bd_state, inst);
6103f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
6113f7d54a6SGarrett D'Amore 	}
6123f7d54a6SGarrett D'Amore 
6133f7d54a6SGarrett D'Amore 	if (bd->d_ops.o_devid_init != NULL) {
6143f7d54a6SGarrett D'Amore 		rv = bd->d_ops.o_devid_init(bd->d_private, dip, &bd->d_devid);
6153f7d54a6SGarrett D'Amore 		if (rv == DDI_SUCCESS) {
6163f7d54a6SGarrett D'Amore 			if (ddi_devid_register(dip, bd->d_devid) !=
6173f7d54a6SGarrett D'Amore 			    DDI_SUCCESS) {
6183f7d54a6SGarrett D'Amore 				cmn_err(CE_WARN,
6193f7d54a6SGarrett D'Amore 				    "%s: unable to register devid", name);
6203f7d54a6SGarrett D'Amore 			}
6213f7d54a6SGarrett D'Amore 		}
6223f7d54a6SGarrett D'Amore 	}
6233f7d54a6SGarrett D'Amore 
6243f7d54a6SGarrett D'Amore 	/*
6253f7d54a6SGarrett D'Amore 	 * Add a zero-length attribute to tell the world we support
6263f7d54a6SGarrett D'Amore 	 * kernel ioctls (for layered drivers).  Also set up properties
6273f7d54a6SGarrett D'Amore 	 * used by HAL to identify removable media.
6283f7d54a6SGarrett D'Amore 	 */
6293f7d54a6SGarrett D'Amore 	(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
6303f7d54a6SGarrett D'Amore 	    DDI_KERNEL_IOCTL, NULL, 0);
6313f7d54a6SGarrett D'Amore 	if (bd->d_removable) {
6323f7d54a6SGarrett D'Amore 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
6333f7d54a6SGarrett D'Amore 		    "removable-media", NULL, 0);
6343f7d54a6SGarrett D'Amore 	}
6353f7d54a6SGarrett D'Amore 	if (bd->d_hotpluggable) {
6363f7d54a6SGarrett D'Amore 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
6373f7d54a6SGarrett D'Amore 		    "hotpluggable", NULL, 0);
6383f7d54a6SGarrett D'Amore 	}
6393f7d54a6SGarrett D'Amore 
6403f7d54a6SGarrett D'Amore 	ddi_report_dev(dip);
6413f7d54a6SGarrett D'Amore 
6423f7d54a6SGarrett D'Amore 	return (DDI_SUCCESS);
6433f7d54a6SGarrett D'Amore }
6443f7d54a6SGarrett D'Amore 
6453f7d54a6SGarrett D'Amore static int
6463f7d54a6SGarrett D'Amore bd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6473f7d54a6SGarrett D'Amore {
6483f7d54a6SGarrett D'Amore 	bd_t	*bd;
6493f7d54a6SGarrett D'Amore 
6503f7d54a6SGarrett D'Amore 	bd = ddi_get_driver_private(dip);
6513f7d54a6SGarrett D'Amore 
6523f7d54a6SGarrett D'Amore 	switch (cmd) {
6533f7d54a6SGarrett D'Amore 	case DDI_DETACH:
6543f7d54a6SGarrett D'Amore 		break;
6553f7d54a6SGarrett D'Amore 	case DDI_SUSPEND:
6563f7d54a6SGarrett D'Amore 		/* We don't suspend, but our parent does */
6573f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
6583f7d54a6SGarrett D'Amore 	default:
6593f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
6603f7d54a6SGarrett D'Amore 	}
6613f7d54a6SGarrett D'Amore 	if (bd->d_ksp != NULL) {
6623f7d54a6SGarrett D'Amore 		kstat_delete(bd->d_ksp);
6633f7d54a6SGarrett D'Amore 		bd->d_ksp = NULL;
6643f7d54a6SGarrett D'Amore 	} else {
6653f7d54a6SGarrett D'Amore 		kmem_free(bd->d_kiop, sizeof (kstat_io_t));
6663f7d54a6SGarrett D'Amore 	}
667bef9e21aSHans Rosenfeld 
668bef9e21aSHans Rosenfeld 	if (bd->d_errstats != NULL) {
669bef9e21aSHans Rosenfeld 		kstat_delete(bd->d_errstats);
670bef9e21aSHans Rosenfeld 		bd->d_errstats = NULL;
671bef9e21aSHans Rosenfeld 	} else {
672bef9e21aSHans Rosenfeld 		kmem_free(bd->d_kerr, sizeof (struct bd_errstats));
673bef9e21aSHans Rosenfeld 		mutex_destroy(bd->d_errmutex);
674bef9e21aSHans Rosenfeld 	}
675bef9e21aSHans Rosenfeld 
67686e3bca6SGarrett D'Amore 	cmlb_detach(bd->d_cmlbh, 0);
6773f7d54a6SGarrett D'Amore 	cmlb_free_handle(&bd->d_cmlbh);
6783f7d54a6SGarrett D'Amore 	if (bd->d_devid)
6793f7d54a6SGarrett D'Amore 		ddi_devid_free(bd->d_devid);
6803f7d54a6SGarrett D'Amore 	kmem_cache_destroy(bd->d_cache);
6813f7d54a6SGarrett D'Amore 	mutex_destroy(&bd->d_iomutex);
6823f7d54a6SGarrett D'Amore 	mutex_destroy(&bd->d_ocmutex);
6833f7d54a6SGarrett D'Amore 	mutex_destroy(&bd->d_statemutex);
6843f7d54a6SGarrett D'Amore 	cv_destroy(&bd->d_statecv);
6853f7d54a6SGarrett D'Amore 	list_destroy(&bd->d_waitq);
6863f7d54a6SGarrett D'Amore 	list_destroy(&bd->d_runq);
6873f7d54a6SGarrett D'Amore 	ddi_soft_state_free(bd_state, ddi_get_instance(dip));
6883f7d54a6SGarrett D'Amore 	return (DDI_SUCCESS);
6893f7d54a6SGarrett D'Amore }
6903f7d54a6SGarrett D'Amore 
6913f7d54a6SGarrett D'Amore static int
6923f7d54a6SGarrett D'Amore bd_xfer_ctor(void *buf, void *arg, int kmflag)
6933f7d54a6SGarrett D'Amore {
6943f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
6953f7d54a6SGarrett D'Amore 	bd_t		*bd = arg;
6963f7d54a6SGarrett D'Amore 	int		(*dcb)(caddr_t);
6973f7d54a6SGarrett D'Amore 
698679ac156SAlexey Zaytsev 	if (kmflag == KM_PUSHPAGE || kmflag == KM_SLEEP) {
6993f7d54a6SGarrett D'Amore 		dcb = DDI_DMA_SLEEP;
7003f7d54a6SGarrett D'Amore 	} else {
7013f7d54a6SGarrett D'Amore 		dcb = DDI_DMA_DONTWAIT;
7023f7d54a6SGarrett D'Amore 	}
7033f7d54a6SGarrett D'Amore 
7043f7d54a6SGarrett D'Amore 	xi = buf;
7053f7d54a6SGarrett D'Amore 	bzero(xi, sizeof (*xi));
7063f7d54a6SGarrett D'Amore 	xi->i_bd = bd;
7073f7d54a6SGarrett D'Amore 
7083f7d54a6SGarrett D'Amore 	if (bd->d_use_dma) {
7093f7d54a6SGarrett D'Amore 		if (ddi_dma_alloc_handle(bd->d_dip, &bd->d_dma, dcb, NULL,
7103f7d54a6SGarrett D'Amore 		    &xi->i_dmah) != DDI_SUCCESS) {
7113f7d54a6SGarrett D'Amore 			return (-1);
7123f7d54a6SGarrett D'Amore 		}
7133f7d54a6SGarrett D'Amore 	}
7143f7d54a6SGarrett D'Amore 
7153f7d54a6SGarrett D'Amore 	return (0);
7163f7d54a6SGarrett D'Amore }
7173f7d54a6SGarrett D'Amore 
7183f7d54a6SGarrett D'Amore static void
7193f7d54a6SGarrett D'Amore bd_xfer_dtor(void *buf, void *arg)
7203f7d54a6SGarrett D'Amore {
7213f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi = buf;
7223f7d54a6SGarrett D'Amore 
7233f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
7243f7d54a6SGarrett D'Amore 
7253f7d54a6SGarrett D'Amore 	if (xi->i_dmah)
7263f7d54a6SGarrett D'Amore 		ddi_dma_free_handle(&xi->i_dmah);
7273f7d54a6SGarrett D'Amore 	xi->i_dmah = NULL;
7283f7d54a6SGarrett D'Amore }
7293f7d54a6SGarrett D'Amore 
7303f7d54a6SGarrett D'Amore static bd_xfer_impl_t *
7313f7d54a6SGarrett D'Amore bd_xfer_alloc(bd_t *bd, struct buf *bp, int (*func)(void *, bd_xfer_t *),
7323f7d54a6SGarrett D'Amore     int kmflag)
7333f7d54a6SGarrett D'Amore {
7343f7d54a6SGarrett D'Amore 	bd_xfer_impl_t		*xi;
735a0cb694bSSteve Ma 	int			rv = 0;
7363f7d54a6SGarrett D'Amore 	int			status;
7373f7d54a6SGarrett D'Amore 	unsigned		dir;
7383f7d54a6SGarrett D'Amore 	int			(*cb)(caddr_t);
7393f7d54a6SGarrett D'Amore 	size_t			len;
7403f7d54a6SGarrett D'Amore 	uint32_t		shift;
7413f7d54a6SGarrett D'Amore 
7423f7d54a6SGarrett D'Amore 	if (kmflag == KM_SLEEP) {
7433f7d54a6SGarrett D'Amore 		cb = DDI_DMA_SLEEP;
7443f7d54a6SGarrett D'Amore 	} else {
7453f7d54a6SGarrett D'Amore 		cb = DDI_DMA_DONTWAIT;
7463f7d54a6SGarrett D'Amore 	}
7473f7d54a6SGarrett D'Amore 
7483f7d54a6SGarrett D'Amore 	xi = kmem_cache_alloc(bd->d_cache, kmflag);
7493f7d54a6SGarrett D'Amore 	if (xi == NULL) {
7503f7d54a6SGarrett D'Amore 		bioerror(bp, ENOMEM);
7513f7d54a6SGarrett D'Amore 		return (NULL);
7523f7d54a6SGarrett D'Amore 	}
7533f7d54a6SGarrett D'Amore 
7543f7d54a6SGarrett D'Amore 	ASSERT(bp);
7553f7d54a6SGarrett D'Amore 
7563f7d54a6SGarrett D'Amore 	xi->i_bp = bp;
7573f7d54a6SGarrett D'Amore 	xi->i_func = func;
75810624986SYouzhong Yang 	xi->i_blkno = bp->b_lblkno >> (bd->d_blkshift - DEV_BSHIFT);
7593f7d54a6SGarrett D'Amore 
7603f7d54a6SGarrett D'Amore 	if (bp->b_bcount == 0) {
7613f7d54a6SGarrett D'Amore 		xi->i_len = 0;
7623f7d54a6SGarrett D'Amore 		xi->i_nblks = 0;
7633f7d54a6SGarrett D'Amore 		xi->i_kaddr = NULL;
7643f7d54a6SGarrett D'Amore 		xi->i_resid = 0;
7653f7d54a6SGarrett D'Amore 		xi->i_num_win = 0;
7663f7d54a6SGarrett D'Amore 		goto done;
7673f7d54a6SGarrett D'Amore 	}
7683f7d54a6SGarrett D'Amore 
7693f7d54a6SGarrett D'Amore 	if (bp->b_flags & B_READ) {
7703f7d54a6SGarrett D'Amore 		dir = DDI_DMA_READ;
7713f7d54a6SGarrett D'Amore 		xi->i_func = bd->d_ops.o_read;
7723f7d54a6SGarrett D'Amore 	} else {
7733f7d54a6SGarrett D'Amore 		dir = DDI_DMA_WRITE;
7743f7d54a6SGarrett D'Amore 		xi->i_func = bd->d_ops.o_write;
7753f7d54a6SGarrett D'Amore 	}
7763f7d54a6SGarrett D'Amore 
7773f7d54a6SGarrett D'Amore 	shift = bd->d_blkshift;
7783f7d54a6SGarrett D'Amore 	xi->i_blkshift = shift;
7793f7d54a6SGarrett D'Amore 
7803f7d54a6SGarrett D'Amore 	if (!bd->d_use_dma) {
7813f7d54a6SGarrett D'Amore 		bp_mapin(bp);
7823f7d54a6SGarrett D'Amore 		rv = 0;
7833f7d54a6SGarrett D'Amore 		xi->i_offset = 0;
7843f7d54a6SGarrett D'Amore 		xi->i_num_win =
7853f7d54a6SGarrett D'Amore 		    (bp->b_bcount + (bd->d_maxxfer - 1)) / bd->d_maxxfer;
7863f7d54a6SGarrett D'Amore 		xi->i_cur_win = 0;
7873f7d54a6SGarrett D'Amore 		xi->i_len = min(bp->b_bcount, bd->d_maxxfer);
7883f7d54a6SGarrett D'Amore 		xi->i_nblks = xi->i_len >> shift;
7893f7d54a6SGarrett D'Amore 		xi->i_kaddr = bp->b_un.b_addr;
7903f7d54a6SGarrett D'Amore 		xi->i_resid = bp->b_bcount;
7913f7d54a6SGarrett D'Amore 	} else {
7923f7d54a6SGarrett D'Amore 
7933f7d54a6SGarrett D'Amore 		/*
7943f7d54a6SGarrett D'Amore 		 * We have to use consistent DMA if the address is misaligned.
7953f7d54a6SGarrett D'Amore 		 */
7963f7d54a6SGarrett D'Amore 		if (((bp->b_flags & (B_PAGEIO | B_REMAPPED)) != B_PAGEIO) &&
7973f7d54a6SGarrett D'Amore 		    ((uintptr_t)bp->b_un.b_addr & 0x7)) {
7983f7d54a6SGarrett D'Amore 			dir |= DDI_DMA_CONSISTENT | DDI_DMA_PARTIAL;
7993f7d54a6SGarrett D'Amore 		} else {
8003f7d54a6SGarrett D'Amore 			dir |= DDI_DMA_STREAMING | DDI_DMA_PARTIAL;
8013f7d54a6SGarrett D'Amore 		}
8023f7d54a6SGarrett D'Amore 
8033f7d54a6SGarrett D'Amore 		status = ddi_dma_buf_bind_handle(xi->i_dmah, bp, dir, cb,
8043f7d54a6SGarrett D'Amore 		    NULL, &xi->i_dmac, &xi->i_ndmac);
8053f7d54a6SGarrett D'Amore 		switch (status) {
8063f7d54a6SGarrett D'Amore 		case DDI_DMA_MAPPED:
8073f7d54a6SGarrett D'Amore 			xi->i_num_win = 1;
8083f7d54a6SGarrett D'Amore 			xi->i_cur_win = 0;
8093f7d54a6SGarrett D'Amore 			xi->i_offset = 0;
8103f7d54a6SGarrett D'Amore 			xi->i_len = bp->b_bcount;
8113f7d54a6SGarrett D'Amore 			xi->i_nblks = xi->i_len >> shift;
8123f7d54a6SGarrett D'Amore 			xi->i_resid = bp->b_bcount;
8133f7d54a6SGarrett D'Amore 			rv = 0;
8143f7d54a6SGarrett D'Amore 			break;
8153f7d54a6SGarrett D'Amore 		case DDI_DMA_PARTIAL_MAP:
8163f7d54a6SGarrett D'Amore 			xi->i_cur_win = 0;
8173f7d54a6SGarrett D'Amore 
8183f7d54a6SGarrett D'Amore 			if ((ddi_dma_numwin(xi->i_dmah, &xi->i_num_win) !=
8193f7d54a6SGarrett D'Amore 			    DDI_SUCCESS) ||
8203f7d54a6SGarrett D'Amore 			    (ddi_dma_getwin(xi->i_dmah, 0, &xi->i_offset,
8213f7d54a6SGarrett D'Amore 			    &len, &xi->i_dmac, &xi->i_ndmac) !=
8223f7d54a6SGarrett D'Amore 			    DDI_SUCCESS) ||
82310624986SYouzhong Yang 			    (P2PHASE(len, (1U << shift)) != 0)) {
8243f7d54a6SGarrett D'Amore 				(void) ddi_dma_unbind_handle(xi->i_dmah);
8253f7d54a6SGarrett D'Amore 				rv = EFAULT;
8263f7d54a6SGarrett D'Amore 				goto done;
8273f7d54a6SGarrett D'Amore 			}
8283f7d54a6SGarrett D'Amore 			xi->i_len = len;
8293f7d54a6SGarrett D'Amore 			xi->i_nblks = xi->i_len >> shift;
8303f7d54a6SGarrett D'Amore 			xi->i_resid = bp->b_bcount;
8313f7d54a6SGarrett D'Amore 			rv = 0;
8323f7d54a6SGarrett D'Amore 			break;
8333f7d54a6SGarrett D'Amore 		case DDI_DMA_NORESOURCES:
8343f7d54a6SGarrett D'Amore 			rv = EAGAIN;
8353f7d54a6SGarrett D'Amore 			goto done;
8363f7d54a6SGarrett D'Amore 		case DDI_DMA_TOOBIG:
8373f7d54a6SGarrett D'Amore 			rv = EINVAL;
8383f7d54a6SGarrett D'Amore 			goto done;
8393f7d54a6SGarrett D'Amore 		case DDI_DMA_NOMAPPING:
8403f7d54a6SGarrett D'Amore 		case DDI_DMA_INUSE:
8413f7d54a6SGarrett D'Amore 		default:
8423f7d54a6SGarrett D'Amore 			rv = EFAULT;
8433f7d54a6SGarrett D'Amore 			goto done;
8443f7d54a6SGarrett D'Amore 		}
8453f7d54a6SGarrett D'Amore 	}
8463f7d54a6SGarrett D'Amore 
8473f7d54a6SGarrett D'Amore done:
8483f7d54a6SGarrett D'Amore 	if (rv != 0) {
8493f7d54a6SGarrett D'Amore 		kmem_cache_free(bd->d_cache, xi);
8503f7d54a6SGarrett D'Amore 		bioerror(bp, rv);
8513f7d54a6SGarrett D'Amore 		return (NULL);
8523f7d54a6SGarrett D'Amore 	}
8533f7d54a6SGarrett D'Amore 
8543f7d54a6SGarrett D'Amore 	return (xi);
8553f7d54a6SGarrett D'Amore }
8563f7d54a6SGarrett D'Amore 
8573f7d54a6SGarrett D'Amore static void
8583f7d54a6SGarrett D'Amore bd_xfer_free(bd_xfer_impl_t *xi)
8593f7d54a6SGarrett D'Amore {
8603f7d54a6SGarrett D'Amore 	if (xi->i_dmah) {
8613f7d54a6SGarrett D'Amore 		(void) ddi_dma_unbind_handle(xi->i_dmah);
8623f7d54a6SGarrett D'Amore 	}
8633f7d54a6SGarrett D'Amore 	kmem_cache_free(xi->i_bd->d_cache, xi);
8643f7d54a6SGarrett D'Amore }
8653f7d54a6SGarrett D'Amore 
8663f7d54a6SGarrett D'Amore static int
8673f7d54a6SGarrett D'Amore bd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
8683f7d54a6SGarrett D'Amore {
8693f7d54a6SGarrett D'Amore 	dev_t		dev = *devp;
8703f7d54a6SGarrett D'Amore 	bd_t		*bd;
8713f7d54a6SGarrett D'Amore 	minor_t		part;
8723f7d54a6SGarrett D'Amore 	minor_t		inst;
8733f7d54a6SGarrett D'Amore 	uint64_t	mask;
8743f7d54a6SGarrett D'Amore 	boolean_t	ndelay;
8753f7d54a6SGarrett D'Amore 	int		rv;
8763f7d54a6SGarrett D'Amore 	diskaddr_t	nblks;
8773f7d54a6SGarrett D'Amore 	diskaddr_t	lba;
8783f7d54a6SGarrett D'Amore 
8793f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
8803f7d54a6SGarrett D'Amore 
8813f7d54a6SGarrett D'Amore 	part = BDPART(dev);
8823f7d54a6SGarrett D'Amore 	inst = BDINST(dev);
8833f7d54a6SGarrett D'Amore 
8843f7d54a6SGarrett D'Amore 	if (otyp >= OTYPCNT)
8853f7d54a6SGarrett D'Amore 		return (EINVAL);
8863f7d54a6SGarrett D'Amore 
8873f7d54a6SGarrett D'Amore 	ndelay = (flag & (FNDELAY | FNONBLOCK)) ? B_TRUE : B_FALSE;
8883f7d54a6SGarrett D'Amore 
8893f7d54a6SGarrett D'Amore 	/*
8903f7d54a6SGarrett D'Amore 	 * Block any DR events from changing the set of registered
8913f7d54a6SGarrett D'Amore 	 * devices while we function.
8923f7d54a6SGarrett D'Amore 	 */
8933f7d54a6SGarrett D'Amore 	rw_enter(&bd_lock, RW_READER);
8943f7d54a6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
8953f7d54a6SGarrett D'Amore 		rw_exit(&bd_lock);
8963f7d54a6SGarrett D'Amore 		return (ENXIO);
8973f7d54a6SGarrett D'Amore 	}
8983f7d54a6SGarrett D'Amore 
8993f7d54a6SGarrett D'Amore 	mutex_enter(&bd->d_ocmutex);
9003f7d54a6SGarrett D'Amore 
9013f7d54a6SGarrett D'Amore 	ASSERT(part < 64);
9023f7d54a6SGarrett D'Amore 	mask = (1U << part);
9033f7d54a6SGarrett D'Amore 
9043f7d54a6SGarrett D'Amore 	bd_update_state(bd);
9053f7d54a6SGarrett D'Amore 
90686e3bca6SGarrett D'Amore 	if (cmlb_validate(bd->d_cmlbh, 0, 0) != 0) {
9073f7d54a6SGarrett D'Amore 
9083f7d54a6SGarrett D'Amore 		/* non-blocking opens are allowed to succeed */
9093f7d54a6SGarrett D'Amore 		if (!ndelay) {
9103f7d54a6SGarrett D'Amore 			rv = ENXIO;
9113f7d54a6SGarrett D'Amore 			goto done;
9123f7d54a6SGarrett D'Amore 		}
9133f7d54a6SGarrett D'Amore 	} else if (cmlb_partinfo(bd->d_cmlbh, part, &nblks, &lba,
91486e3bca6SGarrett D'Amore 	    NULL, NULL, 0) == 0) {
9153f7d54a6SGarrett D'Amore 
9163f7d54a6SGarrett D'Amore 		/*
9173f7d54a6SGarrett D'Amore 		 * We read the partinfo, verify valid ranges.  If the
9183f7d54a6SGarrett D'Amore 		 * partition is invalid, and we aren't blocking or
9193f7d54a6SGarrett D'Amore 		 * doing a raw access, then fail. (Non-blocking and
9203f7d54a6SGarrett D'Amore 		 * raw accesses can still succeed to allow a disk with
9213f7d54a6SGarrett D'Amore 		 * bad partition data to opened by format and fdisk.)
9223f7d54a6SGarrett D'Amore 		 */
9233f7d54a6SGarrett D'Amore 		if ((!nblks) && ((!ndelay) || (otyp != OTYP_CHR))) {
9243f7d54a6SGarrett D'Amore 			rv = ENXIO;
9253f7d54a6SGarrett D'Amore 			goto done;
9263f7d54a6SGarrett D'Amore 		}
9273f7d54a6SGarrett D'Amore 	} else if (!ndelay) {
9283f7d54a6SGarrett D'Amore 		/*
9293f7d54a6SGarrett D'Amore 		 * cmlb_partinfo failed -- invalid partition or no
9303f7d54a6SGarrett D'Amore 		 * disk label.
9313f7d54a6SGarrett D'Amore 		 */
9323f7d54a6SGarrett D'Amore 		rv = ENXIO;
9333f7d54a6SGarrett D'Amore 		goto done;
9343f7d54a6SGarrett D'Amore 	}
9353f7d54a6SGarrett D'Amore 
9363f7d54a6SGarrett D'Amore 	if ((flag & FWRITE) && bd->d_rdonly) {
9373f7d54a6SGarrett D'Amore 		rv = EROFS;
9383f7d54a6SGarrett D'Amore 		goto done;
9393f7d54a6SGarrett D'Amore 	}
9403f7d54a6SGarrett D'Amore 
9413f7d54a6SGarrett D'Amore 	if ((bd->d_open_excl) & (mask)) {
9423f7d54a6SGarrett D'Amore 		rv = EBUSY;
9433f7d54a6SGarrett D'Amore 		goto done;
9443f7d54a6SGarrett D'Amore 	}
9453f7d54a6SGarrett D'Amore 	if (flag & FEXCL) {
9463f7d54a6SGarrett D'Amore 		if (bd->d_open_lyr[part]) {
9473f7d54a6SGarrett D'Amore 			rv = EBUSY;
9483f7d54a6SGarrett D'Amore 			goto done;
9493f7d54a6SGarrett D'Amore 		}
9503f7d54a6SGarrett D'Amore 		for (int i = 0; i < OTYP_LYR; i++) {
9513f7d54a6SGarrett D'Amore 			if (bd->d_open_reg[i] & mask) {
9523f7d54a6SGarrett D'Amore 				rv = EBUSY;
9533f7d54a6SGarrett D'Amore 				goto done;
9543f7d54a6SGarrett D'Amore 			}
9553f7d54a6SGarrett D'Amore 		}
9563f7d54a6SGarrett D'Amore 	}
9573f7d54a6SGarrett D'Amore 
9583f7d54a6SGarrett D'Amore 	if (otyp == OTYP_LYR) {
9593f7d54a6SGarrett D'Amore 		bd->d_open_lyr[part]++;
9603f7d54a6SGarrett D'Amore 	} else {
9613f7d54a6SGarrett D'Amore 		bd->d_open_reg[otyp] |= mask;
9623f7d54a6SGarrett D'Amore 	}
9633f7d54a6SGarrett D'Amore 	if (flag & FEXCL) {
9643f7d54a6SGarrett D'Amore 		bd->d_open_excl |= mask;
9653f7d54a6SGarrett D'Amore 	}
9663f7d54a6SGarrett D'Amore 
9673f7d54a6SGarrett D'Amore 	rv = 0;
9683f7d54a6SGarrett D'Amore done:
9693f7d54a6SGarrett D'Amore 	mutex_exit(&bd->d_ocmutex);
9703f7d54a6SGarrett D'Amore 	rw_exit(&bd_lock);
9713f7d54a6SGarrett D'Amore 
9723f7d54a6SGarrett D'Amore 	return (rv);
9733f7d54a6SGarrett D'Amore }
9743f7d54a6SGarrett D'Amore 
9753f7d54a6SGarrett D'Amore static int
9763f7d54a6SGarrett D'Amore bd_close(dev_t dev, int flag, int otyp, cred_t *credp)
9773f7d54a6SGarrett D'Amore {
9783f7d54a6SGarrett D'Amore 	bd_t		*bd;
9793f7d54a6SGarrett D'Amore 	minor_t		inst;
9803f7d54a6SGarrett D'Amore 	minor_t		part;
9813f7d54a6SGarrett D'Amore 	uint64_t	mask;
9823f7d54a6SGarrett D'Amore 	boolean_t	last = B_TRUE;
9833f7d54a6SGarrett D'Amore 
9843f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
9853f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
9863f7d54a6SGarrett D'Amore 
9873f7d54a6SGarrett D'Amore 	part = BDPART(dev);
9883f7d54a6SGarrett D'Amore 	inst = BDINST(dev);
9893f7d54a6SGarrett D'Amore 
9903f7d54a6SGarrett D'Amore 	ASSERT(part < 64);
9913f7d54a6SGarrett D'Amore 	mask = (1U << part);
9923f7d54a6SGarrett D'Amore 
9933f7d54a6SGarrett D'Amore 	rw_enter(&bd_lock, RW_READER);
9943f7d54a6SGarrett D'Amore 
9953f7d54a6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
9963f7d54a6SGarrett D'Amore 		rw_exit(&bd_lock);
9973f7d54a6SGarrett D'Amore 		return (ENXIO);
9983f7d54a6SGarrett D'Amore 	}
9993f7d54a6SGarrett D'Amore 
10003f7d54a6SGarrett D'Amore 	mutex_enter(&bd->d_ocmutex);
10013f7d54a6SGarrett D'Amore 	if (bd->d_open_excl & mask) {
10023f7d54a6SGarrett D'Amore 		bd->d_open_excl &= ~mask;
10033f7d54a6SGarrett D'Amore 	}
10043f7d54a6SGarrett D'Amore 	if (otyp == OTYP_LYR) {
10053f7d54a6SGarrett D'Amore 		bd->d_open_lyr[part]--;
10063f7d54a6SGarrett D'Amore 	} else {
10073f7d54a6SGarrett D'Amore 		bd->d_open_reg[otyp] &= ~mask;
10083f7d54a6SGarrett D'Amore 	}
10093f7d54a6SGarrett D'Amore 	for (int i = 0; i < 64; i++) {
10103f7d54a6SGarrett D'Amore 		if (bd->d_open_lyr[part]) {
10113f7d54a6SGarrett D'Amore 			last = B_FALSE;
10123f7d54a6SGarrett D'Amore 		}
10133f7d54a6SGarrett D'Amore 	}
10143f7d54a6SGarrett D'Amore 	for (int i = 0; last && (i < OTYP_LYR); i++) {
10153f7d54a6SGarrett D'Amore 		if (bd->d_open_reg[i]) {
10163f7d54a6SGarrett D'Amore 			last = B_FALSE;
10173f7d54a6SGarrett D'Amore 		}
10183f7d54a6SGarrett D'Amore 	}
10193f7d54a6SGarrett D'Amore 	mutex_exit(&bd->d_ocmutex);
10203f7d54a6SGarrett D'Amore 
10213f7d54a6SGarrett D'Amore 	if (last) {
102286e3bca6SGarrett D'Amore 		cmlb_invalidate(bd->d_cmlbh, 0);
10233f7d54a6SGarrett D'Amore 	}
10243f7d54a6SGarrett D'Amore 	rw_exit(&bd_lock);
10253f7d54a6SGarrett D'Amore 
10263f7d54a6SGarrett D'Amore 	return (0);
10273f7d54a6SGarrett D'Amore }
10283f7d54a6SGarrett D'Amore 
10293f7d54a6SGarrett D'Amore static int
103086e3bca6SGarrett D'Amore bd_dump(dev_t dev, caddr_t caddr, daddr_t blkno, int nblk)
103186e3bca6SGarrett D'Amore {
103286e3bca6SGarrett D'Amore 	minor_t		inst;
103386e3bca6SGarrett D'Amore 	minor_t		part;
103486e3bca6SGarrett D'Amore 	diskaddr_t	pstart;
103586e3bca6SGarrett D'Amore 	diskaddr_t	psize;
103686e3bca6SGarrett D'Amore 	bd_t		*bd;
103786e3bca6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
103886e3bca6SGarrett D'Amore 	buf_t		*bp;
103986e3bca6SGarrett D'Amore 	int		rv;
104010624986SYouzhong Yang 	uint32_t	shift;
104110624986SYouzhong Yang 	daddr_t		d_blkno;
104210624986SYouzhong Yang 	int	d_nblk;
104386e3bca6SGarrett D'Amore 
104486e3bca6SGarrett D'Amore 	rw_enter(&bd_lock, RW_READER);
104586e3bca6SGarrett D'Amore 
104686e3bca6SGarrett D'Amore 	part = BDPART(dev);
104786e3bca6SGarrett D'Amore 	inst = BDINST(dev);
104886e3bca6SGarrett D'Amore 
104986e3bca6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
105086e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
105186e3bca6SGarrett D'Amore 		return (ENXIO);
105286e3bca6SGarrett D'Amore 	}
105310624986SYouzhong Yang 	shift = bd->d_blkshift;
105410624986SYouzhong Yang 	d_blkno = blkno >> (shift - DEV_BSHIFT);
105510624986SYouzhong Yang 	d_nblk = nblk >> (shift - DEV_BSHIFT);
105686e3bca6SGarrett D'Amore 	/*
105786e3bca6SGarrett D'Amore 	 * do cmlb, but do it synchronously unless we already have the
105886e3bca6SGarrett D'Amore 	 * partition (which we probably should.)
105986e3bca6SGarrett D'Amore 	 */
106086e3bca6SGarrett D'Amore 	if (cmlb_partinfo(bd->d_cmlbh, part, &psize, &pstart, NULL, NULL,
106186e3bca6SGarrett D'Amore 	    (void *)1)) {
106286e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
106386e3bca6SGarrett D'Amore 		return (ENXIO);
106486e3bca6SGarrett D'Amore 	}
106586e3bca6SGarrett D'Amore 
106610624986SYouzhong Yang 	if ((d_blkno + d_nblk) > psize) {
106786e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
106886e3bca6SGarrett D'Amore 		return (EINVAL);
106986e3bca6SGarrett D'Amore 	}
107086e3bca6SGarrett D'Amore 	bp = getrbuf(KM_NOSLEEP);
107186e3bca6SGarrett D'Amore 	if (bp == NULL) {
107286e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
107386e3bca6SGarrett D'Amore 		return (ENOMEM);
107486e3bca6SGarrett D'Amore 	}
107586e3bca6SGarrett D'Amore 
107610624986SYouzhong Yang 	bp->b_bcount = nblk << DEV_BSHIFT;
107786e3bca6SGarrett D'Amore 	bp->b_resid = bp->b_bcount;
107886e3bca6SGarrett D'Amore 	bp->b_lblkno = blkno;
107986e3bca6SGarrett D'Amore 	bp->b_un.b_addr = caddr;
108086e3bca6SGarrett D'Amore 
108186e3bca6SGarrett D'Amore 	xi = bd_xfer_alloc(bd, bp,  bd->d_ops.o_write, KM_NOSLEEP);
108286e3bca6SGarrett D'Amore 	if (xi == NULL) {
108386e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
108486e3bca6SGarrett D'Amore 		freerbuf(bp);
108586e3bca6SGarrett D'Amore 		return (ENOMEM);
108686e3bca6SGarrett D'Amore 	}
108710624986SYouzhong Yang 	xi->i_blkno = d_blkno + pstart;
108886e3bca6SGarrett D'Amore 	xi->i_flags = BD_XFER_POLL;
108986e3bca6SGarrett D'Amore 	bd_submit(bd, xi);
109086e3bca6SGarrett D'Amore 	rw_exit(&bd_lock);
109186e3bca6SGarrett D'Amore 
109286e3bca6SGarrett D'Amore 	/*
109386e3bca6SGarrett D'Amore 	 * Generally, we should have run this entirely synchronously
109486e3bca6SGarrett D'Amore 	 * at this point and the biowait call should be a no-op.  If
109586e3bca6SGarrett D'Amore 	 * it didn't happen this way, it's a bug in the underlying
109686e3bca6SGarrett D'Amore 	 * driver not honoring BD_XFER_POLL.
109786e3bca6SGarrett D'Amore 	 */
109886e3bca6SGarrett D'Amore 	(void) biowait(bp);
109986e3bca6SGarrett D'Amore 	rv = geterror(bp);
110086e3bca6SGarrett D'Amore 	freerbuf(bp);
110186e3bca6SGarrett D'Amore 	return (rv);
110286e3bca6SGarrett D'Amore }
110386e3bca6SGarrett D'Amore 
11049d75dfdaSAlexey Zaytsev void
11059d75dfdaSAlexey Zaytsev bd_minphys(struct buf *bp)
11069d75dfdaSAlexey Zaytsev {
11079d75dfdaSAlexey Zaytsev 	minor_t inst;
11089d75dfdaSAlexey Zaytsev 	bd_t	*bd;
11099d75dfdaSAlexey Zaytsev 	inst = BDINST(bp->b_edev);
11109d75dfdaSAlexey Zaytsev 
11119d75dfdaSAlexey Zaytsev 	bd = ddi_get_soft_state(bd_state, inst);
11129d75dfdaSAlexey Zaytsev 
11139d75dfdaSAlexey Zaytsev 	/*
11149d75dfdaSAlexey Zaytsev 	 * In a non-debug kernel, bd_strategy will catch !bd as
11159d75dfdaSAlexey Zaytsev 	 * well, and will fail nicely.
11169d75dfdaSAlexey Zaytsev 	 */
11179d75dfdaSAlexey Zaytsev 	ASSERT(bd);
11189d75dfdaSAlexey Zaytsev 
11199d75dfdaSAlexey Zaytsev 	if (bp->b_bcount > bd->d_maxxfer)
11209d75dfdaSAlexey Zaytsev 		bp->b_bcount = bd->d_maxxfer;
11219d75dfdaSAlexey Zaytsev }
11229d75dfdaSAlexey Zaytsev 
112386e3bca6SGarrett D'Amore static int
112410624986SYouzhong Yang bd_check_uio(dev_t dev, struct uio *uio)
112510624986SYouzhong Yang {
112610624986SYouzhong Yang 	bd_t		*bd;
112710624986SYouzhong Yang 	uint32_t	shift;
112810624986SYouzhong Yang 
112910624986SYouzhong Yang 	if ((bd = ddi_get_soft_state(bd_state, BDINST(dev))) == NULL) {
113010624986SYouzhong Yang 		return (ENXIO);
113110624986SYouzhong Yang 	}
113210624986SYouzhong Yang 
113310624986SYouzhong Yang 	shift = bd->d_blkshift;
113410624986SYouzhong Yang 	if ((P2PHASE(uio->uio_loffset, (1U << shift)) != 0) ||
113510624986SYouzhong Yang 	    (P2PHASE(uio->uio_iov->iov_len, (1U << shift)) != 0)) {
113610624986SYouzhong Yang 		return (EINVAL);
113710624986SYouzhong Yang 	}
113810624986SYouzhong Yang 
113910624986SYouzhong Yang 	return (0);
114010624986SYouzhong Yang }
114110624986SYouzhong Yang 
114210624986SYouzhong Yang static int
11433f7d54a6SGarrett D'Amore bd_read(dev_t dev, struct uio *uio, cred_t *credp)
11443f7d54a6SGarrett D'Amore {
11453f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
114610624986SYouzhong Yang 	int	ret = bd_check_uio(dev, uio);
114710624986SYouzhong Yang 	if (ret != 0) {
114810624986SYouzhong Yang 		return (ret);
114910624986SYouzhong Yang 	}
11509d75dfdaSAlexey Zaytsev 	return (physio(bd_strategy, NULL, dev, B_READ, bd_minphys, uio));
11513f7d54a6SGarrett D'Amore }
11523f7d54a6SGarrett D'Amore 
11533f7d54a6SGarrett D'Amore static int
11543f7d54a6SGarrett D'Amore bd_write(dev_t dev, struct uio *uio, cred_t *credp)
11553f7d54a6SGarrett D'Amore {
11563f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
115710624986SYouzhong Yang 	int	ret = bd_check_uio(dev, uio);
115810624986SYouzhong Yang 	if (ret != 0) {
115910624986SYouzhong Yang 		return (ret);
116010624986SYouzhong Yang 	}
11619d75dfdaSAlexey Zaytsev 	return (physio(bd_strategy, NULL, dev, B_WRITE, bd_minphys, uio));
11623f7d54a6SGarrett D'Amore }
11633f7d54a6SGarrett D'Amore 
11643f7d54a6SGarrett D'Amore static int
11653f7d54a6SGarrett D'Amore bd_aread(dev_t dev, struct aio_req *aio, cred_t *credp)
11663f7d54a6SGarrett D'Amore {
11673f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
116810624986SYouzhong Yang 	int	ret = bd_check_uio(dev, aio->aio_uio);
116910624986SYouzhong Yang 	if (ret != 0) {
117010624986SYouzhong Yang 		return (ret);
117110624986SYouzhong Yang 	}
11729d75dfdaSAlexey Zaytsev 	return (aphysio(bd_strategy, anocancel, dev, B_READ, bd_minphys, aio));
11733f7d54a6SGarrett D'Amore }
11743f7d54a6SGarrett D'Amore 
11753f7d54a6SGarrett D'Amore static int
11763f7d54a6SGarrett D'Amore bd_awrite(dev_t dev, struct aio_req *aio, cred_t *credp)
11773f7d54a6SGarrett D'Amore {
11783f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
117910624986SYouzhong Yang 	int	ret = bd_check_uio(dev, aio->aio_uio);
118010624986SYouzhong Yang 	if (ret != 0) {
118110624986SYouzhong Yang 		return (ret);
118210624986SYouzhong Yang 	}
11839d75dfdaSAlexey Zaytsev 	return (aphysio(bd_strategy, anocancel, dev, B_WRITE, bd_minphys, aio));
11843f7d54a6SGarrett D'Amore }
11853f7d54a6SGarrett D'Amore 
11863f7d54a6SGarrett D'Amore static int
11873f7d54a6SGarrett D'Amore bd_strategy(struct buf *bp)
11883f7d54a6SGarrett D'Amore {
11893f7d54a6SGarrett D'Amore 	minor_t		inst;
11903f7d54a6SGarrett D'Amore 	minor_t		part;
11913f7d54a6SGarrett D'Amore 	bd_t		*bd;
11923f7d54a6SGarrett D'Amore 	diskaddr_t	p_lba;
11933f7d54a6SGarrett D'Amore 	diskaddr_t	p_nblks;
11943f7d54a6SGarrett D'Amore 	diskaddr_t	b_nblks;
11953f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
11963f7d54a6SGarrett D'Amore 	uint32_t	shift;
11973f7d54a6SGarrett D'Amore 	int		(*func)(void *, bd_xfer_t *);
119810624986SYouzhong Yang 	diskaddr_t 	lblkno;
11993f7d54a6SGarrett D'Amore 
12003f7d54a6SGarrett D'Amore 	part = BDPART(bp->b_edev);
12013f7d54a6SGarrett D'Amore 	inst = BDINST(bp->b_edev);
12023f7d54a6SGarrett D'Amore 
12033f7d54a6SGarrett D'Amore 	ASSERT(bp);
12043f7d54a6SGarrett D'Amore 
12053f7d54a6SGarrett D'Amore 	bp->b_resid = bp->b_bcount;
12063f7d54a6SGarrett D'Amore 
12073f7d54a6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
12083f7d54a6SGarrett D'Amore 		bioerror(bp, ENXIO);
12093f7d54a6SGarrett D'Amore 		biodone(bp);
12103f7d54a6SGarrett D'Amore 		return (0);
12113f7d54a6SGarrett D'Amore 	}
12123f7d54a6SGarrett D'Amore 
12133f7d54a6SGarrett D'Amore 	if (cmlb_partinfo(bd->d_cmlbh, part, &p_nblks, &p_lba,
121486e3bca6SGarrett D'Amore 	    NULL, NULL, 0)) {
12153f7d54a6SGarrett D'Amore 		bioerror(bp, ENXIO);
12163f7d54a6SGarrett D'Amore 		biodone(bp);
12173f7d54a6SGarrett D'Amore 		return (0);
12183f7d54a6SGarrett D'Amore 	}
12193f7d54a6SGarrett D'Amore 
12203f7d54a6SGarrett D'Amore 	shift = bd->d_blkshift;
122110624986SYouzhong Yang 	lblkno = bp->b_lblkno >> (shift - DEV_BSHIFT);
122210624986SYouzhong Yang 	if ((P2PHASE(bp->b_lblkno, (1U << (shift - DEV_BSHIFT))) != 0) ||
122310624986SYouzhong Yang 	    (P2PHASE(bp->b_bcount, (1U << shift)) != 0) ||
122410624986SYouzhong Yang 	    (lblkno > p_nblks)) {
122510624986SYouzhong Yang 		bioerror(bp, EINVAL);
12263f7d54a6SGarrett D'Amore 		biodone(bp);
12273f7d54a6SGarrett D'Amore 		return (0);
12283f7d54a6SGarrett D'Amore 	}
12293f7d54a6SGarrett D'Amore 	b_nblks = bp->b_bcount >> shift;
123010624986SYouzhong Yang 	if ((lblkno == p_nblks) || (bp->b_bcount == 0)) {
12313f7d54a6SGarrett D'Amore 		biodone(bp);
12323f7d54a6SGarrett D'Amore 		return (0);
12333f7d54a6SGarrett D'Amore 	}
12343f7d54a6SGarrett D'Amore 
123510624986SYouzhong Yang 	if ((b_nblks + lblkno) > p_nblks) {
123610624986SYouzhong Yang 		bp->b_resid = ((lblkno + b_nblks - p_nblks) << shift);
12373f7d54a6SGarrett D'Amore 		bp->b_bcount -= bp->b_resid;
12383f7d54a6SGarrett D'Amore 	} else {
12393f7d54a6SGarrett D'Amore 		bp->b_resid = 0;
12403f7d54a6SGarrett D'Amore 	}
12413f7d54a6SGarrett D'Amore 	func = (bp->b_flags & B_READ) ? bd->d_ops.o_read : bd->d_ops.o_write;
12423f7d54a6SGarrett D'Amore 
12433f7d54a6SGarrett D'Amore 	xi = bd_xfer_alloc(bd, bp, func, KM_NOSLEEP);
12443f7d54a6SGarrett D'Amore 	if (xi == NULL) {
12453f7d54a6SGarrett D'Amore 		xi = bd_xfer_alloc(bd, bp, func, KM_PUSHPAGE);
12463f7d54a6SGarrett D'Amore 	}
12473f7d54a6SGarrett D'Amore 	if (xi == NULL) {
12483f7d54a6SGarrett D'Amore 		/* bd_request_alloc will have done bioerror */
12493f7d54a6SGarrett D'Amore 		biodone(bp);
12503f7d54a6SGarrett D'Amore 		return (0);
12513f7d54a6SGarrett D'Amore 	}
125210624986SYouzhong Yang 	xi->i_blkno = lblkno + p_lba;
12533f7d54a6SGarrett D'Amore 
12543f7d54a6SGarrett D'Amore 	bd_submit(bd, xi);
12553f7d54a6SGarrett D'Amore 
12563f7d54a6SGarrett D'Amore 	return (0);
12573f7d54a6SGarrett D'Amore }
12583f7d54a6SGarrett D'Amore 
12593f7d54a6SGarrett D'Amore static int
12603f7d54a6SGarrett D'Amore bd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp)
12613f7d54a6SGarrett D'Amore {
12623f7d54a6SGarrett D'Amore 	minor_t		inst;
12633f7d54a6SGarrett D'Amore 	uint16_t	part;
12643f7d54a6SGarrett D'Amore 	bd_t		*bd;
12653f7d54a6SGarrett D'Amore 	void		*ptr = (void *)arg;
12663f7d54a6SGarrett D'Amore 	int		rv;
12673f7d54a6SGarrett D'Amore 
12683f7d54a6SGarrett D'Amore 	part = BDPART(dev);
12693f7d54a6SGarrett D'Amore 	inst = BDINST(dev);
12703f7d54a6SGarrett D'Amore 
12713f7d54a6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
12723f7d54a6SGarrett D'Amore 		return (ENXIO);
12733f7d54a6SGarrett D'Amore 	}
12743f7d54a6SGarrett D'Amore 
127586e3bca6SGarrett D'Amore 	rv = cmlb_ioctl(bd->d_cmlbh, dev, cmd, arg, flag, credp, rvalp, 0);
12763f7d54a6SGarrett D'Amore 	if (rv != ENOTTY)
12773f7d54a6SGarrett D'Amore 		return (rv);
12783f7d54a6SGarrett D'Amore 
1279a0cb694bSSteve Ma 	if (rvalp != NULL) {
1280a0cb694bSSteve Ma 		/* the return value of the ioctl is 0 by default */
1281a0cb694bSSteve Ma 		*rvalp = 0;
1282a0cb694bSSteve Ma 	}
1283a0cb694bSSteve Ma 
12843f7d54a6SGarrett D'Amore 	switch (cmd) {
12853f7d54a6SGarrett D'Amore 	case DKIOCGMEDIAINFO: {
12863f7d54a6SGarrett D'Amore 		struct dk_minfo minfo;
12873f7d54a6SGarrett D'Amore 
12883f7d54a6SGarrett D'Amore 		/* make sure our state information is current */
12893f7d54a6SGarrett D'Amore 		bd_update_state(bd);
12903f7d54a6SGarrett D'Amore 		bzero(&minfo, sizeof (minfo));
12913f7d54a6SGarrett D'Amore 		minfo.dki_media_type = DK_FIXED_DISK;
12923f7d54a6SGarrett D'Amore 		minfo.dki_lbsize = (1U << bd->d_blkshift);
12933f7d54a6SGarrett D'Amore 		minfo.dki_capacity = bd->d_numblks;
12943f7d54a6SGarrett D'Amore 		if (ddi_copyout(&minfo, ptr, sizeof (minfo), flag)) {
12953f7d54a6SGarrett D'Amore 			return (EFAULT);
12963f7d54a6SGarrett D'Amore 		}
12973f7d54a6SGarrett D'Amore 		return (0);
12983f7d54a6SGarrett D'Amore 	}
1299dba604f9SDan McDonald 	case DKIOCGMEDIAINFOEXT: {
1300dba604f9SDan McDonald 		struct dk_minfo_ext miext;
1301dba604f9SDan McDonald 
1302dba604f9SDan McDonald 		/* make sure our state information is current */
1303dba604f9SDan McDonald 		bd_update_state(bd);
1304dba604f9SDan McDonald 		bzero(&miext, sizeof (miext));
1305dba604f9SDan McDonald 		miext.dki_media_type = DK_FIXED_DISK;
1306dba604f9SDan McDonald 		miext.dki_lbsize = (1U << bd->d_blkshift);
130732ce6b81SHans Rosenfeld 		miext.dki_pbsize = (1U << bd->d_pblkshift);
1308dba604f9SDan McDonald 		miext.dki_capacity = bd->d_numblks;
1309dba604f9SDan McDonald 		if (ddi_copyout(&miext, ptr, sizeof (miext), flag)) {
1310dba604f9SDan McDonald 			return (EFAULT);
1311dba604f9SDan McDonald 		}
1312dba604f9SDan McDonald 		return (0);
1313dba604f9SDan McDonald 	}
13143f7d54a6SGarrett D'Amore 	case DKIOCINFO: {
13153f7d54a6SGarrett D'Amore 		struct dk_cinfo cinfo;
13163f7d54a6SGarrett D'Amore 		bzero(&cinfo, sizeof (cinfo));
13173f7d54a6SGarrett D'Amore 		cinfo.dki_ctype = DKC_BLKDEV;
13183f7d54a6SGarrett D'Amore 		cinfo.dki_cnum = ddi_get_instance(ddi_get_parent(bd->d_dip));
13193f7d54a6SGarrett D'Amore 		(void) snprintf(cinfo.dki_cname, sizeof (cinfo.dki_cname),
13203f7d54a6SGarrett D'Amore 		    "%s", ddi_driver_name(ddi_get_parent(bd->d_dip)));
13213f7d54a6SGarrett D'Amore 		(void) snprintf(cinfo.dki_dname, sizeof (cinfo.dki_dname),
13223f7d54a6SGarrett D'Amore 		    "%s", ddi_driver_name(bd->d_dip));
13233f7d54a6SGarrett D'Amore 		cinfo.dki_unit = inst;
13243f7d54a6SGarrett D'Amore 		cinfo.dki_flags = DKI_FMTVOL;
13253f7d54a6SGarrett D'Amore 		cinfo.dki_partition = part;
13263f7d54a6SGarrett D'Amore 		cinfo.dki_maxtransfer = bd->d_maxxfer / DEV_BSIZE;
13273f7d54a6SGarrett D'Amore 		cinfo.dki_addr = 0;
13283f7d54a6SGarrett D'Amore 		cinfo.dki_slave = 0;
13293f7d54a6SGarrett D'Amore 		cinfo.dki_space = 0;
13303f7d54a6SGarrett D'Amore 		cinfo.dki_prio = 0;
13313f7d54a6SGarrett D'Amore 		cinfo.dki_vec = 0;
13323f7d54a6SGarrett D'Amore 		if (ddi_copyout(&cinfo, ptr, sizeof (cinfo), flag)) {
13333f7d54a6SGarrett D'Amore 			return (EFAULT);
13343f7d54a6SGarrett D'Amore 		}
13353f7d54a6SGarrett D'Amore 		return (0);
13363f7d54a6SGarrett D'Amore 	}
13373f7d54a6SGarrett D'Amore 	case DKIOCREMOVABLE: {
13383f7d54a6SGarrett D'Amore 		int i;
13393f7d54a6SGarrett D'Amore 		i = bd->d_removable ? 1 : 0;
13403f7d54a6SGarrett D'Amore 		if (ddi_copyout(&i, ptr, sizeof (i), flag)) {
13413f7d54a6SGarrett D'Amore 			return (EFAULT);
13423f7d54a6SGarrett D'Amore 		}
13433f7d54a6SGarrett D'Amore 		return (0);
13443f7d54a6SGarrett D'Amore 	}
13453f7d54a6SGarrett D'Amore 	case DKIOCHOTPLUGGABLE: {
13463f7d54a6SGarrett D'Amore 		int i;
13473f7d54a6SGarrett D'Amore 		i = bd->d_hotpluggable ? 1 : 0;
13483f7d54a6SGarrett D'Amore 		if (ddi_copyout(&i, ptr, sizeof (i), flag)) {
13493f7d54a6SGarrett D'Amore 			return (EFAULT);
13503f7d54a6SGarrett D'Amore 		}
13513f7d54a6SGarrett D'Amore 		return (0);
13523f7d54a6SGarrett D'Amore 	}
13533f7d54a6SGarrett D'Amore 	case DKIOCREADONLY: {
13543f7d54a6SGarrett D'Amore 		int i;
13553f7d54a6SGarrett D'Amore 		i = bd->d_rdonly ? 1 : 0;
13563f7d54a6SGarrett D'Amore 		if (ddi_copyout(&i, ptr, sizeof (i), flag)) {
13573f7d54a6SGarrett D'Amore 			return (EFAULT);
13583f7d54a6SGarrett D'Amore 		}
13593f7d54a6SGarrett D'Amore 		return (0);
13603f7d54a6SGarrett D'Amore 	}
136159d8f100SGarrett D'Amore 	case DKIOCSOLIDSTATE: {
136259d8f100SGarrett D'Amore 		int i;
136359d8f100SGarrett D'Amore 		i = bd->d_ssd ? 1 : 0;
136459d8f100SGarrett D'Amore 		if (ddi_copyout(&i, ptr, sizeof (i), flag)) {
136559d8f100SGarrett D'Amore 			return (EFAULT);
136659d8f100SGarrett D'Amore 		}
136759d8f100SGarrett D'Amore 		return (0);
136859d8f100SGarrett D'Amore 	}
13693f7d54a6SGarrett D'Amore 	case DKIOCSTATE: {
13703f7d54a6SGarrett D'Amore 		enum dkio_state	state;
13713f7d54a6SGarrett D'Amore 		if (ddi_copyin(ptr, &state, sizeof (state), flag)) {
13723f7d54a6SGarrett D'Amore 			return (EFAULT);
13733f7d54a6SGarrett D'Amore 		}
13743f7d54a6SGarrett D'Amore 		if ((rv = bd_check_state(bd, &state)) != 0) {
13753f7d54a6SGarrett D'Amore 			return (rv);
13763f7d54a6SGarrett D'Amore 		}
13773f7d54a6SGarrett D'Amore 		if (ddi_copyout(&state, ptr, sizeof (state), flag)) {
13783f7d54a6SGarrett D'Amore 			return (EFAULT);
13793f7d54a6SGarrett D'Amore 		}
13803f7d54a6SGarrett D'Amore 		return (0);
13813f7d54a6SGarrett D'Amore 	}
13823f7d54a6SGarrett D'Amore 	case DKIOCFLUSHWRITECACHE: {
1383f097ef9cSAlexey Zaytsev 		struct dk_callback *dkc = NULL;
13843f7d54a6SGarrett D'Amore 
1385f097ef9cSAlexey Zaytsev 		if (flag & FKIOCTL)
1386f097ef9cSAlexey Zaytsev 			dkc = (void *)arg;
1387f097ef9cSAlexey Zaytsev 
13883f7d54a6SGarrett D'Amore 		rv = bd_flush_write_cache(bd, dkc);
13893f7d54a6SGarrett D'Amore 		return (rv);
13903f7d54a6SGarrett D'Amore 	}
13913f7d54a6SGarrett D'Amore 
13923f7d54a6SGarrett D'Amore 	default:
13933f7d54a6SGarrett D'Amore 		break;
13943f7d54a6SGarrett D'Amore 
13953f7d54a6SGarrett D'Amore 	}
13963f7d54a6SGarrett D'Amore 	return (ENOTTY);
13973f7d54a6SGarrett D'Amore }
13983f7d54a6SGarrett D'Amore 
13993f7d54a6SGarrett D'Amore static int
14003f7d54a6SGarrett D'Amore bd_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
14013f7d54a6SGarrett D'Amore     char *name, caddr_t valuep, int *lengthp)
14023f7d54a6SGarrett D'Amore {
14033f7d54a6SGarrett D'Amore 	bd_t	*bd;
14043f7d54a6SGarrett D'Amore 
14053f7d54a6SGarrett D'Amore 	bd = ddi_get_soft_state(bd_state, ddi_get_instance(dip));
14063f7d54a6SGarrett D'Amore 	if (bd == NULL)
14073f7d54a6SGarrett D'Amore 		return (ddi_prop_op(dev, dip, prop_op, mod_flags,
14083f7d54a6SGarrett D'Amore 		    name, valuep, lengthp));
14093f7d54a6SGarrett D'Amore 
14103f7d54a6SGarrett D'Amore 	return (cmlb_prop_op(bd->d_cmlbh, dev, dip, prop_op, mod_flags, name,
141186e3bca6SGarrett D'Amore 	    valuep, lengthp, BDPART(dev), 0));
14123f7d54a6SGarrett D'Amore }
14133f7d54a6SGarrett D'Amore 
14143f7d54a6SGarrett D'Amore 
14153f7d54a6SGarrett D'Amore static int
14163f7d54a6SGarrett D'Amore bd_tg_rdwr(dev_info_t *dip, uchar_t cmd, void *bufaddr, diskaddr_t start,
14173f7d54a6SGarrett D'Amore     size_t length, void *tg_cookie)
14183f7d54a6SGarrett D'Amore {
14193f7d54a6SGarrett D'Amore 	bd_t		*bd;
14203f7d54a6SGarrett D'Amore 	buf_t		*bp;
14213f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
14223f7d54a6SGarrett D'Amore 	int		rv;
14233f7d54a6SGarrett D'Amore 	int		(*func)(void *, bd_xfer_t *);
142486e3bca6SGarrett D'Amore 	int		kmflag;
14253f7d54a6SGarrett D'Amore 
142686e3bca6SGarrett D'Amore 	/*
142786e3bca6SGarrett D'Amore 	 * If we are running in polled mode (such as during dump(9e)
142886e3bca6SGarrett D'Amore 	 * execution), then we cannot sleep for kernel allocations.
142986e3bca6SGarrett D'Amore 	 */
143086e3bca6SGarrett D'Amore 	kmflag = tg_cookie ? KM_NOSLEEP : KM_SLEEP;
14313f7d54a6SGarrett D'Amore 
143286e3bca6SGarrett D'Amore 	bd = ddi_get_soft_state(bd_state, ddi_get_instance(dip));
14333f7d54a6SGarrett D'Amore 
14343f7d54a6SGarrett D'Amore 	if (P2PHASE(length, (1U << bd->d_blkshift)) != 0) {
14353f7d54a6SGarrett D'Amore 		/* We can only transfer whole blocks at a time! */
14363f7d54a6SGarrett D'Amore 		return (EINVAL);
14373f7d54a6SGarrett D'Amore 	}
14383f7d54a6SGarrett D'Amore 
143986e3bca6SGarrett D'Amore 	if ((bp = getrbuf(kmflag)) == NULL) {
144086e3bca6SGarrett D'Amore 		return (ENOMEM);
144186e3bca6SGarrett D'Amore 	}
14423f7d54a6SGarrett D'Amore 
14433f7d54a6SGarrett D'Amore 	switch (cmd) {
14443f7d54a6SGarrett D'Amore 	case TG_READ:
14453f7d54a6SGarrett D'Amore 		bp->b_flags = B_READ;
14463f7d54a6SGarrett D'Amore 		func = bd->d_ops.o_read;
14473f7d54a6SGarrett D'Amore 		break;
14483f7d54a6SGarrett D'Amore 	case TG_WRITE:
14493f7d54a6SGarrett D'Amore 		bp->b_flags = B_WRITE;
14503f7d54a6SGarrett D'Amore 		func = bd->d_ops.o_write;
14513f7d54a6SGarrett D'Amore 		break;
14523f7d54a6SGarrett D'Amore 	default:
14533f7d54a6SGarrett D'Amore 		freerbuf(bp);
14543f7d54a6SGarrett D'Amore 		return (EINVAL);
14553f7d54a6SGarrett D'Amore 	}
14563f7d54a6SGarrett D'Amore 
14573f7d54a6SGarrett D'Amore 	bp->b_un.b_addr = bufaddr;
14583f7d54a6SGarrett D'Amore 	bp->b_bcount = length;
145986e3bca6SGarrett D'Amore 	xi = bd_xfer_alloc(bd, bp, func, kmflag);
14603f7d54a6SGarrett D'Amore 	if (xi == NULL) {
14613f7d54a6SGarrett D'Amore 		rv = geterror(bp);
14623f7d54a6SGarrett D'Amore 		freerbuf(bp);
14633f7d54a6SGarrett D'Amore 		return (rv);
14643f7d54a6SGarrett D'Amore 	}
146586e3bca6SGarrett D'Amore 	xi->i_flags = tg_cookie ? BD_XFER_POLL : 0;
14663f7d54a6SGarrett D'Amore 	xi->i_blkno = start;
14673f7d54a6SGarrett D'Amore 	bd_submit(bd, xi);
14683f7d54a6SGarrett D'Amore 	(void) biowait(bp);
14693f7d54a6SGarrett D'Amore 	rv = geterror(bp);
14703f7d54a6SGarrett D'Amore 	freerbuf(bp);
14713f7d54a6SGarrett D'Amore 
14723f7d54a6SGarrett D'Amore 	return (rv);
14733f7d54a6SGarrett D'Amore }
14743f7d54a6SGarrett D'Amore 
14753f7d54a6SGarrett D'Amore static int
14763f7d54a6SGarrett D'Amore bd_tg_getinfo(dev_info_t *dip, int cmd, void *arg, void *tg_cookie)
14773f7d54a6SGarrett D'Amore {
14783f7d54a6SGarrett D'Amore 	bd_t		*bd;
14793f7d54a6SGarrett D'Amore 
148086e3bca6SGarrett D'Amore 	_NOTE(ARGUNUSED(tg_cookie));
148186e3bca6SGarrett D'Amore 	bd = ddi_get_soft_state(bd_state, ddi_get_instance(dip));
14823f7d54a6SGarrett D'Amore 
14833f7d54a6SGarrett D'Amore 	switch (cmd) {
14843f7d54a6SGarrett D'Amore 	case TG_GETPHYGEOM:
14853f7d54a6SGarrett D'Amore 	case TG_GETVIRTGEOM:
14863f7d54a6SGarrett D'Amore 		/*
14873f7d54a6SGarrett D'Amore 		 * We don't have any "geometry" as such, let cmlb
14883f7d54a6SGarrett D'Amore 		 * fabricate something.
14893f7d54a6SGarrett D'Amore 		 */
14903f7d54a6SGarrett D'Amore 		return (ENOTTY);
14913f7d54a6SGarrett D'Amore 
14923f7d54a6SGarrett D'Amore 	case TG_GETCAPACITY:
14933f7d54a6SGarrett D'Amore 		bd_update_state(bd);
14943f7d54a6SGarrett D'Amore 		*(diskaddr_t *)arg = bd->d_numblks;
14953f7d54a6SGarrett D'Amore 		return (0);
14963f7d54a6SGarrett D'Amore 
14973f7d54a6SGarrett D'Amore 	case TG_GETBLOCKSIZE:
14983f7d54a6SGarrett D'Amore 		*(uint32_t *)arg = (1U << bd->d_blkshift);
14993f7d54a6SGarrett D'Amore 		return (0);
15003f7d54a6SGarrett D'Amore 
15013f7d54a6SGarrett D'Amore 	case TG_GETATTR:
15023f7d54a6SGarrett D'Amore 		/*
15033f7d54a6SGarrett D'Amore 		 * It turns out that cmlb really doesn't do much for
15043f7d54a6SGarrett D'Amore 		 * non-writable media, but lets make the information
15053f7d54a6SGarrett D'Amore 		 * available for it in case it does more in the
15063f7d54a6SGarrett D'Amore 		 * future.  (The value is currently used for
15073f7d54a6SGarrett D'Amore 		 * triggering special behavior for CD-ROMs.)
15083f7d54a6SGarrett D'Amore 		 */
15093f7d54a6SGarrett D'Amore 		bd_update_state(bd);
15103f7d54a6SGarrett D'Amore 		((tg_attribute_t *)arg)->media_is_writable =
15113f7d54a6SGarrett D'Amore 		    bd->d_rdonly ? B_FALSE : B_TRUE;
151259d8f100SGarrett D'Amore 		((tg_attribute_t *)arg)->media_is_solid_state = bd->d_ssd;
1513b2b61b8fSYuri Pankov 		((tg_attribute_t *)arg)->media_is_rotational = B_FALSE;
15143f7d54a6SGarrett D'Amore 		return (0);
15153f7d54a6SGarrett D'Amore 
15163f7d54a6SGarrett D'Amore 	default:
15173f7d54a6SGarrett D'Amore 		return (EINVAL);
15183f7d54a6SGarrett D'Amore 	}
15193f7d54a6SGarrett D'Amore }
15203f7d54a6SGarrett D'Amore 
15213f7d54a6SGarrett D'Amore 
15223f7d54a6SGarrett D'Amore static void
15233f7d54a6SGarrett D'Amore bd_sched(bd_t *bd)
15243f7d54a6SGarrett D'Amore {
15253f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
15263f7d54a6SGarrett D'Amore 	struct buf	*bp;
15273f7d54a6SGarrett D'Amore 	int		rv;
15283f7d54a6SGarrett D'Amore 
152986e3bca6SGarrett D'Amore 	mutex_enter(&bd->d_iomutex);
15303f7d54a6SGarrett D'Amore 
15313f7d54a6SGarrett D'Amore 	while ((bd->d_qactive < bd->d_qsize) &&
15323f7d54a6SGarrett D'Amore 	    ((xi = list_remove_head(&bd->d_waitq)) != NULL)) {
15333f7d54a6SGarrett D'Amore 		bd->d_qactive++;
15343f7d54a6SGarrett D'Amore 		kstat_waitq_to_runq(bd->d_kiop);
15353f7d54a6SGarrett D'Amore 		list_insert_tail(&bd->d_runq, xi);
15363f7d54a6SGarrett D'Amore 
153786e3bca6SGarrett D'Amore 		/*
153886e3bca6SGarrett D'Amore 		 * Submit the job to the driver.  We drop the I/O mutex
153986e3bca6SGarrett D'Amore 		 * so that we can deal with the case where the driver
154086e3bca6SGarrett D'Amore 		 * completion routine calls back into us synchronously.
154186e3bca6SGarrett D'Amore 		 */
15423f7d54a6SGarrett D'Amore 
15433f7d54a6SGarrett D'Amore 		mutex_exit(&bd->d_iomutex);
154486e3bca6SGarrett D'Amore 
154586e3bca6SGarrett D'Amore 		rv = xi->i_func(bd->d_private, &xi->i_public);
154686e3bca6SGarrett D'Amore 		if (rv != 0) {
15473f7d54a6SGarrett D'Amore 			bp = xi->i_bp;
15483f7d54a6SGarrett D'Amore 			bioerror(bp, rv);
15493f7d54a6SGarrett D'Amore 			biodone(bp);
155086e3bca6SGarrett D'Amore 
1551bef9e21aSHans Rosenfeld 			atomic_inc_32(&bd->d_kerr->bd_transerrs.value.ui32);
1552bef9e21aSHans Rosenfeld 
155386e3bca6SGarrett D'Amore 			mutex_enter(&bd->d_iomutex);
155486e3bca6SGarrett D'Amore 			bd->d_qactive--;
155586e3bca6SGarrett D'Amore 			kstat_runq_exit(bd->d_kiop);
155686e3bca6SGarrett D'Amore 			list_remove(&bd->d_runq, xi);
1557dd3928f8SHans Rosenfeld 			bd_xfer_free(xi);
155886e3bca6SGarrett D'Amore 		} else {
15593f7d54a6SGarrett D'Amore 			mutex_enter(&bd->d_iomutex);
15603f7d54a6SGarrett D'Amore 		}
15613f7d54a6SGarrett D'Amore 	}
156286e3bca6SGarrett D'Amore 
156386e3bca6SGarrett D'Amore 	mutex_exit(&bd->d_iomutex);
15643f7d54a6SGarrett D'Amore }
15653f7d54a6SGarrett D'Amore 
15663f7d54a6SGarrett D'Amore static void
15673f7d54a6SGarrett D'Amore bd_submit(bd_t *bd, bd_xfer_impl_t *xi)
15683f7d54a6SGarrett D'Amore {
15693f7d54a6SGarrett D'Amore 	mutex_enter(&bd->d_iomutex);
15703f7d54a6SGarrett D'Amore 	list_insert_tail(&bd->d_waitq, xi);
15713f7d54a6SGarrett D'Amore 	kstat_waitq_enter(bd->d_kiop);
15723f7d54a6SGarrett D'Amore 	mutex_exit(&bd->d_iomutex);
157386e3bca6SGarrett D'Amore 
157486e3bca6SGarrett D'Amore 	bd_sched(bd);
15753f7d54a6SGarrett D'Amore }
15763f7d54a6SGarrett D'Amore 
15773f7d54a6SGarrett D'Amore static void
15783f7d54a6SGarrett D'Amore bd_runq_exit(bd_xfer_impl_t *xi, int err)
15793f7d54a6SGarrett D'Amore {
15803f7d54a6SGarrett D'Amore 	bd_t	*bd = xi->i_bd;
15813f7d54a6SGarrett D'Amore 	buf_t	*bp = xi->i_bp;
15823f7d54a6SGarrett D'Amore 
158386e3bca6SGarrett D'Amore 	mutex_enter(&bd->d_iomutex);
15843f7d54a6SGarrett D'Amore 	bd->d_qactive--;
15853f7d54a6SGarrett D'Amore 	kstat_runq_exit(bd->d_kiop);
158686e3bca6SGarrett D'Amore 	list_remove(&bd->d_runq, xi);
158786e3bca6SGarrett D'Amore 	mutex_exit(&bd->d_iomutex);
158886e3bca6SGarrett D'Amore 
15893f7d54a6SGarrett D'Amore 	if (err == 0) {
15903f7d54a6SGarrett D'Amore 		if (bp->b_flags & B_READ) {
15913f7d54a6SGarrett D'Amore 			bd->d_kiop->reads++;
15923f7d54a6SGarrett D'Amore 			bd->d_kiop->nread += (bp->b_bcount - xi->i_resid);
15933f7d54a6SGarrett D'Amore 		} else {
15943f7d54a6SGarrett D'Amore 			bd->d_kiop->writes++;
15953f7d54a6SGarrett D'Amore 			bd->d_kiop->nwritten += (bp->b_bcount - xi->i_resid);
15963f7d54a6SGarrett D'Amore 		}
15973f7d54a6SGarrett D'Amore 	}
15983f7d54a6SGarrett D'Amore 	bd_sched(bd);
15993f7d54a6SGarrett D'Amore }
16003f7d54a6SGarrett D'Amore 
16013f7d54a6SGarrett D'Amore static void
16023f7d54a6SGarrett D'Amore bd_update_state(bd_t *bd)
16033f7d54a6SGarrett D'Amore {
160432ce6b81SHans Rosenfeld 	enum	dkio_state	state = DKIO_INSERTED;
16053f7d54a6SGarrett D'Amore 	boolean_t		docmlb = B_FALSE;
160632ce6b81SHans Rosenfeld 	bd_media_t		media;
16073f7d54a6SGarrett D'Amore 
16083f7d54a6SGarrett D'Amore 	bzero(&media, sizeof (media));
16093f7d54a6SGarrett D'Amore 
16103f7d54a6SGarrett D'Amore 	mutex_enter(&bd->d_statemutex);
161132ce6b81SHans Rosenfeld 	if (bd->d_ops.o_media_info(bd->d_private, &media) != 0) {
161232ce6b81SHans Rosenfeld 		bd->d_numblks = 0;
161332ce6b81SHans Rosenfeld 		state = DKIO_EJECTED;
161432ce6b81SHans Rosenfeld 		goto done;
161532ce6b81SHans Rosenfeld 	}
161632ce6b81SHans Rosenfeld 
16173f7d54a6SGarrett D'Amore 	if ((media.m_blksize < 512) ||
16183f7d54a6SGarrett D'Amore 	    (!ISP2(media.m_blksize)) ||
16193f7d54a6SGarrett D'Amore 	    (P2PHASE(bd->d_maxxfer, media.m_blksize))) {
162032ce6b81SHans Rosenfeld 		cmn_err(CE_WARN, "%s%d: Invalid media block size (%d)",
162132ce6b81SHans Rosenfeld 		    ddi_driver_name(bd->d_dip), ddi_get_instance(bd->d_dip),
16223f7d54a6SGarrett D'Amore 		    media.m_blksize);
16233f7d54a6SGarrett D'Amore 		/*
162432ce6b81SHans Rosenfeld 		 * We can't use the media, treat it as not present.
16253f7d54a6SGarrett D'Amore 		 */
16263f7d54a6SGarrett D'Amore 		state = DKIO_EJECTED;
16273f7d54a6SGarrett D'Amore 		bd->d_numblks = 0;
162832ce6b81SHans Rosenfeld 		goto done;
162932ce6b81SHans Rosenfeld 	}
163032ce6b81SHans Rosenfeld 
163132ce6b81SHans Rosenfeld 	if (((1U << bd->d_blkshift) != media.m_blksize) ||
163232ce6b81SHans Rosenfeld 	    (bd->d_numblks != media.m_nblks)) {
163332ce6b81SHans Rosenfeld 		/* Device size changed */
163432ce6b81SHans Rosenfeld 		docmlb = B_TRUE;
163532ce6b81SHans Rosenfeld 	}
163632ce6b81SHans Rosenfeld 
16373f7d54a6SGarrett D'Amore 	bd->d_blkshift = ddi_ffs(media.m_blksize) - 1;
163832ce6b81SHans Rosenfeld 	bd->d_pblkshift = bd->d_blkshift;
16393f7d54a6SGarrett D'Amore 	bd->d_numblks = media.m_nblks;
16403f7d54a6SGarrett D'Amore 	bd->d_rdonly = media.m_readonly;
164159d8f100SGarrett D'Amore 	bd->d_ssd = media.m_solidstate;
16423f7d54a6SGarrett D'Amore 
164332ce6b81SHans Rosenfeld 	/*
164432ce6b81SHans Rosenfeld 	 * Only use the supplied physical block size if it is non-zero,
164532ce6b81SHans Rosenfeld 	 * greater or equal to the block size, and a power of 2. Ignore it
164632ce6b81SHans Rosenfeld 	 * if not, it's just informational and we can still use the media.
164732ce6b81SHans Rosenfeld 	 */
164832ce6b81SHans Rosenfeld 	if ((media.m_pblksize != 0) &&
164932ce6b81SHans Rosenfeld 	    (media.m_pblksize >= media.m_blksize) &&
165032ce6b81SHans Rosenfeld 	    (ISP2(media.m_pblksize)))
165132ce6b81SHans Rosenfeld 		bd->d_pblkshift = ddi_ffs(media.m_pblksize) - 1;
16523f7d54a6SGarrett D'Amore 
165332ce6b81SHans Rosenfeld done:
16543f7d54a6SGarrett D'Amore 	if (state != bd->d_state) {
16553f7d54a6SGarrett D'Amore 		bd->d_state = state;
16563f7d54a6SGarrett D'Amore 		cv_broadcast(&bd->d_statecv);
16573f7d54a6SGarrett D'Amore 		docmlb = B_TRUE;
16583f7d54a6SGarrett D'Amore 	}
16593f7d54a6SGarrett D'Amore 	mutex_exit(&bd->d_statemutex);
16603f7d54a6SGarrett D'Amore 
1661bef9e21aSHans Rosenfeld 	bd->d_kerr->bd_capacity.value.ui64 = bd->d_numblks << bd->d_blkshift;
1662bef9e21aSHans Rosenfeld 
16633f7d54a6SGarrett D'Amore 	if (docmlb) {
16643f7d54a6SGarrett D'Amore 		if (state == DKIO_INSERTED) {
166586e3bca6SGarrett D'Amore 			(void) cmlb_validate(bd->d_cmlbh, 0, 0);
16663f7d54a6SGarrett D'Amore 		} else {
166786e3bca6SGarrett D'Amore 			cmlb_invalidate(bd->d_cmlbh, 0);
16683f7d54a6SGarrett D'Amore 		}
16693f7d54a6SGarrett D'Amore 	}
16703f7d54a6SGarrett D'Amore }
16713f7d54a6SGarrett D'Amore 
16723f7d54a6SGarrett D'Amore static int
16733f7d54a6SGarrett D'Amore bd_check_state(bd_t *bd, enum dkio_state *state)
16743f7d54a6SGarrett D'Amore {
16753f7d54a6SGarrett D'Amore 	clock_t		when;
16763f7d54a6SGarrett D'Amore 
16773f7d54a6SGarrett D'Amore 	for (;;) {
16783f7d54a6SGarrett D'Amore 
16793f7d54a6SGarrett D'Amore 		bd_update_state(bd);
16803f7d54a6SGarrett D'Amore 
16813f7d54a6SGarrett D'Amore 		mutex_enter(&bd->d_statemutex);
16823f7d54a6SGarrett D'Amore 
16833f7d54a6SGarrett D'Amore 		if (bd->d_state != *state) {
16843f7d54a6SGarrett D'Amore 			*state = bd->d_state;
16853f7d54a6SGarrett D'Amore 			mutex_exit(&bd->d_statemutex);
16863f7d54a6SGarrett D'Amore 			break;
16873f7d54a6SGarrett D'Amore 		}
16883f7d54a6SGarrett D'Amore 
16893f7d54a6SGarrett D'Amore 		when = drv_usectohz(1000000);
16903f7d54a6SGarrett D'Amore 		if (cv_reltimedwait_sig(&bd->d_statecv, &bd->d_statemutex,
16913f7d54a6SGarrett D'Amore 		    when, TR_CLOCK_TICK) == 0) {
16923f7d54a6SGarrett D'Amore 			mutex_exit(&bd->d_statemutex);
16933f7d54a6SGarrett D'Amore 			return (EINTR);
16943f7d54a6SGarrett D'Amore 		}
16953f7d54a6SGarrett D'Amore 
16963f7d54a6SGarrett D'Amore 		mutex_exit(&bd->d_statemutex);
16973f7d54a6SGarrett D'Amore 	}
16983f7d54a6SGarrett D'Amore 
16993f7d54a6SGarrett D'Amore 	return (0);
17003f7d54a6SGarrett D'Amore }
17013f7d54a6SGarrett D'Amore 
17023f7d54a6SGarrett D'Amore static int
17033f7d54a6SGarrett D'Amore bd_flush_write_cache_done(struct buf *bp)
17043f7d54a6SGarrett D'Amore {
17053f7d54a6SGarrett D'Amore 	struct dk_callback *dc = (void *)bp->b_private;
17063f7d54a6SGarrett D'Amore 
17073f7d54a6SGarrett D'Amore 	(*dc->dkc_callback)(dc->dkc_cookie, geterror(bp));
17083f7d54a6SGarrett D'Amore 	kmem_free(dc, sizeof (*dc));
17093f7d54a6SGarrett D'Amore 	freerbuf(bp);
17103f7d54a6SGarrett D'Amore 	return (0);
17113f7d54a6SGarrett D'Amore }
17123f7d54a6SGarrett D'Amore 
17133f7d54a6SGarrett D'Amore static int
17143f7d54a6SGarrett D'Amore bd_flush_write_cache(bd_t *bd, struct dk_callback *dkc)
17153f7d54a6SGarrett D'Amore {
17163f7d54a6SGarrett D'Amore 	buf_t			*bp;
17173f7d54a6SGarrett D'Amore 	struct dk_callback	*dc;
17183f7d54a6SGarrett D'Amore 	bd_xfer_impl_t		*xi;
17193f7d54a6SGarrett D'Amore 	int			rv;
17203f7d54a6SGarrett D'Amore 
17213f7d54a6SGarrett D'Amore 	if (bd->d_ops.o_sync_cache == NULL) {
17223f7d54a6SGarrett D'Amore 		return (ENOTSUP);
17233f7d54a6SGarrett D'Amore 	}
17243f7d54a6SGarrett D'Amore 	if ((bp = getrbuf(KM_SLEEP)) == NULL) {
17253f7d54a6SGarrett D'Amore 		return (ENOMEM);
17263f7d54a6SGarrett D'Amore 	}
17273f7d54a6SGarrett D'Amore 	bp->b_resid = 0;
17283f7d54a6SGarrett D'Amore 	bp->b_bcount = 0;
17293f7d54a6SGarrett D'Amore 
17303f7d54a6SGarrett D'Amore 	xi = bd_xfer_alloc(bd, bp, bd->d_ops.o_sync_cache, KM_SLEEP);
17313f7d54a6SGarrett D'Amore 	if (xi == NULL) {
17323f7d54a6SGarrett D'Amore 		rv = geterror(bp);
17333f7d54a6SGarrett D'Amore 		freerbuf(bp);
17343f7d54a6SGarrett D'Amore 		return (rv);
17353f7d54a6SGarrett D'Amore 	}
17363f7d54a6SGarrett D'Amore 
1737f097ef9cSAlexey Zaytsev 	/* Make an asynchronous flush, but only if there is a callback */
1738f097ef9cSAlexey Zaytsev 	if (dkc != NULL && dkc->dkc_callback != NULL) {
17393f7d54a6SGarrett D'Amore 		/* Make a private copy of the callback structure */
17403f7d54a6SGarrett D'Amore 		dc = kmem_alloc(sizeof (*dc), KM_SLEEP);
17413f7d54a6SGarrett D'Amore 		*dc = *dkc;
17423f7d54a6SGarrett D'Amore 		bp->b_private = dc;
17433f7d54a6SGarrett D'Amore 		bp->b_iodone = bd_flush_write_cache_done;
17443f7d54a6SGarrett D'Amore 
17453f7d54a6SGarrett D'Amore 		bd_submit(bd, xi);
1746f097ef9cSAlexey Zaytsev 		return (0);
1747f097ef9cSAlexey Zaytsev 	}
1748f097ef9cSAlexey Zaytsev 
1749f097ef9cSAlexey Zaytsev 	/* In case there is no callback, perform a synchronous flush */
1750f097ef9cSAlexey Zaytsev 	bd_submit(bd, xi);
17513f7d54a6SGarrett D'Amore 	(void) biowait(bp);
17523f7d54a6SGarrett D'Amore 	rv = geterror(bp);
17533f7d54a6SGarrett D'Amore 	freerbuf(bp);
1754f097ef9cSAlexey Zaytsev 
17553f7d54a6SGarrett D'Amore 	return (rv);
17563f7d54a6SGarrett D'Amore }
17573f7d54a6SGarrett D'Amore 
17583f7d54a6SGarrett D'Amore /*
17593f7d54a6SGarrett D'Amore  * Nexus support.
17603f7d54a6SGarrett D'Amore  */
17613f7d54a6SGarrett D'Amore int
17623f7d54a6SGarrett D'Amore bd_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
17633f7d54a6SGarrett D'Amore     void *arg, void *result)
17643f7d54a6SGarrett D'Amore {
17653f7d54a6SGarrett D'Amore 	bd_handle_t	hdl;
17663f7d54a6SGarrett D'Amore 
17673f7d54a6SGarrett D'Amore 	switch (ctlop) {
17683f7d54a6SGarrett D'Amore 	case DDI_CTLOPS_REPORTDEV:
17693f7d54a6SGarrett D'Amore 		cmn_err(CE_CONT, "?Block device: %s@%s, %s%d\n",
17703f7d54a6SGarrett D'Amore 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
17713f7d54a6SGarrett D'Amore 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
17723f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
17733f7d54a6SGarrett D'Amore 
17743f7d54a6SGarrett D'Amore 	case DDI_CTLOPS_INITCHILD:
17753f7d54a6SGarrett D'Amore 		hdl = ddi_get_parent_data((dev_info_t *)arg);
17763f7d54a6SGarrett D'Amore 		if (hdl == NULL) {
17773f7d54a6SGarrett D'Amore 			return (DDI_NOT_WELL_FORMED);
17783f7d54a6SGarrett D'Amore 		}
17793f7d54a6SGarrett D'Amore 		ddi_set_name_addr((dev_info_t *)arg, hdl->h_addr);
17803f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
17813f7d54a6SGarrett D'Amore 
17823f7d54a6SGarrett D'Amore 	case DDI_CTLOPS_UNINITCHILD:
17833f7d54a6SGarrett D'Amore 		ddi_set_name_addr((dev_info_t *)arg, NULL);
17843f7d54a6SGarrett D'Amore 		ndi_prop_remove_all((dev_info_t *)arg);
17853f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
17863f7d54a6SGarrett D'Amore 
17873f7d54a6SGarrett D'Amore 	default:
17883f7d54a6SGarrett D'Amore 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
17893f7d54a6SGarrett D'Amore 	}
17903f7d54a6SGarrett D'Amore }
17913f7d54a6SGarrett D'Amore 
17923f7d54a6SGarrett D'Amore /*
17933f7d54a6SGarrett D'Amore  * Functions for device drivers.
17943f7d54a6SGarrett D'Amore  */
17953f7d54a6SGarrett D'Amore bd_handle_t
17963f7d54a6SGarrett D'Amore bd_alloc_handle(void *private, bd_ops_t *ops, ddi_dma_attr_t *dma, int kmflag)
17973f7d54a6SGarrett D'Amore {
17983f7d54a6SGarrett D'Amore 	bd_handle_t	hdl;
17993f7d54a6SGarrett D'Amore 
18003f7d54a6SGarrett D'Amore 	hdl = kmem_zalloc(sizeof (*hdl), kmflag);
18013f7d54a6SGarrett D'Amore 	if (hdl != NULL) {
18023f7d54a6SGarrett D'Amore 		hdl->h_ops = *ops;
18033f7d54a6SGarrett D'Amore 		hdl->h_dma = dma;
18043f7d54a6SGarrett D'Amore 		hdl->h_private = private;
18053f7d54a6SGarrett D'Amore 	}
18063f7d54a6SGarrett D'Amore 
18073f7d54a6SGarrett D'Amore 	return (hdl);
18083f7d54a6SGarrett D'Amore }
18093f7d54a6SGarrett D'Amore 
18103f7d54a6SGarrett D'Amore void
18113f7d54a6SGarrett D'Amore bd_free_handle(bd_handle_t hdl)
18123f7d54a6SGarrett D'Amore {
18133f7d54a6SGarrett D'Amore 	kmem_free(hdl, sizeof (*hdl));
18143f7d54a6SGarrett D'Amore }
18153f7d54a6SGarrett D'Amore 
18163f7d54a6SGarrett D'Amore int
18173f7d54a6SGarrett D'Amore bd_attach_handle(dev_info_t *dip, bd_handle_t hdl)
18183f7d54a6SGarrett D'Amore {
18193f7d54a6SGarrett D'Amore 	dev_info_t	*child;
1820510a6847SHans Rosenfeld 	bd_drive_t	drive = { 0 };
18213f7d54a6SGarrett D'Amore 
1822*ecee5a1fSHans Rosenfeld 	/*
1823*ecee5a1fSHans Rosenfeld 	 * It's not an error if bd_attach_handle() is called on a handle that
1824*ecee5a1fSHans Rosenfeld 	 * already is attached. We just ignore the request to attach and return.
1825*ecee5a1fSHans Rosenfeld 	 * This way drivers using blkdev don't have to keep track about blkdev
1826*ecee5a1fSHans Rosenfeld 	 * state, they can just call this function to make sure it attached.
1827*ecee5a1fSHans Rosenfeld 	 */
1828*ecee5a1fSHans Rosenfeld 	if (hdl->h_child != NULL) {
1829*ecee5a1fSHans Rosenfeld 		return (DDI_SUCCESS);
1830*ecee5a1fSHans Rosenfeld 	}
1831*ecee5a1fSHans Rosenfeld 
18323f7d54a6SGarrett D'Amore 	/* if drivers don't override this, make it assume none */
18333f7d54a6SGarrett D'Amore 	drive.d_lun = -1;
18343f7d54a6SGarrett D'Amore 	hdl->h_ops.o_drive_info(hdl->h_private, &drive);
18353f7d54a6SGarrett D'Amore 
18363f7d54a6SGarrett D'Amore 	hdl->h_parent = dip;
18373f7d54a6SGarrett D'Amore 	hdl->h_name = "blkdev";
18383f7d54a6SGarrett D'Amore 
1839fa27e351SHans Rosenfeld 	/*LINTED: E_BAD_PTR_CAST_ALIGN*/
1840fa27e351SHans Rosenfeld 	if (*(uint64_t *)drive.d_eui64 != 0) {
18413f7d54a6SGarrett D'Amore 		if (drive.d_lun >= 0) {
1842fa27e351SHans Rosenfeld 			(void) snprintf(hdl->h_addr, sizeof (hdl->h_addr),
1843fa27e351SHans Rosenfeld 			    "w%02X%02X%02X%02X%02X%02X%02X%02X,%X",
1844fa27e351SHans Rosenfeld 			    drive.d_eui64[0], drive.d_eui64[1],
1845fa27e351SHans Rosenfeld 			    drive.d_eui64[2], drive.d_eui64[3],
1846fa27e351SHans Rosenfeld 			    drive.d_eui64[4], drive.d_eui64[5],
1847fa27e351SHans Rosenfeld 			    drive.d_eui64[6], drive.d_eui64[7], drive.d_lun);
18483f7d54a6SGarrett D'Amore 		} else {
1849fa27e351SHans Rosenfeld 			(void) snprintf(hdl->h_addr, sizeof (hdl->h_addr),
1850fa27e351SHans Rosenfeld 			    "w%02X%02X%02X%02X%02X%02X%02X%02X",
1851fa27e351SHans Rosenfeld 			    drive.d_eui64[0], drive.d_eui64[1],
1852fa27e351SHans Rosenfeld 			    drive.d_eui64[2], drive.d_eui64[3],
1853fa27e351SHans Rosenfeld 			    drive.d_eui64[4], drive.d_eui64[5],
1854fa27e351SHans Rosenfeld 			    drive.d_eui64[6], drive.d_eui64[7]);
18553f7d54a6SGarrett D'Amore 		}
1856fa27e351SHans Rosenfeld 	} else {
1857fa27e351SHans Rosenfeld 		if (drive.d_lun >= 0) {
1858fa27e351SHans Rosenfeld 			(void) snprintf(hdl->h_addr, sizeof (hdl->h_addr),
1859fa27e351SHans Rosenfeld 			    "%X,%X", drive.d_target, drive.d_lun);
1860fa27e351SHans Rosenfeld 		} else {
1861fa27e351SHans Rosenfeld 			(void) snprintf(hdl->h_addr, sizeof (hdl->h_addr),
1862fa27e351SHans Rosenfeld 			    "%X", drive.d_target);
1863fa27e351SHans Rosenfeld 		}
1864fa27e351SHans Rosenfeld 	}
1865fa27e351SHans Rosenfeld 
18663f7d54a6SGarrett D'Amore 	if (ndi_devi_alloc(dip, hdl->h_name, (pnode_t)DEVI_SID_NODEID,
18673f7d54a6SGarrett D'Amore 	    &child) != NDI_SUCCESS) {
18683f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s%d: unable to allocate node %s@%s",
18693f7d54a6SGarrett D'Amore 		    ddi_driver_name(dip), ddi_get_instance(dip),
18703f7d54a6SGarrett D'Amore 		    "blkdev", hdl->h_addr);
18713f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
18723f7d54a6SGarrett D'Amore 	}
18733f7d54a6SGarrett D'Amore 
18743f7d54a6SGarrett D'Amore 	ddi_set_parent_data(child, hdl);
18753f7d54a6SGarrett D'Amore 	hdl->h_child = child;
18763f7d54a6SGarrett D'Amore 
18773f7d54a6SGarrett D'Amore 	if (ndi_devi_online(child, 0) == NDI_FAILURE) {
18783f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s%d: failed bringing node %s@%s online",
18793f7d54a6SGarrett D'Amore 		    ddi_driver_name(dip), ddi_get_instance(dip),
18803f7d54a6SGarrett D'Amore 		    hdl->h_name, hdl->h_addr);
18813f7d54a6SGarrett D'Amore 		(void) ndi_devi_free(child);
18823f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
18833f7d54a6SGarrett D'Amore 	}
18843f7d54a6SGarrett D'Amore 
18853f7d54a6SGarrett D'Amore 	return (DDI_SUCCESS);
18863f7d54a6SGarrett D'Amore }
18873f7d54a6SGarrett D'Amore 
18883f7d54a6SGarrett D'Amore int
18893f7d54a6SGarrett D'Amore bd_detach_handle(bd_handle_t hdl)
18903f7d54a6SGarrett D'Amore {
18913f7d54a6SGarrett D'Amore 	int	circ;
18923f7d54a6SGarrett D'Amore 	int	rv;
18933f7d54a6SGarrett D'Amore 	char	*devnm;
18943f7d54a6SGarrett D'Amore 
1895*ecee5a1fSHans Rosenfeld 	/*
1896*ecee5a1fSHans Rosenfeld 	 * It's not an error if bd_detach_handle() is called on a handle that
1897*ecee5a1fSHans Rosenfeld 	 * already is detached. We just ignore the request to detach and return.
1898*ecee5a1fSHans Rosenfeld 	 * This way drivers using blkdev don't have to keep track about blkdev
1899*ecee5a1fSHans Rosenfeld 	 * state, they can just call this function to make sure it detached.
1900*ecee5a1fSHans Rosenfeld 	 */
19013f7d54a6SGarrett D'Amore 	if (hdl->h_child == NULL) {
19023f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
19033f7d54a6SGarrett D'Amore 	}
19043f7d54a6SGarrett D'Amore 	ndi_devi_enter(hdl->h_parent, &circ);
19053f7d54a6SGarrett D'Amore 	if (i_ddi_node_state(hdl->h_child) < DS_INITIALIZED) {
19063f7d54a6SGarrett D'Amore 		rv = ddi_remove_child(hdl->h_child, 0);
19073f7d54a6SGarrett D'Amore 	} else {
19083f7d54a6SGarrett D'Amore 		devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
19093f7d54a6SGarrett D'Amore 		(void) ddi_deviname(hdl->h_child, devnm);
19103f7d54a6SGarrett D'Amore 		(void) devfs_clean(hdl->h_parent, devnm + 1, DV_CLEAN_FORCE);
19113f7d54a6SGarrett D'Amore 		rv = ndi_devi_unconfig_one(hdl->h_parent, devnm + 1, NULL,
19123f7d54a6SGarrett D'Amore 		    NDI_DEVI_REMOVE | NDI_UNCONFIG);
19133f7d54a6SGarrett D'Amore 		kmem_free(devnm, MAXNAMELEN + 1);
19143f7d54a6SGarrett D'Amore 	}
19153f7d54a6SGarrett D'Amore 	if (rv == 0) {
19163f7d54a6SGarrett D'Amore 		hdl->h_child = NULL;
19173f7d54a6SGarrett D'Amore 	}
19183f7d54a6SGarrett D'Amore 
19193f7d54a6SGarrett D'Amore 	ndi_devi_exit(hdl->h_parent, circ);
1920aad3a447SHans Rosenfeld 	return (rv == NDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
19213f7d54a6SGarrett D'Amore }
19223f7d54a6SGarrett D'Amore 
19233f7d54a6SGarrett D'Amore void
19243f7d54a6SGarrett D'Amore bd_xfer_done(bd_xfer_t *xfer, int err)
19253f7d54a6SGarrett D'Amore {
19263f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi = (void *)xfer;
19273f7d54a6SGarrett D'Amore 	buf_t		*bp = xi->i_bp;
192806711e0cSDmitry Yusupov 	int		rv = DDI_SUCCESS;
19293f7d54a6SGarrett D'Amore 	bd_t		*bd = xi->i_bd;
19303f7d54a6SGarrett D'Amore 	size_t		len;
19313f7d54a6SGarrett D'Amore 
19323f7d54a6SGarrett D'Amore 	if (err != 0) {
19333f7d54a6SGarrett D'Amore 		bd_runq_exit(xi, err);
1934bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_harderrs.value.ui32);
19353f7d54a6SGarrett D'Amore 
19363f7d54a6SGarrett D'Amore 		bp->b_resid += xi->i_resid;
19373f7d54a6SGarrett D'Amore 		bd_xfer_free(xi);
19383f7d54a6SGarrett D'Amore 		bioerror(bp, err);
19393f7d54a6SGarrett D'Amore 		biodone(bp);
19403f7d54a6SGarrett D'Amore 		return;
19413f7d54a6SGarrett D'Amore 	}
19423f7d54a6SGarrett D'Amore 
19433f7d54a6SGarrett D'Amore 	xi->i_cur_win++;
19443f7d54a6SGarrett D'Amore 	xi->i_resid -= xi->i_len;
19453f7d54a6SGarrett D'Amore 
19463f7d54a6SGarrett D'Amore 	if (xi->i_resid == 0) {
19473f7d54a6SGarrett D'Amore 		/* Job completed succcessfully! */
19483f7d54a6SGarrett D'Amore 		bd_runq_exit(xi, 0);
19493f7d54a6SGarrett D'Amore 
19503f7d54a6SGarrett D'Amore 		bd_xfer_free(xi);
19513f7d54a6SGarrett D'Amore 		biodone(bp);
19523f7d54a6SGarrett D'Amore 		return;
19533f7d54a6SGarrett D'Amore 	}
19543f7d54a6SGarrett D'Amore 
19553f7d54a6SGarrett D'Amore 	xi->i_blkno += xi->i_nblks;
19563f7d54a6SGarrett D'Amore 
19573f7d54a6SGarrett D'Amore 	if (bd->d_use_dma) {
19583f7d54a6SGarrett D'Amore 		/* More transfer still pending... advance to next DMA window. */
19593f7d54a6SGarrett D'Amore 		rv = ddi_dma_getwin(xi->i_dmah, xi->i_cur_win,
19603f7d54a6SGarrett D'Amore 		    &xi->i_offset, &len, &xi->i_dmac, &xi->i_ndmac);
19613f7d54a6SGarrett D'Amore 	} else {
19623f7d54a6SGarrett D'Amore 		/* Advance memory window. */
19633f7d54a6SGarrett D'Amore 		xi->i_kaddr += xi->i_len;
19643f7d54a6SGarrett D'Amore 		xi->i_offset += xi->i_len;
19653f7d54a6SGarrett D'Amore 		len = min(bp->b_bcount - xi->i_offset, bd->d_maxxfer);
19663f7d54a6SGarrett D'Amore 	}
19673f7d54a6SGarrett D'Amore 
19683f7d54a6SGarrett D'Amore 
19693f7d54a6SGarrett D'Amore 	if ((rv != DDI_SUCCESS) ||
197010624986SYouzhong Yang 	    (P2PHASE(len, (1U << xi->i_blkshift)) != 0)) {
19713f7d54a6SGarrett D'Amore 		bd_runq_exit(xi, EFAULT);
19723f7d54a6SGarrett D'Amore 
19733f7d54a6SGarrett D'Amore 		bp->b_resid += xi->i_resid;
19743f7d54a6SGarrett D'Amore 		bd_xfer_free(xi);
19753f7d54a6SGarrett D'Amore 		bioerror(bp, EFAULT);
19763f7d54a6SGarrett D'Amore 		biodone(bp);
19773f7d54a6SGarrett D'Amore 		return;
19783f7d54a6SGarrett D'Amore 	}
19793f7d54a6SGarrett D'Amore 	xi->i_len = len;
19803f7d54a6SGarrett D'Amore 	xi->i_nblks = len >> xi->i_blkshift;
19813f7d54a6SGarrett D'Amore 
19823f7d54a6SGarrett D'Amore 	/* Submit next window to hardware. */
19833f7d54a6SGarrett D'Amore 	rv = xi->i_func(bd->d_private, &xi->i_public);
19843f7d54a6SGarrett D'Amore 	if (rv != 0) {
19853f7d54a6SGarrett D'Amore 		bd_runq_exit(xi, rv);
19863f7d54a6SGarrett D'Amore 
1987bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_transerrs.value.ui32);
1988bef9e21aSHans Rosenfeld 
19893f7d54a6SGarrett D'Amore 		bp->b_resid += xi->i_resid;
19903f7d54a6SGarrett D'Amore 		bd_xfer_free(xi);
19913f7d54a6SGarrett D'Amore 		bioerror(bp, rv);
19923f7d54a6SGarrett D'Amore 		biodone(bp);
19933f7d54a6SGarrett D'Amore 	}
19943f7d54a6SGarrett D'Amore }
19953f7d54a6SGarrett D'Amore 
19963f7d54a6SGarrett D'Amore void
1997bef9e21aSHans Rosenfeld bd_error(bd_xfer_t *xfer, int error)
1998bef9e21aSHans Rosenfeld {
1999bef9e21aSHans Rosenfeld 	bd_xfer_impl_t	*xi = (void *)xfer;
2000bef9e21aSHans Rosenfeld 	bd_t		*bd = xi->i_bd;
2001bef9e21aSHans Rosenfeld 
2002bef9e21aSHans Rosenfeld 	switch (error) {
2003bef9e21aSHans Rosenfeld 	case BD_ERR_MEDIA:
2004bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_media_err.value.ui32);
2005bef9e21aSHans Rosenfeld 		break;
2006bef9e21aSHans Rosenfeld 	case BD_ERR_NTRDY:
2007bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_ntrdy_err.value.ui32);
2008bef9e21aSHans Rosenfeld 		break;
2009bef9e21aSHans Rosenfeld 	case BD_ERR_NODEV:
2010bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_nodev_err.value.ui32);
2011bef9e21aSHans Rosenfeld 		break;
2012bef9e21aSHans Rosenfeld 	case BD_ERR_RECOV:
2013bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_recov_err.value.ui32);
2014bef9e21aSHans Rosenfeld 		break;
2015bef9e21aSHans Rosenfeld 	case BD_ERR_ILLRQ:
2016bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_illrq_err.value.ui32);
2017bef9e21aSHans Rosenfeld 		break;
2018bef9e21aSHans Rosenfeld 	case BD_ERR_PFA:
2019bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_pfa_err.value.ui32);
2020bef9e21aSHans Rosenfeld 		break;
2021bef9e21aSHans Rosenfeld 	default:
2022bef9e21aSHans Rosenfeld 		cmn_err(CE_PANIC, "bd_error: unknown error type %d", error);
2023bef9e21aSHans Rosenfeld 		break;
2024bef9e21aSHans Rosenfeld 	}
2025bef9e21aSHans Rosenfeld }
2026bef9e21aSHans Rosenfeld 
2027bef9e21aSHans Rosenfeld void
20283f7d54a6SGarrett D'Amore bd_state_change(bd_handle_t hdl)
20293f7d54a6SGarrett D'Amore {
20303f7d54a6SGarrett D'Amore 	bd_t		*bd;
20313f7d54a6SGarrett D'Amore 
20323f7d54a6SGarrett D'Amore 	if ((bd = hdl->h_bd) != NULL) {
20333f7d54a6SGarrett D'Amore 		bd_update_state(bd);
20343f7d54a6SGarrett D'Amore 	}
20353f7d54a6SGarrett D'Amore }
20363f7d54a6SGarrett D'Amore 
20373f7d54a6SGarrett D'Amore void
20383f7d54a6SGarrett D'Amore bd_mod_init(struct dev_ops *devops)
20393f7d54a6SGarrett D'Amore {
20403f7d54a6SGarrett D'Amore 	static struct bus_ops bd_bus_ops = {
20413f7d54a6SGarrett D'Amore 		BUSO_REV,		/* busops_rev */
20423f7d54a6SGarrett D'Amore 		nullbusmap,		/* bus_map */
20433f7d54a6SGarrett D'Amore 		NULL,			/* bus_get_intrspec (OBSOLETE) */
20443f7d54a6SGarrett D'Amore 		NULL,			/* bus_add_intrspec (OBSOLETE) */
20453f7d54a6SGarrett D'Amore 		NULL,			/* bus_remove_intrspec (OBSOLETE) */
20463f7d54a6SGarrett D'Amore 		i_ddi_map_fault,	/* bus_map_fault */
2047cd21e7c5SGarrett D'Amore 		NULL,			/* bus_dma_map (OBSOLETE) */
20483f7d54a6SGarrett D'Amore 		ddi_dma_allochdl,	/* bus_dma_allochdl */
20493f7d54a6SGarrett D'Amore 		ddi_dma_freehdl,	/* bus_dma_freehdl */
20503f7d54a6SGarrett D'Amore 		ddi_dma_bindhdl,	/* bus_dma_bindhdl */
20513f7d54a6SGarrett D'Amore 		ddi_dma_unbindhdl,	/* bus_dma_unbindhdl */
20523f7d54a6SGarrett D'Amore 		ddi_dma_flush,		/* bus_dma_flush */
20533f7d54a6SGarrett D'Amore 		ddi_dma_win,		/* bus_dma_win */
20543f7d54a6SGarrett D'Amore 		ddi_dma_mctl,		/* bus_dma_ctl */
20553f7d54a6SGarrett D'Amore 		bd_bus_ctl,		/* bus_ctl */
20563f7d54a6SGarrett D'Amore 		ddi_bus_prop_op,	/* bus_prop_op */
20573f7d54a6SGarrett D'Amore 		NULL,			/* bus_get_eventcookie */
20583f7d54a6SGarrett D'Amore 		NULL,			/* bus_add_eventcall */
20593f7d54a6SGarrett D'Amore 		NULL,			/* bus_remove_eventcall */
20603f7d54a6SGarrett D'Amore 		NULL,			/* bus_post_event */
20613f7d54a6SGarrett D'Amore 		NULL,			/* bus_intr_ctl (OBSOLETE) */
20623f7d54a6SGarrett D'Amore 		NULL,			/* bus_config */
20633f7d54a6SGarrett D'Amore 		NULL,			/* bus_unconfig */
20643f7d54a6SGarrett D'Amore 		NULL,			/* bus_fm_init */
20653f7d54a6SGarrett D'Amore 		NULL,			/* bus_fm_fini */
20663f7d54a6SGarrett D'Amore 		NULL,			/* bus_fm_access_enter */
20673f7d54a6SGarrett D'Amore 		NULL,			/* bus_fm_access_exit */
20683f7d54a6SGarrett D'Amore 		NULL,			/* bus_power */
20693f7d54a6SGarrett D'Amore 		NULL,			/* bus_intr_op */
20703f7d54a6SGarrett D'Amore 	};
20713f7d54a6SGarrett D'Amore 
20723f7d54a6SGarrett D'Amore 	devops->devo_bus_ops = &bd_bus_ops;
20733f7d54a6SGarrett D'Amore 
20743f7d54a6SGarrett D'Amore 	/*
20753f7d54a6SGarrett D'Amore 	 * NB: The device driver is free to supply its own
20763f7d54a6SGarrett D'Amore 	 * character entry device support.
20773f7d54a6SGarrett D'Amore 	 */
20783f7d54a6SGarrett D'Amore }
20793f7d54a6SGarrett D'Amore 
20803f7d54a6SGarrett D'Amore void
20813f7d54a6SGarrett D'Amore bd_mod_fini(struct dev_ops *devops)
20823f7d54a6SGarrett D'Amore {
20833f7d54a6SGarrett D'Amore 	devops->devo_bus_ops = NULL;
20843f7d54a6SGarrett D'Amore }
2085