xref: /illumos-gate/usr/src/uts/common/io/blkdev/blkdev.c (revision e6769cd189d50536e21b84431b01eb22e72b6d60)
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.
25c0591a0cSYouzhong Yang  * Copyright 2017 The MathWorks, Inc.  All rights reserved.
2633f84ecfSJason King  * Copyright 2020 Joyent, Inc.
276f0e4dc9SAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
28da00bec1SHans Rosenfeld  * Copyright 2022 Tintri by DDN, Inc. All rights reserved.
293fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
3086e3bca6SGarrett D'Amore  */
3186e3bca6SGarrett D'Amore 
323f7d54a6SGarrett D'Amore #include <sys/types.h>
333f7d54a6SGarrett D'Amore #include <sys/ksynch.h>
343f7d54a6SGarrett D'Amore #include <sys/kmem.h>
353f7d54a6SGarrett D'Amore #include <sys/file.h>
363f7d54a6SGarrett D'Amore #include <sys/errno.h>
373f7d54a6SGarrett D'Amore #include <sys/open.h>
383f7d54a6SGarrett D'Amore #include <sys/buf.h>
393f7d54a6SGarrett D'Amore #include <sys/uio.h>
403f7d54a6SGarrett D'Amore #include <sys/aio_req.h>
413f7d54a6SGarrett D'Amore #include <sys/cred.h>
423f7d54a6SGarrett D'Amore #include <sys/modctl.h>
433f7d54a6SGarrett D'Amore #include <sys/cmlb.h>
443f7d54a6SGarrett D'Amore #include <sys/conf.h>
453f7d54a6SGarrett D'Amore #include <sys/devops.h>
463f7d54a6SGarrett D'Amore #include <sys/list.h>
473f7d54a6SGarrett D'Amore #include <sys/sysmacros.h>
483f7d54a6SGarrett D'Amore #include <sys/dkio.h>
491a5ae140SJason King #include <sys/dkioc_free_util.h>
503f7d54a6SGarrett D'Amore #include <sys/vtoc.h>
513f7d54a6SGarrett D'Amore #include <sys/scsi/scsi.h>	/* for DTYPE_DIRECT */
523f7d54a6SGarrett D'Amore #include <sys/kstat.h>
533f7d54a6SGarrett D'Amore #include <sys/fs/dv_node.h>
543f7d54a6SGarrett D'Amore #include <sys/ddi.h>
553f7d54a6SGarrett D'Amore #include <sys/sunddi.h>
563f7d54a6SGarrett D'Amore #include <sys/note.h>
573f7d54a6SGarrett D'Amore #include <sys/blkdev.h>
58510a6847SHans Rosenfeld #include <sys/scsi/impl/inquiry.h>
596f0e4dc9SAndy Fiddaman #include <sys/taskq.h>
606f0e4dc9SAndy Fiddaman #include <sys/taskq_impl.h>
616f0e4dc9SAndy Fiddaman #include <sys/disp.h>
626f0e4dc9SAndy Fiddaman #include <sys/sysevent/eventdefs.h>
636f0e4dc9SAndy Fiddaman #include <sys/sysevent/dev.h>
643f7d54a6SGarrett D'Amore 
654d95620bSPaul Winder /*
664d95620bSPaul Winder  * blkdev is a driver which provides a lot of the common functionality
674d95620bSPaul Winder  * a block device driver may need and helps by removing code which
684d95620bSPaul Winder  * is frequently duplicated in block device drivers.
694d95620bSPaul Winder  *
704d95620bSPaul Winder  * Within this driver all the struct cb_ops functions required for a
714d95620bSPaul Winder  * block device driver are written with appropriate call back functions
724d95620bSPaul Winder  * to be provided by the parent driver.
734d95620bSPaul Winder  *
744d95620bSPaul Winder  * To use blkdev, a driver needs to:
754d95620bSPaul Winder  *	1. Create a bd_ops_t structure which has the call back operations
764d95620bSPaul Winder  *	   blkdev will use.
774d95620bSPaul Winder  *	2. Create a handle by calling bd_alloc_handle(). One of the
784d95620bSPaul Winder  *	   arguments to this function is the bd_ops_t.
794d95620bSPaul Winder  *	3. Call bd_attach_handle(). This will instantiate a blkdev device
804d95620bSPaul Winder  *	   as a child device node of the calling driver.
814d95620bSPaul Winder  *
824d95620bSPaul Winder  * A parent driver is not restricted to just allocating and attaching a
834d95620bSPaul Winder  * single instance, it may attach as many as it wishes. For each handle
844d95620bSPaul Winder  * attached, appropriate entries in /dev/[r]dsk are created.
854d95620bSPaul Winder  *
864d95620bSPaul Winder  * The bd_ops_t routines that a parent of blkdev need to provide are:
874d95620bSPaul Winder  *
884d95620bSPaul Winder  * o_drive_info: Provide information to blkdev such as how many I/O queues
894d95620bSPaul Winder  *		 to create and the size of those queues. Also some device
904d95620bSPaul Winder  *		 specifics such as EUI, vendor, product, model, serial
914d95620bSPaul Winder  *		 number ....
924d95620bSPaul Winder  *
934d95620bSPaul Winder  * o_media_info: Provide information about the media. Eg size and block size.
944d95620bSPaul Winder  *
954d95620bSPaul Winder  * o_devid_init: Creates and initializes the device id. Typically calls
964d95620bSPaul Winder  *		 ddi_devid_init().
974d95620bSPaul Winder  *
984d95620bSPaul Winder  * o_sync_cache: Issues a device appropriate command to flush any write
994d95620bSPaul Winder  *		 caches.
1004d95620bSPaul Winder  *
1014d95620bSPaul Winder  * o_read:	 Read data as described by bd_xfer_t argument.
1024d95620bSPaul Winder  *
1034d95620bSPaul Winder  * o_write:	 Write data as described by bd_xfer_t argument.
1044d95620bSPaul Winder  *
1051a5ae140SJason King  * o_free_space: Free the space described by bd_xfer_t argument (optional).
1064d95620bSPaul Winder  *
1074d95620bSPaul Winder  * Queues
1084d95620bSPaul Winder  * ------
1094d95620bSPaul Winder  * Part of the drive_info data is a queue count. blkdev will create
1104d95620bSPaul Winder  * "queue count" number of waitq/runq pairs. Each waitq/runq pair
1114d95620bSPaul Winder  * operates independently. As an I/O is scheduled up to the parent
1124d95620bSPaul Winder  * driver via o_read or o_write its queue number is given. If the
1134d95620bSPaul Winder  * parent driver supports multiple hardware queues it can then select
1144d95620bSPaul Winder  * where to submit the I/O request.
1154d95620bSPaul Winder  *
1164d95620bSPaul Winder  * Currently blkdev uses a simplistic round-robin queue selection method.
1174d95620bSPaul Winder  * It has the advantage that it is lockless. In the future it will be
1184d95620bSPaul Winder  * worthwhile reviewing this strategy for something which prioritizes queues
1194d95620bSPaul Winder  * depending on how busy they are.
1204d95620bSPaul Winder  *
1214d95620bSPaul Winder  * Each waitq/runq pair is protected by its mutex (q_iomutex). Incoming
1224d95620bSPaul Winder  * I/O requests are initially added to the waitq. They are taken off the
1234d95620bSPaul Winder  * waitq, added to the runq and submitted, providing the runq is less
1244d95620bSPaul Winder  * than the qsize as specified in the drive_info. As an I/O request
1254d95620bSPaul Winder  * completes, the parent driver is required to call bd_xfer_done(), which
1264d95620bSPaul Winder  * will remove the I/O request from the runq and pass I/O completion
1274d95620bSPaul Winder  * status up the stack.
1284d95620bSPaul Winder  *
1294d95620bSPaul Winder  * Locks
1304d95620bSPaul Winder  * -----
1316f0e4dc9SAndy Fiddaman  * There are 5 instance global locks d_ocmutex, d_ksmutex, d_errmutex,
1326f0e4dc9SAndy Fiddaman  * d_statemutex and d_dle_mutex. As well a q_iomutex per waitq/runq pair.
1334d95620bSPaul Winder  *
13419687f06SPaul Winder  * Lock Hierarchy
13519687f06SPaul Winder  * --------------
13619687f06SPaul Winder  * The only two locks which may be held simultaneously are q_iomutex and
13719687f06SPaul Winder  * d_ksmutex. In all cases q_iomutex must be acquired before d_ksmutex.
1384d95620bSPaul Winder  */
1394d95620bSPaul Winder 
1403f7d54a6SGarrett D'Amore #define	BD_MAXPART	64
1413f7d54a6SGarrett D'Amore #define	BDINST(dev)	(getminor(dev) / BD_MAXPART)
1423f7d54a6SGarrett D'Amore #define	BDPART(dev)	(getminor(dev) % BD_MAXPART)
1433f7d54a6SGarrett D'Amore 
1443f7d54a6SGarrett D'Amore typedef struct bd bd_t;
1453f7d54a6SGarrett D'Amore typedef struct bd_xfer_impl bd_xfer_impl_t;
1464d95620bSPaul Winder typedef struct bd_queue bd_queue_t;
1473f7d54a6SGarrett D'Amore 
1486f0e4dc9SAndy Fiddaman typedef enum {
1496f0e4dc9SAndy Fiddaman 	BD_DLE_PENDING	= 1 << 0,
1506f0e4dc9SAndy Fiddaman 	BD_DLE_RUNNING	= 1 << 1
1516f0e4dc9SAndy Fiddaman } bd_dle_state_t;
1526f0e4dc9SAndy Fiddaman 
1533f7d54a6SGarrett D'Amore struct bd {
1543f7d54a6SGarrett D'Amore 	void		*d_private;
1553f7d54a6SGarrett D'Amore 	dev_info_t	*d_dip;
1566f0e4dc9SAndy Fiddaman 	kmutex_t	d_ocmutex;	/* open/close */
1576f0e4dc9SAndy Fiddaman 	kmutex_t	d_ksmutex;	/* kstat */
1584d95620bSPaul Winder 	kmutex_t	d_errmutex;
1593f7d54a6SGarrett D'Amore 	kmutex_t	d_statemutex;
1603f7d54a6SGarrett D'Amore 	kcondvar_t	d_statecv;
1613f7d54a6SGarrett D'Amore 	enum dkio_state	d_state;
1623f7d54a6SGarrett D'Amore 	cmlb_handle_t	d_cmlbh;
1633f7d54a6SGarrett D'Amore 	unsigned	d_open_lyr[BD_MAXPART];	/* open count */
1643f7d54a6SGarrett D'Amore 	uint64_t	d_open_excl;	/* bit mask indexed by partition */
1653f7d54a6SGarrett D'Amore 	uint64_t	d_open_reg[OTYPCNT];		/* bit mask */
1664d95620bSPaul Winder 	uint64_t	d_io_counter;
1673f7d54a6SGarrett D'Amore 
1684d95620bSPaul Winder 	uint32_t	d_qcount;
1693f7d54a6SGarrett D'Amore 	uint32_t	d_maxxfer;
1703f7d54a6SGarrett D'Amore 	uint32_t	d_blkshift;
17132ce6b81SHans Rosenfeld 	uint32_t	d_pblkshift;
1723f7d54a6SGarrett D'Amore 	uint64_t	d_numblks;
1733f7d54a6SGarrett D'Amore 	ddi_devid_t	d_devid;
1743f7d54a6SGarrett D'Amore 
1751a5ae140SJason King 	uint64_t	d_max_free_seg;
1761a5ae140SJason King 	uint64_t	d_max_free_blks;
1771a5ae140SJason King 	uint64_t	d_max_free_seg_blks;
1781a5ae140SJason King 	uint64_t	d_free_align;
1791a5ae140SJason King 
1803f7d54a6SGarrett D'Amore 	kmem_cache_t	*d_cache;
1814d95620bSPaul Winder 	bd_queue_t	*d_queues;
1823f7d54a6SGarrett D'Amore 	kstat_t		*d_ksp;
1833f7d54a6SGarrett D'Amore 	kstat_io_t	*d_kiop;
184bef9e21aSHans Rosenfeld 	kstat_t		*d_errstats;
185bef9e21aSHans Rosenfeld 	struct bd_errstats *d_kerr;
1863f7d54a6SGarrett D'Amore 
1873f7d54a6SGarrett D'Amore 	boolean_t	d_rdonly;
18859d8f100SGarrett D'Amore 	boolean_t	d_ssd;
1893f7d54a6SGarrett D'Amore 	boolean_t	d_removable;
1903f7d54a6SGarrett D'Amore 	boolean_t	d_hotpluggable;
1913f7d54a6SGarrett D'Amore 	boolean_t	d_use_dma;
1923f7d54a6SGarrett D'Amore 
1933f7d54a6SGarrett D'Amore 	ddi_dma_attr_t	d_dma;
1943f7d54a6SGarrett D'Amore 	bd_ops_t	d_ops;
1953f7d54a6SGarrett D'Amore 	bd_handle_t	d_handle;
1966f0e4dc9SAndy Fiddaman 
1976f0e4dc9SAndy Fiddaman 	kmutex_t	d_dle_mutex;
1986f0e4dc9SAndy Fiddaman 	taskq_ent_t	d_dle_ent;
1996f0e4dc9SAndy Fiddaman 	bd_dle_state_t	d_dle_state;
2003f7d54a6SGarrett D'Amore };
2013f7d54a6SGarrett D'Amore 
2023f7d54a6SGarrett D'Amore struct bd_handle {
2033f7d54a6SGarrett D'Amore 	bd_ops_t	h_ops;
2043f7d54a6SGarrett D'Amore 	ddi_dma_attr_t	*h_dma;
2053f7d54a6SGarrett D'Amore 	dev_info_t	*h_parent;
2063f7d54a6SGarrett D'Amore 	dev_info_t	*h_child;
2073f7d54a6SGarrett D'Amore 	void		*h_private;
2083f7d54a6SGarrett D'Amore 	bd_t		*h_bd;
2093f7d54a6SGarrett D'Amore 	char		*h_name;
210ca0df52aSHans Rosenfeld 	char		h_addr[50];	/* enough for w%0.32x,%X */
2113f7d54a6SGarrett D'Amore };
2123f7d54a6SGarrett D'Amore 
2133f7d54a6SGarrett D'Amore struct bd_xfer_impl {
2143f7d54a6SGarrett D'Amore 	bd_xfer_t	i_public;
2153f7d54a6SGarrett D'Amore 	list_node_t	i_linkage;
2163f7d54a6SGarrett D'Amore 	bd_t		*i_bd;
2173f7d54a6SGarrett D'Amore 	buf_t		*i_bp;
2184d95620bSPaul Winder 	bd_queue_t	*i_bq;
2193f7d54a6SGarrett D'Amore 	uint_t		i_num_win;
2203f7d54a6SGarrett D'Amore 	uint_t		i_cur_win;
2213f7d54a6SGarrett D'Amore 	off_t		i_offset;
2223f7d54a6SGarrett D'Amore 	int		(*i_func)(void *, bd_xfer_t *);
2233f7d54a6SGarrett D'Amore 	uint32_t	i_blkshift;
2243f7d54a6SGarrett D'Amore 	size_t		i_len;
2253f7d54a6SGarrett D'Amore 	size_t		i_resid;
2263f7d54a6SGarrett D'Amore };
2273f7d54a6SGarrett D'Amore 
2284d95620bSPaul Winder struct bd_queue {
2294d95620bSPaul Winder 	kmutex_t	q_iomutex;
2304d95620bSPaul Winder 	uint32_t	q_qsize;
2314d95620bSPaul Winder 	uint32_t	q_qactive;
2324d95620bSPaul Winder 	list_t		q_runq;
2334d95620bSPaul Winder 	list_t		q_waitq;
2344d95620bSPaul Winder };
2354d95620bSPaul Winder 
2363f7d54a6SGarrett D'Amore #define	i_dmah		i_public.x_dmah
2373f7d54a6SGarrett D'Amore #define	i_dmac		i_public.x_dmac
2383f7d54a6SGarrett D'Amore #define	i_ndmac		i_public.x_ndmac
2393f7d54a6SGarrett D'Amore #define	i_kaddr		i_public.x_kaddr
2403f7d54a6SGarrett D'Amore #define	i_nblks		i_public.x_nblks
2413f7d54a6SGarrett D'Amore #define	i_blkno		i_public.x_blkno
24286e3bca6SGarrett D'Amore #define	i_flags		i_public.x_flags
2434d95620bSPaul Winder #define	i_qnum		i_public.x_qnum
2441a5ae140SJason King #define	i_dfl		i_public.x_dfl
2453f7d54a6SGarrett D'Amore 
2461a5ae140SJason King #define	CAN_FREESPACE(bd) \
2471a5ae140SJason King 	(((bd)->d_ops.o_free_space == NULL) ? B_FALSE : B_TRUE)
2483f7d54a6SGarrett D'Amore 
2493f7d54a6SGarrett D'Amore /*
2503f7d54a6SGarrett D'Amore  * Private prototypes.
2513f7d54a6SGarrett D'Amore  */
2523f7d54a6SGarrett D'Amore 
253510a6847SHans Rosenfeld static void bd_prop_update_inqstring(dev_info_t *, char *, char *, size_t);
254510a6847SHans Rosenfeld static void bd_create_inquiry_props(dev_info_t *, bd_drive_t *);
255bef9e21aSHans Rosenfeld static void bd_create_errstats(bd_t *, int, bd_drive_t *);
25633f84ecfSJason King static void bd_destroy_errstats(bd_t *);
257bef9e21aSHans Rosenfeld static void bd_errstats_setstr(kstat_named_t *, char *, size_t, char *);
258bef9e21aSHans Rosenfeld static void bd_init_errstats(bd_t *, bd_drive_t *);
25933f84ecfSJason King static void bd_fini_errstats(bd_t *);
260510a6847SHans Rosenfeld 
2613f7d54a6SGarrett D'Amore static int bd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
2623f7d54a6SGarrett D'Amore static int bd_attach(dev_info_t *, ddi_attach_cmd_t);
2633f7d54a6SGarrett D'Amore static int bd_detach(dev_info_t *, ddi_detach_cmd_t);
2643f7d54a6SGarrett D'Amore 
2653f7d54a6SGarrett D'Amore static int bd_open(dev_t *, int, int, cred_t *);
2663f7d54a6SGarrett D'Amore static int bd_close(dev_t, int, int, cred_t *);
2673f7d54a6SGarrett D'Amore static int bd_strategy(struct buf *);
2683f7d54a6SGarrett D'Amore static int bd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
26986e3bca6SGarrett D'Amore static int bd_dump(dev_t, caddr_t, daddr_t, int);
2703f7d54a6SGarrett D'Amore static int bd_read(dev_t, struct uio *, cred_t *);
2713f7d54a6SGarrett D'Amore static int bd_write(dev_t, struct uio *, cred_t *);
2723f7d54a6SGarrett D'Amore static int bd_aread(dev_t, struct aio_req *, cred_t *);
2733f7d54a6SGarrett D'Amore static int bd_awrite(dev_t, struct aio_req *, cred_t *);
2743f7d54a6SGarrett D'Amore static int bd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
2753f7d54a6SGarrett D'Amore     caddr_t, int *);
2763f7d54a6SGarrett D'Amore 
2773f7d54a6SGarrett D'Amore static int bd_tg_rdwr(dev_info_t *, uchar_t, void *, diskaddr_t, size_t,
2783f7d54a6SGarrett D'Amore     void *);
2793f7d54a6SGarrett D'Amore static int bd_tg_getinfo(dev_info_t *, int, void *, void *);
2803f7d54a6SGarrett D'Amore static int bd_xfer_ctor(void *, void *, int);
2813f7d54a6SGarrett D'Amore static void bd_xfer_dtor(void *, void *);
2824d95620bSPaul Winder static void bd_sched(bd_t *, bd_queue_t *);
2833f7d54a6SGarrett D'Amore static void bd_submit(bd_t *, bd_xfer_impl_t *);
2843f7d54a6SGarrett D'Amore static void bd_runq_exit(bd_xfer_impl_t *, int);
2853f7d54a6SGarrett D'Amore static void bd_update_state(bd_t *);
2863f7d54a6SGarrett D'Amore static int bd_check_state(bd_t *, enum dkio_state *);
2873f7d54a6SGarrett D'Amore static int bd_flush_write_cache(bd_t *, struct dk_callback *);
288c0591a0cSYouzhong Yang static int bd_check_uio(dev_t, struct uio *);
2891a5ae140SJason King static int bd_free_space(dev_t, bd_t *, dkioc_free_list_t *);
2903f7d54a6SGarrett D'Amore 
2913f7d54a6SGarrett D'Amore struct cmlb_tg_ops bd_tg_ops = {
2923f7d54a6SGarrett D'Amore 	TG_DK_OPS_VERSION_1,
2933f7d54a6SGarrett D'Amore 	bd_tg_rdwr,
2943f7d54a6SGarrett D'Amore 	bd_tg_getinfo,
2953f7d54a6SGarrett D'Amore };
2963f7d54a6SGarrett D'Amore 
2973f7d54a6SGarrett D'Amore static struct cb_ops bd_cb_ops = {
2983f7d54a6SGarrett D'Amore 	bd_open,		/* open */
2993f7d54a6SGarrett D'Amore 	bd_close,		/* close */
3003f7d54a6SGarrett D'Amore 	bd_strategy,		/* strategy */
3013f7d54a6SGarrett D'Amore 	nodev,			/* print */
30286e3bca6SGarrett D'Amore 	bd_dump,		/* dump */
3033f7d54a6SGarrett D'Amore 	bd_read,		/* read */
3043f7d54a6SGarrett D'Amore 	bd_write,		/* write */
3053f7d54a6SGarrett D'Amore 	bd_ioctl,		/* ioctl */
3063f7d54a6SGarrett D'Amore 	nodev,			/* devmap */
3073f7d54a6SGarrett D'Amore 	nodev,			/* mmap */
3083f7d54a6SGarrett D'Amore 	nodev,			/* segmap */
3093f7d54a6SGarrett D'Amore 	nochpoll,		/* poll */
3103f7d54a6SGarrett D'Amore 	bd_prop_op,		/* cb_prop_op */
3113f7d54a6SGarrett D'Amore 	0,			/* streamtab  */
3123f7d54a6SGarrett D'Amore 	D_64BIT | D_MP,		/* Driver comaptibility flag */
3133f7d54a6SGarrett D'Amore 	CB_REV,			/* cb_rev */
3143f7d54a6SGarrett D'Amore 	bd_aread,		/* async read */
3153f7d54a6SGarrett D'Amore 	bd_awrite		/* async write */
3163f7d54a6SGarrett D'Amore };
3173f7d54a6SGarrett D'Amore 
3183f7d54a6SGarrett D'Amore struct dev_ops bd_dev_ops = {
3193f7d54a6SGarrett D'Amore 	DEVO_REV,		/* devo_rev, */
3203f7d54a6SGarrett D'Amore 	0,			/* refcnt  */
3213f7d54a6SGarrett D'Amore 	bd_getinfo,		/* getinfo */
3223f7d54a6SGarrett D'Amore 	nulldev,		/* identify */
3233f7d54a6SGarrett D'Amore 	nulldev,		/* probe */
3243f7d54a6SGarrett D'Amore 	bd_attach,		/* attach */
3253f7d54a6SGarrett D'Amore 	bd_detach,		/* detach */
3263f7d54a6SGarrett D'Amore 	nodev,			/* reset */
3273f7d54a6SGarrett D'Amore 	&bd_cb_ops,		/* driver operations */
3283f7d54a6SGarrett D'Amore 	NULL,			/* bus operations */
3293f7d54a6SGarrett D'Amore 	NULL,			/* power */
3303f7d54a6SGarrett D'Amore 	ddi_quiesce_not_needed,	/* quiesce */
3313f7d54a6SGarrett D'Amore };
3323f7d54a6SGarrett D'Amore 
3333f7d54a6SGarrett D'Amore static struct modldrv modldrv = {
3343f7d54a6SGarrett D'Amore 	&mod_driverops,
3353f7d54a6SGarrett D'Amore 	"Generic Block Device",
3363f7d54a6SGarrett D'Amore 	&bd_dev_ops,
3373f7d54a6SGarrett D'Amore };
3383f7d54a6SGarrett D'Amore 
3393f7d54a6SGarrett D'Amore static struct modlinkage modlinkage = {
3403f7d54a6SGarrett D'Amore 	MODREV_1, { &modldrv, NULL }
3413f7d54a6SGarrett D'Amore };
3423f7d54a6SGarrett D'Amore 
3433f7d54a6SGarrett D'Amore static void *bd_state;
3443f7d54a6SGarrett D'Amore static krwlock_t bd_lock;
3456f0e4dc9SAndy Fiddaman static taskq_t *bd_taskq;
3463f7d54a6SGarrett D'Amore 
3473f7d54a6SGarrett D'Amore int
_init(void)3483f7d54a6SGarrett D'Amore _init(void)
3493f7d54a6SGarrett D'Amore {
3506f0e4dc9SAndy Fiddaman 	char taskq_name[TASKQ_NAMELEN];
3516f0e4dc9SAndy Fiddaman 	const char *name;
3523f7d54a6SGarrett D'Amore 	int rv;
3533f7d54a6SGarrett D'Amore 
3543f7d54a6SGarrett D'Amore 	rv = ddi_soft_state_init(&bd_state, sizeof (struct bd), 2);
3556f0e4dc9SAndy Fiddaman 	if (rv != DDI_SUCCESS)
3563f7d54a6SGarrett D'Amore 		return (rv);
3576f0e4dc9SAndy Fiddaman 
3586f0e4dc9SAndy Fiddaman 	name = mod_modname(&modlinkage);
3596f0e4dc9SAndy Fiddaman 	(void) snprintf(taskq_name, sizeof (taskq_name), "%s_taskq", name);
3606f0e4dc9SAndy Fiddaman 	bd_taskq = taskq_create(taskq_name, 1, minclsyspri, 0, 0, 0);
3616f0e4dc9SAndy Fiddaman 	if (bd_taskq == NULL) {
3626f0e4dc9SAndy Fiddaman 		cmn_err(CE_WARN, "%s: unable to create %s", name, taskq_name);
3636f0e4dc9SAndy Fiddaman 		ddi_soft_state_fini(&bd_state);
3646f0e4dc9SAndy Fiddaman 		return (DDI_FAILURE);
3653f7d54a6SGarrett D'Amore 	}
3666f0e4dc9SAndy Fiddaman 
3673f7d54a6SGarrett D'Amore 	rw_init(&bd_lock, NULL, RW_DRIVER, NULL);
3686f0e4dc9SAndy Fiddaman 
3693f7d54a6SGarrett D'Amore 	rv = mod_install(&modlinkage);
3703f7d54a6SGarrett D'Amore 	if (rv != DDI_SUCCESS) {
3713f7d54a6SGarrett D'Amore 		rw_destroy(&bd_lock);
3726f0e4dc9SAndy Fiddaman 		taskq_destroy(bd_taskq);
3733f7d54a6SGarrett D'Amore 		ddi_soft_state_fini(&bd_state);
3743f7d54a6SGarrett D'Amore 	}
3753f7d54a6SGarrett D'Amore 	return (rv);
3763f7d54a6SGarrett D'Amore }
3773f7d54a6SGarrett D'Amore 
3783f7d54a6SGarrett D'Amore int
_fini(void)3793f7d54a6SGarrett D'Amore _fini(void)
3803f7d54a6SGarrett D'Amore {
3813f7d54a6SGarrett D'Amore 	int	rv;
3823f7d54a6SGarrett D'Amore 
3833f7d54a6SGarrett D'Amore 	rv = mod_remove(&modlinkage);
3843f7d54a6SGarrett D'Amore 	if (rv == DDI_SUCCESS) {
3853f7d54a6SGarrett D'Amore 		rw_destroy(&bd_lock);
3866f0e4dc9SAndy Fiddaman 		taskq_destroy(bd_taskq);
3873f7d54a6SGarrett D'Amore 		ddi_soft_state_fini(&bd_state);
3883f7d54a6SGarrett D'Amore 	}
3893f7d54a6SGarrett D'Amore 	return (rv);
3903f7d54a6SGarrett D'Amore }
3913f7d54a6SGarrett D'Amore 
3923f7d54a6SGarrett D'Amore int
_info(struct modinfo * modinfop)3933f7d54a6SGarrett D'Amore _info(struct modinfo *modinfop)
3943f7d54a6SGarrett D'Amore {
3953f7d54a6SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
3963f7d54a6SGarrett D'Amore }
3973f7d54a6SGarrett D'Amore 
3983f7d54a6SGarrett D'Amore static int
bd_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)3993f7d54a6SGarrett D'Amore bd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
4003f7d54a6SGarrett D'Amore {
4013f7d54a6SGarrett D'Amore 	bd_t	*bd;
4023f7d54a6SGarrett D'Amore 	minor_t	inst;
4033f7d54a6SGarrett D'Amore 
4043f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(dip));
4053f7d54a6SGarrett D'Amore 
4063f7d54a6SGarrett D'Amore 	inst = BDINST((dev_t)arg);
4073f7d54a6SGarrett D'Amore 
4083f7d54a6SGarrett D'Amore 	switch (cmd) {
4093f7d54a6SGarrett D'Amore 	case DDI_INFO_DEVT2DEVINFO:
4103f7d54a6SGarrett D'Amore 		bd = ddi_get_soft_state(bd_state, inst);
4113f7d54a6SGarrett D'Amore 		if (bd == NULL) {
4123f7d54a6SGarrett D'Amore 			return (DDI_FAILURE);
4133f7d54a6SGarrett D'Amore 		}
4143f7d54a6SGarrett D'Amore 		*resultp = (void *)bd->d_dip;
4153f7d54a6SGarrett D'Amore 		break;
4163f7d54a6SGarrett D'Amore 
4173f7d54a6SGarrett D'Amore 	case DDI_INFO_DEVT2INSTANCE:
4183f7d54a6SGarrett D'Amore 		*resultp = (void *)(intptr_t)inst;
4193f7d54a6SGarrett D'Amore 		break;
4203f7d54a6SGarrett D'Amore 
4213f7d54a6SGarrett D'Amore 	default:
4223f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
4233f7d54a6SGarrett D'Amore 	}
4243f7d54a6SGarrett D'Amore 	return (DDI_SUCCESS);
4253f7d54a6SGarrett D'Amore }
4263f7d54a6SGarrett D'Amore 
427510a6847SHans Rosenfeld static void
bd_prop_update_inqstring(dev_info_t * dip,char * name,char * data,size_t len)428510a6847SHans Rosenfeld bd_prop_update_inqstring(dev_info_t *dip, char *name, char *data, size_t len)
429510a6847SHans Rosenfeld {
430510a6847SHans Rosenfeld 	int	ilen;
431510a6847SHans Rosenfeld 	char	*data_string;
432510a6847SHans Rosenfeld 
433510a6847SHans Rosenfeld 	ilen = scsi_ascii_inquiry_len(data, len);
434510a6847SHans Rosenfeld 	ASSERT3U(ilen, <=, len);
435510a6847SHans Rosenfeld 	if (ilen <= 0)
436510a6847SHans Rosenfeld 		return;
437510a6847SHans Rosenfeld 	/* ensure null termination */
438510a6847SHans Rosenfeld 	data_string = kmem_zalloc(ilen + 1, KM_SLEEP);
439510a6847SHans Rosenfeld 	bcopy(data, data_string, ilen);
440510a6847SHans Rosenfeld 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, name, data_string);
441510a6847SHans Rosenfeld 	kmem_free(data_string, ilen + 1);
442510a6847SHans Rosenfeld }
443510a6847SHans Rosenfeld 
444510a6847SHans Rosenfeld static void
bd_create_inquiry_props(dev_info_t * dip,bd_drive_t * drive)445510a6847SHans Rosenfeld bd_create_inquiry_props(dev_info_t *dip, bd_drive_t *drive)
446510a6847SHans Rosenfeld {
447510a6847SHans Rosenfeld 	if (drive->d_vendor_len > 0)
448510a6847SHans Rosenfeld 		bd_prop_update_inqstring(dip, INQUIRY_VENDOR_ID,
449510a6847SHans Rosenfeld 		    drive->d_vendor, drive->d_vendor_len);
450510a6847SHans Rosenfeld 
451510a6847SHans Rosenfeld 	if (drive->d_product_len > 0)
452510a6847SHans Rosenfeld 		bd_prop_update_inqstring(dip, INQUIRY_PRODUCT_ID,
453510a6847SHans Rosenfeld 		    drive->d_product, drive->d_product_len);
454510a6847SHans Rosenfeld 
455510a6847SHans Rosenfeld 	if (drive->d_serial_len > 0)
456510a6847SHans Rosenfeld 		bd_prop_update_inqstring(dip, INQUIRY_SERIAL_NO,
457510a6847SHans Rosenfeld 		    drive->d_serial, drive->d_serial_len);
458510a6847SHans Rosenfeld 
459510a6847SHans Rosenfeld 	if (drive->d_revision_len > 0)
460510a6847SHans Rosenfeld 		bd_prop_update_inqstring(dip, INQUIRY_REVISION_ID,
461510a6847SHans Rosenfeld 		    drive->d_revision, drive->d_revision_len);
462510a6847SHans Rosenfeld }
463510a6847SHans Rosenfeld 
464bef9e21aSHans Rosenfeld static void
bd_create_errstats(bd_t * bd,int inst,bd_drive_t * drive)465bef9e21aSHans Rosenfeld bd_create_errstats(bd_t *bd, int inst, bd_drive_t *drive)
466bef9e21aSHans Rosenfeld {
467bef9e21aSHans Rosenfeld 	char	ks_module[KSTAT_STRLEN];
468bef9e21aSHans Rosenfeld 	char	ks_name[KSTAT_STRLEN];
469bef9e21aSHans Rosenfeld 	int	ndata = sizeof (struct bd_errstats) / sizeof (kstat_named_t);
470bef9e21aSHans Rosenfeld 
471bef9e21aSHans Rosenfeld 	if (bd->d_errstats != NULL)
472bef9e21aSHans Rosenfeld 		return;
473bef9e21aSHans Rosenfeld 
474bef9e21aSHans Rosenfeld 	(void) snprintf(ks_module, sizeof (ks_module), "%serr",
475bef9e21aSHans Rosenfeld 	    ddi_driver_name(bd->d_dip));
476bef9e21aSHans Rosenfeld 	(void) snprintf(ks_name, sizeof (ks_name), "%s%d,err",
477bef9e21aSHans Rosenfeld 	    ddi_driver_name(bd->d_dip), inst);
478bef9e21aSHans Rosenfeld 
479bef9e21aSHans Rosenfeld 	bd->d_errstats = kstat_create(ks_module, inst, ks_name, "device_error",
480bef9e21aSHans Rosenfeld 	    KSTAT_TYPE_NAMED, ndata, KSTAT_FLAG_PERSISTENT);
481bef9e21aSHans Rosenfeld 
4824d95620bSPaul Winder 	mutex_init(&bd->d_errmutex, NULL, MUTEX_DRIVER, NULL);
483bef9e21aSHans Rosenfeld 	if (bd->d_errstats == NULL) {
484bef9e21aSHans Rosenfeld 		/*
485bef9e21aSHans Rosenfeld 		 * Even if we cannot create the kstat, we create a
486bef9e21aSHans Rosenfeld 		 * scratch kstat.  The reason for this is to ensure
487bef9e21aSHans Rosenfeld 		 * that we can update the kstat all of the time,
488bef9e21aSHans Rosenfeld 		 * without adding an extra branch instruction.
489bef9e21aSHans Rosenfeld 		 */
490bef9e21aSHans Rosenfeld 		bd->d_kerr = kmem_zalloc(sizeof (struct bd_errstats),
491bef9e21aSHans Rosenfeld 		    KM_SLEEP);
492bef9e21aSHans Rosenfeld 	} else {
4934d95620bSPaul Winder 		bd->d_errstats->ks_lock = &bd->d_errmutex;
494bef9e21aSHans Rosenfeld 		bd->d_kerr = (struct bd_errstats *)bd->d_errstats->ks_data;
495bef9e21aSHans Rosenfeld 	}
496bef9e21aSHans Rosenfeld 
497bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_softerrs,	"Soft Errors",
498bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
499bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_harderrs,	"Hard Errors",
500bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
501bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_transerrs,	"Transport Errors",
502bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
503bef9e21aSHans Rosenfeld 
504bef9e21aSHans Rosenfeld 	if (drive->d_model_len > 0) {
505bef9e21aSHans Rosenfeld 		kstat_named_init(&bd->d_kerr->bd_model,	"Model",
506bef9e21aSHans Rosenfeld 		    KSTAT_DATA_STRING);
507bef9e21aSHans Rosenfeld 	} else {
508bef9e21aSHans Rosenfeld 		kstat_named_init(&bd->d_kerr->bd_vid,	"Vendor",
509bef9e21aSHans Rosenfeld 		    KSTAT_DATA_STRING);
510bef9e21aSHans Rosenfeld 		kstat_named_init(&bd->d_kerr->bd_pid,	"Product",
511bef9e21aSHans Rosenfeld 		    KSTAT_DATA_STRING);
512bef9e21aSHans Rosenfeld 	}
513bef9e21aSHans Rosenfeld 
514bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_revision,	"Revision",
515bef9e21aSHans Rosenfeld 	    KSTAT_DATA_STRING);
516bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_serial,	"Serial No",
517bef9e21aSHans Rosenfeld 	    KSTAT_DATA_STRING);
518bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_capacity,	"Size",
519bef9e21aSHans Rosenfeld 	    KSTAT_DATA_ULONGLONG);
520bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_media_err,	"Media Error",
521bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
522bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_ntrdy_err,	"Device Not Ready",
523bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
524bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_nodev_err,	"No Device",
525bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
526bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_recov_err,	"Recoverable",
527bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
528bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_illrq_err,	"Illegal Request",
529bef9e21aSHans Rosenfeld 	    KSTAT_DATA_UINT32);
530bef9e21aSHans Rosenfeld 	kstat_named_init(&bd->d_kerr->bd_rq_pfa_err,
531bef9e21aSHans Rosenfeld 	    "Predictive Failure Analysis", KSTAT_DATA_UINT32);
532bef9e21aSHans Rosenfeld 
533bef9e21aSHans Rosenfeld 	bd->d_errstats->ks_private = bd;
534bef9e21aSHans Rosenfeld 
535bef9e21aSHans Rosenfeld 	kstat_install(bd->d_errstats);
53633f84ecfSJason King 	bd_init_errstats(bd, drive);
53733f84ecfSJason King }
53833f84ecfSJason King 
53933f84ecfSJason King static void
bd_destroy_errstats(bd_t * bd)54033f84ecfSJason King bd_destroy_errstats(bd_t *bd)
54133f84ecfSJason King {
54233f84ecfSJason King 	if (bd->d_errstats != NULL) {
54333f84ecfSJason King 		bd_fini_errstats(bd);
54433f84ecfSJason King 		kstat_delete(bd->d_errstats);
54533f84ecfSJason King 		bd->d_errstats = NULL;
54633f84ecfSJason King 	} else {
54733f84ecfSJason King 		kmem_free(bd->d_kerr, sizeof (struct bd_errstats));
54833f84ecfSJason King 		bd->d_kerr = NULL;
54933f84ecfSJason King 		mutex_destroy(&bd->d_errmutex);
55033f84ecfSJason King 	}
551bef9e21aSHans Rosenfeld }
552bef9e21aSHans Rosenfeld 
553bef9e21aSHans Rosenfeld static void
bd_errstats_setstr(kstat_named_t * k,char * str,size_t len,char * alt)554bef9e21aSHans Rosenfeld bd_errstats_setstr(kstat_named_t *k, char *str, size_t len, char *alt)
555bef9e21aSHans Rosenfeld {
556bef9e21aSHans Rosenfeld 	char	*tmp;
557247e9a8eSPaul Winder 	size_t	km_len;
558bef9e21aSHans Rosenfeld 
559bef9e21aSHans Rosenfeld 	if (KSTAT_NAMED_STR_PTR(k) == NULL) {
560247e9a8eSPaul Winder 		if (len > 0)
561247e9a8eSPaul Winder 			km_len = strnlen(str, len);
562247e9a8eSPaul Winder 		else if (alt != NULL)
563247e9a8eSPaul Winder 			km_len = strlen(alt);
564247e9a8eSPaul Winder 		else
565247e9a8eSPaul Winder 			return;
566247e9a8eSPaul Winder 
567247e9a8eSPaul Winder 		tmp = kmem_alloc(km_len + 1, KM_SLEEP);
568247e9a8eSPaul Winder 		bcopy(len > 0 ? str : alt, tmp, km_len);
569247e9a8eSPaul Winder 		tmp[km_len] = '\0';
570bef9e21aSHans Rosenfeld 
571bef9e21aSHans Rosenfeld 		kstat_named_setstr(k, tmp);
572bef9e21aSHans Rosenfeld 	}
573bef9e21aSHans Rosenfeld }
574bef9e21aSHans Rosenfeld 
575bef9e21aSHans Rosenfeld static void
bd_errstats_clrstr(kstat_named_t * k)576247e9a8eSPaul Winder bd_errstats_clrstr(kstat_named_t *k)
577247e9a8eSPaul Winder {
578247e9a8eSPaul Winder 	if (KSTAT_NAMED_STR_PTR(k) == NULL)
579247e9a8eSPaul Winder 		return;
580247e9a8eSPaul Winder 
581247e9a8eSPaul Winder 	kmem_free(KSTAT_NAMED_STR_PTR(k), KSTAT_NAMED_STR_BUFLEN(k));
582247e9a8eSPaul Winder 	kstat_named_setstr(k, NULL);
583247e9a8eSPaul Winder }
584247e9a8eSPaul Winder 
585247e9a8eSPaul Winder static void
bd_init_errstats(bd_t * bd,bd_drive_t * drive)586bef9e21aSHans Rosenfeld bd_init_errstats(bd_t *bd, bd_drive_t *drive)
587bef9e21aSHans Rosenfeld {
588bef9e21aSHans Rosenfeld 	struct bd_errstats	*est = bd->d_kerr;
589bef9e21aSHans Rosenfeld 
5904d95620bSPaul Winder 	mutex_enter(&bd->d_errmutex);
591bef9e21aSHans Rosenfeld 
592bef9e21aSHans Rosenfeld 	if (drive->d_model_len > 0 &&
593bef9e21aSHans Rosenfeld 	    KSTAT_NAMED_STR_PTR(&est->bd_model) == NULL) {
594bef9e21aSHans Rosenfeld 		bd_errstats_setstr(&est->bd_model, drive->d_model,
595bef9e21aSHans Rosenfeld 		    drive->d_model_len, NULL);
596bef9e21aSHans Rosenfeld 	} else {
597bef9e21aSHans Rosenfeld 		bd_errstats_setstr(&est->bd_vid, drive->d_vendor,
598bef9e21aSHans Rosenfeld 		    drive->d_vendor_len, "Unknown ");
599bef9e21aSHans Rosenfeld 		bd_errstats_setstr(&est->bd_pid, drive->d_product,
600bef9e21aSHans Rosenfeld 		    drive->d_product_len, "Unknown         ");
601bef9e21aSHans Rosenfeld 	}
602bef9e21aSHans Rosenfeld 
603bef9e21aSHans Rosenfeld 	bd_errstats_setstr(&est->bd_revision, drive->d_revision,
604bef9e21aSHans Rosenfeld 	    drive->d_revision_len, "0001");
605bef9e21aSHans Rosenfeld 	bd_errstats_setstr(&est->bd_serial, drive->d_serial,
606bef9e21aSHans Rosenfeld 	    drive->d_serial_len, "0               ");
607bef9e21aSHans Rosenfeld 
6084d95620bSPaul Winder 	mutex_exit(&bd->d_errmutex);
6094d95620bSPaul Winder }
6104d95620bSPaul Winder 
6114d95620bSPaul Winder static void
bd_fini_errstats(bd_t * bd)612247e9a8eSPaul Winder bd_fini_errstats(bd_t *bd)
613247e9a8eSPaul Winder {
614247e9a8eSPaul Winder 	struct bd_errstats	*est = bd->d_kerr;
615247e9a8eSPaul Winder 
616247e9a8eSPaul Winder 	mutex_enter(&bd->d_errmutex);
617247e9a8eSPaul Winder 
618247e9a8eSPaul Winder 	bd_errstats_clrstr(&est->bd_model);
619247e9a8eSPaul Winder 	bd_errstats_clrstr(&est->bd_vid);
620247e9a8eSPaul Winder 	bd_errstats_clrstr(&est->bd_pid);
621247e9a8eSPaul Winder 	bd_errstats_clrstr(&est->bd_revision);
622247e9a8eSPaul Winder 	bd_errstats_clrstr(&est->bd_serial);
623247e9a8eSPaul Winder 
624247e9a8eSPaul Winder 	mutex_exit(&bd->d_errmutex);
625247e9a8eSPaul Winder }
626247e9a8eSPaul Winder 
627247e9a8eSPaul Winder static void
bd_queues_free(bd_t * bd)6284d95620bSPaul Winder bd_queues_free(bd_t *bd)
6294d95620bSPaul Winder {
6304d95620bSPaul Winder 	uint32_t i;
6314d95620bSPaul Winder 
6324d95620bSPaul Winder 	for (i = 0; i < bd->d_qcount; i++) {
6334d95620bSPaul Winder 		bd_queue_t *bq = &bd->d_queues[i];
6344d95620bSPaul Winder 
6354d95620bSPaul Winder 		mutex_destroy(&bq->q_iomutex);
6364d95620bSPaul Winder 		list_destroy(&bq->q_waitq);
6374d95620bSPaul Winder 		list_destroy(&bq->q_runq);
6384d95620bSPaul Winder 	}
6394d95620bSPaul Winder 
6404d95620bSPaul Winder 	kmem_free(bd->d_queues, sizeof (*bd->d_queues) * bd->d_qcount);
641bef9e21aSHans Rosenfeld }
642bef9e21aSHans Rosenfeld 
6433f7d54a6SGarrett D'Amore static int
bd_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)6443f7d54a6SGarrett D'Amore bd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
6453f7d54a6SGarrett D'Amore {
6463f7d54a6SGarrett D'Amore 	int		inst;
6473f7d54a6SGarrett D'Amore 	bd_handle_t	hdl;
6483f7d54a6SGarrett D'Amore 	bd_t		*bd;
6493f7d54a6SGarrett D'Amore 	bd_drive_t	drive;
6504d95620bSPaul Winder 	uint32_t	i;
6513f7d54a6SGarrett D'Amore 	int		rv;
6523f7d54a6SGarrett D'Amore 	char		name[16];
6533f7d54a6SGarrett D'Amore 	char		kcache[32];
654ca0df52aSHans Rosenfeld 	char		*node_type;
6553f7d54a6SGarrett D'Amore 
6563f7d54a6SGarrett D'Amore 	switch (cmd) {
6573f7d54a6SGarrett D'Amore 	case DDI_ATTACH:
6583f7d54a6SGarrett D'Amore 		break;
6593f7d54a6SGarrett D'Amore 	case DDI_RESUME:
6603f7d54a6SGarrett D'Amore 		/* We don't do anything native for suspend/resume */
6613f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
6623f7d54a6SGarrett D'Amore 	default:
6633f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
6643f7d54a6SGarrett D'Amore 	}
6653f7d54a6SGarrett D'Amore 
6663f7d54a6SGarrett D'Amore 	inst = ddi_get_instance(dip);
6673f7d54a6SGarrett D'Amore 	hdl = ddi_get_parent_data(dip);
6683f7d54a6SGarrett D'Amore 
6693f7d54a6SGarrett D'Amore 	(void) snprintf(name, sizeof (name), "%s%d",
6703f7d54a6SGarrett D'Amore 	    ddi_driver_name(dip), ddi_get_instance(dip));
6713f7d54a6SGarrett D'Amore 	(void) snprintf(kcache, sizeof (kcache), "%s_xfer", name);
6723f7d54a6SGarrett D'Amore 
6733f7d54a6SGarrett D'Amore 	if (hdl == NULL) {
6743f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s: missing parent data!", name);
6753f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
6763f7d54a6SGarrett D'Amore 	}
6773f7d54a6SGarrett D'Amore 
6783f7d54a6SGarrett D'Amore 	if (ddi_soft_state_zalloc(bd_state, inst) != DDI_SUCCESS) {
6793f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s: unable to zalloc soft state!", name);
6803f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
6813f7d54a6SGarrett D'Amore 	}
6823f7d54a6SGarrett D'Amore 	bd = ddi_get_soft_state(bd_state, inst);
6833f7d54a6SGarrett D'Amore 
6843f7d54a6SGarrett D'Amore 	if (hdl->h_dma) {
6853f7d54a6SGarrett D'Amore 		bd->d_dma = *(hdl->h_dma);
6863f7d54a6SGarrett D'Amore 		bd->d_dma.dma_attr_granular =
6873f7d54a6SGarrett D'Amore 		    max(DEV_BSIZE, bd->d_dma.dma_attr_granular);
6883f7d54a6SGarrett D'Amore 		bd->d_use_dma = B_TRUE;
6893f7d54a6SGarrett D'Amore 
6903f7d54a6SGarrett D'Amore 		if (bd->d_maxxfer &&
6913f7d54a6SGarrett D'Amore 		    (bd->d_maxxfer != bd->d_dma.dma_attr_maxxfer)) {
6923f7d54a6SGarrett D'Amore 			cmn_err(CE_WARN,
6933f7d54a6SGarrett D'Amore 			    "%s: inconsistent maximum transfer size!",
6943f7d54a6SGarrett D'Amore 			    name);
6953f7d54a6SGarrett D'Amore 			/* We force it */
6963f7d54a6SGarrett D'Amore 			bd->d_maxxfer = bd->d_dma.dma_attr_maxxfer;
6973f7d54a6SGarrett D'Amore 		} else {
6983f7d54a6SGarrett D'Amore 			bd->d_maxxfer = bd->d_dma.dma_attr_maxxfer;
6993f7d54a6SGarrett D'Amore 		}
7003f7d54a6SGarrett D'Amore 	} else {
7013f7d54a6SGarrett D'Amore 		bd->d_use_dma = B_FALSE;
7023f7d54a6SGarrett D'Amore 		if (bd->d_maxxfer == 0) {
7033f7d54a6SGarrett D'Amore 			bd->d_maxxfer = 1024 * 1024;
7043f7d54a6SGarrett D'Amore 		}
7053f7d54a6SGarrett D'Amore 	}
7063f7d54a6SGarrett D'Amore 	bd->d_ops = hdl->h_ops;
7073f7d54a6SGarrett D'Amore 	bd->d_private = hdl->h_private;
7081a5ae140SJason King 	bd->d_blkshift = DEV_BSHIFT;	/* 512 bytes, to start */
7093f7d54a6SGarrett D'Amore 
7103f7d54a6SGarrett D'Amore 	if (bd->d_maxxfer % DEV_BSIZE) {
7113f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s: maximum transfer misaligned!", name);
7123f7d54a6SGarrett D'Amore 		bd->d_maxxfer &= ~(DEV_BSIZE - 1);
7133f7d54a6SGarrett D'Amore 	}
7143f7d54a6SGarrett D'Amore 	if (bd->d_maxxfer < DEV_BSIZE) {
7153f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s: maximum transfer size too small!", name);
7163f7d54a6SGarrett D'Amore 		ddi_soft_state_free(bd_state, inst);
7173f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
7183f7d54a6SGarrett D'Amore 	}
7193f7d54a6SGarrett D'Amore 
7203f7d54a6SGarrett D'Amore 	bd->d_dip = dip;
7213f7d54a6SGarrett D'Amore 	bd->d_handle = hdl;
7223f7d54a6SGarrett D'Amore 	ddi_set_driver_private(dip, bd);
7233f7d54a6SGarrett D'Amore 
7244d95620bSPaul Winder 	mutex_init(&bd->d_ksmutex, NULL, MUTEX_DRIVER, NULL);
7253f7d54a6SGarrett D'Amore 	mutex_init(&bd->d_ocmutex, NULL, MUTEX_DRIVER, NULL);
7263f7d54a6SGarrett D'Amore 	mutex_init(&bd->d_statemutex, NULL, MUTEX_DRIVER, NULL);
7273f7d54a6SGarrett D'Amore 	cv_init(&bd->d_statecv, NULL, CV_DRIVER, NULL);
7286f0e4dc9SAndy Fiddaman 	mutex_init(&bd->d_dle_mutex, NULL, MUTEX_DRIVER, NULL);
7296f0e4dc9SAndy Fiddaman 	bd->d_dle_state = 0;
7303f7d54a6SGarrett D'Amore 
7313f7d54a6SGarrett D'Amore 	bd->d_cache = kmem_cache_create(kcache, sizeof (bd_xfer_impl_t), 8,
7323f7d54a6SGarrett D'Amore 	    bd_xfer_ctor, bd_xfer_dtor, NULL, bd, NULL, 0);
7333f7d54a6SGarrett D'Amore 
7343f7d54a6SGarrett D'Amore 	bd->d_ksp = kstat_create(ddi_driver_name(dip), inst, NULL, "disk",
7353f7d54a6SGarrett D'Amore 	    KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
7363f7d54a6SGarrett D'Amore 	if (bd->d_ksp != NULL) {
7374d95620bSPaul Winder 		bd->d_ksp->ks_lock = &bd->d_ksmutex;
7383f7d54a6SGarrett D'Amore 		kstat_install(bd->d_ksp);
7393f7d54a6SGarrett D'Amore 		bd->d_kiop = bd->d_ksp->ks_data;
7403f7d54a6SGarrett D'Amore 	} else {
7413f7d54a6SGarrett D'Amore 		/*
7423f7d54a6SGarrett D'Amore 		 * Even if we cannot create the kstat, we create a
7433f7d54a6SGarrett D'Amore 		 * scratch kstat.  The reason for this is to ensure
7443f7d54a6SGarrett D'Amore 		 * that we can update the kstat all of the time,
7453f7d54a6SGarrett D'Amore 		 * without adding an extra branch instruction.
7463f7d54a6SGarrett D'Amore 		 */
7473f7d54a6SGarrett D'Amore 		bd->d_kiop = kmem_zalloc(sizeof (kstat_io_t), KM_SLEEP);
7483f7d54a6SGarrett D'Amore 	}
7493f7d54a6SGarrett D'Amore 
7503f7d54a6SGarrett D'Amore 	cmlb_alloc_handle(&bd->d_cmlbh);
7513f7d54a6SGarrett D'Amore 
7523f7d54a6SGarrett D'Amore 	bd->d_state = DKIO_NONE;
7533f7d54a6SGarrett D'Amore 
7543f7d54a6SGarrett D'Amore 	bzero(&drive, sizeof (drive));
7554d95620bSPaul Winder 	/*
7561a5ae140SJason King 	 * Default to one queue, and no restrictions on free space requests
7571a5ae140SJason King 	 * (if driver provides method) parent driver can override.
7584d95620bSPaul Winder 	 */
7594d95620bSPaul Winder 	drive.d_qcount = 1;
7601a5ae140SJason King 	drive.d_free_align = 1;
7613f7d54a6SGarrett D'Amore 	bd->d_ops.o_drive_info(bd->d_private, &drive);
7621a5ae140SJason King 
7631a5ae140SJason King 	/*
7641a5ae140SJason King 	 * Several checks to make sure o_drive_info() didn't return bad
7651a5ae140SJason King 	 * values:
7661a5ae140SJason King 	 *
7671a5ae140SJason King 	 * There must be at least one queue
7681a5ae140SJason King 	 */
7691a5ae140SJason King 	if (drive.d_qcount == 0)
7701a5ae140SJason King 		goto fail_drive_info;
7711a5ae140SJason King 
7721a5ae140SJason King 	/* FREE/UNMAP/TRIM alignment needs to be at least 1 block */
7731a5ae140SJason King 	if (drive.d_free_align == 0)
7741a5ae140SJason King 		goto fail_drive_info;
7751a5ae140SJason King 
7761a5ae140SJason King 	/*
7771a5ae140SJason King 	 * If d_max_free_blks is not unlimited (not 0), then we cannot allow
7781a5ae140SJason King 	 * an unlimited segment size. It is however permissible to not impose
7791a5ae140SJason King 	 * a limit on the total number of blocks freed while limiting the
7801a5ae140SJason King 	 * amount allowed in an individual segment.
7811a5ae140SJason King 	 */
7821a5ae140SJason King 	if ((drive.d_max_free_blks > 0 && drive.d_max_free_seg_blks == 0))
7831a5ae140SJason King 		goto fail_drive_info;
7841a5ae140SJason King 
7851a5ae140SJason King 	/*
7861a5ae140SJason King 	 * If a limit is set on d_max_free_blks (by the above check, we know
7871a5ae140SJason King 	 * if there's a limit on d_max_free_blks, d_max_free_seg_blks cannot
7881a5ae140SJason King 	 * be unlimited), it cannot be smaller than the limit on an individual
7891a5ae140SJason King 	 * segment.
7901a5ae140SJason King 	 */
7911a5ae140SJason King 	if ((drive.d_max_free_blks > 0 &&
7921a5ae140SJason King 	    drive.d_max_free_seg_blks > drive.d_max_free_blks)) {
7931a5ae140SJason King 		goto fail_drive_info;
7941a5ae140SJason King 	}
7951a5ae140SJason King 
7964d95620bSPaul Winder 	bd->d_qcount = drive.d_qcount;
7973f7d54a6SGarrett D'Amore 	bd->d_removable = drive.d_removable;
7983f7d54a6SGarrett D'Amore 	bd->d_hotpluggable = drive.d_hotpluggable;
7993f7d54a6SGarrett D'Amore 
800bc220884SAlexey Zaytsev 	if (drive.d_maxxfer && drive.d_maxxfer < bd->d_maxxfer)
801bc220884SAlexey Zaytsev 		bd->d_maxxfer = drive.d_maxxfer;
802bc220884SAlexey Zaytsev 
8031a5ae140SJason King 	bd->d_free_align = drive.d_free_align;
8041a5ae140SJason King 	bd->d_max_free_seg = drive.d_max_free_seg;
8051a5ae140SJason King 	bd->d_max_free_blks = drive.d_max_free_blks;
8061a5ae140SJason King 	bd->d_max_free_seg_blks = drive.d_max_free_seg_blks;
807bc220884SAlexey Zaytsev 
8081a5ae140SJason King 	bd_create_inquiry_props(dip, &drive);
809bef9e21aSHans Rosenfeld 	bd_create_errstats(bd, inst, &drive);
810bef9e21aSHans Rosenfeld 	bd_update_state(bd);
811bef9e21aSHans Rosenfeld 
8124d95620bSPaul Winder 	bd->d_queues = kmem_alloc(sizeof (*bd->d_queues) * bd->d_qcount,
8134d95620bSPaul Winder 	    KM_SLEEP);
8144d95620bSPaul Winder 	for (i = 0; i < bd->d_qcount; i++) {
8154d95620bSPaul Winder 		bd_queue_t *bq = &bd->d_queues[i];
8164d95620bSPaul Winder 
8174d95620bSPaul Winder 		bq->q_qsize = drive.d_qsize;
8184d95620bSPaul Winder 		bq->q_qactive = 0;
8194d95620bSPaul Winder 		mutex_init(&bq->q_iomutex, NULL, MUTEX_DRIVER, NULL);
8204d95620bSPaul Winder 
8214d95620bSPaul Winder 		list_create(&bq->q_waitq, sizeof (bd_xfer_impl_t),
8224d95620bSPaul Winder 		    offsetof(struct bd_xfer_impl, i_linkage));
8234d95620bSPaul Winder 		list_create(&bq->q_runq, sizeof (bd_xfer_impl_t),
8244d95620bSPaul Winder 		    offsetof(struct bd_xfer_impl, i_linkage));
8254d95620bSPaul Winder 	}
8264d95620bSPaul Winder 
827ca0df52aSHans Rosenfeld 	if (*(uint64_t *)drive.d_eui64 != 0 ||
828ca0df52aSHans Rosenfeld 	    *(uint64_t *)drive.d_guid != 0 ||
829ca0df52aSHans Rosenfeld 	    *((uint64_t *)drive.d_guid + 1) != 0)
830ca0df52aSHans Rosenfeld 		node_type = DDI_NT_BLOCK_BLKDEV;
831ca0df52aSHans Rosenfeld 	else if (drive.d_lun >= 0)
832ca0df52aSHans Rosenfeld 		node_type = DDI_NT_BLOCK_CHAN;
833ca0df52aSHans Rosenfeld 	else
834ca0df52aSHans Rosenfeld 		node_type = DDI_NT_BLOCK;
835ca0df52aSHans Rosenfeld 
8363f7d54a6SGarrett D'Amore 	rv = cmlb_attach(dip, &bd_tg_ops, DTYPE_DIRECT,
837ca0df52aSHans Rosenfeld 	    bd->d_removable, bd->d_hotpluggable, node_type,
83886e3bca6SGarrett D'Amore 	    CMLB_FAKE_LABEL_ONE_PARTITION, bd->d_cmlbh, 0);
8393f7d54a6SGarrett D'Amore 	if (rv != 0) {
8401a5ae140SJason King 		goto fail_cmlb_attach;
8413f7d54a6SGarrett D'Amore 	}
8423f7d54a6SGarrett D'Amore 
8433f7d54a6SGarrett D'Amore 	if (bd->d_ops.o_devid_init != NULL) {
8443f7d54a6SGarrett D'Amore 		rv = bd->d_ops.o_devid_init(bd->d_private, dip, &bd->d_devid);
8453f7d54a6SGarrett D'Amore 		if (rv == DDI_SUCCESS) {
8463f7d54a6SGarrett D'Amore 			if (ddi_devid_register(dip, bd->d_devid) !=
8473f7d54a6SGarrett D'Amore 			    DDI_SUCCESS) {
8483f7d54a6SGarrett D'Amore 				cmn_err(CE_WARN,
8493f7d54a6SGarrett D'Amore 				    "%s: unable to register devid", name);
8503f7d54a6SGarrett D'Amore 			}
8513f7d54a6SGarrett D'Amore 		}
8523f7d54a6SGarrett D'Amore 	}
8533f7d54a6SGarrett D'Amore 
8543f7d54a6SGarrett D'Amore 	/*
8553f7d54a6SGarrett D'Amore 	 * Add a zero-length attribute to tell the world we support
8563f7d54a6SGarrett D'Amore 	 * kernel ioctls (for layered drivers).  Also set up properties
8573f7d54a6SGarrett D'Amore 	 * used by HAL to identify removable media.
8583f7d54a6SGarrett D'Amore 	 */
8593f7d54a6SGarrett D'Amore 	(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
8603f7d54a6SGarrett D'Amore 	    DDI_KERNEL_IOCTL, NULL, 0);
8613f7d54a6SGarrett D'Amore 	if (bd->d_removable) {
8623f7d54a6SGarrett D'Amore 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
8633f7d54a6SGarrett D'Amore 		    "removable-media", NULL, 0);
8643f7d54a6SGarrett D'Amore 	}
8653f7d54a6SGarrett D'Amore 	if (bd->d_hotpluggable) {
8663f7d54a6SGarrett D'Amore 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
8673f7d54a6SGarrett D'Amore 		    "hotpluggable", NULL, 0);
8683f7d54a6SGarrett D'Amore 	}
8693f7d54a6SGarrett D'Amore 
8704b2d0200SRobert Mustacchi 	/*
8714b2d0200SRobert Mustacchi 	 * Before we proceed, we need to ensure that the geometry and labels on
8724b2d0200SRobert Mustacchi 	 * the cmlb disk are reasonable. When cmlb first attaches, it does not
8734b2d0200SRobert Mustacchi 	 * perform label validation and creates minor nodes based on the
8744b2d0200SRobert Mustacchi 	 * assumption of the size. This may not be correct and the rest of the
8754b2d0200SRobert Mustacchi 	 * system assumes that this will have been done before we allow opens
8764b2d0200SRobert Mustacchi 	 * to proceed. Otherwise, on first open, this'll all end up changing
8774b2d0200SRobert Mustacchi 	 * around on users. We do not care if it succeeds or not. It is totally
8784b2d0200SRobert Mustacchi 	 * acceptable for this device to be unlabeled or not to have anything on
8794b2d0200SRobert Mustacchi 	 * it.
8804b2d0200SRobert Mustacchi 	 */
8814b2d0200SRobert Mustacchi 	(void) cmlb_validate(bd->d_cmlbh, 0, 0);
8824b2d0200SRobert Mustacchi 
883da00bec1SHans Rosenfeld 	hdl->h_bd = bd;
8843f7d54a6SGarrett D'Amore 	ddi_report_dev(dip);
8853f7d54a6SGarrett D'Amore 
8863f7d54a6SGarrett D'Amore 	return (DDI_SUCCESS);
8871a5ae140SJason King 
8881a5ae140SJason King fail_cmlb_attach:
8891a5ae140SJason King 	bd_queues_free(bd);
8901a5ae140SJason King 	bd_destroy_errstats(bd);
8911a5ae140SJason King 
8921a5ae140SJason King fail_drive_info:
8931a5ae140SJason King 	cmlb_free_handle(&bd->d_cmlbh);
8941a5ae140SJason King 
8951a5ae140SJason King 	if (bd->d_ksp != NULL) {
8961a5ae140SJason King 		kstat_delete(bd->d_ksp);
8971a5ae140SJason King 		bd->d_ksp = NULL;
8981a5ae140SJason King 	} else {
8991a5ae140SJason King 		kmem_free(bd->d_kiop, sizeof (kstat_io_t));
9001a5ae140SJason King 	}
9011a5ae140SJason King 
9021a5ae140SJason King 	kmem_cache_destroy(bd->d_cache);
9031a5ae140SJason King 	cv_destroy(&bd->d_statecv);
9041a5ae140SJason King 	mutex_destroy(&bd->d_statemutex);
9051a5ae140SJason King 	mutex_destroy(&bd->d_ocmutex);
9061a5ae140SJason King 	mutex_destroy(&bd->d_ksmutex);
9076f0e4dc9SAndy Fiddaman 	mutex_destroy(&bd->d_dle_mutex);
9081a5ae140SJason King 	ddi_soft_state_free(bd_state, inst);
9091a5ae140SJason King 	return (DDI_FAILURE);
9103f7d54a6SGarrett D'Amore }
9113f7d54a6SGarrett D'Amore 
9123f7d54a6SGarrett D'Amore static int
bd_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)9133f7d54a6SGarrett D'Amore bd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
9143f7d54a6SGarrett D'Amore {
915da00bec1SHans Rosenfeld 	bd_handle_t	hdl;
9163f7d54a6SGarrett D'Amore 	bd_t		*bd;
9173f7d54a6SGarrett D'Amore 
9183f7d54a6SGarrett D'Amore 	bd = ddi_get_driver_private(dip);
919da00bec1SHans Rosenfeld 	hdl = ddi_get_parent_data(dip);
9203f7d54a6SGarrett D'Amore 
9213f7d54a6SGarrett D'Amore 	switch (cmd) {
9223f7d54a6SGarrett D'Amore 	case DDI_DETACH:
9233f7d54a6SGarrett D'Amore 		break;
9243f7d54a6SGarrett D'Amore 	case DDI_SUSPEND:
9253f7d54a6SGarrett D'Amore 		/* We don't suspend, but our parent does */
9263f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
9273f7d54a6SGarrett D'Amore 	default:
9283f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
9293f7d54a6SGarrett D'Amore 	}
93033f84ecfSJason King 
931da00bec1SHans Rosenfeld 	hdl->h_bd = NULL;
932da00bec1SHans Rosenfeld 
9333f7d54a6SGarrett D'Amore 	if (bd->d_ksp != NULL) {
9343f7d54a6SGarrett D'Amore 		kstat_delete(bd->d_ksp);
9353f7d54a6SGarrett D'Amore 		bd->d_ksp = NULL;
9363f7d54a6SGarrett D'Amore 	} else {
9373f7d54a6SGarrett D'Amore 		kmem_free(bd->d_kiop, sizeof (kstat_io_t));
9383f7d54a6SGarrett D'Amore 	}
939bef9e21aSHans Rosenfeld 
94033f84ecfSJason King 	bd_destroy_errstats(bd);
94186e3bca6SGarrett D'Amore 	cmlb_detach(bd->d_cmlbh, 0);
9423f7d54a6SGarrett D'Amore 	cmlb_free_handle(&bd->d_cmlbh);
9433f7d54a6SGarrett D'Amore 	if (bd->d_devid)
9443f7d54a6SGarrett D'Amore 		ddi_devid_free(bd->d_devid);
9453f7d54a6SGarrett D'Amore 	kmem_cache_destroy(bd->d_cache);
9464d95620bSPaul Winder 	mutex_destroy(&bd->d_ksmutex);
9473f7d54a6SGarrett D'Amore 	mutex_destroy(&bd->d_ocmutex);
9483f7d54a6SGarrett D'Amore 	mutex_destroy(&bd->d_statemutex);
9493f7d54a6SGarrett D'Amore 	cv_destroy(&bd->d_statecv);
9506f0e4dc9SAndy Fiddaman 	mutex_destroy(&bd->d_dle_mutex);
9514d95620bSPaul Winder 	bd_queues_free(bd);
9523f7d54a6SGarrett D'Amore 	ddi_soft_state_free(bd_state, ddi_get_instance(dip));
9533f7d54a6SGarrett D'Amore 	return (DDI_SUCCESS);
9543f7d54a6SGarrett D'Amore }
9553f7d54a6SGarrett D'Amore 
9563f7d54a6SGarrett D'Amore static int
bd_xfer_ctor(void * buf,void * arg,int kmflag)9573f7d54a6SGarrett D'Amore bd_xfer_ctor(void *buf, void *arg, int kmflag)
9583f7d54a6SGarrett D'Amore {
9593f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
9603f7d54a6SGarrett D'Amore 	bd_t		*bd = arg;
9613f7d54a6SGarrett D'Amore 	int		(*dcb)(caddr_t);
9623f7d54a6SGarrett D'Amore 
963679ac156SAlexey Zaytsev 	if (kmflag == KM_PUSHPAGE || kmflag == KM_SLEEP) {
9643f7d54a6SGarrett D'Amore 		dcb = DDI_DMA_SLEEP;
9653f7d54a6SGarrett D'Amore 	} else {
9663f7d54a6SGarrett D'Amore 		dcb = DDI_DMA_DONTWAIT;
9673f7d54a6SGarrett D'Amore 	}
9683f7d54a6SGarrett D'Amore 
9693f7d54a6SGarrett D'Amore 	xi = buf;
9703f7d54a6SGarrett D'Amore 	bzero(xi, sizeof (*xi));
9713f7d54a6SGarrett D'Amore 	xi->i_bd = bd;
9723f7d54a6SGarrett D'Amore 
9733f7d54a6SGarrett D'Amore 	if (bd->d_use_dma) {
9743f7d54a6SGarrett D'Amore 		if (ddi_dma_alloc_handle(bd->d_dip, &bd->d_dma, dcb, NULL,
9753f7d54a6SGarrett D'Amore 		    &xi->i_dmah) != DDI_SUCCESS) {
9763f7d54a6SGarrett D'Amore 			return (-1);
9773f7d54a6SGarrett D'Amore 		}
9783f7d54a6SGarrett D'Amore 	}
9793f7d54a6SGarrett D'Amore 
9803f7d54a6SGarrett D'Amore 	return (0);
9813f7d54a6SGarrett D'Amore }
9823f7d54a6SGarrett D'Amore 
9833f7d54a6SGarrett D'Amore static void
bd_xfer_dtor(void * buf,void * arg)9843f7d54a6SGarrett D'Amore bd_xfer_dtor(void *buf, void *arg)
9853f7d54a6SGarrett D'Amore {
9863f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi = buf;
9873f7d54a6SGarrett D'Amore 
9883f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
9893f7d54a6SGarrett D'Amore 
9903f7d54a6SGarrett D'Amore 	if (xi->i_dmah)
9913f7d54a6SGarrett D'Amore 		ddi_dma_free_handle(&xi->i_dmah);
9923f7d54a6SGarrett D'Amore 	xi->i_dmah = NULL;
9933f7d54a6SGarrett D'Amore }
9943f7d54a6SGarrett D'Amore 
9953f7d54a6SGarrett D'Amore static bd_xfer_impl_t *
bd_xfer_alloc(bd_t * bd,struct buf * bp,int (* func)(void *,bd_xfer_t *),int kmflag)9963f7d54a6SGarrett D'Amore bd_xfer_alloc(bd_t *bd, struct buf *bp, int (*func)(void *, bd_xfer_t *),
9973f7d54a6SGarrett D'Amore     int kmflag)
9983f7d54a6SGarrett D'Amore {
9993f7d54a6SGarrett D'Amore 	bd_xfer_impl_t		*xi;
1000a0cb694bSSteve Ma 	int			rv = 0;
10013f7d54a6SGarrett D'Amore 	int			status;
10023f7d54a6SGarrett D'Amore 	unsigned		dir;
10033f7d54a6SGarrett D'Amore 	int			(*cb)(caddr_t);
10043f7d54a6SGarrett D'Amore 	size_t			len;
10053f7d54a6SGarrett D'Amore 	uint32_t		shift;
10063f7d54a6SGarrett D'Amore 
10073f7d54a6SGarrett D'Amore 	if (kmflag == KM_SLEEP) {
10083f7d54a6SGarrett D'Amore 		cb = DDI_DMA_SLEEP;
10093f7d54a6SGarrett D'Amore 	} else {
10103f7d54a6SGarrett D'Amore 		cb = DDI_DMA_DONTWAIT;
10113f7d54a6SGarrett D'Amore 	}
10123f7d54a6SGarrett D'Amore 
10133f7d54a6SGarrett D'Amore 	xi = kmem_cache_alloc(bd->d_cache, kmflag);
10143f7d54a6SGarrett D'Amore 	if (xi == NULL) {
10153f7d54a6SGarrett D'Amore 		bioerror(bp, ENOMEM);
10163f7d54a6SGarrett D'Amore 		return (NULL);
10173f7d54a6SGarrett D'Amore 	}
10183f7d54a6SGarrett D'Amore 
10193f7d54a6SGarrett D'Amore 	ASSERT(bp);
10203f7d54a6SGarrett D'Amore 
10213f7d54a6SGarrett D'Amore 	xi->i_bp = bp;
10223f7d54a6SGarrett D'Amore 	xi->i_func = func;
1023c0591a0cSYouzhong Yang 	xi->i_blkno = bp->b_lblkno >> (bd->d_blkshift - DEV_BSHIFT);
10243f7d54a6SGarrett D'Amore 
10253f7d54a6SGarrett D'Amore 	if (bp->b_bcount == 0) {
10263f7d54a6SGarrett D'Amore 		xi->i_len = 0;
10273f7d54a6SGarrett D'Amore 		xi->i_nblks = 0;
10283f7d54a6SGarrett D'Amore 		xi->i_kaddr = NULL;
10293f7d54a6SGarrett D'Amore 		xi->i_resid = 0;
10303f7d54a6SGarrett D'Amore 		xi->i_num_win = 0;
10313f7d54a6SGarrett D'Amore 		goto done;
10323f7d54a6SGarrett D'Amore 	}
10333f7d54a6SGarrett D'Amore 
10343f7d54a6SGarrett D'Amore 	if (bp->b_flags & B_READ) {
10353f7d54a6SGarrett D'Amore 		dir = DDI_DMA_READ;
10363f7d54a6SGarrett D'Amore 		xi->i_func = bd->d_ops.o_read;
10373f7d54a6SGarrett D'Amore 	} else {
10383f7d54a6SGarrett D'Amore 		dir = DDI_DMA_WRITE;
10393f7d54a6SGarrett D'Amore 		xi->i_func = bd->d_ops.o_write;
10403f7d54a6SGarrett D'Amore 	}
10413f7d54a6SGarrett D'Amore 
10423f7d54a6SGarrett D'Amore 	shift = bd->d_blkshift;
10433f7d54a6SGarrett D'Amore 	xi->i_blkshift = shift;
10443f7d54a6SGarrett D'Amore 
10453f7d54a6SGarrett D'Amore 	if (!bd->d_use_dma) {
10463f7d54a6SGarrett D'Amore 		bp_mapin(bp);
10473f7d54a6SGarrett D'Amore 		rv = 0;
10483f7d54a6SGarrett D'Amore 		xi->i_offset = 0;
10493f7d54a6SGarrett D'Amore 		xi->i_num_win =
10503f7d54a6SGarrett D'Amore 		    (bp->b_bcount + (bd->d_maxxfer - 1)) / bd->d_maxxfer;
10513f7d54a6SGarrett D'Amore 		xi->i_cur_win = 0;
10523f7d54a6SGarrett D'Amore 		xi->i_len = min(bp->b_bcount, bd->d_maxxfer);
105304904ca2SDan McDonald 		xi->i_nblks = xi->i_len >> shift;
10543f7d54a6SGarrett D'Amore 		xi->i_kaddr = bp->b_un.b_addr;
10553f7d54a6SGarrett D'Amore 		xi->i_resid = bp->b_bcount;
10563f7d54a6SGarrett D'Amore 	} else {
10573f7d54a6SGarrett D'Amore 
10583f7d54a6SGarrett D'Amore 		/*
10593f7d54a6SGarrett D'Amore 		 * We have to use consistent DMA if the address is misaligned.
10603f7d54a6SGarrett D'Amore 		 */
10613f7d54a6SGarrett D'Amore 		if (((bp->b_flags & (B_PAGEIO | B_REMAPPED)) != B_PAGEIO) &&
10623f7d54a6SGarrett D'Amore 		    ((uintptr_t)bp->b_un.b_addr & 0x7)) {
10633f7d54a6SGarrett D'Amore 			dir |= DDI_DMA_CONSISTENT | DDI_DMA_PARTIAL;
10643f7d54a6SGarrett D'Amore 		} else {
10653f7d54a6SGarrett D'Amore 			dir |= DDI_DMA_STREAMING | DDI_DMA_PARTIAL;
10663f7d54a6SGarrett D'Amore 		}
10673f7d54a6SGarrett D'Amore 
10683f7d54a6SGarrett D'Amore 		status = ddi_dma_buf_bind_handle(xi->i_dmah, bp, dir, cb,
10693f7d54a6SGarrett D'Amore 		    NULL, &xi->i_dmac, &xi->i_ndmac);
10703f7d54a6SGarrett D'Amore 		switch (status) {
10713f7d54a6SGarrett D'Amore 		case DDI_DMA_MAPPED:
10723f7d54a6SGarrett D'Amore 			xi->i_num_win = 1;
10733f7d54a6SGarrett D'Amore 			xi->i_cur_win = 0;
10743f7d54a6SGarrett D'Amore 			xi->i_offset = 0;
10753f7d54a6SGarrett D'Amore 			xi->i_len = bp->b_bcount;
107604904ca2SDan McDonald 			xi->i_nblks = xi->i_len >> shift;
10773f7d54a6SGarrett D'Amore 			xi->i_resid = bp->b_bcount;
10783f7d54a6SGarrett D'Amore 			rv = 0;
10793f7d54a6SGarrett D'Amore 			break;
10803f7d54a6SGarrett D'Amore 		case DDI_DMA_PARTIAL_MAP:
10813f7d54a6SGarrett D'Amore 			xi->i_cur_win = 0;
10823f7d54a6SGarrett D'Amore 
10833f7d54a6SGarrett D'Amore 			if ((ddi_dma_numwin(xi->i_dmah, &xi->i_num_win) !=
10843f7d54a6SGarrett D'Amore 			    DDI_SUCCESS) ||
10853f7d54a6SGarrett D'Amore 			    (ddi_dma_getwin(xi->i_dmah, 0, &xi->i_offset,
10863f7d54a6SGarrett D'Amore 			    &len, &xi->i_dmac, &xi->i_ndmac) !=
10873f7d54a6SGarrett D'Amore 			    DDI_SUCCESS) ||
1088c0591a0cSYouzhong Yang 			    (P2PHASE(len, (1U << shift)) != 0)) {
10893f7d54a6SGarrett D'Amore 				(void) ddi_dma_unbind_handle(xi->i_dmah);
10903f7d54a6SGarrett D'Amore 				rv = EFAULT;
10913f7d54a6SGarrett D'Amore 				goto done;
10923f7d54a6SGarrett D'Amore 			}
10933f7d54a6SGarrett D'Amore 			xi->i_len = len;
109404904ca2SDan McDonald 			xi->i_nblks = xi->i_len >> shift;
10953f7d54a6SGarrett D'Amore 			xi->i_resid = bp->b_bcount;
10963f7d54a6SGarrett D'Amore 			rv = 0;
10973f7d54a6SGarrett D'Amore 			break;
10983f7d54a6SGarrett D'Amore 		case DDI_DMA_NORESOURCES:
10993f7d54a6SGarrett D'Amore 			rv = EAGAIN;
11003f7d54a6SGarrett D'Amore 			goto done;
11013f7d54a6SGarrett D'Amore 		case DDI_DMA_TOOBIG:
11023f7d54a6SGarrett D'Amore 			rv = EINVAL;
11033f7d54a6SGarrett D'Amore 			goto done;
11043f7d54a6SGarrett D'Amore 		case DDI_DMA_NOMAPPING:
11053f7d54a6SGarrett D'Amore 		case DDI_DMA_INUSE:
11063f7d54a6SGarrett D'Amore 		default:
11073f7d54a6SGarrett D'Amore 			rv = EFAULT;
11083f7d54a6SGarrett D'Amore 			goto done;
11093f7d54a6SGarrett D'Amore 		}
11103f7d54a6SGarrett D'Amore 	}
11113f7d54a6SGarrett D'Amore 
11123f7d54a6SGarrett D'Amore done:
11133f7d54a6SGarrett D'Amore 	if (rv != 0) {
11143f7d54a6SGarrett D'Amore 		kmem_cache_free(bd->d_cache, xi);
11153f7d54a6SGarrett D'Amore 		bioerror(bp, rv);
11163f7d54a6SGarrett D'Amore 		return (NULL);
11173f7d54a6SGarrett D'Amore 	}
11183f7d54a6SGarrett D'Amore 
11193f7d54a6SGarrett D'Amore 	return (xi);
11203f7d54a6SGarrett D'Amore }
11213f7d54a6SGarrett D'Amore 
11223f7d54a6SGarrett D'Amore static void
bd_xfer_free(bd_xfer_impl_t * xi)11233f7d54a6SGarrett D'Amore bd_xfer_free(bd_xfer_impl_t *xi)
11243f7d54a6SGarrett D'Amore {
11253f7d54a6SGarrett D'Amore 	if (xi->i_dmah) {
11263f7d54a6SGarrett D'Amore 		(void) ddi_dma_unbind_handle(xi->i_dmah);
11273f7d54a6SGarrett D'Amore 	}
11281a5ae140SJason King 	if (xi->i_dfl != NULL) {
11291a5ae140SJason King 		dfl_free((dkioc_free_list_t *)xi->i_dfl);
11301a5ae140SJason King 		xi->i_dfl = NULL;
11311a5ae140SJason King 	}
11323f7d54a6SGarrett D'Amore 	kmem_cache_free(xi->i_bd->d_cache, xi);
11333f7d54a6SGarrett D'Amore }
11343f7d54a6SGarrett D'Amore 
11353f7d54a6SGarrett D'Amore static int
bd_open(dev_t * devp,int flag,int otyp,cred_t * credp)11363f7d54a6SGarrett D'Amore bd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
11373f7d54a6SGarrett D'Amore {
11383f7d54a6SGarrett D'Amore 	dev_t		dev = *devp;
11393f7d54a6SGarrett D'Amore 	bd_t		*bd;
11403f7d54a6SGarrett D'Amore 	minor_t		part;
11413f7d54a6SGarrett D'Amore 	minor_t		inst;
11423f7d54a6SGarrett D'Amore 	uint64_t	mask;
11433f7d54a6SGarrett D'Amore 	boolean_t	ndelay;
11443f7d54a6SGarrett D'Amore 	int		rv;
11453f7d54a6SGarrett D'Amore 	diskaddr_t	nblks;
11463f7d54a6SGarrett D'Amore 	diskaddr_t	lba;
11473f7d54a6SGarrett D'Amore 
11483f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
11493f7d54a6SGarrett D'Amore 
11503f7d54a6SGarrett D'Amore 	part = BDPART(dev);
11513f7d54a6SGarrett D'Amore 	inst = BDINST(dev);
11523f7d54a6SGarrett D'Amore 
11533f7d54a6SGarrett D'Amore 	if (otyp >= OTYPCNT)
11543f7d54a6SGarrett D'Amore 		return (EINVAL);
11553f7d54a6SGarrett D'Amore 
11563f7d54a6SGarrett D'Amore 	ndelay = (flag & (FNDELAY | FNONBLOCK)) ? B_TRUE : B_FALSE;
11573f7d54a6SGarrett D'Amore 
11583f7d54a6SGarrett D'Amore 	/*
11593f7d54a6SGarrett D'Amore 	 * Block any DR events from changing the set of registered
11603f7d54a6SGarrett D'Amore 	 * devices while we function.
11613f7d54a6SGarrett D'Amore 	 */
11623f7d54a6SGarrett D'Amore 	rw_enter(&bd_lock, RW_READER);
11633f7d54a6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
11643f7d54a6SGarrett D'Amore 		rw_exit(&bd_lock);
11653f7d54a6SGarrett D'Amore 		return (ENXIO);
11663f7d54a6SGarrett D'Amore 	}
11673f7d54a6SGarrett D'Amore 
11683f7d54a6SGarrett D'Amore 	mutex_enter(&bd->d_ocmutex);
11693f7d54a6SGarrett D'Amore 
11703f7d54a6SGarrett D'Amore 	ASSERT(part < 64);
11713f7d54a6SGarrett D'Amore 	mask = (1U << part);
11723f7d54a6SGarrett D'Amore 
11733f7d54a6SGarrett D'Amore 	bd_update_state(bd);
11743f7d54a6SGarrett D'Amore 
117586e3bca6SGarrett D'Amore 	if (cmlb_validate(bd->d_cmlbh, 0, 0) != 0) {
11763f7d54a6SGarrett D'Amore 
11773f7d54a6SGarrett D'Amore 		/* non-blocking opens are allowed to succeed */
11783f7d54a6SGarrett D'Amore 		if (!ndelay) {
11793f7d54a6SGarrett D'Amore 			rv = ENXIO;
11803f7d54a6SGarrett D'Amore 			goto done;
11813f7d54a6SGarrett D'Amore 		}
11823f7d54a6SGarrett D'Amore 	} else if (cmlb_partinfo(bd->d_cmlbh, part, &nblks, &lba,
118386e3bca6SGarrett D'Amore 	    NULL, NULL, 0) == 0) {
11843f7d54a6SGarrett D'Amore 
11853f7d54a6SGarrett D'Amore 		/*
11863f7d54a6SGarrett D'Amore 		 * We read the partinfo, verify valid ranges.  If the
11873f7d54a6SGarrett D'Amore 		 * partition is invalid, and we aren't blocking or
11883f7d54a6SGarrett D'Amore 		 * doing a raw access, then fail. (Non-blocking and
11893f7d54a6SGarrett D'Amore 		 * raw accesses can still succeed to allow a disk with
11903f7d54a6SGarrett D'Amore 		 * bad partition data to opened by format and fdisk.)
11913f7d54a6SGarrett D'Amore 		 */
11923f7d54a6SGarrett D'Amore 		if ((!nblks) && ((!ndelay) || (otyp != OTYP_CHR))) {
11933f7d54a6SGarrett D'Amore 			rv = ENXIO;
11943f7d54a6SGarrett D'Amore 			goto done;
11953f7d54a6SGarrett D'Amore 		}
11963f7d54a6SGarrett D'Amore 	} else if (!ndelay) {
11973f7d54a6SGarrett D'Amore 		/*
11983f7d54a6SGarrett D'Amore 		 * cmlb_partinfo failed -- invalid partition or no
11993f7d54a6SGarrett D'Amore 		 * disk label.
12003f7d54a6SGarrett D'Amore 		 */
12013f7d54a6SGarrett D'Amore 		rv = ENXIO;
12023f7d54a6SGarrett D'Amore 		goto done;
12033f7d54a6SGarrett D'Amore 	}
12043f7d54a6SGarrett D'Amore 
12053f7d54a6SGarrett D'Amore 	if ((flag & FWRITE) && bd->d_rdonly) {
12063f7d54a6SGarrett D'Amore 		rv = EROFS;
12073f7d54a6SGarrett D'Amore 		goto done;
12083f7d54a6SGarrett D'Amore 	}
12093f7d54a6SGarrett D'Amore 
12103f7d54a6SGarrett D'Amore 	if ((bd->d_open_excl) & (mask)) {
12113f7d54a6SGarrett D'Amore 		rv = EBUSY;
12123f7d54a6SGarrett D'Amore 		goto done;
12133f7d54a6SGarrett D'Amore 	}
12143f7d54a6SGarrett D'Amore 	if (flag & FEXCL) {
12153f7d54a6SGarrett D'Amore 		if (bd->d_open_lyr[part]) {
12163f7d54a6SGarrett D'Amore 			rv = EBUSY;
12173f7d54a6SGarrett D'Amore 			goto done;
12183f7d54a6SGarrett D'Amore 		}
12193f7d54a6SGarrett D'Amore 		for (int i = 0; i < OTYP_LYR; i++) {
12203f7d54a6SGarrett D'Amore 			if (bd->d_open_reg[i] & mask) {
12213f7d54a6SGarrett D'Amore 				rv = EBUSY;
12223f7d54a6SGarrett D'Amore 				goto done;
12233f7d54a6SGarrett D'Amore 			}
12243f7d54a6SGarrett D'Amore 		}
12253f7d54a6SGarrett D'Amore 	}
12263f7d54a6SGarrett D'Amore 
12273f7d54a6SGarrett D'Amore 	if (otyp == OTYP_LYR) {
12283f7d54a6SGarrett D'Amore 		bd->d_open_lyr[part]++;
12293f7d54a6SGarrett D'Amore 	} else {
12303f7d54a6SGarrett D'Amore 		bd->d_open_reg[otyp] |= mask;
12313f7d54a6SGarrett D'Amore 	}
12323f7d54a6SGarrett D'Amore 	if (flag & FEXCL) {
12333f7d54a6SGarrett D'Amore 		bd->d_open_excl |= mask;
12343f7d54a6SGarrett D'Amore 	}
12353f7d54a6SGarrett D'Amore 
12363f7d54a6SGarrett D'Amore 	rv = 0;
12373f7d54a6SGarrett D'Amore done:
12383f7d54a6SGarrett D'Amore 	mutex_exit(&bd->d_ocmutex);
12393f7d54a6SGarrett D'Amore 	rw_exit(&bd_lock);
12403f7d54a6SGarrett D'Amore 
12413f7d54a6SGarrett D'Amore 	return (rv);
12423f7d54a6SGarrett D'Amore }
12433f7d54a6SGarrett D'Amore 
12443f7d54a6SGarrett D'Amore static int
bd_close(dev_t dev,int flag,int otyp,cred_t * credp)12453f7d54a6SGarrett D'Amore bd_close(dev_t dev, int flag, int otyp, cred_t *credp)
12463f7d54a6SGarrett D'Amore {
12473f7d54a6SGarrett D'Amore 	bd_t		*bd;
12483f7d54a6SGarrett D'Amore 	minor_t		inst;
12493f7d54a6SGarrett D'Amore 	minor_t		part;
12503f7d54a6SGarrett D'Amore 	uint64_t	mask;
12513f7d54a6SGarrett D'Amore 	boolean_t	last = B_TRUE;
12523f7d54a6SGarrett D'Amore 
12533f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
12543f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
12553f7d54a6SGarrett D'Amore 
12563f7d54a6SGarrett D'Amore 	part = BDPART(dev);
12573f7d54a6SGarrett D'Amore 	inst = BDINST(dev);
12583f7d54a6SGarrett D'Amore 
12593f7d54a6SGarrett D'Amore 	ASSERT(part < 64);
12603f7d54a6SGarrett D'Amore 	mask = (1U << part);
12613f7d54a6SGarrett D'Amore 
12623f7d54a6SGarrett D'Amore 	rw_enter(&bd_lock, RW_READER);
12633f7d54a6SGarrett D'Amore 
12643f7d54a6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
12653f7d54a6SGarrett D'Amore 		rw_exit(&bd_lock);
12663f7d54a6SGarrett D'Amore 		return (ENXIO);
12673f7d54a6SGarrett D'Amore 	}
12683f7d54a6SGarrett D'Amore 
12693f7d54a6SGarrett D'Amore 	mutex_enter(&bd->d_ocmutex);
12703f7d54a6SGarrett D'Amore 	if (bd->d_open_excl & mask) {
12713f7d54a6SGarrett D'Amore 		bd->d_open_excl &= ~mask;
12723f7d54a6SGarrett D'Amore 	}
12733f7d54a6SGarrett D'Amore 	if (otyp == OTYP_LYR) {
12743f7d54a6SGarrett D'Amore 		bd->d_open_lyr[part]--;
12753f7d54a6SGarrett D'Amore 	} else {
12763f7d54a6SGarrett D'Amore 		bd->d_open_reg[otyp] &= ~mask;
12773f7d54a6SGarrett D'Amore 	}
12783f7d54a6SGarrett D'Amore 	for (int i = 0; i < 64; i++) {
12793f7d54a6SGarrett D'Amore 		if (bd->d_open_lyr[part]) {
12803f7d54a6SGarrett D'Amore 			last = B_FALSE;
12813f7d54a6SGarrett D'Amore 		}
12823f7d54a6SGarrett D'Amore 	}
12833f7d54a6SGarrett D'Amore 	for (int i = 0; last && (i < OTYP_LYR); i++) {
12843f7d54a6SGarrett D'Amore 		if (bd->d_open_reg[i]) {
12853f7d54a6SGarrett D'Amore 			last = B_FALSE;
12863f7d54a6SGarrett D'Amore 		}
12873f7d54a6SGarrett D'Amore 	}
12883f7d54a6SGarrett D'Amore 	mutex_exit(&bd->d_ocmutex);
12893f7d54a6SGarrett D'Amore 
12903f7d54a6SGarrett D'Amore 	if (last) {
129186e3bca6SGarrett D'Amore 		cmlb_invalidate(bd->d_cmlbh, 0);
12923f7d54a6SGarrett D'Amore 	}
12933f7d54a6SGarrett D'Amore 	rw_exit(&bd_lock);
12943f7d54a6SGarrett D'Amore 
12953f7d54a6SGarrett D'Amore 	return (0);
12963f7d54a6SGarrett D'Amore }
12973f7d54a6SGarrett D'Amore 
12983f7d54a6SGarrett D'Amore static int
bd_dump(dev_t dev,caddr_t caddr,daddr_t blkno,int nblk)129986e3bca6SGarrett D'Amore bd_dump(dev_t dev, caddr_t caddr, daddr_t blkno, int nblk)
130086e3bca6SGarrett D'Amore {
130186e3bca6SGarrett D'Amore 	minor_t		inst;
130286e3bca6SGarrett D'Amore 	minor_t		part;
130386e3bca6SGarrett D'Amore 	diskaddr_t	pstart;
130486e3bca6SGarrett D'Amore 	diskaddr_t	psize;
130586e3bca6SGarrett D'Amore 	bd_t		*bd;
130686e3bca6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
130786e3bca6SGarrett D'Amore 	buf_t		*bp;
130886e3bca6SGarrett D'Amore 	int		rv;
1309c0591a0cSYouzhong Yang 	uint32_t	shift;
1310c0591a0cSYouzhong Yang 	daddr_t		d_blkno;
1311c0591a0cSYouzhong Yang 	int	d_nblk;
131286e3bca6SGarrett D'Amore 
131386e3bca6SGarrett D'Amore 	rw_enter(&bd_lock, RW_READER);
131486e3bca6SGarrett D'Amore 
131586e3bca6SGarrett D'Amore 	part = BDPART(dev);
131686e3bca6SGarrett D'Amore 	inst = BDINST(dev);
131786e3bca6SGarrett D'Amore 
131886e3bca6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
131986e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
132086e3bca6SGarrett D'Amore 		return (ENXIO);
132186e3bca6SGarrett D'Amore 	}
1322c0591a0cSYouzhong Yang 	shift = bd->d_blkshift;
1323c0591a0cSYouzhong Yang 	d_blkno = blkno >> (shift - DEV_BSHIFT);
1324c0591a0cSYouzhong Yang 	d_nblk = nblk >> (shift - DEV_BSHIFT);
132586e3bca6SGarrett D'Amore 	/*
132686e3bca6SGarrett D'Amore 	 * do cmlb, but do it synchronously unless we already have the
132786e3bca6SGarrett D'Amore 	 * partition (which we probably should.)
132886e3bca6SGarrett D'Amore 	 */
132986e3bca6SGarrett D'Amore 	if (cmlb_partinfo(bd->d_cmlbh, part, &psize, &pstart, NULL, NULL,
133086e3bca6SGarrett D'Amore 	    (void *)1)) {
133186e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
133286e3bca6SGarrett D'Amore 		return (ENXIO);
133386e3bca6SGarrett D'Amore 	}
133486e3bca6SGarrett D'Amore 
1335c0591a0cSYouzhong Yang 	if ((d_blkno + d_nblk) > psize) {
133686e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
133786e3bca6SGarrett D'Amore 		return (EINVAL);
133886e3bca6SGarrett D'Amore 	}
133986e3bca6SGarrett D'Amore 	bp = getrbuf(KM_NOSLEEP);
134086e3bca6SGarrett D'Amore 	if (bp == NULL) {
134186e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
134286e3bca6SGarrett D'Amore 		return (ENOMEM);
134386e3bca6SGarrett D'Amore 	}
134486e3bca6SGarrett D'Amore 
1345c0591a0cSYouzhong Yang 	bp->b_bcount = nblk << DEV_BSHIFT;
134686e3bca6SGarrett D'Amore 	bp->b_resid = bp->b_bcount;
134786e3bca6SGarrett D'Amore 	bp->b_lblkno = blkno;
134886e3bca6SGarrett D'Amore 	bp->b_un.b_addr = caddr;
134986e3bca6SGarrett D'Amore 
135086e3bca6SGarrett D'Amore 	xi = bd_xfer_alloc(bd, bp,  bd->d_ops.o_write, KM_NOSLEEP);
135186e3bca6SGarrett D'Amore 	if (xi == NULL) {
135286e3bca6SGarrett D'Amore 		rw_exit(&bd_lock);
135386e3bca6SGarrett D'Amore 		freerbuf(bp);
135486e3bca6SGarrett D'Amore 		return (ENOMEM);
135586e3bca6SGarrett D'Amore 	}
1356c0591a0cSYouzhong Yang 	xi->i_blkno = d_blkno + pstart;
135786e3bca6SGarrett D'Amore 	xi->i_flags = BD_XFER_POLL;
135886e3bca6SGarrett D'Amore 	bd_submit(bd, xi);
135986e3bca6SGarrett D'Amore 	rw_exit(&bd_lock);
136086e3bca6SGarrett D'Amore 
136186e3bca6SGarrett D'Amore 	/*
136286e3bca6SGarrett D'Amore 	 * Generally, we should have run this entirely synchronously
136386e3bca6SGarrett D'Amore 	 * at this point and the biowait call should be a no-op.  If
136486e3bca6SGarrett D'Amore 	 * it didn't happen this way, it's a bug in the underlying
136586e3bca6SGarrett D'Amore 	 * driver not honoring BD_XFER_POLL.
136686e3bca6SGarrett D'Amore 	 */
136786e3bca6SGarrett D'Amore 	(void) biowait(bp);
136886e3bca6SGarrett D'Amore 	rv = geterror(bp);
136986e3bca6SGarrett D'Amore 	freerbuf(bp);
137086e3bca6SGarrett D'Amore 	return (rv);
137186e3bca6SGarrett D'Amore }
137286e3bca6SGarrett D'Amore 
13739d75dfdaSAlexey Zaytsev void
bd_minphys(struct buf * bp)13749d75dfdaSAlexey Zaytsev bd_minphys(struct buf *bp)
13759d75dfdaSAlexey Zaytsev {
13769d75dfdaSAlexey Zaytsev 	minor_t inst;
13779d75dfdaSAlexey Zaytsev 	bd_t	*bd;
13789d75dfdaSAlexey Zaytsev 	inst = BDINST(bp->b_edev);
13799d75dfdaSAlexey Zaytsev 
13809d75dfdaSAlexey Zaytsev 	bd = ddi_get_soft_state(bd_state, inst);
13819d75dfdaSAlexey Zaytsev 
13829d75dfdaSAlexey Zaytsev 	/*
13839d75dfdaSAlexey Zaytsev 	 * In a non-debug kernel, bd_strategy will catch !bd as
13849d75dfdaSAlexey Zaytsev 	 * well, and will fail nicely.
13859d75dfdaSAlexey Zaytsev 	 */
13869d75dfdaSAlexey Zaytsev 	ASSERT(bd);
13879d75dfdaSAlexey Zaytsev 
13889d75dfdaSAlexey Zaytsev 	if (bp->b_bcount > bd->d_maxxfer)
13899d75dfdaSAlexey Zaytsev 		bp->b_bcount = bd->d_maxxfer;
13909d75dfdaSAlexey Zaytsev }
13919d75dfdaSAlexey Zaytsev 
139286e3bca6SGarrett D'Amore static int
bd_check_uio(dev_t dev,struct uio * uio)1393c0591a0cSYouzhong Yang bd_check_uio(dev_t dev, struct uio *uio)
1394c0591a0cSYouzhong Yang {
1395c0591a0cSYouzhong Yang 	bd_t		*bd;
1396c0591a0cSYouzhong Yang 	uint32_t	shift;
1397c0591a0cSYouzhong Yang 
1398c0591a0cSYouzhong Yang 	if ((bd = ddi_get_soft_state(bd_state, BDINST(dev))) == NULL) {
1399c0591a0cSYouzhong Yang 		return (ENXIO);
1400c0591a0cSYouzhong Yang 	}
1401c0591a0cSYouzhong Yang 
1402c0591a0cSYouzhong Yang 	shift = bd->d_blkshift;
1403c0591a0cSYouzhong Yang 	if ((P2PHASE(uio->uio_loffset, (1U << shift)) != 0) ||
1404c0591a0cSYouzhong Yang 	    (P2PHASE(uio->uio_iov->iov_len, (1U << shift)) != 0)) {
1405c0591a0cSYouzhong Yang 		return (EINVAL);
1406c0591a0cSYouzhong Yang 	}
1407c0591a0cSYouzhong Yang 
1408c0591a0cSYouzhong Yang 	return (0);
1409c0591a0cSYouzhong Yang }
1410c0591a0cSYouzhong Yang 
1411c0591a0cSYouzhong Yang static int
bd_read(dev_t dev,struct uio * uio,cred_t * credp)14123f7d54a6SGarrett D'Amore bd_read(dev_t dev, struct uio *uio, cred_t *credp)
14133f7d54a6SGarrett D'Amore {
14143f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
1415c0591a0cSYouzhong Yang 	int	ret = bd_check_uio(dev, uio);
1416c0591a0cSYouzhong Yang 	if (ret != 0) {
1417c0591a0cSYouzhong Yang 		return (ret);
1418c0591a0cSYouzhong Yang 	}
14199d75dfdaSAlexey Zaytsev 	return (physio(bd_strategy, NULL, dev, B_READ, bd_minphys, uio));
14203f7d54a6SGarrett D'Amore }
14213f7d54a6SGarrett D'Amore 
14223f7d54a6SGarrett D'Amore static int
bd_write(dev_t dev,struct uio * uio,cred_t * credp)14233f7d54a6SGarrett D'Amore bd_write(dev_t dev, struct uio *uio, cred_t *credp)
14243f7d54a6SGarrett D'Amore {
14253f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
1426c0591a0cSYouzhong Yang 	int	ret = bd_check_uio(dev, uio);
1427c0591a0cSYouzhong Yang 	if (ret != 0) {
1428c0591a0cSYouzhong Yang 		return (ret);
1429c0591a0cSYouzhong Yang 	}
14309d75dfdaSAlexey Zaytsev 	return (physio(bd_strategy, NULL, dev, B_WRITE, bd_minphys, uio));
14313f7d54a6SGarrett D'Amore }
14323f7d54a6SGarrett D'Amore 
14333f7d54a6SGarrett D'Amore static int
bd_aread(dev_t dev,struct aio_req * aio,cred_t * credp)14343f7d54a6SGarrett D'Amore bd_aread(dev_t dev, struct aio_req *aio, cred_t *credp)
14353f7d54a6SGarrett D'Amore {
14363f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
1437c0591a0cSYouzhong Yang 	int	ret = bd_check_uio(dev, aio->aio_uio);
1438c0591a0cSYouzhong Yang 	if (ret != 0) {
1439c0591a0cSYouzhong Yang 		return (ret);
1440c0591a0cSYouzhong Yang 	}
14419d75dfdaSAlexey Zaytsev 	return (aphysio(bd_strategy, anocancel, dev, B_READ, bd_minphys, aio));
14423f7d54a6SGarrett D'Amore }
14433f7d54a6SGarrett D'Amore 
14443f7d54a6SGarrett D'Amore static int
bd_awrite(dev_t dev,struct aio_req * aio,cred_t * credp)14453f7d54a6SGarrett D'Amore bd_awrite(dev_t dev, struct aio_req *aio, cred_t *credp)
14463f7d54a6SGarrett D'Amore {
14473f7d54a6SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
1448c0591a0cSYouzhong Yang 	int	ret = bd_check_uio(dev, aio->aio_uio);
1449c0591a0cSYouzhong Yang 	if (ret != 0) {
1450c0591a0cSYouzhong Yang 		return (ret);
1451c0591a0cSYouzhong Yang 	}
14529d75dfdaSAlexey Zaytsev 	return (aphysio(bd_strategy, anocancel, dev, B_WRITE, bd_minphys, aio));
14533f7d54a6SGarrett D'Amore }
14543f7d54a6SGarrett D'Amore 
14553f7d54a6SGarrett D'Amore static int
bd_strategy(struct buf * bp)14563f7d54a6SGarrett D'Amore bd_strategy(struct buf *bp)
14573f7d54a6SGarrett D'Amore {
14583f7d54a6SGarrett D'Amore 	minor_t		inst;
14593f7d54a6SGarrett D'Amore 	minor_t		part;
14603f7d54a6SGarrett D'Amore 	bd_t		*bd;
14613f7d54a6SGarrett D'Amore 	diskaddr_t	p_lba;
14623f7d54a6SGarrett D'Amore 	diskaddr_t	p_nblks;
14633f7d54a6SGarrett D'Amore 	diskaddr_t	b_nblks;
14643f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
14653f7d54a6SGarrett D'Amore 	uint32_t	shift;
14663f7d54a6SGarrett D'Amore 	int		(*func)(void *, bd_xfer_t *);
1467c0591a0cSYouzhong Yang 	diskaddr_t	lblkno;
14683f7d54a6SGarrett D'Amore 
14693f7d54a6SGarrett D'Amore 	part = BDPART(bp->b_edev);
14703f7d54a6SGarrett D'Amore 	inst = BDINST(bp->b_edev);
14713f7d54a6SGarrett D'Amore 
14723f7d54a6SGarrett D'Amore 	ASSERT(bp);
14733f7d54a6SGarrett D'Amore 
14743f7d54a6SGarrett D'Amore 	bp->b_resid = bp->b_bcount;
14753f7d54a6SGarrett D'Amore 
14763f7d54a6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
14773f7d54a6SGarrett D'Amore 		bioerror(bp, ENXIO);
14783f7d54a6SGarrett D'Amore 		biodone(bp);
14793f7d54a6SGarrett D'Amore 		return (0);
14803f7d54a6SGarrett D'Amore 	}
14813f7d54a6SGarrett D'Amore 
14823f7d54a6SGarrett D'Amore 	if (cmlb_partinfo(bd->d_cmlbh, part, &p_nblks, &p_lba,
148386e3bca6SGarrett D'Amore 	    NULL, NULL, 0)) {
14843f7d54a6SGarrett D'Amore 		bioerror(bp, ENXIO);
14853f7d54a6SGarrett D'Amore 		biodone(bp);
14863f7d54a6SGarrett D'Amore 		return (0);
14873f7d54a6SGarrett D'Amore 	}
14883f7d54a6SGarrett D'Amore 
14893f7d54a6SGarrett D'Amore 	shift = bd->d_blkshift;
1490c0591a0cSYouzhong Yang 	lblkno = bp->b_lblkno >> (shift - DEV_BSHIFT);
1491c0591a0cSYouzhong Yang 	if ((P2PHASE(bp->b_lblkno, (1U << (shift - DEV_BSHIFT))) != 0) ||
1492c0591a0cSYouzhong Yang 	    (P2PHASE(bp->b_bcount, (1U << shift)) != 0) ||
1493c0591a0cSYouzhong Yang 	    (lblkno > p_nblks)) {
1494c0591a0cSYouzhong Yang 		bioerror(bp, EINVAL);
14953f7d54a6SGarrett D'Amore 		biodone(bp);
14963f7d54a6SGarrett D'Amore 		return (0);
14973f7d54a6SGarrett D'Amore 	}
149804904ca2SDan McDonald 	b_nblks = bp->b_bcount >> shift;
1499c0591a0cSYouzhong Yang 	if ((lblkno == p_nblks) || (bp->b_bcount == 0)) {
15003f7d54a6SGarrett D'Amore 		biodone(bp);
15013f7d54a6SGarrett D'Amore 		return (0);
15023f7d54a6SGarrett D'Amore 	}
15033f7d54a6SGarrett D'Amore 
1504c0591a0cSYouzhong Yang 	if ((b_nblks + lblkno) > p_nblks) {
1505c0591a0cSYouzhong Yang 		bp->b_resid = ((lblkno + b_nblks - p_nblks) << shift);
15063f7d54a6SGarrett D'Amore 		bp->b_bcount -= bp->b_resid;
15073f7d54a6SGarrett D'Amore 	} else {
15083f7d54a6SGarrett D'Amore 		bp->b_resid = 0;
15093f7d54a6SGarrett D'Amore 	}
15103f7d54a6SGarrett D'Amore 	func = (bp->b_flags & B_READ) ? bd->d_ops.o_read : bd->d_ops.o_write;
15113f7d54a6SGarrett D'Amore 
15123f7d54a6SGarrett D'Amore 	xi = bd_xfer_alloc(bd, bp, func, KM_NOSLEEP);
15133f7d54a6SGarrett D'Amore 	if (xi == NULL) {
15143f7d54a6SGarrett D'Amore 		xi = bd_xfer_alloc(bd, bp, func, KM_PUSHPAGE);
15153f7d54a6SGarrett D'Amore 	}
15163f7d54a6SGarrett D'Amore 	if (xi == NULL) {
15173f7d54a6SGarrett D'Amore 		/* bd_request_alloc will have done bioerror */
15183f7d54a6SGarrett D'Amore 		biodone(bp);
15193f7d54a6SGarrett D'Amore 		return (0);
15203f7d54a6SGarrett D'Amore 	}
1521c0591a0cSYouzhong Yang 	xi->i_blkno = lblkno + p_lba;
15223f7d54a6SGarrett D'Amore 
15233f7d54a6SGarrett D'Amore 	bd_submit(bd, xi);
15243f7d54a6SGarrett D'Amore 
15253f7d54a6SGarrett D'Amore 	return (0);
15263f7d54a6SGarrett D'Amore }
15273f7d54a6SGarrett D'Amore 
15283f7d54a6SGarrett D'Amore static int
bd_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * credp,int * rvalp)15293f7d54a6SGarrett D'Amore bd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp)
15303f7d54a6SGarrett D'Amore {
15313f7d54a6SGarrett D'Amore 	minor_t		inst;
15323f7d54a6SGarrett D'Amore 	uint16_t	part;
15333f7d54a6SGarrett D'Amore 	bd_t		*bd;
15343f7d54a6SGarrett D'Amore 	void		*ptr = (void *)arg;
15353f7d54a6SGarrett D'Amore 	int		rv;
15363f7d54a6SGarrett D'Amore 
15373f7d54a6SGarrett D'Amore 	part = BDPART(dev);
15383f7d54a6SGarrett D'Amore 	inst = BDINST(dev);
15393f7d54a6SGarrett D'Amore 
15403f7d54a6SGarrett D'Amore 	if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
15413f7d54a6SGarrett D'Amore 		return (ENXIO);
15423f7d54a6SGarrett D'Amore 	}
15433f7d54a6SGarrett D'Amore 
154486e3bca6SGarrett D'Amore 	rv = cmlb_ioctl(bd->d_cmlbh, dev, cmd, arg, flag, credp, rvalp, 0);
15453f7d54a6SGarrett D'Amore 	if (rv != ENOTTY)
15463f7d54a6SGarrett D'Amore 		return (rv);
15473f7d54a6SGarrett D'Amore 
1548a0cb694bSSteve Ma 	if (rvalp != NULL) {
1549a0cb694bSSteve Ma 		/* the return value of the ioctl is 0 by default */
1550a0cb694bSSteve Ma 		*rvalp = 0;
1551a0cb694bSSteve Ma 	}
1552a0cb694bSSteve Ma 
15533f7d54a6SGarrett D'Amore 	switch (cmd) {
15543f7d54a6SGarrett D'Amore 	case DKIOCGMEDIAINFO: {
15553f7d54a6SGarrett D'Amore 		struct dk_minfo minfo;
15563f7d54a6SGarrett D'Amore 
15573f7d54a6SGarrett D'Amore 		/* make sure our state information is current */
15583f7d54a6SGarrett D'Amore 		bd_update_state(bd);
15593f7d54a6SGarrett D'Amore 		bzero(&minfo, sizeof (minfo));
15603f7d54a6SGarrett D'Amore 		minfo.dki_media_type = DK_FIXED_DISK;
15613f7d54a6SGarrett D'Amore 		minfo.dki_lbsize = (1U << bd->d_blkshift);
15623f7d54a6SGarrett D'Amore 		minfo.dki_capacity = bd->d_numblks;
15633f7d54a6SGarrett D'Amore 		if (ddi_copyout(&minfo, ptr, sizeof (minfo), flag)) {
15643f7d54a6SGarrett D'Amore 			return (EFAULT);
15653f7d54a6SGarrett D'Amore 		}
15663f7d54a6SGarrett D'Amore 		return (0);
15673f7d54a6SGarrett D'Amore 	}
1568dba604f9SDan McDonald 	case DKIOCGMEDIAINFOEXT: {
1569dba604f9SDan McDonald 		struct dk_minfo_ext miext;
15707b421453SRobert Mustacchi 		size_t len;
1571dba604f9SDan McDonald 
1572dba604f9SDan McDonald 		/* make sure our state information is current */
1573dba604f9SDan McDonald 		bd_update_state(bd);
1574dba604f9SDan McDonald 		bzero(&miext, sizeof (miext));
1575dba604f9SDan McDonald 		miext.dki_media_type = DK_FIXED_DISK;
1576dba604f9SDan McDonald 		miext.dki_lbsize = (1U << bd->d_blkshift);
157732ce6b81SHans Rosenfeld 		miext.dki_pbsize = (1U << bd->d_pblkshift);
1578dba604f9SDan McDonald 		miext.dki_capacity = bd->d_numblks;
15797b421453SRobert Mustacchi 
15807b421453SRobert Mustacchi 		switch (ddi_model_convert_from(flag & FMODELS)) {
15817b421453SRobert Mustacchi 		case DDI_MODEL_ILP32:
15827b421453SRobert Mustacchi 			len = sizeof (struct dk_minfo_ext32);
15837b421453SRobert Mustacchi 			break;
15847b421453SRobert Mustacchi 		default:
15857b421453SRobert Mustacchi 			len = sizeof (struct dk_minfo_ext);
15867b421453SRobert Mustacchi 			break;
15877b421453SRobert Mustacchi 		}
15887b421453SRobert Mustacchi 
15897b421453SRobert Mustacchi 		if (ddi_copyout(&miext, ptr, len, flag)) {
1590dba604f9SDan McDonald 			return (EFAULT);
1591dba604f9SDan McDonald 		}
1592dba604f9SDan McDonald 		return (0);
1593dba604f9SDan McDonald 	}
15943f7d54a6SGarrett D'Amore 	case DKIOCINFO: {
15953f7d54a6SGarrett D'Amore 		struct dk_cinfo cinfo;
15963f7d54a6SGarrett D'Amore 		bzero(&cinfo, sizeof (cinfo));
15973f7d54a6SGarrett D'Amore 		cinfo.dki_ctype = DKC_BLKDEV;
15983f7d54a6SGarrett D'Amore 		cinfo.dki_cnum = ddi_get_instance(ddi_get_parent(bd->d_dip));
15993f7d54a6SGarrett D'Amore 		(void) snprintf(cinfo.dki_cname, sizeof (cinfo.dki_cname),
16003f7d54a6SGarrett D'Amore 		    "%s", ddi_driver_name(ddi_get_parent(bd->d_dip)));
16013f7d54a6SGarrett D'Amore 		(void) snprintf(cinfo.dki_dname, sizeof (cinfo.dki_dname),
16023f7d54a6SGarrett D'Amore 		    "%s", ddi_driver_name(bd->d_dip));
16033f7d54a6SGarrett D'Amore 		cinfo.dki_unit = inst;
16043f7d54a6SGarrett D'Amore 		cinfo.dki_flags = DKI_FMTVOL;
16053f7d54a6SGarrett D'Amore 		cinfo.dki_partition = part;
16063f7d54a6SGarrett D'Amore 		cinfo.dki_maxtransfer = bd->d_maxxfer / DEV_BSIZE;
16073f7d54a6SGarrett D'Amore 		cinfo.dki_addr = 0;
16083f7d54a6SGarrett D'Amore 		cinfo.dki_slave = 0;
16093f7d54a6SGarrett D'Amore 		cinfo.dki_space = 0;
16103f7d54a6SGarrett D'Amore 		cinfo.dki_prio = 0;
16113f7d54a6SGarrett D'Amore 		cinfo.dki_vec = 0;
16123f7d54a6SGarrett D'Amore 		if (ddi_copyout(&cinfo, ptr, sizeof (cinfo), flag)) {
16133f7d54a6SGarrett D'Amore 			return (EFAULT);
16143f7d54a6SGarrett D'Amore 		}
16153f7d54a6SGarrett D'Amore 		return (0);
16163f7d54a6SGarrett D'Amore 	}
16173f7d54a6SGarrett D'Amore 	case DKIOCREMOVABLE: {
16183f7d54a6SGarrett D'Amore 		int i;
16193f7d54a6SGarrett D'Amore 		i = bd->d_removable ? 1 : 0;
16203f7d54a6SGarrett D'Amore 		if (ddi_copyout(&i, ptr, sizeof (i), flag)) {
16213f7d54a6SGarrett D'Amore 			return (EFAULT);
16223f7d54a6SGarrett D'Amore 		}
16233f7d54a6SGarrett D'Amore 		return (0);
16243f7d54a6SGarrett D'Amore 	}
16253f7d54a6SGarrett D'Amore 	case DKIOCHOTPLUGGABLE: {
16263f7d54a6SGarrett D'Amore 		int i;
16273f7d54a6SGarrett D'Amore 		i = bd->d_hotpluggable ? 1 : 0;
16283f7d54a6SGarrett D'Amore 		if (ddi_copyout(&i, ptr, sizeof (i), flag)) {
16293f7d54a6SGarrett D'Amore 			return (EFAULT);
16303f7d54a6SGarrett D'Amore 		}
16313f7d54a6SGarrett D'Amore 		return (0);
16323f7d54a6SGarrett D'Amore 	}
16333f7d54a6SGarrett D'Amore 	case DKIOCREADONLY: {
16343f7d54a6SGarrett D'Amore 		int i;
16353f7d54a6SGarrett D'Amore 		i = bd->d_rdonly ? 1 : 0;
16363f7d54a6SGarrett D'Amore 		if (ddi_copyout(&i, ptr, sizeof (i), flag)) {
16373f7d54a6SGarrett D'Amore 			return (EFAULT);
16383f7d54a6SGarrett D'Amore 		}
16393f7d54a6SGarrett D'Amore 		return (0);
16403f7d54a6SGarrett D'Amore 	}
164159d8f100SGarrett D'Amore 	case DKIOCSOLIDSTATE: {
164259d8f100SGarrett D'Amore 		int i;
164359d8f100SGarrett D'Amore 		i = bd->d_ssd ? 1 : 0;
164459d8f100SGarrett D'Amore 		if (ddi_copyout(&i, ptr, sizeof (i), flag)) {
164559d8f100SGarrett D'Amore 			return (EFAULT);
164659d8f100SGarrett D'Amore 		}
164759d8f100SGarrett D'Amore 		return (0);
164859d8f100SGarrett D'Amore 	}
16493f7d54a6SGarrett D'Amore 	case DKIOCSTATE: {
16503f7d54a6SGarrett D'Amore 		enum dkio_state	state;
16513f7d54a6SGarrett D'Amore 		if (ddi_copyin(ptr, &state, sizeof (state), flag)) {
16523f7d54a6SGarrett D'Amore 			return (EFAULT);
16533f7d54a6SGarrett D'Amore 		}
16543f7d54a6SGarrett D'Amore 		if ((rv = bd_check_state(bd, &state)) != 0) {
16553f7d54a6SGarrett D'Amore 			return (rv);
16563f7d54a6SGarrett D'Amore 		}
16573f7d54a6SGarrett D'Amore 		if (ddi_copyout(&state, ptr, sizeof (state), flag)) {
16583f7d54a6SGarrett D'Amore 			return (EFAULT);
16593f7d54a6SGarrett D'Amore 		}
16603f7d54a6SGarrett D'Amore 		return (0);
16613f7d54a6SGarrett D'Amore 	}
16623f7d54a6SGarrett D'Amore 	case DKIOCFLUSHWRITECACHE: {
1663f097ef9cSAlexey Zaytsev 		struct dk_callback *dkc = NULL;
16643f7d54a6SGarrett D'Amore 
1665f097ef9cSAlexey Zaytsev 		if (flag & FKIOCTL)
1666f097ef9cSAlexey Zaytsev 			dkc = (void *)arg;
1667f097ef9cSAlexey Zaytsev 
16683f7d54a6SGarrett D'Amore 		rv = bd_flush_write_cache(bd, dkc);
16693f7d54a6SGarrett D'Amore 		return (rv);
16703f7d54a6SGarrett D'Amore 	}
16711a5ae140SJason King 	case DKIOCFREE: {
16721a5ae140SJason King 		dkioc_free_list_t *dfl = NULL;
16731a5ae140SJason King 
16741a5ae140SJason King 		/*
16751a5ae140SJason King 		 * Check free space support early to avoid copyin/allocation
16761a5ae140SJason King 		 * when unnecessary.
16771a5ae140SJason King 		 */
16781a5ae140SJason King 		if (!CAN_FREESPACE(bd))
16791a5ae140SJason King 			return (ENOTSUP);
16801a5ae140SJason King 
16811a5ae140SJason King 		rv = dfl_copyin(ptr, &dfl, flag, KM_SLEEP);
16821a5ae140SJason King 		if (rv != 0)
16831a5ae140SJason King 			return (rv);
16841a5ae140SJason King 
16851a5ae140SJason King 		/*
16861a5ae140SJason King 		 * bd_free_space() consumes 'dfl'. bd_free_space() will
16871a5ae140SJason King 		 * call dfl_iter() which will normally try to pass dfl through
16881a5ae140SJason King 		 * to bd_free_space_cb() which attaches dfl to the bd_xfer_t
16891a5ae140SJason King 		 * that is then queued for the underlying driver. Once the
16901a5ae140SJason King 		 * driver processes the request, the bd_xfer_t instance is
16911a5ae140SJason King 		 * disposed of, including any attached dkioc_free_list_t.
16921a5ae140SJason King 		 *
16931a5ae140SJason King 		 * If dfl cannot be processed by the underlying driver due to
16941a5ae140SJason King 		 * size or alignment requirements of the driver, dfl_iter()
16951a5ae140SJason King 		 * will replace dfl with one or more new dkioc_free_list_t
16961a5ae140SJason King 		 * instances with the correct alignment and sizes for the driver
16971a5ae140SJason King 		 * (and free the original dkioc_free_list_t).
16981a5ae140SJason King 		 */
16991a5ae140SJason King 		rv = bd_free_space(dev, bd, dfl);
17001a5ae140SJason King 		return (rv);
17011a5ae140SJason King 	}
17021a5ae140SJason King 
17031a5ae140SJason King 	case DKIOC_CANFREE: {
17041a5ae140SJason King 		boolean_t supported = CAN_FREESPACE(bd);
17051a5ae140SJason King 
17061a5ae140SJason King 		if (ddi_copyout(&supported, (void *)arg, sizeof (supported),
17071a5ae140SJason King 		    flag) != 0) {
17081a5ae140SJason King 			return (EFAULT);
17091a5ae140SJason King 		}
17101a5ae140SJason King 
17111a5ae140SJason King 		return (0);
17121a5ae140SJason King 	}
17133f7d54a6SGarrett D'Amore 
17143f7d54a6SGarrett D'Amore 	default:
17153f7d54a6SGarrett D'Amore 		break;
17163f7d54a6SGarrett D'Amore 
17173f7d54a6SGarrett D'Amore 	}
17183f7d54a6SGarrett D'Amore 	return (ENOTTY);
17193f7d54a6SGarrett D'Amore }
17203f7d54a6SGarrett D'Amore 
17213f7d54a6SGarrett D'Amore static int
bd_prop_op(dev_t dev,dev_info_t * dip,ddi_prop_op_t prop_op,int mod_flags,char * name,caddr_t valuep,int * lengthp)17223f7d54a6SGarrett D'Amore bd_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
17233f7d54a6SGarrett D'Amore     char *name, caddr_t valuep, int *lengthp)
17243f7d54a6SGarrett D'Amore {
17253f7d54a6SGarrett D'Amore 	bd_t	*bd;
17263f7d54a6SGarrett D'Amore 
17273f7d54a6SGarrett D'Amore 	bd = ddi_get_soft_state(bd_state, ddi_get_instance(dip));
17283f7d54a6SGarrett D'Amore 	if (bd == NULL)
17293f7d54a6SGarrett D'Amore 		return (ddi_prop_op(dev, dip, prop_op, mod_flags,
17303f7d54a6SGarrett D'Amore 		    name, valuep, lengthp));
17313f7d54a6SGarrett D'Amore 
17323f7d54a6SGarrett D'Amore 	return (cmlb_prop_op(bd->d_cmlbh, dev, dip, prop_op, mod_flags, name,
173386e3bca6SGarrett D'Amore 	    valuep, lengthp, BDPART(dev), 0));
17343f7d54a6SGarrett D'Amore }
17353f7d54a6SGarrett D'Amore 
17363f7d54a6SGarrett D'Amore 
17373f7d54a6SGarrett D'Amore static int
bd_tg_rdwr(dev_info_t * dip,uchar_t cmd,void * bufaddr,diskaddr_t start,size_t length,void * tg_cookie)17383f7d54a6SGarrett D'Amore bd_tg_rdwr(dev_info_t *dip, uchar_t cmd, void *bufaddr, diskaddr_t start,
17393f7d54a6SGarrett D'Amore     size_t length, void *tg_cookie)
17403f7d54a6SGarrett D'Amore {
17413f7d54a6SGarrett D'Amore 	bd_t		*bd;
17423f7d54a6SGarrett D'Amore 	buf_t		*bp;
17433f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
17443f7d54a6SGarrett D'Amore 	int		rv;
17453f7d54a6SGarrett D'Amore 	int		(*func)(void *, bd_xfer_t *);
174686e3bca6SGarrett D'Amore 	int		kmflag;
17473f7d54a6SGarrett D'Amore 
174886e3bca6SGarrett D'Amore 	/*
174986e3bca6SGarrett D'Amore 	 * If we are running in polled mode (such as during dump(9e)
175086e3bca6SGarrett D'Amore 	 * execution), then we cannot sleep for kernel allocations.
175186e3bca6SGarrett D'Amore 	 */
175286e3bca6SGarrett D'Amore 	kmflag = tg_cookie ? KM_NOSLEEP : KM_SLEEP;
17533f7d54a6SGarrett D'Amore 
175486e3bca6SGarrett D'Amore 	bd = ddi_get_soft_state(bd_state, ddi_get_instance(dip));
17553f7d54a6SGarrett D'Amore 
175604904ca2SDan McDonald 	if (P2PHASE(length, (1U << bd->d_blkshift)) != 0) {
17573f7d54a6SGarrett D'Amore 		/* We can only transfer whole blocks at a time! */
17583f7d54a6SGarrett D'Amore 		return (EINVAL);
17593f7d54a6SGarrett D'Amore 	}
17603f7d54a6SGarrett D'Amore 
176186e3bca6SGarrett D'Amore 	if ((bp = getrbuf(kmflag)) == NULL) {
176286e3bca6SGarrett D'Amore 		return (ENOMEM);
176386e3bca6SGarrett D'Amore 	}
17643f7d54a6SGarrett D'Amore 
17653f7d54a6SGarrett D'Amore 	switch (cmd) {
17663f7d54a6SGarrett D'Amore 	case TG_READ:
17673f7d54a6SGarrett D'Amore 		bp->b_flags = B_READ;
17683f7d54a6SGarrett D'Amore 		func = bd->d_ops.o_read;
17693f7d54a6SGarrett D'Amore 		break;
17703f7d54a6SGarrett D'Amore 	case TG_WRITE:
17713f7d54a6SGarrett D'Amore 		bp->b_flags = B_WRITE;
17723f7d54a6SGarrett D'Amore 		func = bd->d_ops.o_write;
17733f7d54a6SGarrett D'Amore 		break;
17743f7d54a6SGarrett D'Amore 	default:
17753f7d54a6SGarrett D'Amore 		freerbuf(bp);
17763f7d54a6SGarrett D'Amore 		return (EINVAL);
17773f7d54a6SGarrett D'Amore 	}
17783f7d54a6SGarrett D'Amore 
17793f7d54a6SGarrett D'Amore 	bp->b_un.b_addr = bufaddr;
17803f7d54a6SGarrett D'Amore 	bp->b_bcount = length;
178186e3bca6SGarrett D'Amore 	xi = bd_xfer_alloc(bd, bp, func, kmflag);
17823f7d54a6SGarrett D'Amore 	if (xi == NULL) {
17833f7d54a6SGarrett D'Amore 		rv = geterror(bp);
17843f7d54a6SGarrett D'Amore 		freerbuf(bp);
17853f7d54a6SGarrett D'Amore 		return (rv);
17863f7d54a6SGarrett D'Amore 	}
178786e3bca6SGarrett D'Amore 	xi->i_flags = tg_cookie ? BD_XFER_POLL : 0;
17883f7d54a6SGarrett D'Amore 	xi->i_blkno = start;
17893f7d54a6SGarrett D'Amore 	bd_submit(bd, xi);
17903f7d54a6SGarrett D'Amore 	(void) biowait(bp);
17913f7d54a6SGarrett D'Amore 	rv = geterror(bp);
17923f7d54a6SGarrett D'Amore 	freerbuf(bp);
17933f7d54a6SGarrett D'Amore 
17943f7d54a6SGarrett D'Amore 	return (rv);
17953f7d54a6SGarrett D'Amore }
17963f7d54a6SGarrett D'Amore 
17973f7d54a6SGarrett D'Amore static int
bd_tg_getinfo(dev_info_t * dip,int cmd,void * arg,void * tg_cookie)17983f7d54a6SGarrett D'Amore bd_tg_getinfo(dev_info_t *dip, int cmd, void *arg, void *tg_cookie)
17993f7d54a6SGarrett D'Amore {
18003f7d54a6SGarrett D'Amore 	bd_t		*bd;
18013f7d54a6SGarrett D'Amore 
180286e3bca6SGarrett D'Amore 	_NOTE(ARGUNUSED(tg_cookie));
180386e3bca6SGarrett D'Amore 	bd = ddi_get_soft_state(bd_state, ddi_get_instance(dip));
18043f7d54a6SGarrett D'Amore 
18053f7d54a6SGarrett D'Amore 	switch (cmd) {
18063f7d54a6SGarrett D'Amore 	case TG_GETPHYGEOM:
18073f7d54a6SGarrett D'Amore 	case TG_GETVIRTGEOM:
18083f7d54a6SGarrett D'Amore 		/*
18093f7d54a6SGarrett D'Amore 		 * We don't have any "geometry" as such, let cmlb
18103f7d54a6SGarrett D'Amore 		 * fabricate something.
18113f7d54a6SGarrett D'Amore 		 */
18123f7d54a6SGarrett D'Amore 		return (ENOTTY);
18133f7d54a6SGarrett D'Amore 
18143f7d54a6SGarrett D'Amore 	case TG_GETCAPACITY:
18153f7d54a6SGarrett D'Amore 		bd_update_state(bd);
18163f7d54a6SGarrett D'Amore 		*(diskaddr_t *)arg = bd->d_numblks;
18173f7d54a6SGarrett D'Amore 		return (0);
18183f7d54a6SGarrett D'Amore 
18193f7d54a6SGarrett D'Amore 	case TG_GETBLOCKSIZE:
18203f7d54a6SGarrett D'Amore 		*(uint32_t *)arg = (1U << bd->d_blkshift);
18213f7d54a6SGarrett D'Amore 		return (0);
18223f7d54a6SGarrett D'Amore 
18233f7d54a6SGarrett D'Amore 	case TG_GETATTR:
18243f7d54a6SGarrett D'Amore 		/*
18253f7d54a6SGarrett D'Amore 		 * It turns out that cmlb really doesn't do much for
18263f7d54a6SGarrett D'Amore 		 * non-writable media, but lets make the information
18273f7d54a6SGarrett D'Amore 		 * available for it in case it does more in the
18283f7d54a6SGarrett D'Amore 		 * future.  (The value is currently used for
18293f7d54a6SGarrett D'Amore 		 * triggering special behavior for CD-ROMs.)
18303f7d54a6SGarrett D'Amore 		 */
18313f7d54a6SGarrett D'Amore 		bd_update_state(bd);
18323f7d54a6SGarrett D'Amore 		((tg_attribute_t *)arg)->media_is_writable =
18333f7d54a6SGarrett D'Amore 		    bd->d_rdonly ? B_FALSE : B_TRUE;
183459d8f100SGarrett D'Amore 		((tg_attribute_t *)arg)->media_is_solid_state = bd->d_ssd;
1835acb450ddSYuri Pankov 		((tg_attribute_t *)arg)->media_is_rotational = B_FALSE;
18363f7d54a6SGarrett D'Amore 		return (0);
18373f7d54a6SGarrett D'Amore 
18383f7d54a6SGarrett D'Amore 	default:
18393f7d54a6SGarrett D'Amore 		return (EINVAL);
18403f7d54a6SGarrett D'Amore 	}
18413f7d54a6SGarrett D'Amore }
18423f7d54a6SGarrett D'Amore 
18433f7d54a6SGarrett D'Amore 
18443f7d54a6SGarrett D'Amore static void
bd_sched(bd_t * bd,bd_queue_t * bq)18454d95620bSPaul Winder bd_sched(bd_t *bd, bd_queue_t *bq)
18463f7d54a6SGarrett D'Amore {
18473f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi;
18483f7d54a6SGarrett D'Amore 	struct buf	*bp;
18493f7d54a6SGarrett D'Amore 	int		rv;
18503f7d54a6SGarrett D'Amore 
18514d95620bSPaul Winder 	mutex_enter(&bq->q_iomutex);
18523f7d54a6SGarrett D'Amore 
18534d95620bSPaul Winder 	while ((bq->q_qactive < bq->q_qsize) &&
18544d95620bSPaul Winder 	    ((xi = list_remove_head(&bq->q_waitq)) != NULL)) {
185519687f06SPaul Winder 		mutex_enter(&bd->d_ksmutex);
185619687f06SPaul Winder 		kstat_waitq_to_runq(bd->d_kiop);
185719687f06SPaul Winder 		mutex_exit(&bd->d_ksmutex);
185819687f06SPaul Winder 
18594d95620bSPaul Winder 		bq->q_qactive++;
18604d95620bSPaul Winder 		list_insert_tail(&bq->q_runq, xi);
18613f7d54a6SGarrett D'Amore 
186286e3bca6SGarrett D'Amore 		/*
186386e3bca6SGarrett D'Amore 		 * Submit the job to the driver.  We drop the I/O mutex
186486e3bca6SGarrett D'Amore 		 * so that we can deal with the case where the driver
186586e3bca6SGarrett D'Amore 		 * completion routine calls back into us synchronously.
186686e3bca6SGarrett D'Amore 		 */
18673f7d54a6SGarrett D'Amore 
18684d95620bSPaul Winder 		mutex_exit(&bq->q_iomutex);
18694d95620bSPaul Winder 
187086e3bca6SGarrett D'Amore 		rv = xi->i_func(bd->d_private, &xi->i_public);
187186e3bca6SGarrett D'Amore 		if (rv != 0) {
18723f7d54a6SGarrett D'Amore 			bp = xi->i_bp;
18733f7d54a6SGarrett D'Amore 			bioerror(bp, rv);
18743f7d54a6SGarrett D'Amore 			biodone(bp);
187586e3bca6SGarrett D'Amore 
1876bef9e21aSHans Rosenfeld 			atomic_inc_32(&bd->d_kerr->bd_transerrs.value.ui32);
187719687f06SPaul Winder 
187819687f06SPaul Winder 			mutex_enter(&bq->q_iomutex);
187919687f06SPaul Winder 
18804d95620bSPaul Winder 			mutex_enter(&bd->d_ksmutex);
188186e3bca6SGarrett D'Amore 			kstat_runq_exit(bd->d_kiop);
18824d95620bSPaul Winder 			mutex_exit(&bd->d_ksmutex);
18834d95620bSPaul Winder 
18844d95620bSPaul Winder 			bq->q_qactive--;
18854d95620bSPaul Winder 			list_remove(&bq->q_runq, xi);
1886dd3928f8SHans Rosenfeld 			bd_xfer_free(xi);
188786e3bca6SGarrett D'Amore 		} else {
18884d95620bSPaul Winder 			mutex_enter(&bq->q_iomutex);
18893f7d54a6SGarrett D'Amore 		}
18903f7d54a6SGarrett D'Amore 	}
189186e3bca6SGarrett D'Amore 
18924d95620bSPaul Winder 	mutex_exit(&bq->q_iomutex);
18933f7d54a6SGarrett D'Amore }
18943f7d54a6SGarrett D'Amore 
18953f7d54a6SGarrett D'Amore static void
bd_submit(bd_t * bd,bd_xfer_impl_t * xi)18963f7d54a6SGarrett D'Amore bd_submit(bd_t *bd, bd_xfer_impl_t *xi)
18973f7d54a6SGarrett D'Amore {
18984d95620bSPaul Winder 	uint64_t	nv = atomic_inc_64_nv(&bd->d_io_counter);
18994d95620bSPaul Winder 	unsigned	q = nv % bd->d_qcount;
19004d95620bSPaul Winder 	bd_queue_t	*bq = &bd->d_queues[q];
190186e3bca6SGarrett D'Amore 
19024d95620bSPaul Winder 	xi->i_bq = bq;
19034d95620bSPaul Winder 	xi->i_qnum = q;
19044d95620bSPaul Winder 
19054d95620bSPaul Winder 	mutex_enter(&bq->q_iomutex);
190619687f06SPaul Winder 
19074d95620bSPaul Winder 	list_insert_tail(&bq->q_waitq, xi);
19084d95620bSPaul Winder 
19094d95620bSPaul Winder 	mutex_enter(&bd->d_ksmutex);
19104d95620bSPaul Winder 	kstat_waitq_enter(bd->d_kiop);
19114d95620bSPaul Winder 	mutex_exit(&bd->d_ksmutex);
19124d95620bSPaul Winder 
191319687f06SPaul Winder 	mutex_exit(&bq->q_iomutex);
191419687f06SPaul Winder 
19154d95620bSPaul Winder 	bd_sched(bd, bq);
19163f7d54a6SGarrett D'Amore }
19173f7d54a6SGarrett D'Amore 
19183f7d54a6SGarrett D'Amore static void
bd_runq_exit(bd_xfer_impl_t * xi,int err)19193f7d54a6SGarrett D'Amore bd_runq_exit(bd_xfer_impl_t *xi, int err)
19203f7d54a6SGarrett D'Amore {
19213f7d54a6SGarrett D'Amore 	bd_t		*bd = xi->i_bd;
19223f7d54a6SGarrett D'Amore 	buf_t		*bp = xi->i_bp;
19234d95620bSPaul Winder 	bd_queue_t	*bq = xi->i_bq;
19243f7d54a6SGarrett D'Amore 
19254d95620bSPaul Winder 	mutex_enter(&bq->q_iomutex);
19264d95620bSPaul Winder 	bq->q_qactive--;
19274d95620bSPaul Winder 
19284d95620bSPaul Winder 	mutex_enter(&bd->d_ksmutex);
19293f7d54a6SGarrett D'Amore 	kstat_runq_exit(bd->d_kiop);
19304d95620bSPaul Winder 	mutex_exit(&bd->d_ksmutex);
193186e3bca6SGarrett D'Amore 
193219687f06SPaul Winder 	list_remove(&bq->q_runq, xi);
193319687f06SPaul Winder 	mutex_exit(&bq->q_iomutex);
193419687f06SPaul Winder 
19353f7d54a6SGarrett D'Amore 	if (err == 0) {
19363f7d54a6SGarrett D'Amore 		if (bp->b_flags & B_READ) {
19374d95620bSPaul Winder 			atomic_inc_uint(&bd->d_kiop->reads);
19384d95620bSPaul Winder 			atomic_add_64((uint64_t *)&bd->d_kiop->nread,
19394d95620bSPaul Winder 			    bp->b_bcount - xi->i_resid);
19403f7d54a6SGarrett D'Amore 		} else {
19414d95620bSPaul Winder 			atomic_inc_uint(&bd->d_kiop->writes);
19424d95620bSPaul Winder 			atomic_add_64((uint64_t *)&bd->d_kiop->nwritten,
19434d95620bSPaul Winder 			    bp->b_bcount - xi->i_resid);
19443f7d54a6SGarrett D'Amore 		}
19453f7d54a6SGarrett D'Amore 	}
19464d95620bSPaul Winder 	bd_sched(bd, bq);
19473f7d54a6SGarrett D'Amore }
19483f7d54a6SGarrett D'Amore 
19493f7d54a6SGarrett D'Amore static void
bd_dle_sysevent_task(void * arg)19506f0e4dc9SAndy Fiddaman bd_dle_sysevent_task(void *arg)
19516f0e4dc9SAndy Fiddaman {
19526f0e4dc9SAndy Fiddaman 	nvlist_t *attr = NULL;
19536f0e4dc9SAndy Fiddaman 	char *path = NULL;
19546f0e4dc9SAndy Fiddaman 	bd_t *bd = arg;
19556f0e4dc9SAndy Fiddaman 	dev_info_t *dip = bd->d_dip;
19566f0e4dc9SAndy Fiddaman 	size_t n;
19576f0e4dc9SAndy Fiddaman 
19586f0e4dc9SAndy Fiddaman 	mutex_enter(&bd->d_dle_mutex);
19596f0e4dc9SAndy Fiddaman 	bd->d_dle_state &= ~BD_DLE_PENDING;
19606f0e4dc9SAndy Fiddaman 	bd->d_dle_state |= BD_DLE_RUNNING;
19616f0e4dc9SAndy Fiddaman 	mutex_exit(&bd->d_dle_mutex);
19626f0e4dc9SAndy Fiddaman 
19636f0e4dc9SAndy Fiddaman 	dev_err(dip, CE_NOTE, "!dynamic LUN expansion");
19646f0e4dc9SAndy Fiddaman 
19656f0e4dc9SAndy Fiddaman 	if (nvlist_alloc(&attr, NV_UNIQUE_NAME_TYPE, KM_SLEEP) != 0) {
19666f0e4dc9SAndy Fiddaman 		mutex_enter(&bd->d_dle_mutex);
19676f0e4dc9SAndy Fiddaman 		bd->d_dle_state &= ~(BD_DLE_RUNNING|BD_DLE_PENDING);
19686f0e4dc9SAndy Fiddaman 		mutex_exit(&bd->d_dle_mutex);
19696f0e4dc9SAndy Fiddaman 		return;
19706f0e4dc9SAndy Fiddaman 	}
19716f0e4dc9SAndy Fiddaman 
19726f0e4dc9SAndy Fiddaman 	path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
19736f0e4dc9SAndy Fiddaman 
19746f0e4dc9SAndy Fiddaman 	n = snprintf(path, MAXPATHLEN, "/devices");
19756f0e4dc9SAndy Fiddaman 	(void) ddi_pathname(dip, path + n);
19766f0e4dc9SAndy Fiddaman 	n = strlen(path);
19776f0e4dc9SAndy Fiddaman 	n += snprintf(path + n, MAXPATHLEN - n, ":x");
19786f0e4dc9SAndy Fiddaman 
19796f0e4dc9SAndy Fiddaman 	for (;;) {
19806f0e4dc9SAndy Fiddaman 		/*
19816f0e4dc9SAndy Fiddaman 		 * On receipt of this event, the ZFS sysevent module will scan
19826f0e4dc9SAndy Fiddaman 		 * active zpools for child vdevs matching this physical path.
19836f0e4dc9SAndy Fiddaman 		 * In order to catch both whole disk pools and those with an
19846f0e4dc9SAndy Fiddaman 		 * EFI boot partition, generate separate sysevents for minor
1985f7457249SAndy Fiddaman 		 * node 'a' and 'b'.
19866f0e4dc9SAndy Fiddaman 		 */
19876f0e4dc9SAndy Fiddaman 		for (char c = 'a'; c < 'c'; c++) {
19886f0e4dc9SAndy Fiddaman 			path[n - 1] = c;
19896f0e4dc9SAndy Fiddaman 
19906f0e4dc9SAndy Fiddaman 			if (nvlist_add_string(attr, DEV_PHYS_PATH, path) != 0)
19916f0e4dc9SAndy Fiddaman 				break;
19926f0e4dc9SAndy Fiddaman 
19936f0e4dc9SAndy Fiddaman 			(void) ddi_log_sysevent(dip, DDI_VENDOR_SUNW,
19946f0e4dc9SAndy Fiddaman 			    EC_DEV_STATUS, ESC_DEV_DLE, attr, NULL, DDI_SLEEP);
19956f0e4dc9SAndy Fiddaman 		}
19966f0e4dc9SAndy Fiddaman 
19976f0e4dc9SAndy Fiddaman 		mutex_enter(&bd->d_dle_mutex);
19986f0e4dc9SAndy Fiddaman 		if ((bd->d_dle_state & BD_DLE_PENDING) == 0) {
19996f0e4dc9SAndy Fiddaman 			bd->d_dle_state &= ~BD_DLE_RUNNING;
20006f0e4dc9SAndy Fiddaman 			mutex_exit(&bd->d_dle_mutex);
20016f0e4dc9SAndy Fiddaman 			break;
20026f0e4dc9SAndy Fiddaman 		}
20036f0e4dc9SAndy Fiddaman 		bd->d_dle_state &= ~BD_DLE_PENDING;
20046f0e4dc9SAndy Fiddaman 		mutex_exit(&bd->d_dle_mutex);
20056f0e4dc9SAndy Fiddaman 	}
20066f0e4dc9SAndy Fiddaman 
20076f0e4dc9SAndy Fiddaman 	nvlist_free(attr);
20086f0e4dc9SAndy Fiddaman 	kmem_free(path, MAXPATHLEN);
20096f0e4dc9SAndy Fiddaman }
20106f0e4dc9SAndy Fiddaman 
20116f0e4dc9SAndy Fiddaman static void
bd_update_state(bd_t * bd)20123f7d54a6SGarrett D'Amore bd_update_state(bd_t *bd)
20133f7d54a6SGarrett D'Amore {
201432ce6b81SHans Rosenfeld 	enum	dkio_state	state = DKIO_INSERTED;
20153f7d54a6SGarrett D'Amore 	boolean_t		docmlb = B_FALSE;
201632ce6b81SHans Rosenfeld 	bd_media_t		media;
20173f7d54a6SGarrett D'Amore 
20183f7d54a6SGarrett D'Amore 	bzero(&media, sizeof (media));
20193f7d54a6SGarrett D'Amore 
20203f7d54a6SGarrett D'Amore 	mutex_enter(&bd->d_statemutex);
202132ce6b81SHans Rosenfeld 	if (bd->d_ops.o_media_info(bd->d_private, &media) != 0) {
202232ce6b81SHans Rosenfeld 		bd->d_numblks = 0;
202332ce6b81SHans Rosenfeld 		state = DKIO_EJECTED;
202432ce6b81SHans Rosenfeld 		goto done;
202532ce6b81SHans Rosenfeld 	}
202632ce6b81SHans Rosenfeld 
20273f7d54a6SGarrett D'Amore 	if ((media.m_blksize < 512) ||
20283f7d54a6SGarrett D'Amore 	    (!ISP2(media.m_blksize)) ||
20293f7d54a6SGarrett D'Amore 	    (P2PHASE(bd->d_maxxfer, media.m_blksize))) {
20306f0e4dc9SAndy Fiddaman 		dev_err(bd->d_dip, CE_WARN, "Invalid media block size (%d)",
20313f7d54a6SGarrett D'Amore 		    media.m_blksize);
20323f7d54a6SGarrett D'Amore 		/*
203332ce6b81SHans Rosenfeld 		 * We can't use the media, treat it as not present.
20343f7d54a6SGarrett D'Amore 		 */
20353f7d54a6SGarrett D'Amore 		state = DKIO_EJECTED;
20363f7d54a6SGarrett D'Amore 		bd->d_numblks = 0;
203732ce6b81SHans Rosenfeld 		goto done;
203832ce6b81SHans Rosenfeld 	}
203932ce6b81SHans Rosenfeld 
204032ce6b81SHans Rosenfeld 	if (((1U << bd->d_blkshift) != media.m_blksize) ||
204132ce6b81SHans Rosenfeld 	    (bd->d_numblks != media.m_nblks)) {
204232ce6b81SHans Rosenfeld 		/* Device size changed */
204332ce6b81SHans Rosenfeld 		docmlb = B_TRUE;
204432ce6b81SHans Rosenfeld 	}
204532ce6b81SHans Rosenfeld 
20463f7d54a6SGarrett D'Amore 	bd->d_blkshift = ddi_ffs(media.m_blksize) - 1;
204732ce6b81SHans Rosenfeld 	bd->d_pblkshift = bd->d_blkshift;
20483f7d54a6SGarrett D'Amore 	bd->d_numblks = media.m_nblks;
20493f7d54a6SGarrett D'Amore 	bd->d_rdonly = media.m_readonly;
205059d8f100SGarrett D'Amore 	bd->d_ssd = media.m_solidstate;
20513f7d54a6SGarrett D'Amore 
205232ce6b81SHans Rosenfeld 	/*
205332ce6b81SHans Rosenfeld 	 * Only use the supplied physical block size if it is non-zero,
205432ce6b81SHans Rosenfeld 	 * greater or equal to the block size, and a power of 2. Ignore it
205532ce6b81SHans Rosenfeld 	 * if not, it's just informational and we can still use the media.
205632ce6b81SHans Rosenfeld 	 */
205732ce6b81SHans Rosenfeld 	if ((media.m_pblksize != 0) &&
205832ce6b81SHans Rosenfeld 	    (media.m_pblksize >= media.m_blksize) &&
205932ce6b81SHans Rosenfeld 	    (ISP2(media.m_pblksize)))
206032ce6b81SHans Rosenfeld 		bd->d_pblkshift = ddi_ffs(media.m_pblksize) - 1;
20613f7d54a6SGarrett D'Amore 
206232ce6b81SHans Rosenfeld done:
20633f7d54a6SGarrett D'Amore 	if (state != bd->d_state) {
20643f7d54a6SGarrett D'Amore 		bd->d_state = state;
20653f7d54a6SGarrett D'Amore 		cv_broadcast(&bd->d_statecv);
20663f7d54a6SGarrett D'Amore 		docmlb = B_TRUE;
20673f7d54a6SGarrett D'Amore 	}
20683f7d54a6SGarrett D'Amore 	mutex_exit(&bd->d_statemutex);
20693f7d54a6SGarrett D'Amore 
2070bef9e21aSHans Rosenfeld 	bd->d_kerr->bd_capacity.value.ui64 = bd->d_numblks << bd->d_blkshift;
2071bef9e21aSHans Rosenfeld 
20723f7d54a6SGarrett D'Amore 	if (docmlb) {
20733f7d54a6SGarrett D'Amore 		if (state == DKIO_INSERTED) {
207486e3bca6SGarrett D'Amore 			(void) cmlb_validate(bd->d_cmlbh, 0, 0);
20756f0e4dc9SAndy Fiddaman 
20766f0e4dc9SAndy Fiddaman 			mutex_enter(&bd->d_dle_mutex);
20776f0e4dc9SAndy Fiddaman 			/*
20786f0e4dc9SAndy Fiddaman 			 * If there is already an event pending, there's
20796f0e4dc9SAndy Fiddaman 			 * nothing to do; we coalesce multiple events.
20806f0e4dc9SAndy Fiddaman 			 */
20816f0e4dc9SAndy Fiddaman 			if ((bd->d_dle_state & BD_DLE_PENDING) == 0) {
20826f0e4dc9SAndy Fiddaman 				if ((bd->d_dle_state & BD_DLE_RUNNING) == 0) {
20836f0e4dc9SAndy Fiddaman 					taskq_dispatch_ent(bd_taskq,
20846f0e4dc9SAndy Fiddaman 					    bd_dle_sysevent_task, bd, 0,
20856f0e4dc9SAndy Fiddaman 					    &bd->d_dle_ent);
20866f0e4dc9SAndy Fiddaman 				}
20876f0e4dc9SAndy Fiddaman 				bd->d_dle_state |= BD_DLE_PENDING;
20886f0e4dc9SAndy Fiddaman 			}
20896f0e4dc9SAndy Fiddaman 			mutex_exit(&bd->d_dle_mutex);
20903f7d54a6SGarrett D'Amore 		} else {
209186e3bca6SGarrett D'Amore 			cmlb_invalidate(bd->d_cmlbh, 0);
20923f7d54a6SGarrett D'Amore 		}
20933f7d54a6SGarrett D'Amore 	}
20943f7d54a6SGarrett D'Amore }
20953f7d54a6SGarrett D'Amore 
20963f7d54a6SGarrett D'Amore static int
bd_check_state(bd_t * bd,enum dkio_state * state)20973f7d54a6SGarrett D'Amore bd_check_state(bd_t *bd, enum dkio_state *state)
20983f7d54a6SGarrett D'Amore {
20993f7d54a6SGarrett D'Amore 	clock_t		when;
21003f7d54a6SGarrett D'Amore 
21013f7d54a6SGarrett D'Amore 	for (;;) {
21023f7d54a6SGarrett D'Amore 
21033f7d54a6SGarrett D'Amore 		bd_update_state(bd);
21043f7d54a6SGarrett D'Amore 
21053f7d54a6SGarrett D'Amore 		mutex_enter(&bd->d_statemutex);
21063f7d54a6SGarrett D'Amore 
21073f7d54a6SGarrett D'Amore 		if (bd->d_state != *state) {
21083f7d54a6SGarrett D'Amore 			*state = bd->d_state;
21093f7d54a6SGarrett D'Amore 			mutex_exit(&bd->d_statemutex);
21103f7d54a6SGarrett D'Amore 			break;
21113f7d54a6SGarrett D'Amore 		}
21123f7d54a6SGarrett D'Amore 
21133f7d54a6SGarrett D'Amore 		when = drv_usectohz(1000000);
21143f7d54a6SGarrett D'Amore 		if (cv_reltimedwait_sig(&bd->d_statecv, &bd->d_statemutex,
21153f7d54a6SGarrett D'Amore 		    when, TR_CLOCK_TICK) == 0) {
21163f7d54a6SGarrett D'Amore 			mutex_exit(&bd->d_statemutex);
21173f7d54a6SGarrett D'Amore 			return (EINTR);
21183f7d54a6SGarrett D'Amore 		}
21193f7d54a6SGarrett D'Amore 
21203f7d54a6SGarrett D'Amore 		mutex_exit(&bd->d_statemutex);
21213f7d54a6SGarrett D'Amore 	}
21223f7d54a6SGarrett D'Amore 
21233f7d54a6SGarrett D'Amore 	return (0);
21243f7d54a6SGarrett D'Amore }
21253f7d54a6SGarrett D'Amore 
21263f7d54a6SGarrett D'Amore static int
bd_flush_write_cache_done(struct buf * bp)21273f7d54a6SGarrett D'Amore bd_flush_write_cache_done(struct buf *bp)
21283f7d54a6SGarrett D'Amore {
21293f7d54a6SGarrett D'Amore 	struct dk_callback *dc = (void *)bp->b_private;
21303f7d54a6SGarrett D'Amore 
21313f7d54a6SGarrett D'Amore 	(*dc->dkc_callback)(dc->dkc_cookie, geterror(bp));
21323f7d54a6SGarrett D'Amore 	kmem_free(dc, sizeof (*dc));
21333f7d54a6SGarrett D'Amore 	freerbuf(bp);
21343f7d54a6SGarrett D'Amore 	return (0);
21353f7d54a6SGarrett D'Amore }
21363f7d54a6SGarrett D'Amore 
21373f7d54a6SGarrett D'Amore static int
bd_flush_write_cache(bd_t * bd,struct dk_callback * dkc)21383f7d54a6SGarrett D'Amore bd_flush_write_cache(bd_t *bd, struct dk_callback *dkc)
21393f7d54a6SGarrett D'Amore {
21403f7d54a6SGarrett D'Amore 	buf_t			*bp;
21413f7d54a6SGarrett D'Amore 	struct dk_callback	*dc;
21423f7d54a6SGarrett D'Amore 	bd_xfer_impl_t		*xi;
21433f7d54a6SGarrett D'Amore 	int			rv;
21443f7d54a6SGarrett D'Amore 
21453f7d54a6SGarrett D'Amore 	if (bd->d_ops.o_sync_cache == NULL) {
21463f7d54a6SGarrett D'Amore 		return (ENOTSUP);
21473f7d54a6SGarrett D'Amore 	}
21483f7d54a6SGarrett D'Amore 	if ((bp = getrbuf(KM_SLEEP)) == NULL) {
21493f7d54a6SGarrett D'Amore 		return (ENOMEM);
21503f7d54a6SGarrett D'Amore 	}
21513f7d54a6SGarrett D'Amore 	bp->b_resid = 0;
21523f7d54a6SGarrett D'Amore 	bp->b_bcount = 0;
21533f7d54a6SGarrett D'Amore 
21543f7d54a6SGarrett D'Amore 	xi = bd_xfer_alloc(bd, bp, bd->d_ops.o_sync_cache, KM_SLEEP);
21553f7d54a6SGarrett D'Amore 	if (xi == NULL) {
21563f7d54a6SGarrett D'Amore 		rv = geterror(bp);
21573f7d54a6SGarrett D'Amore 		freerbuf(bp);
21583f7d54a6SGarrett D'Amore 		return (rv);
21593f7d54a6SGarrett D'Amore 	}
21603f7d54a6SGarrett D'Amore 
2161f097ef9cSAlexey Zaytsev 	/* Make an asynchronous flush, but only if there is a callback */
2162f097ef9cSAlexey Zaytsev 	if (dkc != NULL && dkc->dkc_callback != NULL) {
21633f7d54a6SGarrett D'Amore 		/* Make a private copy of the callback structure */
21643f7d54a6SGarrett D'Amore 		dc = kmem_alloc(sizeof (*dc), KM_SLEEP);
21653f7d54a6SGarrett D'Amore 		*dc = *dkc;
21663f7d54a6SGarrett D'Amore 		bp->b_private = dc;
21673f7d54a6SGarrett D'Amore 		bp->b_iodone = bd_flush_write_cache_done;
21683f7d54a6SGarrett D'Amore 
21693f7d54a6SGarrett D'Amore 		bd_submit(bd, xi);
2170f097ef9cSAlexey Zaytsev 		return (0);
2171f097ef9cSAlexey Zaytsev 	}
2172f097ef9cSAlexey Zaytsev 
2173f097ef9cSAlexey Zaytsev 	/* In case there is no callback, perform a synchronous flush */
2174f097ef9cSAlexey Zaytsev 	bd_submit(bd, xi);
21753f7d54a6SGarrett D'Amore 	(void) biowait(bp);
21763f7d54a6SGarrett D'Amore 	rv = geterror(bp);
21773f7d54a6SGarrett D'Amore 	freerbuf(bp);
2178f097ef9cSAlexey Zaytsev 
21793f7d54a6SGarrett D'Amore 	return (rv);
21803f7d54a6SGarrett D'Amore }
21813f7d54a6SGarrett D'Amore 
21821a5ae140SJason King static int
bd_free_space_done(struct buf * bp)21831a5ae140SJason King bd_free_space_done(struct buf *bp)
21841a5ae140SJason King {
21851a5ae140SJason King 	freerbuf(bp);
21861a5ae140SJason King 	return (0);
21871a5ae140SJason King }
21881a5ae140SJason King 
21891a5ae140SJason King static int
bd_free_space_cb(dkioc_free_list_t * dfl,void * arg,int kmflag)21901a5ae140SJason King bd_free_space_cb(dkioc_free_list_t *dfl, void *arg, int kmflag)
21911a5ae140SJason King {
21921a5ae140SJason King 	bd_t		*bd = arg;
21931a5ae140SJason King 	buf_t		*bp = NULL;
21941a5ae140SJason King 	bd_xfer_impl_t	*xi = NULL;
21951a5ae140SJason King 	boolean_t	sync = DFL_ISSYNC(dfl) ?  B_TRUE : B_FALSE;
21961a5ae140SJason King 	int		rv = 0;
21971a5ae140SJason King 
21981a5ae140SJason King 	bp = getrbuf(KM_SLEEP);
21991a5ae140SJason King 	bp->b_resid = 0;
22001a5ae140SJason King 	bp->b_bcount = 0;
22011a5ae140SJason King 	bp->b_lblkno = 0;
22021a5ae140SJason King 
22031a5ae140SJason King 	xi = bd_xfer_alloc(bd, bp, bd->d_ops.o_free_space, kmflag);
22041a5ae140SJason King 	xi->i_dfl = dfl;
22051a5ae140SJason King 
22061a5ae140SJason King 	if (!sync) {
22071a5ae140SJason King 		bp->b_iodone = bd_free_space_done;
22081a5ae140SJason King 		bd_submit(bd, xi);
22091a5ae140SJason King 		return (0);
22101a5ae140SJason King 	}
22111a5ae140SJason King 
22121a5ae140SJason King 	xi->i_flags |= BD_XFER_POLL;
22131a5ae140SJason King 	bd_submit(bd, xi);
22141a5ae140SJason King 
22151a5ae140SJason King 	(void) biowait(bp);
22161a5ae140SJason King 	rv = geterror(bp);
22171a5ae140SJason King 	freerbuf(bp);
22181a5ae140SJason King 
22191a5ae140SJason King 	return (rv);
22201a5ae140SJason King }
22211a5ae140SJason King 
22221a5ae140SJason King static int
bd_free_space(dev_t dev,bd_t * bd,dkioc_free_list_t * dfl)22231a5ae140SJason King bd_free_space(dev_t dev, bd_t *bd, dkioc_free_list_t *dfl)
22241a5ae140SJason King {
22251a5ae140SJason King 	diskaddr_t p_len, p_offset;
22261a5ae140SJason King 	uint64_t offset_bytes, len_bytes;
22271a5ae140SJason King 	minor_t part = BDPART(dev);
22281a5ae140SJason King 	const uint_t bshift = bd->d_blkshift;
22291a5ae140SJason King 	dkioc_free_info_t dfi = {
22301a5ae140SJason King 		.dfi_bshift = bshift,
22311a5ae140SJason King 		.dfi_align = bd->d_free_align << bshift,
22321a5ae140SJason King 		.dfi_max_bytes = bd->d_max_free_blks << bshift,
22331a5ae140SJason King 		.dfi_max_ext = bd->d_max_free_seg,
22341a5ae140SJason King 		.dfi_max_ext_bytes = bd->d_max_free_seg_blks << bshift,
22351a5ae140SJason King 	};
22361a5ae140SJason King 
22371a5ae140SJason King 	if (cmlb_partinfo(bd->d_cmlbh, part, &p_len, &p_offset, NULL,
22381a5ae140SJason King 	    NULL, 0) != 0) {
22391a5ae140SJason King 		dfl_free(dfl);
22401a5ae140SJason King 		return (ENXIO);
22411a5ae140SJason King 	}
22421a5ae140SJason King 
22431a5ae140SJason King 	/*
22441a5ae140SJason King 	 * bd_ioctl created our own copy of dfl, so we can modify as
22451a5ae140SJason King 	 * necessary
22461a5ae140SJason King 	 */
22471a5ae140SJason King 	offset_bytes = (uint64_t)p_offset << bshift;
22481a5ae140SJason King 	len_bytes = (uint64_t)p_len << bshift;
22491a5ae140SJason King 
22501a5ae140SJason King 	dfl->dfl_offset += offset_bytes;
22511a5ae140SJason King 	if (dfl->dfl_offset < offset_bytes) {
22521a5ae140SJason King 		dfl_free(dfl);
22531a5ae140SJason King 		return (EOVERFLOW);
22541a5ae140SJason King 	}
22551a5ae140SJason King 
22561a5ae140SJason King 	return (dfl_iter(dfl, &dfi, offset_bytes + len_bytes, bd_free_space_cb,
22571a5ae140SJason King 	    bd, KM_SLEEP));
22581a5ae140SJason King }
22591a5ae140SJason King 
22603f7d54a6SGarrett D'Amore /*
22613f7d54a6SGarrett D'Amore  * Nexus support.
22623f7d54a6SGarrett D'Amore  */
22633f7d54a6SGarrett D'Amore int
bd_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)22643f7d54a6SGarrett D'Amore bd_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
22653f7d54a6SGarrett D'Amore     void *arg, void *result)
22663f7d54a6SGarrett D'Amore {
22673f7d54a6SGarrett D'Amore 	bd_handle_t	hdl;
22683f7d54a6SGarrett D'Amore 
22693f7d54a6SGarrett D'Amore 	switch (ctlop) {
22703f7d54a6SGarrett D'Amore 	case DDI_CTLOPS_REPORTDEV:
22713f7d54a6SGarrett D'Amore 		cmn_err(CE_CONT, "?Block device: %s@%s, %s%d\n",
22723f7d54a6SGarrett D'Amore 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
22733f7d54a6SGarrett D'Amore 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
22743f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
22753f7d54a6SGarrett D'Amore 
22763f7d54a6SGarrett D'Amore 	case DDI_CTLOPS_INITCHILD:
22773f7d54a6SGarrett D'Amore 		hdl = ddi_get_parent_data((dev_info_t *)arg);
22783f7d54a6SGarrett D'Amore 		if (hdl == NULL) {
22793f7d54a6SGarrett D'Amore 			return (DDI_NOT_WELL_FORMED);
22803f7d54a6SGarrett D'Amore 		}
22813f7d54a6SGarrett D'Amore 		ddi_set_name_addr((dev_info_t *)arg, hdl->h_addr);
22823f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
22833f7d54a6SGarrett D'Amore 
22843f7d54a6SGarrett D'Amore 	case DDI_CTLOPS_UNINITCHILD:
22853f7d54a6SGarrett D'Amore 		ddi_set_name_addr((dev_info_t *)arg, NULL);
22863f7d54a6SGarrett D'Amore 		ndi_prop_remove_all((dev_info_t *)arg);
22873f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
22883f7d54a6SGarrett D'Amore 
22893f7d54a6SGarrett D'Amore 	default:
22903f7d54a6SGarrett D'Amore 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
22913f7d54a6SGarrett D'Amore 	}
22923f7d54a6SGarrett D'Amore }
22933f7d54a6SGarrett D'Amore 
22943f7d54a6SGarrett D'Amore /*
22953f7d54a6SGarrett D'Amore  * Functions for device drivers.
22963f7d54a6SGarrett D'Amore  */
22973f7d54a6SGarrett D'Amore bd_handle_t
bd_alloc_handle(void * private,bd_ops_t * ops,ddi_dma_attr_t * dma,int kmflag)22983f7d54a6SGarrett D'Amore bd_alloc_handle(void *private, bd_ops_t *ops, ddi_dma_attr_t *dma, int kmflag)
22993f7d54a6SGarrett D'Amore {
23003f7d54a6SGarrett D'Amore 	bd_handle_t	hdl;
23013f7d54a6SGarrett D'Amore 
23024d95620bSPaul Winder 	switch (ops->o_version) {
23034d95620bSPaul Winder 	case BD_OPS_VERSION_0:
23041a5ae140SJason King 	case BD_OPS_VERSION_1:
23051a5ae140SJason King 	case BD_OPS_VERSION_2:
23064d95620bSPaul Winder 		break;
23074d95620bSPaul Winder 
23084d95620bSPaul Winder 	default:
23091a5ae140SJason King 		/* Unsupported version */
23104d95620bSPaul Winder 		return (NULL);
23114d95620bSPaul Winder 	}
23124d95620bSPaul Winder 
23133f7d54a6SGarrett D'Amore 	hdl = kmem_zalloc(sizeof (*hdl), kmflag);
23141a5ae140SJason King 	if (hdl == NULL) {
23151a5ae140SJason King 		return (NULL);
23161a5ae140SJason King 	}
23171a5ae140SJason King 
23181a5ae140SJason King 	switch (ops->o_version) {
23191a5ae140SJason King 	case BD_OPS_VERSION_2:
23201a5ae140SJason King 		hdl->h_ops.o_free_space = ops->o_free_space;
23211a5ae140SJason King 		/*FALLTHRU*/
23221a5ae140SJason King 	case BD_OPS_VERSION_1:
23231a5ae140SJason King 	case BD_OPS_VERSION_0:
23241a5ae140SJason King 		hdl->h_ops.o_drive_info = ops->o_drive_info;
23251a5ae140SJason King 		hdl->h_ops.o_media_info = ops->o_media_info;
23261a5ae140SJason King 		hdl->h_ops.o_devid_init = ops->o_devid_init;
23271a5ae140SJason King 		hdl->h_ops.o_sync_cache = ops->o_sync_cache;
23281a5ae140SJason King 		hdl->h_ops.o_read = ops->o_read;
23291a5ae140SJason King 		hdl->h_ops.o_write = ops->o_write;
23301a5ae140SJason King 		break;
23311a5ae140SJason King 	}
23321a5ae140SJason King 
23333f7d54a6SGarrett D'Amore 	hdl->h_dma = dma;
23343f7d54a6SGarrett D'Amore 	hdl->h_private = private;
23353f7d54a6SGarrett D'Amore 
23363f7d54a6SGarrett D'Amore 	return (hdl);
23373f7d54a6SGarrett D'Amore }
23383f7d54a6SGarrett D'Amore 
23393f7d54a6SGarrett D'Amore void
bd_free_handle(bd_handle_t hdl)23403f7d54a6SGarrett D'Amore bd_free_handle(bd_handle_t hdl)
23413f7d54a6SGarrett D'Amore {
23423f7d54a6SGarrett D'Amore 	kmem_free(hdl, sizeof (*hdl));
23433f7d54a6SGarrett D'Amore }
23443f7d54a6SGarrett D'Amore 
23453f7d54a6SGarrett D'Amore int
bd_attach_handle(dev_info_t * dip,bd_handle_t hdl)23463f7d54a6SGarrett D'Amore bd_attach_handle(dev_info_t *dip, bd_handle_t hdl)
23473f7d54a6SGarrett D'Amore {
2348510a6847SHans Rosenfeld 	bd_drive_t	drive = { 0 };
2349ca0df52aSHans Rosenfeld 	dev_info_t	*child;
2350ca0df52aSHans Rosenfeld 	size_t		len;
23513f7d54a6SGarrett D'Amore 
23523d9b1a2aSHans Rosenfeld 	/*
23533d9b1a2aSHans Rosenfeld 	 * It's not an error if bd_attach_handle() is called on a handle that
23543d9b1a2aSHans Rosenfeld 	 * already is attached. We just ignore the request to attach and return.
23553d9b1a2aSHans Rosenfeld 	 * This way drivers using blkdev don't have to keep track about blkdev
23563d9b1a2aSHans Rosenfeld 	 * state, they can just call this function to make sure it attached.
23573d9b1a2aSHans Rosenfeld 	 */
23583d9b1a2aSHans Rosenfeld 	if (hdl->h_child != NULL) {
23593d9b1a2aSHans Rosenfeld 		return (DDI_SUCCESS);
23603d9b1a2aSHans Rosenfeld 	}
23613d9b1a2aSHans Rosenfeld 
23623f7d54a6SGarrett D'Amore 	/* if drivers don't override this, make it assume none */
23633f7d54a6SGarrett D'Amore 	drive.d_lun = -1;
23643f7d54a6SGarrett D'Amore 	hdl->h_ops.o_drive_info(hdl->h_private, &drive);
23653f7d54a6SGarrett D'Amore 
23663f7d54a6SGarrett D'Amore 	hdl->h_parent = dip;
23673f7d54a6SGarrett D'Amore 	hdl->h_name = "blkdev";
23683f7d54a6SGarrett D'Amore 
2369ca0df52aSHans Rosenfeld 	/*
2370ca0df52aSHans Rosenfeld 	 * Prefer the GUID over the EUI64.
2371ca0df52aSHans Rosenfeld 	 */
2372ca0df52aSHans Rosenfeld 	if (*(uint64_t *)drive.d_guid != 0 ||
2373ca0df52aSHans Rosenfeld 	    *((uint64_t *)drive.d_guid + 1) != 0) {
2374ca0df52aSHans Rosenfeld 		len = snprintf(hdl->h_addr, sizeof (hdl->h_addr),
2375ca0df52aSHans Rosenfeld 		    "w%02X%02X%02X%02X%02X%02X%02X%02X"
2376ca0df52aSHans Rosenfeld 		    "%02X%02X%02X%02X%02X%02X%02X%02X",
2377ca0df52aSHans Rosenfeld 		    drive.d_guid[0], drive.d_guid[1], drive.d_guid[2],
2378ca0df52aSHans Rosenfeld 		    drive.d_guid[3], drive.d_guid[4], drive.d_guid[5],
2379ca0df52aSHans Rosenfeld 		    drive.d_guid[6], drive.d_guid[7], drive.d_guid[8],
2380ca0df52aSHans Rosenfeld 		    drive.d_guid[9], drive.d_guid[10], drive.d_guid[11],
2381ca0df52aSHans Rosenfeld 		    drive.d_guid[12], drive.d_guid[13], drive.d_guid[14],
2382ca0df52aSHans Rosenfeld 		    drive.d_guid[15]);
2383ca0df52aSHans Rosenfeld 	} else if (*(uint64_t *)drive.d_eui64 != 0) {
2384ca0df52aSHans Rosenfeld 		len = snprintf(hdl->h_addr, sizeof (hdl->h_addr),
2385df8c2287SHans Rosenfeld 		    "w%02X%02X%02X%02X%02X%02X%02X%02X",
2386df8c2287SHans Rosenfeld 		    drive.d_eui64[0], drive.d_eui64[1],
2387df8c2287SHans Rosenfeld 		    drive.d_eui64[2], drive.d_eui64[3],
2388df8c2287SHans Rosenfeld 		    drive.d_eui64[4], drive.d_eui64[5],
2389df8c2287SHans Rosenfeld 		    drive.d_eui64[6], drive.d_eui64[7]);
2390df8c2287SHans Rosenfeld 	} else {
2391ca0df52aSHans Rosenfeld 		len = snprintf(hdl->h_addr, sizeof (hdl->h_addr),
2392df8c2287SHans Rosenfeld 		    "%X", drive.d_target);
2393df8c2287SHans Rosenfeld 	}
2394ca0df52aSHans Rosenfeld 
2395ca0df52aSHans Rosenfeld 	VERIFY(len <= sizeof (hdl->h_addr));
2396ca0df52aSHans Rosenfeld 
2397ca0df52aSHans Rosenfeld 	if (drive.d_lun >= 0) {
2398ca0df52aSHans Rosenfeld 		(void) snprintf(hdl->h_addr + len, sizeof (hdl->h_addr) - len,
2399ca0df52aSHans Rosenfeld 		    ",%X", drive.d_lun);
2400df8c2287SHans Rosenfeld 	}
2401df8c2287SHans Rosenfeld 
24023f7d54a6SGarrett D'Amore 	if (ndi_devi_alloc(dip, hdl->h_name, (pnode_t)DEVI_SID_NODEID,
24033f7d54a6SGarrett D'Amore 	    &child) != NDI_SUCCESS) {
24043f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s%d: unable to allocate node %s@%s",
24053f7d54a6SGarrett D'Amore 		    ddi_driver_name(dip), ddi_get_instance(dip),
24063f7d54a6SGarrett D'Amore 		    "blkdev", hdl->h_addr);
24073f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
24083f7d54a6SGarrett D'Amore 	}
24093f7d54a6SGarrett D'Amore 
24103f7d54a6SGarrett D'Amore 	ddi_set_parent_data(child, hdl);
24113f7d54a6SGarrett D'Amore 	hdl->h_child = child;
24123f7d54a6SGarrett D'Amore 
241333f84ecfSJason King 	if (ndi_devi_online(child, 0) != NDI_SUCCESS) {
24143f7d54a6SGarrett D'Amore 		cmn_err(CE_WARN, "%s%d: failed bringing node %s@%s online",
24153f7d54a6SGarrett D'Amore 		    ddi_driver_name(dip), ddi_get_instance(dip),
24163f7d54a6SGarrett D'Amore 		    hdl->h_name, hdl->h_addr);
24173f7d54a6SGarrett D'Amore 		(void) ndi_devi_free(child);
241833f84ecfSJason King 		hdl->h_child = NULL;
24193f7d54a6SGarrett D'Amore 		return (DDI_FAILURE);
24203f7d54a6SGarrett D'Amore 	}
24213f7d54a6SGarrett D'Amore 
24223f7d54a6SGarrett D'Amore 	return (DDI_SUCCESS);
24233f7d54a6SGarrett D'Amore }
24243f7d54a6SGarrett D'Amore 
24253f7d54a6SGarrett D'Amore int
bd_detach_handle(bd_handle_t hdl)24263f7d54a6SGarrett D'Amore bd_detach_handle(bd_handle_t hdl)
24273f7d54a6SGarrett D'Amore {
24283f7d54a6SGarrett D'Amore 	int	rv;
24293f7d54a6SGarrett D'Amore 	char	*devnm;
24303f7d54a6SGarrett D'Amore 
24313d9b1a2aSHans Rosenfeld 	/*
24323d9b1a2aSHans Rosenfeld 	 * It's not an error if bd_detach_handle() is called on a handle that
24333d9b1a2aSHans Rosenfeld 	 * already is detached. We just ignore the request to detach and return.
24343d9b1a2aSHans Rosenfeld 	 * This way drivers using blkdev don't have to keep track about blkdev
24353d9b1a2aSHans Rosenfeld 	 * state, they can just call this function to make sure it detached.
24363d9b1a2aSHans Rosenfeld 	 */
24373f7d54a6SGarrett D'Amore 	if (hdl->h_child == NULL) {
24383f7d54a6SGarrett D'Amore 		return (DDI_SUCCESS);
24393f7d54a6SGarrett D'Amore 	}
24403fe80ca4SDan Cross 	ndi_devi_enter(hdl->h_parent);
24413f7d54a6SGarrett D'Amore 	if (i_ddi_node_state(hdl->h_child) < DS_INITIALIZED) {
24423f7d54a6SGarrett D'Amore 		rv = ddi_remove_child(hdl->h_child, 0);
24433f7d54a6SGarrett D'Amore 	} else {
24443f7d54a6SGarrett D'Amore 		devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
24453f7d54a6SGarrett D'Amore 		(void) ddi_deviname(hdl->h_child, devnm);
24463f7d54a6SGarrett D'Amore 		(void) devfs_clean(hdl->h_parent, devnm + 1, DV_CLEAN_FORCE);
24473f7d54a6SGarrett D'Amore 		rv = ndi_devi_unconfig_one(hdl->h_parent, devnm + 1, NULL,
24483f7d54a6SGarrett D'Amore 		    NDI_DEVI_REMOVE | NDI_UNCONFIG);
24493f7d54a6SGarrett D'Amore 		kmem_free(devnm, MAXNAMELEN + 1);
24503f7d54a6SGarrett D'Amore 	}
24513f7d54a6SGarrett D'Amore 	if (rv == 0) {
24523f7d54a6SGarrett D'Amore 		hdl->h_child = NULL;
24533f7d54a6SGarrett D'Amore 	}
24543f7d54a6SGarrett D'Amore 
24553fe80ca4SDan Cross 	ndi_devi_exit(hdl->h_parent);
2456aad3a447SHans Rosenfeld 	return (rv == NDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
24573f7d54a6SGarrett D'Amore }
24583f7d54a6SGarrett D'Amore 
24593f7d54a6SGarrett D'Amore void
bd_xfer_done(bd_xfer_t * xfer,int err)24603f7d54a6SGarrett D'Amore bd_xfer_done(bd_xfer_t *xfer, int err)
24613f7d54a6SGarrett D'Amore {
24623f7d54a6SGarrett D'Amore 	bd_xfer_impl_t	*xi = (void *)xfer;
24633f7d54a6SGarrett D'Amore 	buf_t		*bp = xi->i_bp;
246406711e0cSDmitry Yusupov 	int		rv = DDI_SUCCESS;
24653f7d54a6SGarrett D'Amore 	bd_t		*bd = xi->i_bd;
24663f7d54a6SGarrett D'Amore 	size_t		len;
24673f7d54a6SGarrett D'Amore 
24683f7d54a6SGarrett D'Amore 	if (err != 0) {
24693f7d54a6SGarrett D'Amore 		bd_runq_exit(xi, err);
2470bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_harderrs.value.ui32);
24713f7d54a6SGarrett D'Amore 
24723f7d54a6SGarrett D'Amore 		bp->b_resid += xi->i_resid;
24733f7d54a6SGarrett D'Amore 		bd_xfer_free(xi);
24743f7d54a6SGarrett D'Amore 		bioerror(bp, err);
24753f7d54a6SGarrett D'Amore 		biodone(bp);
24763f7d54a6SGarrett D'Amore 		return;
24773f7d54a6SGarrett D'Amore 	}
24783f7d54a6SGarrett D'Amore 
24793f7d54a6SGarrett D'Amore 	xi->i_cur_win++;
24803f7d54a6SGarrett D'Amore 	xi->i_resid -= xi->i_len;
24813f7d54a6SGarrett D'Amore 
24823f7d54a6SGarrett D'Amore 	if (xi->i_resid == 0) {
24833f7d54a6SGarrett D'Amore 		/* Job completed succcessfully! */
24843f7d54a6SGarrett D'Amore 		bd_runq_exit(xi, 0);
24853f7d54a6SGarrett D'Amore 
24863f7d54a6SGarrett D'Amore 		bd_xfer_free(xi);
24873f7d54a6SGarrett D'Amore 		biodone(bp);
24883f7d54a6SGarrett D'Amore 		return;
24893f7d54a6SGarrett D'Amore 	}
24903f7d54a6SGarrett D'Amore 
24913f7d54a6SGarrett D'Amore 	xi->i_blkno += xi->i_nblks;
24923f7d54a6SGarrett D'Amore 
24933f7d54a6SGarrett D'Amore 	if (bd->d_use_dma) {
24943f7d54a6SGarrett D'Amore 		/* More transfer still pending... advance to next DMA window. */
24953f7d54a6SGarrett D'Amore 		rv = ddi_dma_getwin(xi->i_dmah, xi->i_cur_win,
24963f7d54a6SGarrett D'Amore 		    &xi->i_offset, &len, &xi->i_dmac, &xi->i_ndmac);
24973f7d54a6SGarrett D'Amore 	} else {
24983f7d54a6SGarrett D'Amore 		/* Advance memory window. */
24993f7d54a6SGarrett D'Amore 		xi->i_kaddr += xi->i_len;
25003f7d54a6SGarrett D'Amore 		xi->i_offset += xi->i_len;
25013f7d54a6SGarrett D'Amore 		len = min(bp->b_bcount - xi->i_offset, bd->d_maxxfer);
25023f7d54a6SGarrett D'Amore 	}
25033f7d54a6SGarrett D'Amore 
25043f7d54a6SGarrett D'Amore 
25053f7d54a6SGarrett D'Amore 	if ((rv != DDI_SUCCESS) ||
2506c0591a0cSYouzhong Yang 	    (P2PHASE(len, (1U << xi->i_blkshift)) != 0)) {
25073f7d54a6SGarrett D'Amore 		bd_runq_exit(xi, EFAULT);
25083f7d54a6SGarrett D'Amore 
25093f7d54a6SGarrett D'Amore 		bp->b_resid += xi->i_resid;
25103f7d54a6SGarrett D'Amore 		bd_xfer_free(xi);
25113f7d54a6SGarrett D'Amore 		bioerror(bp, EFAULT);
25123f7d54a6SGarrett D'Amore 		biodone(bp);
25133f7d54a6SGarrett D'Amore 		return;
25143f7d54a6SGarrett D'Amore 	}
25153f7d54a6SGarrett D'Amore 	xi->i_len = len;
251604904ca2SDan McDonald 	xi->i_nblks = len >> xi->i_blkshift;
25173f7d54a6SGarrett D'Amore 
25183f7d54a6SGarrett D'Amore 	/* Submit next window to hardware. */
25193f7d54a6SGarrett D'Amore 	rv = xi->i_func(bd->d_private, &xi->i_public);
25203f7d54a6SGarrett D'Amore 	if (rv != 0) {
25213f7d54a6SGarrett D'Amore 		bd_runq_exit(xi, rv);
25223f7d54a6SGarrett D'Amore 
2523bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_transerrs.value.ui32);
2524bef9e21aSHans Rosenfeld 
25253f7d54a6SGarrett D'Amore 		bp->b_resid += xi->i_resid;
25263f7d54a6SGarrett D'Amore 		bd_xfer_free(xi);
25273f7d54a6SGarrett D'Amore 		bioerror(bp, rv);
25283f7d54a6SGarrett D'Amore 		biodone(bp);
25293f7d54a6SGarrett D'Amore 	}
25303f7d54a6SGarrett D'Amore }
25313f7d54a6SGarrett D'Amore 
25323f7d54a6SGarrett D'Amore void
bd_error(bd_xfer_t * xfer,int error)2533bef9e21aSHans Rosenfeld bd_error(bd_xfer_t *xfer, int error)
2534bef9e21aSHans Rosenfeld {
2535bef9e21aSHans Rosenfeld 	bd_xfer_impl_t	*xi = (void *)xfer;
2536bef9e21aSHans Rosenfeld 	bd_t		*bd = xi->i_bd;
2537bef9e21aSHans Rosenfeld 
2538bef9e21aSHans Rosenfeld 	switch (error) {
2539bef9e21aSHans Rosenfeld 	case BD_ERR_MEDIA:
2540bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_media_err.value.ui32);
2541bef9e21aSHans Rosenfeld 		break;
2542bef9e21aSHans Rosenfeld 	case BD_ERR_NTRDY:
2543bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_ntrdy_err.value.ui32);
2544bef9e21aSHans Rosenfeld 		break;
2545bef9e21aSHans Rosenfeld 	case BD_ERR_NODEV:
2546bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_nodev_err.value.ui32);
2547bef9e21aSHans Rosenfeld 		break;
2548bef9e21aSHans Rosenfeld 	case BD_ERR_RECOV:
2549bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_recov_err.value.ui32);
2550bef9e21aSHans Rosenfeld 		break;
2551bef9e21aSHans Rosenfeld 	case BD_ERR_ILLRQ:
2552bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_illrq_err.value.ui32);
2553bef9e21aSHans Rosenfeld 		break;
2554bef9e21aSHans Rosenfeld 	case BD_ERR_PFA:
2555bef9e21aSHans Rosenfeld 		atomic_inc_32(&bd->d_kerr->bd_rq_pfa_err.value.ui32);
2556bef9e21aSHans Rosenfeld 		break;
2557bef9e21aSHans Rosenfeld 	default:
2558bef9e21aSHans Rosenfeld 		cmn_err(CE_PANIC, "bd_error: unknown error type %d", error);
2559bef9e21aSHans Rosenfeld 		break;
2560bef9e21aSHans Rosenfeld 	}
2561bef9e21aSHans Rosenfeld }
2562bef9e21aSHans Rosenfeld 
2563bef9e21aSHans Rosenfeld void
bd_state_change(bd_handle_t hdl)25643f7d54a6SGarrett D'Amore bd_state_change(bd_handle_t hdl)
25653f7d54a6SGarrett D'Amore {
25663f7d54a6SGarrett D'Amore 	bd_t		*bd;
25673f7d54a6SGarrett D'Amore 
25683f7d54a6SGarrett D'Amore 	if ((bd = hdl->h_bd) != NULL) {
25693f7d54a6SGarrett D'Amore 		bd_update_state(bd);
25703f7d54a6SGarrett D'Amore 	}
25713f7d54a6SGarrett D'Amore }
25723f7d54a6SGarrett D'Amore 
2573*744642a2SRobert Mustacchi const char *
bd_address(bd_handle_t hdl)2574*744642a2SRobert Mustacchi bd_address(bd_handle_t hdl)
2575*744642a2SRobert Mustacchi {
2576*744642a2SRobert Mustacchi 	return (hdl->h_addr);
2577*744642a2SRobert Mustacchi }
2578*744642a2SRobert Mustacchi 
25793f7d54a6SGarrett D'Amore void
bd_mod_init(struct dev_ops * devops)25803f7d54a6SGarrett D'Amore bd_mod_init(struct dev_ops *devops)
25813f7d54a6SGarrett D'Amore {
25823f7d54a6SGarrett D'Amore 	static struct bus_ops bd_bus_ops = {
25833f7d54a6SGarrett D'Amore 		BUSO_REV,		/* busops_rev */
25843f7d54a6SGarrett D'Amore 		nullbusmap,		/* bus_map */
25853f7d54a6SGarrett D'Amore 		NULL,			/* bus_get_intrspec (OBSOLETE) */
25863f7d54a6SGarrett D'Amore 		NULL,			/* bus_add_intrspec (OBSOLETE) */
25873f7d54a6SGarrett D'Amore 		NULL,			/* bus_remove_intrspec (OBSOLETE) */
25883f7d54a6SGarrett D'Amore 		i_ddi_map_fault,	/* bus_map_fault */
2589cd21e7c5SGarrett D'Amore 		NULL,			/* bus_dma_map (OBSOLETE) */
25903f7d54a6SGarrett D'Amore 		ddi_dma_allochdl,	/* bus_dma_allochdl */
25913f7d54a6SGarrett D'Amore 		ddi_dma_freehdl,	/* bus_dma_freehdl */
25923f7d54a6SGarrett D'Amore 		ddi_dma_bindhdl,	/* bus_dma_bindhdl */
25933f7d54a6SGarrett D'Amore 		ddi_dma_unbindhdl,	/* bus_dma_unbindhdl */
25943f7d54a6SGarrett D'Amore 		ddi_dma_flush,		/* bus_dma_flush */
25953f7d54a6SGarrett D'Amore 		ddi_dma_win,		/* bus_dma_win */
25963f7d54a6SGarrett D'Amore 		ddi_dma_mctl,		/* bus_dma_ctl */
25973f7d54a6SGarrett D'Amore 		bd_bus_ctl,		/* bus_ctl */
25983f7d54a6SGarrett D'Amore 		ddi_bus_prop_op,	/* bus_prop_op */
25993f7d54a6SGarrett D'Amore 		NULL,			/* bus_get_eventcookie */
26003f7d54a6SGarrett D'Amore 		NULL,			/* bus_add_eventcall */
26013f7d54a6SGarrett D'Amore 		NULL,			/* bus_remove_eventcall */
26023f7d54a6SGarrett D'Amore 		NULL,			/* bus_post_event */
26033f7d54a6SGarrett D'Amore 		NULL,			/* bus_intr_ctl (OBSOLETE) */
26043f7d54a6SGarrett D'Amore 		NULL,			/* bus_config */
26053f7d54a6SGarrett D'Amore 		NULL,			/* bus_unconfig */
26063f7d54a6SGarrett D'Amore 		NULL,			/* bus_fm_init */
26073f7d54a6SGarrett D'Amore 		NULL,			/* bus_fm_fini */
26083f7d54a6SGarrett D'Amore 		NULL,			/* bus_fm_access_enter */
26093f7d54a6SGarrett D'Amore 		NULL,			/* bus_fm_access_exit */
26103f7d54a6SGarrett D'Amore 		NULL,			/* bus_power */
26113f7d54a6SGarrett D'Amore 		NULL,			/* bus_intr_op */
26123f7d54a6SGarrett D'Amore 	};
26133f7d54a6SGarrett D'Amore 
26143f7d54a6SGarrett D'Amore 	devops->devo_bus_ops = &bd_bus_ops;
26153f7d54a6SGarrett D'Amore 
26163f7d54a6SGarrett D'Amore 	/*
26173f7d54a6SGarrett D'Amore 	 * NB: The device driver is free to supply its own
26183f7d54a6SGarrett D'Amore 	 * character entry device support.
26193f7d54a6SGarrett D'Amore 	 */
26203f7d54a6SGarrett D'Amore }
26213f7d54a6SGarrett D'Amore 
26223f7d54a6SGarrett D'Amore void
bd_mod_fini(struct dev_ops * devops)26233f7d54a6SGarrett D'Amore bd_mod_fini(struct dev_ops *devops)
26243f7d54a6SGarrett D'Amore {
26253f7d54a6SGarrett D'Amore 	devops->devo_bus_ops = NULL;
26263f7d54a6SGarrett D'Amore }
2627