xref: /illumos-gate/usr/src/uts/common/xen/io/xdb.c (revision 2952f70a)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev 
22843e1988Sjohnlev /*
237f0b8309SEdward Pilatowicz  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
27843e1988Sjohnlev /*
28843e1988Sjohnlev  * Note: This is the backend part of the split PV disk driver. This driver
29843e1988Sjohnlev  * is not a nexus driver, nor is it a leaf driver(block/char/stream driver).
30843e1988Sjohnlev  * Currently, it does not create any minor node. So, although, it runs in
31843e1988Sjohnlev  * backend domain, it will not be used directly from within dom0.
32843e1988Sjohnlev  * It simply gets block I/O requests issued by frontend from a shared page
33843e1988Sjohnlev  * (blkif ring buffer - defined by Xen) between backend and frontend domain,
34843e1988Sjohnlev  * generates a buf, and push it down to underlying disk target driver via
35843e1988Sjohnlev  * ldi interface. When buf is done, this driver will generate a response
36843e1988Sjohnlev  * and put it into ring buffer to inform frontend of the status of the I/O
37843e1988Sjohnlev  * request issued by it. When a new virtual device entry is added in xenstore,
38843e1988Sjohnlev  * there will be an watch event sent from Xen to xvdi framework, who will,
39843e1988Sjohnlev  * in turn, create the devinfo node and try to attach this driver
40843e1988Sjohnlev  * (see xvdi_create_dev). When frontend peer changes its state to
41843e1988Sjohnlev  * XenbusStateClose, an event will also be sent from Xen to xvdi framework,
42843e1988Sjohnlev  * who will detach and remove this devinfo node (see i_xvdi_oestate_handler).
43843e1988Sjohnlev  * I/O requests get from ring buffer and event coming from xenstore cannot be
44843e1988Sjohnlev  * trusted. We verify them in xdb_get_buf() and xdb_check_state_transition().
45843e1988Sjohnlev  *
46843e1988Sjohnlev  * Virtual device configuration is read/written from/to the database via
47843e1988Sjohnlev  * xenbus_* interfaces. Driver also use xvdi_* to interact with hypervisor.
48843e1988Sjohnlev  * There is an on-going effort to make xvdi_* cover all xenbus_*.
49843e1988Sjohnlev  */
50843e1988Sjohnlev 
51551bc2a6Smrj #include <sys/types.h>
52551bc2a6Smrj #include <sys/conf.h>
53551bc2a6Smrj #include <sys/ddi.h>
54551bc2a6Smrj #include <sys/dditypes.h>
55551bc2a6Smrj #include <sys/sunddi.h>
56551bc2a6Smrj #include <sys/list.h>
57551bc2a6Smrj #include <sys/dkio.h>
58551bc2a6Smrj #include <sys/cmlb.h>
59551bc2a6Smrj #include <sys/vtoc.h>
60551bc2a6Smrj #include <sys/modctl.h>
61551bc2a6Smrj #include <sys/bootconf.h>
62551bc2a6Smrj #include <sys/promif.h>
63551bc2a6Smrj #include <sys/sysmacros.h>
64551bc2a6Smrj #include <public/io/xenbus.h>
657f0b8309SEdward Pilatowicz #include <public/io/xs_wire.h>
66551bc2a6Smrj #include <xen/sys/xenbus_impl.h>
67551bc2a6Smrj #include <xen/sys/xendev.h>
68551bc2a6Smrj #include <sys/gnttab.h>
69551bc2a6Smrj #include <sys/scsi/generic/inquiry.h>
70551bc2a6Smrj #include <vm/seg_kmem.h>
71843e1988Sjohnlev #include <vm/hat_i86.h>
72551bc2a6Smrj #include <sys/gnttab.h>
73551bc2a6Smrj #include <sys/lofi.h>
74551bc2a6Smrj #include <io/xdf.h>
75a576ab5bSrab #include <xen/io/blkif_impl.h>
76551bc2a6Smrj #include <io/xdb.h>
77843e1988Sjohnlev 
78843e1988Sjohnlev static xdb_t *xdb_statep;
79843e1988Sjohnlev static int xdb_debug = 0;
80843e1988Sjohnlev 
817f0b8309SEdward Pilatowicz static void xdb_close(dev_info_t *);
82a576ab5bSrab static int xdb_push_response(xdb_t *, uint64_t, uint8_t, uint16_t);
83a576ab5bSrab static int xdb_get_request(xdb_t *, blkif_request_t *);
84a576ab5bSrab static void blkif_get_x86_32_req(blkif_request_t *, blkif_x86_32_request_t *);
85a576ab5bSrab static void blkif_get_x86_64_req(blkif_request_t *, blkif_x86_64_request_t *);
867f0b8309SEdward Pilatowicz static int xdb_biodone(buf_t *);
877f0b8309SEdward Pilatowicz 
88a576ab5bSrab 
89843e1988Sjohnlev #ifdef DEBUG
90843e1988Sjohnlev /*
91843e1988Sjohnlev  * debug aid functions
92843e1988Sjohnlev  */
93843e1988Sjohnlev 
94843e1988Sjohnlev static void
logva(xdb_t * vdp,uint64_t va)95843e1988Sjohnlev logva(xdb_t *vdp, uint64_t va)
96843e1988Sjohnlev {
97843e1988Sjohnlev 	uint64_t *page_addrs;
98843e1988Sjohnlev 	int i;
99843e1988Sjohnlev 
100843e1988Sjohnlev 	page_addrs = vdp->page_addrs;
101a576ab5bSrab 	for (i = 0; i < XDB_MAX_IO_PAGES(vdp); i++) {
102843e1988Sjohnlev 		if (page_addrs[i] == va)
103843e1988Sjohnlev 			debug_enter("VA remapping found!");
104843e1988Sjohnlev 	}
105843e1988Sjohnlev 
106a576ab5bSrab 	for (i = 0; i < XDB_MAX_IO_PAGES(vdp); i++) {
107843e1988Sjohnlev 		if (page_addrs[i] == 0) {
108843e1988Sjohnlev 			page_addrs[i] = va;
109843e1988Sjohnlev 			break;
110843e1988Sjohnlev 		}
111843e1988Sjohnlev 	}
112a576ab5bSrab 	ASSERT(i < XDB_MAX_IO_PAGES(vdp));
113843e1988Sjohnlev }
114843e1988Sjohnlev 
115843e1988Sjohnlev static void
unlogva(xdb_t * vdp,uint64_t va)116843e1988Sjohnlev unlogva(xdb_t *vdp, uint64_t va)
117843e1988Sjohnlev {
118843e1988Sjohnlev 	uint64_t *page_addrs;
119843e1988Sjohnlev 	int i;
120843e1988Sjohnlev 
121843e1988Sjohnlev 	page_addrs = vdp->page_addrs;
122a576ab5bSrab 	for (i = 0; i < XDB_MAX_IO_PAGES(vdp); i++) {
123843e1988Sjohnlev 		if (page_addrs[i] == va) {
124843e1988Sjohnlev 			page_addrs[i] = 0;
125843e1988Sjohnlev 			break;
126843e1988Sjohnlev 		}
127843e1988Sjohnlev 	}
128a576ab5bSrab 	ASSERT(i < XDB_MAX_IO_PAGES(vdp));
129843e1988Sjohnlev }
130843e1988Sjohnlev 
131843e1988Sjohnlev static void
xdb_dump_request_oe(blkif_request_t * req)132843e1988Sjohnlev xdb_dump_request_oe(blkif_request_t *req)
133843e1988Sjohnlev {
134843e1988Sjohnlev 	int i;
135843e1988Sjohnlev 
136843e1988Sjohnlev 	/*
137843e1988Sjohnlev 	 * Exploit the public interface definitions for BLKIF_OP_READ
138843e1988Sjohnlev 	 * etc..
139843e1988Sjohnlev 	 */
140843e1988Sjohnlev 	char *op_name[] = { "read", "write", "barrier", "flush" };
141843e1988Sjohnlev 
142843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE, "op=%s", op_name[req->operation]));
143843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE, "num of segments=%d",
144843e1988Sjohnlev 	    req->nr_segments));
145843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE, "handle=%d", req->handle));
146843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE, "id=%llu",
147843e1988Sjohnlev 	    (unsigned long long)req->id));
148843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE, "start sector=%llu",
149843e1988Sjohnlev 	    (unsigned long long)req->sector_number));
150843e1988Sjohnlev 	for (i = 0; i < req->nr_segments; i++) {
151843e1988Sjohnlev 		XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE, "gref=%d, first sec=%d,"
152843e1988Sjohnlev 		    "last sec=%d", req->seg[i].gref, req->seg[i].first_sect,
153843e1988Sjohnlev 		    req->seg[i].last_sect));
154843e1988Sjohnlev 	}
155843e1988Sjohnlev }
156843e1988Sjohnlev #endif /* DEBUG */
157843e1988Sjohnlev 
158843e1988Sjohnlev /*
159843e1988Sjohnlev  * Statistics.
160843e1988Sjohnlev  */
161843e1988Sjohnlev static char *xdb_stats[] = {
162843e1988Sjohnlev 	"rd_reqs",
163843e1988Sjohnlev 	"wr_reqs",
164843e1988Sjohnlev 	"br_reqs",
165843e1988Sjohnlev 	"fl_reqs",
166843e1988Sjohnlev 	"oo_reqs"
167843e1988Sjohnlev };
168843e1988Sjohnlev 
169843e1988Sjohnlev static int
xdb_kstat_update(kstat_t * ksp,int flag)170843e1988Sjohnlev xdb_kstat_update(kstat_t *ksp, int flag)
171843e1988Sjohnlev {
172843e1988Sjohnlev 	xdb_t *vdp;
173843e1988Sjohnlev 	kstat_named_t *knp;
174843e1988Sjohnlev 
175843e1988Sjohnlev 	if (flag != KSTAT_READ)
176843e1988Sjohnlev 		return (EACCES);
177843e1988Sjohnlev 
178843e1988Sjohnlev 	vdp = ksp->ks_private;
179843e1988Sjohnlev 	knp = ksp->ks_data;
180843e1988Sjohnlev 
181843e1988Sjohnlev 	/*
182843e1988Sjohnlev 	 * Assignment order should match that of the names in
183843e1988Sjohnlev 	 * xdb_stats.
184843e1988Sjohnlev 	 */
185843e1988Sjohnlev 	(knp++)->value.ui64 = vdp->xs_stat_req_reads;
186843e1988Sjohnlev 	(knp++)->value.ui64 = vdp->xs_stat_req_writes;
187843e1988Sjohnlev 	(knp++)->value.ui64 = vdp->xs_stat_req_barriers;
188843e1988Sjohnlev 	(knp++)->value.ui64 = vdp->xs_stat_req_flushes;
189843e1988Sjohnlev 	(knp++)->value.ui64 = 0; /* oo_req */
190843e1988Sjohnlev 
191843e1988Sjohnlev 	return (0);
192843e1988Sjohnlev }
193843e1988Sjohnlev 
194843e1988Sjohnlev static boolean_t
xdb_kstat_init(xdb_t * vdp)195843e1988Sjohnlev xdb_kstat_init(xdb_t *vdp)
196843e1988Sjohnlev {
197843e1988Sjohnlev 	int nstat = sizeof (xdb_stats) / sizeof (xdb_stats[0]);
198843e1988Sjohnlev 	char **cp = xdb_stats;
199843e1988Sjohnlev 	kstat_named_t *knp;
200843e1988Sjohnlev 
201843e1988Sjohnlev 	if ((vdp->xs_kstats = kstat_create("xdb",
202843e1988Sjohnlev 	    ddi_get_instance(vdp->xs_dip),
203843e1988Sjohnlev 	    "req_statistics", "block", KSTAT_TYPE_NAMED,
204843e1988Sjohnlev 	    nstat, 0)) == NULL)
205843e1988Sjohnlev 		return (B_FALSE);
206843e1988Sjohnlev 
207843e1988Sjohnlev 	vdp->xs_kstats->ks_private = vdp;
208843e1988Sjohnlev 	vdp->xs_kstats->ks_update = xdb_kstat_update;
209843e1988Sjohnlev 
210843e1988Sjohnlev 	knp = vdp->xs_kstats->ks_data;
211843e1988Sjohnlev 	while (nstat > 0) {
212843e1988Sjohnlev 		kstat_named_init(knp, *cp, KSTAT_DATA_UINT64);
213843e1988Sjohnlev 		knp++;
214843e1988Sjohnlev 		cp++;
215843e1988Sjohnlev 		nstat--;
216843e1988Sjohnlev 	}
217843e1988Sjohnlev 
218843e1988Sjohnlev 	kstat_install(vdp->xs_kstats);
219843e1988Sjohnlev 
220843e1988Sjohnlev 	return (B_TRUE);
221843e1988Sjohnlev }
222843e1988Sjohnlev 
2237f0b8309SEdward Pilatowicz static char *
i_pathname(dev_info_t * dip)2247f0b8309SEdward Pilatowicz i_pathname(dev_info_t *dip)
2257f0b8309SEdward Pilatowicz {
2267f0b8309SEdward Pilatowicz 	char *path, *rv;
2277f0b8309SEdward Pilatowicz 
2287f0b8309SEdward Pilatowicz 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2297f0b8309SEdward Pilatowicz 	(void) ddi_pathname(dip, path);
2307f0b8309SEdward Pilatowicz 	rv = strdup(path);
2317f0b8309SEdward Pilatowicz 	kmem_free(path, MAXPATHLEN);
2327f0b8309SEdward Pilatowicz 
2337f0b8309SEdward Pilatowicz 	return (rv);
2347f0b8309SEdward Pilatowicz }
235843e1988Sjohnlev 
236843e1988Sjohnlev static buf_t *
xdb_get_buf(xdb_t * vdp,blkif_request_t * req,xdb_request_t * xreq)237843e1988Sjohnlev xdb_get_buf(xdb_t *vdp, blkif_request_t *req, xdb_request_t *xreq)
238843e1988Sjohnlev {
239843e1988Sjohnlev 	buf_t *bp;
240843e1988Sjohnlev 	uint8_t segs, curseg;
241843e1988Sjohnlev 	int sectors;
242843e1988Sjohnlev 	int i, err;
243843e1988Sjohnlev 	gnttab_map_grant_ref_t mapops[BLKIF_MAX_SEGMENTS_PER_REQUEST];
244843e1988Sjohnlev 	ddi_acc_handle_t acchdl;
245843e1988Sjohnlev 
246843e1988Sjohnlev 	acchdl = vdp->xs_ring_hdl;
247843e1988Sjohnlev 	bp = XDB_XREQ2BP(xreq);
248843e1988Sjohnlev 	curseg = xreq->xr_curseg;
249843e1988Sjohnlev 	/* init a new xdb request */
250843e1988Sjohnlev 	if (req != NULL) {
251843e1988Sjohnlev 		ASSERT(MUTEX_HELD(&vdp->xs_iomutex));
252843e1988Sjohnlev 		boolean_t pagemapok = B_TRUE;
253843e1988Sjohnlev 		uint8_t op = ddi_get8(acchdl, &req->operation);
254843e1988Sjohnlev 
255843e1988Sjohnlev 		xreq->xr_vdp = vdp;
256843e1988Sjohnlev 		xreq->xr_op = op;
257843e1988Sjohnlev 		xreq->xr_id = ddi_get64(acchdl, &req->id);
258843e1988Sjohnlev 		segs = xreq->xr_buf_pages = ddi_get8(acchdl, &req->nr_segments);
259843e1988Sjohnlev 		if (segs == 0) {
260843e1988Sjohnlev 			if (op != BLKIF_OP_FLUSH_DISKCACHE)
261843e1988Sjohnlev 				cmn_err(CE_WARN, "!non-BLKIF_OP_FLUSH_DISKCACHE"
262843e1988Sjohnlev 				    " is seen from domain %d with zero "
263843e1988Sjohnlev 				    "length data buffer!", vdp->xs_peer);
264843e1988Sjohnlev 			bioinit(bp);
265843e1988Sjohnlev 			bp->b_bcount = 0;
266843e1988Sjohnlev 			bp->b_lblkno = 0;
267843e1988Sjohnlev 			bp->b_un.b_addr = NULL;
268843e1988Sjohnlev 			return (bp);
269843e1988Sjohnlev 		} else if (op == BLKIF_OP_FLUSH_DISKCACHE) {
270843e1988Sjohnlev 			cmn_err(CE_WARN, "!BLKIF_OP_FLUSH_DISKCACHE"
271843e1988Sjohnlev 			    " is seen from domain %d with non-zero "
272843e1988Sjohnlev 			    "length data buffer!", vdp->xs_peer);
273843e1988Sjohnlev 		}
274843e1988Sjohnlev 
275843e1988Sjohnlev 		/*
276843e1988Sjohnlev 		 * segs should be no bigger than BLKIF_MAX_SEGMENTS_PER_REQUEST
277843e1988Sjohnlev 		 * according to the definition of blk interface by Xen
278843e1988Sjohnlev 		 * we do sanity check here
279843e1988Sjohnlev 		 */
280843e1988Sjohnlev 		if (segs > BLKIF_MAX_SEGMENTS_PER_REQUEST)
281843e1988Sjohnlev 			segs = xreq->xr_buf_pages =
282843e1988Sjohnlev 			    BLKIF_MAX_SEGMENTS_PER_REQUEST;
283843e1988Sjohnlev 
284843e1988Sjohnlev 		for (i = 0; i < segs; i++) {
285843e1988Sjohnlev 			uint8_t fs, ls;
286843e1988Sjohnlev 
287843e1988Sjohnlev 			mapops[i].host_addr =
288843e1988Sjohnlev 			    (uint64_t)(uintptr_t)XDB_IOPAGE_VA(
289843e1988Sjohnlev 			    vdp->xs_iopage_va, xreq->xr_idx, i);
290843e1988Sjohnlev 			mapops[i].dom = vdp->xs_peer;
291843e1988Sjohnlev 			mapops[i].ref = ddi_get32(acchdl, &req->seg[i].gref);
292843e1988Sjohnlev 			mapops[i].flags = GNTMAP_host_map;
293843e1988Sjohnlev 			if (op != BLKIF_OP_READ)
294843e1988Sjohnlev 				mapops[i].flags |= GNTMAP_readonly;
295843e1988Sjohnlev 
296843e1988Sjohnlev 			fs = ddi_get8(acchdl, &req->seg[i].first_sect);
297843e1988Sjohnlev 			ls = ddi_get8(acchdl, &req->seg[i].last_sect);
298843e1988Sjohnlev 
299843e1988Sjohnlev 			/*
300843e1988Sjohnlev 			 * first_sect should be no bigger than last_sect and
301843e1988Sjohnlev 			 * both of them should be no bigger than
302ee56d0c8SMark Johnson 			 * XB_LAST_SECTOR_IN_SEG according to definition
303843e1988Sjohnlev 			 * of blk interface by Xen, so sanity check again
304843e1988Sjohnlev 			 */
305ee56d0c8SMark Johnson 			if (fs > XB_LAST_SECTOR_IN_SEG)
306ee56d0c8SMark Johnson 				fs = XB_LAST_SECTOR_IN_SEG;
307ee56d0c8SMark Johnson 			if (ls > XB_LAST_SECTOR_IN_SEG)
308ee56d0c8SMark Johnson 				ls = XB_LAST_SECTOR_IN_SEG;
309843e1988Sjohnlev 			if (fs > ls)
310843e1988Sjohnlev 				fs = ls;
311843e1988Sjohnlev 
312843e1988Sjohnlev 			xreq->xr_segs[i].fs = fs;
313843e1988Sjohnlev 			xreq->xr_segs[i].ls = ls;
314843e1988Sjohnlev 		}
315843e1988Sjohnlev 
316843e1988Sjohnlev 		/* map in io pages */
3177eea693dSMark Johnson 		err = xen_map_gref(GNTTABOP_map_grant_ref, mapops, i, B_FALSE);
318843e1988Sjohnlev 		if (err != 0)
319843e1988Sjohnlev 			return (NULL);
320843e1988Sjohnlev 		for (i = 0; i < segs; i++) {
321843e1988Sjohnlev 			/*
322843e1988Sjohnlev 			 * Although HYPERVISOR_grant_table_op() returned no
323843e1988Sjohnlev 			 * error, mapping of each single page can fail. So,
324843e1988Sjohnlev 			 * we have to do the check here and handle the error
325843e1988Sjohnlev 			 * if needed
326843e1988Sjohnlev 			 */
327843e1988Sjohnlev 			if (mapops[i].status != GNTST_okay) {
328843e1988Sjohnlev 				int j;
329843e1988Sjohnlev 				for (j = 0; j < i; j++) {
330843e1988Sjohnlev #ifdef DEBUG
331843e1988Sjohnlev 					unlogva(vdp, mapops[j].host_addr);
332843e1988Sjohnlev #endif
333843e1988Sjohnlev 					xen_release_pfn(
334843e1988Sjohnlev 					    xreq->xr_plist[j].p_pagenum);
335843e1988Sjohnlev 				}
336843e1988Sjohnlev 				pagemapok = B_FALSE;
337843e1988Sjohnlev 				break;
338843e1988Sjohnlev 			}
339843e1988Sjohnlev 			/* record page mapping handle for unmapping later */
340843e1988Sjohnlev 			xreq->xr_page_hdls[i] = mapops[i].handle;
341843e1988Sjohnlev #ifdef DEBUG
342843e1988Sjohnlev 			logva(vdp, mapops[i].host_addr);
343843e1988Sjohnlev #endif
344843e1988Sjohnlev 			/*
345843e1988Sjohnlev 			 * Pass the MFNs down using the shadow list (xr_pplist)
346843e1988Sjohnlev 			 *
347843e1988Sjohnlev 			 * This is pretty ugly since we have implict knowledge
348843e1988Sjohnlev 			 * of how the rootnex binds buffers.
349843e1988Sjohnlev 			 * The GNTTABOP_map_grant_ref op makes us do some ugly
350843e1988Sjohnlev 			 * stuff since we're not allowed to touch these PTEs
351843e1988Sjohnlev 			 * from the VM.
352843e1988Sjohnlev 			 *
353843e1988Sjohnlev 			 * Obviously, these aren't real page_t's. The rootnex
354843e1988Sjohnlev 			 * only needs p_pagenum.
355843e1988Sjohnlev 			 * Also, don't use btop() here or 32 bit PAE breaks.
356843e1988Sjohnlev 			 */
357843e1988Sjohnlev 			xreq->xr_pplist[i] = &xreq->xr_plist[i];
358843e1988Sjohnlev 			xreq->xr_plist[i].p_pagenum =
359843e1988Sjohnlev 			    xen_assign_pfn(mapops[i].dev_bus_addr >> PAGESHIFT);
360843e1988Sjohnlev 		}
361843e1988Sjohnlev 
362843e1988Sjohnlev 		/*
363843e1988Sjohnlev 		 * not all pages mapped in successfully, unmap those mapped-in
364843e1988Sjohnlev 		 * page and return failure
365843e1988Sjohnlev 		 */
366843e1988Sjohnlev 		if (!pagemapok) {
367843e1988Sjohnlev 			gnttab_unmap_grant_ref_t unmapop;
368843e1988Sjohnlev 
369843e1988Sjohnlev 			for (i = 0; i < segs; i++) {
370843e1988Sjohnlev 				if (mapops[i].status != GNTST_okay)
371843e1988Sjohnlev 					continue;
372843e1988Sjohnlev 				unmapop.host_addr =
373843e1988Sjohnlev 				    (uint64_t)(uintptr_t)XDB_IOPAGE_VA(
374843e1988Sjohnlev 				    vdp->xs_iopage_va, xreq->xr_idx, i);
375*2952f70aSToomas Soome 				unmapop.dev_bus_addr = 0;
376843e1988Sjohnlev 				unmapop.handle = mapops[i].handle;
377843e1988Sjohnlev 				(void) HYPERVISOR_grant_table_op(
378843e1988Sjohnlev 				    GNTTABOP_unmap_grant_ref, &unmapop, 1);
379843e1988Sjohnlev 			}
380843e1988Sjohnlev 
381843e1988Sjohnlev 			return (NULL);
382843e1988Sjohnlev 		}
383843e1988Sjohnlev 		bioinit(bp);
384843e1988Sjohnlev 		bp->b_lblkno = ddi_get64(acchdl, &req->sector_number);
385843e1988Sjohnlev 		bp->b_flags = B_BUSY | B_SHADOW | B_PHYS;
386843e1988Sjohnlev 		bp->b_flags |= (ddi_get8(acchdl, &req->operation) ==
387843e1988Sjohnlev 		    BLKIF_OP_READ) ? B_READ : (B_WRITE | B_ASYNC);
388843e1988Sjohnlev 	} else {
389843e1988Sjohnlev 		uint64_t blkst;
390843e1988Sjohnlev 		int isread;
391843e1988Sjohnlev 
392843e1988Sjohnlev 		/* reuse this buf */
393843e1988Sjohnlev 		blkst = bp->b_lblkno + bp->b_bcount / DEV_BSIZE;
394843e1988Sjohnlev 		isread = bp->b_flags & B_READ;
395843e1988Sjohnlev 		bioreset(bp);
396843e1988Sjohnlev 		bp->b_lblkno = blkst;
397843e1988Sjohnlev 		bp->b_flags = B_BUSY | B_SHADOW | B_PHYS;
398843e1988Sjohnlev 		bp->b_flags |= isread ? B_READ : (B_WRITE | B_ASYNC);
399843e1988Sjohnlev 		XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE, "reuse buf, xreq is %d!!",
400843e1988Sjohnlev 		    xreq->xr_idx));
401843e1988Sjohnlev 	}
402843e1988Sjohnlev 
403843e1988Sjohnlev 	/* form a buf */
404843e1988Sjohnlev 	bp->b_un.b_addr = XDB_IOPAGE_VA(vdp->xs_iopage_va, xreq->xr_idx,
405843e1988Sjohnlev 	    curseg) + xreq->xr_segs[curseg].fs * DEV_BSIZE;
406843e1988Sjohnlev 	bp->b_shadow = &xreq->xr_pplist[curseg];
407843e1988Sjohnlev 	bp->b_iodone = xdb_biodone;
408843e1988Sjohnlev 	sectors = 0;
409ee56d0c8SMark Johnson 
410ee56d0c8SMark Johnson 	/*
411ee56d0c8SMark Johnson 	 * Run through the segments. There are XB_NUM_SECTORS_PER_SEG sectors
412ee56d0c8SMark Johnson 	 * per segment. On some OSes (e.g. Linux), there may be empty gaps
413ee56d0c8SMark Johnson 	 * between segments. (i.e. the first segment may end on sector 6 and
414ee56d0c8SMark Johnson 	 * the second segment start on sector 4).
415ee56d0c8SMark Johnson 	 *
416ee56d0c8SMark Johnson 	 * if a segments first sector is not set to 0, and this is not the
417ee56d0c8SMark Johnson 	 * first segment in our buf, end this buf now.
418ee56d0c8SMark Johnson 	 *
419ee56d0c8SMark Johnson 	 * if a segments last sector is not set to XB_LAST_SECTOR_IN_SEG, and
420ee56d0c8SMark Johnson 	 * this is not the last segment in the request, add this segment into
421ee56d0c8SMark Johnson 	 * the buf, then end this buf (updating the pointer to point to the
422ee56d0c8SMark Johnson 	 * next segment next time around).
423ee56d0c8SMark Johnson 	 */
424843e1988Sjohnlev 	for (i = curseg; i < xreq->xr_buf_pages; i++) {
425ee56d0c8SMark Johnson 		if ((xreq->xr_segs[i].fs != 0) && (i != curseg)) {
426843e1988Sjohnlev 			break;
427843e1988Sjohnlev 		}
428843e1988Sjohnlev 		sectors += (xreq->xr_segs[i].ls - xreq->xr_segs[i].fs + 1);
429ee56d0c8SMark Johnson 		if ((xreq->xr_segs[i].ls != XB_LAST_SECTOR_IN_SEG) &&
430ee56d0c8SMark Johnson 		    (i != (xreq->xr_buf_pages - 1))) {
431ee56d0c8SMark Johnson 			i++;
432ee56d0c8SMark Johnson 			break;
433ee56d0c8SMark Johnson 		}
434843e1988Sjohnlev 	}
435843e1988Sjohnlev 	xreq->xr_curseg = i;
436843e1988Sjohnlev 	bp->b_bcount = sectors * DEV_BSIZE;
437843e1988Sjohnlev 	bp->b_bufsize = bp->b_bcount;
438843e1988Sjohnlev 
439843e1988Sjohnlev 	return (bp);
440843e1988Sjohnlev }
441843e1988Sjohnlev 
442843e1988Sjohnlev static xdb_request_t *
xdb_get_req(xdb_t * vdp)443843e1988Sjohnlev xdb_get_req(xdb_t *vdp)
444843e1988Sjohnlev {
445843e1988Sjohnlev 	xdb_request_t *req;
446843e1988Sjohnlev 	int idx;
447843e1988Sjohnlev 
448843e1988Sjohnlev 	ASSERT(MUTEX_HELD(&vdp->xs_iomutex));
449843e1988Sjohnlev 	ASSERT(vdp->xs_free_req != -1);
450843e1988Sjohnlev 	req = &vdp->xs_req[vdp->xs_free_req];
451843e1988Sjohnlev 	vdp->xs_free_req = req->xr_next;
452843e1988Sjohnlev 	idx = req->xr_idx;
453843e1988Sjohnlev 	bzero(req, sizeof (xdb_request_t));
454843e1988Sjohnlev 	req->xr_idx = idx;
455843e1988Sjohnlev 	return (req);
456843e1988Sjohnlev }
457843e1988Sjohnlev 
458843e1988Sjohnlev static void
xdb_free_req(xdb_request_t * req)459843e1988Sjohnlev xdb_free_req(xdb_request_t *req)
460843e1988Sjohnlev {
461843e1988Sjohnlev 	xdb_t *vdp = req->xr_vdp;
462843e1988Sjohnlev 
463843e1988Sjohnlev 	ASSERT(MUTEX_HELD(&vdp->xs_iomutex));
464843e1988Sjohnlev 	req->xr_next = vdp->xs_free_req;
465843e1988Sjohnlev 	vdp->xs_free_req = req->xr_idx;
466843e1988Sjohnlev }
467843e1988Sjohnlev 
468843e1988Sjohnlev static void
xdb_response(xdb_t * vdp,blkif_request_t * req,boolean_t ok)469843e1988Sjohnlev xdb_response(xdb_t *vdp, blkif_request_t *req, boolean_t ok)
470843e1988Sjohnlev {
471843e1988Sjohnlev 	ddi_acc_handle_t acchdl = vdp->xs_ring_hdl;
472843e1988Sjohnlev 
473a576ab5bSrab 	if (xdb_push_response(vdp, ddi_get64(acchdl, &req->id),
474a576ab5bSrab 	    ddi_get8(acchdl, &req->operation), ok))
475843e1988Sjohnlev 		xvdi_notify_oe(vdp->xs_dip);
476843e1988Sjohnlev }
477843e1988Sjohnlev 
478843e1988Sjohnlev static void
xdb_init_ioreqs(xdb_t * vdp)479843e1988Sjohnlev xdb_init_ioreqs(xdb_t *vdp)
480843e1988Sjohnlev {
481843e1988Sjohnlev 	int i;
482843e1988Sjohnlev 
483a576ab5bSrab 	ASSERT(vdp->xs_nentry);
484a576ab5bSrab 
485a576ab5bSrab 	if (vdp->xs_req == NULL)
486a576ab5bSrab 		vdp->xs_req = kmem_alloc(vdp->xs_nentry *
487a576ab5bSrab 		    sizeof (xdb_request_t), KM_SLEEP);
488a576ab5bSrab #ifdef DEBUG
489a576ab5bSrab 	if (vdp->page_addrs == NULL)
490a576ab5bSrab 		vdp->page_addrs = kmem_zalloc(XDB_MAX_IO_PAGES(vdp) *
491a576ab5bSrab 		    sizeof (uint64_t), KM_SLEEP);
492a576ab5bSrab #endif
493a576ab5bSrab 	for (i = 0; i < vdp->xs_nentry; i++) {
494843e1988Sjohnlev 		vdp->xs_req[i].xr_idx = i;
495843e1988Sjohnlev 		vdp->xs_req[i].xr_next = i + 1;
496843e1988Sjohnlev 	}
497a576ab5bSrab 	vdp->xs_req[vdp->xs_nentry - 1].xr_next = -1;
498843e1988Sjohnlev 	vdp->xs_free_req = 0;
499843e1988Sjohnlev 
500843e1988Sjohnlev 	/* alloc va in host dom for io page mapping */
501843e1988Sjohnlev 	vdp->xs_iopage_va = vmem_xalloc(heap_arena,
502a576ab5bSrab 	    XDB_MAX_IO_PAGES(vdp) * PAGESIZE, PAGESIZE, 0, 0, 0, 0,
503843e1988Sjohnlev 	    VM_SLEEP);
504a576ab5bSrab 	for (i = 0; i < XDB_MAX_IO_PAGES(vdp); i++)
505843e1988Sjohnlev 		hat_prepare_mapping(kas.a_hat,
5067eea693dSMark Johnson 		    vdp->xs_iopage_va + i * PAGESIZE, NULL);
507843e1988Sjohnlev }
508843e1988Sjohnlev 
509843e1988Sjohnlev static void
xdb_uninit_ioreqs(xdb_t * vdp)510843e1988Sjohnlev xdb_uninit_ioreqs(xdb_t *vdp)
511843e1988Sjohnlev {
512843e1988Sjohnlev 	int i;
513843e1988Sjohnlev 
514a576ab5bSrab 	for (i = 0; i < XDB_MAX_IO_PAGES(vdp); i++)
515843e1988Sjohnlev 		hat_release_mapping(kas.a_hat,
516843e1988Sjohnlev 		    vdp->xs_iopage_va + i * PAGESIZE);
517843e1988Sjohnlev 	vmem_xfree(heap_arena, vdp->xs_iopage_va,
518a576ab5bSrab 	    XDB_MAX_IO_PAGES(vdp) * PAGESIZE);
519a576ab5bSrab 	if (vdp->xs_req != NULL) {
520a576ab5bSrab 		kmem_free(vdp->xs_req, vdp->xs_nentry * sizeof (xdb_request_t));
521a576ab5bSrab 		vdp->xs_req = NULL;
522a576ab5bSrab 	}
523a576ab5bSrab #ifdef DEBUG
524a576ab5bSrab 	if (vdp->page_addrs != NULL) {
525a576ab5bSrab 		kmem_free(vdp->page_addrs, XDB_MAX_IO_PAGES(vdp) *
526a576ab5bSrab 		    sizeof (uint64_t));
527a576ab5bSrab 		vdp->page_addrs = NULL;
528a576ab5bSrab 	}
529a576ab5bSrab #endif
530843e1988Sjohnlev }
531843e1988Sjohnlev 
532843e1988Sjohnlev static uint_t
xdb_intr(caddr_t arg)533843e1988Sjohnlev xdb_intr(caddr_t arg)
534843e1988Sjohnlev {
5357f0b8309SEdward Pilatowicz 	xdb_t		*vdp = (xdb_t *)arg;
5367f0b8309SEdward Pilatowicz 	dev_info_t	*dip = vdp->xs_dip;
5377f0b8309SEdward Pilatowicz 	blkif_request_t	req, *reqp = &req;
5387f0b8309SEdward Pilatowicz 	xdb_request_t	*xreq;
5397f0b8309SEdward Pilatowicz 	buf_t		*bp;
5407f0b8309SEdward Pilatowicz 	uint8_t		op;
5417f0b8309SEdward Pilatowicz 	int		ret = DDI_INTR_UNCLAIMED;
542843e1988Sjohnlev 
543843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE,
544843e1988Sjohnlev 	    "xdb@%s: I/O request received from dom %d",
545843e1988Sjohnlev 	    ddi_get_name_addr(dip), vdp->xs_peer));
546843e1988Sjohnlev 
547843e1988Sjohnlev 	mutex_enter(&vdp->xs_iomutex);
548843e1988Sjohnlev 
549843e1988Sjohnlev 	/* shouldn't touch ring buffer if not in connected state */
5507f0b8309SEdward Pilatowicz 	if (!vdp->xs_if_connected) {
551843e1988Sjohnlev 		mutex_exit(&vdp->xs_iomutex);
552843e1988Sjohnlev 		return (DDI_INTR_UNCLAIMED);
553843e1988Sjohnlev 	}
5547f0b8309SEdward Pilatowicz 	ASSERT(vdp->xs_hp_connected && vdp->xs_fe_initialised);
555843e1988Sjohnlev 
556843e1988Sjohnlev 	/*
557843e1988Sjohnlev 	 * We'll loop till there is no more request in the ring
558843e1988Sjohnlev 	 * We won't stuck in this loop for ever since the size of ring buffer
559843e1988Sjohnlev 	 * is limited, and frontend will stop pushing requests into it when
560843e1988Sjohnlev 	 * the ring buffer is full
561843e1988Sjohnlev 	 */
562843e1988Sjohnlev 
563843e1988Sjohnlev 	/* req_event will be increased in xvdi_ring_get_request() */
564a576ab5bSrab 	while (xdb_get_request(vdp, reqp)) {
565843e1988Sjohnlev 		ret = DDI_INTR_CLAIMED;
566843e1988Sjohnlev 
567a576ab5bSrab 		op = ddi_get8(vdp->xs_ring_hdl, &reqp->operation);
568843e1988Sjohnlev 		if (op == BLKIF_OP_READ			||
569843e1988Sjohnlev 		    op == BLKIF_OP_WRITE		||
570843e1988Sjohnlev 		    op == BLKIF_OP_WRITE_BARRIER	||
571843e1988Sjohnlev 		    op == BLKIF_OP_FLUSH_DISKCACHE) {
572843e1988Sjohnlev #ifdef DEBUG
573a576ab5bSrab 			xdb_dump_request_oe(reqp);
574843e1988Sjohnlev #endif
575843e1988Sjohnlev 			xreq = xdb_get_req(vdp);
576843e1988Sjohnlev 			ASSERT(xreq);
577843e1988Sjohnlev 			switch (op) {
578843e1988Sjohnlev 			case BLKIF_OP_READ:
579843e1988Sjohnlev 				vdp->xs_stat_req_reads++;
580843e1988Sjohnlev 				break;
581843e1988Sjohnlev 			case BLKIF_OP_WRITE_BARRIER:
582843e1988Sjohnlev 				vdp->xs_stat_req_barriers++;
583843e1988Sjohnlev 				/* FALLTHRU */
584843e1988Sjohnlev 			case BLKIF_OP_WRITE:
585843e1988Sjohnlev 				vdp->xs_stat_req_writes++;
586843e1988Sjohnlev 				break;
587843e1988Sjohnlev 			case BLKIF_OP_FLUSH_DISKCACHE:
588843e1988Sjohnlev 				vdp->xs_stat_req_flushes++;
589843e1988Sjohnlev 				break;
590843e1988Sjohnlev 			}
591843e1988Sjohnlev 
592843e1988Sjohnlev 			xreq->xr_curseg = 0; /* start from first segment */
593a576ab5bSrab 			bp = xdb_get_buf(vdp, reqp, xreq);
594843e1988Sjohnlev 			if (bp == NULL) {
595843e1988Sjohnlev 				/* failed to form a buf */
596843e1988Sjohnlev 				xdb_free_req(xreq);
597a576ab5bSrab 				xdb_response(vdp, reqp, B_FALSE);
598843e1988Sjohnlev 				continue;
599843e1988Sjohnlev 			}
600843e1988Sjohnlev 			bp->av_forw = NULL;
601843e1988Sjohnlev 
602843e1988Sjohnlev 			XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE,
603843e1988Sjohnlev 			    " buf %p, blkno %lld, size %lu, addr %p",
604843e1988Sjohnlev 			    (void *)bp, (longlong_t)bp->b_blkno,
605843e1988Sjohnlev 			    (ulong_t)bp->b_bcount, (void *)bp->b_un.b_addr));
606843e1988Sjohnlev 
607843e1988Sjohnlev 			/* send bp to underlying blk driver */
608843e1988Sjohnlev 			if (vdp->xs_f_iobuf == NULL) {
609843e1988Sjohnlev 				vdp->xs_f_iobuf = vdp->xs_l_iobuf = bp;
610843e1988Sjohnlev 			} else {
611843e1988Sjohnlev 				vdp->xs_l_iobuf->av_forw = bp;
612843e1988Sjohnlev 				vdp->xs_l_iobuf = bp;
613843e1988Sjohnlev 			}
614843e1988Sjohnlev 		} else {
615a576ab5bSrab 			xdb_response(vdp, reqp, B_FALSE);
616843e1988Sjohnlev 			XDB_DBPRINT(XDB_DBG_IO, (CE_WARN, "xdb@%s: "
617843e1988Sjohnlev 			    "Unsupported cmd received from dom %d",
618843e1988Sjohnlev 			    ddi_get_name_addr(dip), vdp->xs_peer));
619843e1988Sjohnlev 		}
620843e1988Sjohnlev 	}
621843e1988Sjohnlev 	/* notify our taskq to push buf to underlying blk driver */
622843e1988Sjohnlev 	if (ret == DDI_INTR_CLAIMED)
623843e1988Sjohnlev 		cv_broadcast(&vdp->xs_iocv);
624843e1988Sjohnlev 
625843e1988Sjohnlev 	mutex_exit(&vdp->xs_iomutex);
626843e1988Sjohnlev 
627843e1988Sjohnlev 	return (ret);
628843e1988Sjohnlev }
629843e1988Sjohnlev 
630843e1988Sjohnlev static int
xdb_biodone(buf_t * bp)631843e1988Sjohnlev xdb_biodone(buf_t *bp)
632843e1988Sjohnlev {
633843e1988Sjohnlev 	int i, err, bioerr;
634843e1988Sjohnlev 	uint8_t segs;
635843e1988Sjohnlev 	gnttab_unmap_grant_ref_t unmapops[BLKIF_MAX_SEGMENTS_PER_REQUEST];
636843e1988Sjohnlev 	xdb_request_t *xreq = XDB_BP2XREQ(bp);
637843e1988Sjohnlev 	xdb_t *vdp = xreq->xr_vdp;
638843e1988Sjohnlev 	buf_t *nbp;
639843e1988Sjohnlev 
640843e1988Sjohnlev 	bioerr = geterror(bp);
641843e1988Sjohnlev 	if (bioerr)
642843e1988Sjohnlev 		XDB_DBPRINT(XDB_DBG_IO, (CE_WARN, "xdb@%s: I/O error %d",
643843e1988Sjohnlev 		    ddi_get_name_addr(vdp->xs_dip), bioerr));
644843e1988Sjohnlev 
645843e1988Sjohnlev 	/* check if we are done w/ this I/O request */
646843e1988Sjohnlev 	if ((bioerr == 0) && (xreq->xr_curseg < xreq->xr_buf_pages)) {
647843e1988Sjohnlev 		nbp = xdb_get_buf(vdp, NULL, xreq);
648843e1988Sjohnlev 		if (nbp) {
649843e1988Sjohnlev 			err = ldi_strategy(vdp->xs_ldi_hdl, nbp);
650843e1988Sjohnlev 			if (err == 0) {
651843e1988Sjohnlev 				XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE,
652843e1988Sjohnlev 				    "sent buf to backend ok"));
653843e1988Sjohnlev 				return (DDI_SUCCESS);
654843e1988Sjohnlev 			}
655843e1988Sjohnlev 			bioerr = EIO;
656843e1988Sjohnlev 			XDB_DBPRINT(XDB_DBG_IO, (CE_WARN, "xdb@%s: "
657843e1988Sjohnlev 			    "sent buf to backend dev failed, err=%d",
658843e1988Sjohnlev 			    ddi_get_name_addr(vdp->xs_dip), err));
659843e1988Sjohnlev 		} else {
660843e1988Sjohnlev 			bioerr = EIO;
661843e1988Sjohnlev 		}
662843e1988Sjohnlev 	}
663843e1988Sjohnlev 
664843e1988Sjohnlev 	/* unmap io pages */
665843e1988Sjohnlev 	segs = xreq->xr_buf_pages;
666843e1988Sjohnlev 	/*
667843e1988Sjohnlev 	 * segs should be no bigger than BLKIF_MAX_SEGMENTS_PER_REQUEST
668843e1988Sjohnlev 	 * according to the definition of blk interface by Xen
669843e1988Sjohnlev 	 */
670843e1988Sjohnlev 	ASSERT(segs <= BLKIF_MAX_SEGMENTS_PER_REQUEST);
671843e1988Sjohnlev 	for (i = 0; i < segs; i++) {
672843e1988Sjohnlev 		unmapops[i].host_addr = (uint64_t)(uintptr_t)XDB_IOPAGE_VA(
673843e1988Sjohnlev 		    vdp->xs_iopage_va, xreq->xr_idx, i);
674843e1988Sjohnlev #ifdef DEBUG
675843e1988Sjohnlev 		mutex_enter(&vdp->xs_iomutex);
676843e1988Sjohnlev 		unlogva(vdp, unmapops[i].host_addr);
677843e1988Sjohnlev 		mutex_exit(&vdp->xs_iomutex);
678843e1988Sjohnlev #endif
679*2952f70aSToomas Soome 		unmapops[i].dev_bus_addr = 0;
680843e1988Sjohnlev 		unmapops[i].handle = xreq->xr_page_hdls[i];
681843e1988Sjohnlev 	}
682843e1988Sjohnlev 	err = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
683843e1988Sjohnlev 	    unmapops, segs);
684843e1988Sjohnlev 	ASSERT(!err);
685843e1988Sjohnlev 
686843e1988Sjohnlev 	/*
687843e1988Sjohnlev 	 * If we have reached a barrier write or a cache flush , then we must
688843e1988Sjohnlev 	 * flush all our I/Os.
689843e1988Sjohnlev 	 */
690843e1988Sjohnlev 	if (xreq->xr_op == BLKIF_OP_WRITE_BARRIER ||
691843e1988Sjohnlev 	    xreq->xr_op == BLKIF_OP_FLUSH_DISKCACHE) {
692843e1988Sjohnlev 		/*
693843e1988Sjohnlev 		 * XXX At this point the write did succeed, so I don't
694843e1988Sjohnlev 		 * believe we should report an error because the flush
695843e1988Sjohnlev 		 * failed. However, this is a debatable point, so
696843e1988Sjohnlev 		 * maybe we need to think more carefully about this.
697843e1988Sjohnlev 		 * For now, just cast to void.
698843e1988Sjohnlev 		 */
699843e1988Sjohnlev 		(void) ldi_ioctl(vdp->xs_ldi_hdl,
700*2952f70aSToomas Soome 		    DKIOCFLUSHWRITECACHE, 0, FKIOCTL, kcred, NULL);
701843e1988Sjohnlev 	}
702843e1988Sjohnlev 
703843e1988Sjohnlev 	mutex_enter(&vdp->xs_iomutex);
704843e1988Sjohnlev 
705843e1988Sjohnlev 	/* send response back to frontend */
7067f0b8309SEdward Pilatowicz 	if (vdp->xs_if_connected) {
7077f0b8309SEdward Pilatowicz 		ASSERT(vdp->xs_hp_connected && vdp->xs_fe_initialised);
708a576ab5bSrab 		if (xdb_push_response(vdp, xreq->xr_id, xreq->xr_op, bioerr))
709843e1988Sjohnlev 			xvdi_notify_oe(vdp->xs_dip);
710843e1988Sjohnlev 		XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE,
711843e1988Sjohnlev 		    "sent resp back to frontend, id=%llu",
712843e1988Sjohnlev 		    (unsigned long long)xreq->xr_id));
713843e1988Sjohnlev 	}
714843e1988Sjohnlev 	/* free io resources */
715843e1988Sjohnlev 	biofini(bp);
716843e1988Sjohnlev 	xdb_free_req(xreq);
717843e1988Sjohnlev 
718843e1988Sjohnlev 	vdp->xs_ionum--;
7197f0b8309SEdward Pilatowicz 	if (!vdp->xs_if_connected && (vdp->xs_ionum == 0)) {
720843e1988Sjohnlev 		/* we're closing, someone is waiting for I/O clean-up */
721843e1988Sjohnlev 		cv_signal(&vdp->xs_ionumcv);
722a576ab5bSrab 	}
723843e1988Sjohnlev 
724843e1988Sjohnlev 	mutex_exit(&vdp->xs_iomutex);
725843e1988Sjohnlev 
726843e1988Sjohnlev 	return (DDI_SUCCESS);
727843e1988Sjohnlev }
728843e1988Sjohnlev 
729843e1988Sjohnlev static int
xdb_bindto_frontend(xdb_t * vdp)730843e1988Sjohnlev xdb_bindto_frontend(xdb_t *vdp)
731843e1988Sjohnlev {
732843e1988Sjohnlev 	int err;
733843e1988Sjohnlev 	char *oename;
734843e1988Sjohnlev 	grant_ref_t gref;
735843e1988Sjohnlev 	evtchn_port_t evtchn;
736843e1988Sjohnlev 	dev_info_t *dip = vdp->xs_dip;
737a576ab5bSrab 	char protocol[64] = "";
738843e1988Sjohnlev 
7397f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
7407f0b8309SEdward Pilatowicz 
7417f0b8309SEdward Pilatowicz 	/*
7427f0b8309SEdward Pilatowicz 	 * Switch to the XenbusStateInitialised state.  This let's the
7437f0b8309SEdward Pilatowicz 	 * frontend know that we're about to negotiate a connection.
7447f0b8309SEdward Pilatowicz 	 */
7457f0b8309SEdward Pilatowicz 	(void) xvdi_switch_state(dip, XBT_NULL, XenbusStateInitialised);
7467f0b8309SEdward Pilatowicz 
747843e1988Sjohnlev 	/*
748843e1988Sjohnlev 	 * Gather info from frontend
749843e1988Sjohnlev 	 */
750843e1988Sjohnlev 	oename = xvdi_get_oename(dip);
751843e1988Sjohnlev 	if (oename == NULL)
752843e1988Sjohnlev 		return (DDI_FAILURE);
753843e1988Sjohnlev 
754843e1988Sjohnlev 	err = xenbus_gather(XBT_NULL, oename,
7557f0b8309SEdward Pilatowicz 	    XBP_RING_REF, "%lu", &gref,
7567f0b8309SEdward Pilatowicz 	    XBP_EVENT_CHAN, "%u", &evtchn,
7577f0b8309SEdward Pilatowicz 	    NULL);
758843e1988Sjohnlev 	if (err != 0) {
7597f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, err,
760843e1988Sjohnlev 		    "Getting ring-ref and evtchn from frontend");
761843e1988Sjohnlev 		return (DDI_FAILURE);
762843e1988Sjohnlev 	}
763843e1988Sjohnlev 
764a576ab5bSrab 	vdp->xs_blk_protocol = BLKIF_PROTOCOL_NATIVE;
765a576ab5bSrab 	vdp->xs_nentry = BLKIF_RING_SIZE;
766a576ab5bSrab 	vdp->xs_entrysize = sizeof (union blkif_sring_entry);
767a576ab5bSrab 
768a576ab5bSrab 	err = xenbus_gather(XBT_NULL, oename,
7697f0b8309SEdward Pilatowicz 	    XBP_PROTOCOL, "%63s", protocol, NULL);
770a576ab5bSrab 	if (err)
771a576ab5bSrab 		(void) strcpy(protocol, "unspecified, assuming native");
772a576ab5bSrab 	else {
773a576ab5bSrab 		/*
774a576ab5bSrab 		 * We must check for NATIVE first, so that the fast path
775a576ab5bSrab 		 * is taken for copying data from the guest to the host.
776a576ab5bSrab 		 */
777a576ab5bSrab 		if (strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE) != 0) {
778a576ab5bSrab 			if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
779a576ab5bSrab 				vdp->xs_blk_protocol = BLKIF_PROTOCOL_X86_32;
780a576ab5bSrab 				vdp->xs_nentry = BLKIF_X86_32_RING_SIZE;
781a576ab5bSrab 				vdp->xs_entrysize =
782a576ab5bSrab 				    sizeof (union blkif_x86_32_sring_entry);
783a576ab5bSrab 			} else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) ==
784a576ab5bSrab 			    0) {
785a576ab5bSrab 				vdp->xs_blk_protocol = BLKIF_PROTOCOL_X86_64;
786a576ab5bSrab 				vdp->xs_nentry = BLKIF_X86_64_RING_SIZE;
787a576ab5bSrab 				vdp->xs_entrysize =
788a576ab5bSrab 				    sizeof (union blkif_x86_64_sring_entry);
789a576ab5bSrab 			} else {
790a576ab5bSrab 				xvdi_fatal_error(dip, err, "unknown protocol");
791a576ab5bSrab 				return (DDI_FAILURE);
792a576ab5bSrab 			}
793a576ab5bSrab 		}
794a576ab5bSrab 	}
795a576ab5bSrab #ifdef DEBUG
7961ca30e39Sjohnlev 	cmn_err(CE_NOTE, "!xdb@%s: blkif protocol '%s' ",
797a576ab5bSrab 	    ddi_get_name_addr(dip), protocol);
798a576ab5bSrab #endif
799a576ab5bSrab 
800843e1988Sjohnlev 	/*
8017f0b8309SEdward Pilatowicz 	 * Map and init ring.  The ring parameters must match those which
8027f0b8309SEdward Pilatowicz 	 * have been allocated in the front end.
803843e1988Sjohnlev 	 */
8047f0b8309SEdward Pilatowicz 	if (xvdi_map_ring(dip, vdp->xs_nentry, vdp->xs_entrysize,
8057f0b8309SEdward Pilatowicz 	    gref, &vdp->xs_ring) != DDI_SUCCESS)
806843e1988Sjohnlev 		return (DDI_FAILURE);
8077f0b8309SEdward Pilatowicz 
808843e1988Sjohnlev 	/*
809843e1988Sjohnlev 	 * This will be removed after we use shadow I/O ring request since
810843e1988Sjohnlev 	 * we don't need to access the ring itself directly, thus the access
811843e1988Sjohnlev 	 * handle is not needed
812843e1988Sjohnlev 	 */
813843e1988Sjohnlev 	vdp->xs_ring_hdl = vdp->xs_ring->xr_acc_hdl;
814843e1988Sjohnlev 
8157f0b8309SEdward Pilatowicz 	/* bind event channel */
816843e1988Sjohnlev 	err = xvdi_bind_evtchn(dip, evtchn);
817843e1988Sjohnlev 	if (err != DDI_SUCCESS) {
818843e1988Sjohnlev 		xvdi_unmap_ring(vdp->xs_ring);
819843e1988Sjohnlev 		return (DDI_FAILURE);
820843e1988Sjohnlev 	}
821843e1988Sjohnlev 
822843e1988Sjohnlev 	return (DDI_SUCCESS);
823843e1988Sjohnlev }
824843e1988Sjohnlev 
825843e1988Sjohnlev static void
xdb_unbindfrom_frontend(xdb_t * vdp)826843e1988Sjohnlev xdb_unbindfrom_frontend(xdb_t *vdp)
827843e1988Sjohnlev {
8287f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
8297f0b8309SEdward Pilatowicz 
830843e1988Sjohnlev 	xvdi_free_evtchn(vdp->xs_dip);
831843e1988Sjohnlev 	xvdi_unmap_ring(vdp->xs_ring);
832843e1988Sjohnlev }
833843e1988Sjohnlev 
8347f0b8309SEdward Pilatowicz /*
8357f0b8309SEdward Pilatowicz  * xdb_params_change() initiates a allows change to the underlying device/file
8367f0b8309SEdward Pilatowicz  * that the backend is accessing.  It does this by disconnecting from the
8377f0b8309SEdward Pilatowicz  * frontend, closing the old device, clearing a bunch of xenbus parameters,
8387f0b8309SEdward Pilatowicz  * and switching back to the XenbusStateInitialising state.  The frontend
8397f0b8309SEdward Pilatowicz  * should notice this transition to the XenbusStateInitialising state and
8407f0b8309SEdward Pilatowicz  * should attempt to reconnect to us (the backend).
8417f0b8309SEdward Pilatowicz  */
8427f0b8309SEdward Pilatowicz static void
xdb_params_change(xdb_t * vdp,char * params,boolean_t update_xs)8437f0b8309SEdward Pilatowicz xdb_params_change(xdb_t *vdp, char *params, boolean_t update_xs)
8447f0b8309SEdward Pilatowicz {
8457f0b8309SEdward Pilatowicz 	xenbus_transaction_t	xbt;
8467f0b8309SEdward Pilatowicz 	dev_info_t		*dip = vdp->xs_dip;
8477f0b8309SEdward Pilatowicz 	char			*xsname;
8487f0b8309SEdward Pilatowicz 	int			err;
8497f0b8309SEdward Pilatowicz 
8507f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
8517f0b8309SEdward Pilatowicz 	ASSERT(vdp->xs_params_path != NULL);
8527f0b8309SEdward Pilatowicz 
8537f0b8309SEdward Pilatowicz 	if ((xsname = xvdi_get_xsname(dip)) == NULL)
8547f0b8309SEdward Pilatowicz 		return;
8557f0b8309SEdward Pilatowicz 	if (strcmp(vdp->xs_params_path, params) == 0)
8567f0b8309SEdward Pilatowicz 		return;
8577f0b8309SEdward Pilatowicz 
8587f0b8309SEdward Pilatowicz 	/*
8597f0b8309SEdward Pilatowicz 	 * Close the device we're currently accessing and update the
8607f0b8309SEdward Pilatowicz 	 * path which points to our backend device/file.
8617f0b8309SEdward Pilatowicz 	 */
8627f0b8309SEdward Pilatowicz 	xdb_close(dip);
8637f0b8309SEdward Pilatowicz 	vdp->xs_fe_initialised = B_FALSE;
8647f0b8309SEdward Pilatowicz 
8657f0b8309SEdward Pilatowicz trans_retry:
8667f0b8309SEdward Pilatowicz 	if ((err = xenbus_transaction_start(&xbt)) != 0) {
8677f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, err, "params change transaction init");
8687f0b8309SEdward Pilatowicz 		goto errout;
8697f0b8309SEdward Pilatowicz 	}
8707f0b8309SEdward Pilatowicz 
8717f0b8309SEdward Pilatowicz 	/*
8727f0b8309SEdward Pilatowicz 	 * Delete all the xenbus properties that are connection dependant
8737f0b8309SEdward Pilatowicz 	 * and go back to the initializing state so that the frontend
8747f0b8309SEdward Pilatowicz 	 * driver can re-negotiate a connection.
8757f0b8309SEdward Pilatowicz 	 */
8767f0b8309SEdward Pilatowicz 	if (((err = xenbus_rm(xbt, xsname, XBP_FB)) != 0) ||
8777f0b8309SEdward Pilatowicz 	    ((err = xenbus_rm(xbt, xsname, XBP_INFO)) != 0) ||
8787f0b8309SEdward Pilatowicz 	    ((err = xenbus_rm(xbt, xsname, "sector-size")) != 0) ||
8797f0b8309SEdward Pilatowicz 	    ((err = xenbus_rm(xbt, xsname, XBP_SECTORS)) != 0) ||
8807f0b8309SEdward Pilatowicz 	    ((err = xenbus_rm(xbt, xsname, "instance")) != 0) ||
8817f0b8309SEdward Pilatowicz 	    ((err = xenbus_rm(xbt, xsname, "node")) != 0) ||
8827f0b8309SEdward Pilatowicz 	    (update_xs && ((err = xenbus_printf(xbt, xsname,
8837f0b8309SEdward Pilatowicz 	    "params", "%s", params)) != 0)) ||
8847f0b8309SEdward Pilatowicz 	    ((err = xvdi_switch_state(dip,
8857f0b8309SEdward Pilatowicz 	    xbt, XenbusStateInitialising) > 0))) {
8867f0b8309SEdward Pilatowicz 		(void) xenbus_transaction_end(xbt, 1);
8877f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, err, "params change transaction setup");
8887f0b8309SEdward Pilatowicz 		goto errout;
8897f0b8309SEdward Pilatowicz 	}
8907f0b8309SEdward Pilatowicz 
8917f0b8309SEdward Pilatowicz 	if ((err = xenbus_transaction_end(xbt, 0)) != 0) {
8927f0b8309SEdward Pilatowicz 		if (err == EAGAIN) {
8937f0b8309SEdward Pilatowicz 			/* transaction is ended, don't need to abort it */
8947f0b8309SEdward Pilatowicz 			goto trans_retry;
8957f0b8309SEdward Pilatowicz 		}
8967f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, err, "params change transaction commit");
8977f0b8309SEdward Pilatowicz 		goto errout;
8987f0b8309SEdward Pilatowicz 	}
8997f0b8309SEdward Pilatowicz 
9007f0b8309SEdward Pilatowicz 	/* Change the device that we plan to access */
9017f0b8309SEdward Pilatowicz 	strfree(vdp->xs_params_path);
9027f0b8309SEdward Pilatowicz 	vdp->xs_params_path = strdup(params);
9037f0b8309SEdward Pilatowicz 	return;
9047f0b8309SEdward Pilatowicz 
9057f0b8309SEdward Pilatowicz errout:
9067f0b8309SEdward Pilatowicz 	(void) xvdi_switch_state(dip, xbt, XenbusStateInitialising);
9077f0b8309SEdward Pilatowicz }
9087f0b8309SEdward Pilatowicz 
9097f0b8309SEdward Pilatowicz /*
9107f0b8309SEdward Pilatowicz  * xdb_watch_params_cb() - This callback is invoked whenever there
9117f0b8309SEdward Pilatowicz  * is an update to the following xenbus parameter:
9127f0b8309SEdward Pilatowicz  *     /local/domain/0/backend/vbd/<domU_id>/<domU_dev>/params
9137f0b8309SEdward Pilatowicz  *
9147f0b8309SEdward Pilatowicz  * This normally happens during xm block-configure operations, which
9157f0b8309SEdward Pilatowicz  * are used to change CD device images for HVM domUs.
9167f0b8309SEdward Pilatowicz  */
9177f0b8309SEdward Pilatowicz /*ARGSUSED*/
9187f0b8309SEdward Pilatowicz static void
xdb_watch_params_cb(dev_info_t * dip,const char * path,void * arg)9197f0b8309SEdward Pilatowicz xdb_watch_params_cb(dev_info_t *dip, const char *path, void *arg)
9207f0b8309SEdward Pilatowicz {
9217f0b8309SEdward Pilatowicz 	xdb_t			*vdp = (xdb_t *)ddi_get_driver_private(dip);
9227f0b8309SEdward Pilatowicz 	char			*xsname, *oename, *str, *str2;
9237f0b8309SEdward Pilatowicz 
9247f0b8309SEdward Pilatowicz 	if (((xsname = xvdi_get_xsname(dip)) == NULL) ||
9257f0b8309SEdward Pilatowicz 	    ((oename = xvdi_get_oename(dip)) == NULL)) {
9267f0b8309SEdward Pilatowicz 		return;
9277f0b8309SEdward Pilatowicz 	}
9287f0b8309SEdward Pilatowicz 
9297f0b8309SEdward Pilatowicz 	mutex_enter(&vdp->xs_cbmutex);
9307f0b8309SEdward Pilatowicz 
9317f0b8309SEdward Pilatowicz 	if (xenbus_read_str(xsname, "params", &str) != 0) {
9327f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
9337f0b8309SEdward Pilatowicz 		return;
9347f0b8309SEdward Pilatowicz 	}
9357f0b8309SEdward Pilatowicz 
9367f0b8309SEdward Pilatowicz 	if (strcmp(vdp->xs_params_path, str) == 0) {
9377f0b8309SEdward Pilatowicz 		/* Nothing todo */
9387f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
9397f0b8309SEdward Pilatowicz 		strfree(str);
9407f0b8309SEdward Pilatowicz 		return;
9417f0b8309SEdward Pilatowicz 	}
9427f0b8309SEdward Pilatowicz 
9437f0b8309SEdward Pilatowicz 	/*
9447f0b8309SEdward Pilatowicz 	 * If the frontend isn't a cd device, doesn't support media
9457f0b8309SEdward Pilatowicz 	 * requests, or has locked the media, then we can't change
9467f0b8309SEdward Pilatowicz 	 * the params value.  restore the current value.
9477f0b8309SEdward Pilatowicz 	 */
9487f0b8309SEdward Pilatowicz 	str2 = NULL;
9497f0b8309SEdward Pilatowicz 	if (!XDB_IS_FE_CD(vdp) ||
9507f0b8309SEdward Pilatowicz 	    (xenbus_read_str(oename, XBP_MEDIA_REQ, &str2) != 0) ||
9517f0b8309SEdward Pilatowicz 	    (strcmp(str2, XBV_MEDIA_REQ_LOCK) == 0)) {
9527f0b8309SEdward Pilatowicz 		if (str2 != NULL)
9537f0b8309SEdward Pilatowicz 			strfree(str2);
9547f0b8309SEdward Pilatowicz 		strfree(str);
9557f0b8309SEdward Pilatowicz 
9567f0b8309SEdward Pilatowicz 		str = i_pathname(dip);
9577f0b8309SEdward Pilatowicz 		cmn_err(CE_NOTE,
9587f0b8309SEdward Pilatowicz 		    "!%s: media locked, ignoring params update", str);
9597f0b8309SEdward Pilatowicz 		strfree(str);
9607f0b8309SEdward Pilatowicz 
9617f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
9627f0b8309SEdward Pilatowicz 		return;
9637f0b8309SEdward Pilatowicz 	}
9647f0b8309SEdward Pilatowicz 
9657f0b8309SEdward Pilatowicz 	XDB_DBPRINT(XDB_DBG_INFO, (CE_NOTE,
9667f0b8309SEdward Pilatowicz 	    "block-configure params request: \"%s\"", str));
9677f0b8309SEdward Pilatowicz 
9687f0b8309SEdward Pilatowicz 	xdb_params_change(vdp, str, B_FALSE);
9697f0b8309SEdward Pilatowicz 	mutex_exit(&vdp->xs_cbmutex);
9707f0b8309SEdward Pilatowicz 	strfree(str);
9717f0b8309SEdward Pilatowicz }
9727f0b8309SEdward Pilatowicz 
9737f0b8309SEdward Pilatowicz /*
9747f0b8309SEdward Pilatowicz  * xdb_watch_media_req_cb() - This callback is invoked whenever there
9757f0b8309SEdward Pilatowicz  * is an update to the following xenbus parameter:
9767f0b8309SEdward Pilatowicz  *     /local/domain/<domU_id>/device/vbd/<domU_dev>/media-req
9777f0b8309SEdward Pilatowicz  *
9787f0b8309SEdward Pilatowicz  * Media requests are only supported on CD devices and are issued by
9797f0b8309SEdward Pilatowicz  * the frontend.  Currently the only supported media request operaions
9807f0b8309SEdward Pilatowicz  * are "lock" and "eject".  A "lock" prevents the backend from changing
9817f0b8309SEdward Pilatowicz  * the backing device/file (via xm block-configure).  An "eject" requests
9827f0b8309SEdward Pilatowicz  * tells the backend device that it should disconnect from the frontend
9837f0b8309SEdward Pilatowicz  * and closing the backing device/file that is currently in use.
9847f0b8309SEdward Pilatowicz  */
9857f0b8309SEdward Pilatowicz /*ARGSUSED*/
9867f0b8309SEdward Pilatowicz static void
xdb_watch_media_req_cb(dev_info_t * dip,const char * path,void * arg)9877f0b8309SEdward Pilatowicz xdb_watch_media_req_cb(dev_info_t *dip, const char *path, void *arg)
9887f0b8309SEdward Pilatowicz {
9897f0b8309SEdward Pilatowicz 	xdb_t			*vdp = (xdb_t *)ddi_get_driver_private(dip);
9907f0b8309SEdward Pilatowicz 	char			*oename, *str;
9917f0b8309SEdward Pilatowicz 
9927f0b8309SEdward Pilatowicz 	mutex_enter(&vdp->xs_cbmutex);
9937f0b8309SEdward Pilatowicz 
9947f0b8309SEdward Pilatowicz 	if ((oename = xvdi_get_oename(dip)) == NULL) {
9957f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
9967f0b8309SEdward Pilatowicz 		return;
9977f0b8309SEdward Pilatowicz 	}
9987f0b8309SEdward Pilatowicz 
9997f0b8309SEdward Pilatowicz 	if (xenbus_read_str(oename, XBP_MEDIA_REQ, &str) != 0) {
10007f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
10017f0b8309SEdward Pilatowicz 		return;
10027f0b8309SEdward Pilatowicz 	}
10037f0b8309SEdward Pilatowicz 
10047f0b8309SEdward Pilatowicz 	if (!XDB_IS_FE_CD(vdp)) {
10057f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, EINVAL,
10067f0b8309SEdward Pilatowicz 		    "media-req only supported for cdrom devices");
10077f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
10087f0b8309SEdward Pilatowicz 		return;
10097f0b8309SEdward Pilatowicz 	}
10107f0b8309SEdward Pilatowicz 
10117f0b8309SEdward Pilatowicz 	if (strcmp(str, XBV_MEDIA_REQ_EJECT) != 0) {
10127f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
10137f0b8309SEdward Pilatowicz 		strfree(str);
10147f0b8309SEdward Pilatowicz 		return;
10157f0b8309SEdward Pilatowicz 	}
10167f0b8309SEdward Pilatowicz 	strfree(str);
10177f0b8309SEdward Pilatowicz 
10187f0b8309SEdward Pilatowicz 	XDB_DBPRINT(XDB_DBG_INFO, (CE_NOTE, "media eject request"));
10197f0b8309SEdward Pilatowicz 
10207f0b8309SEdward Pilatowicz 	xdb_params_change(vdp, "", B_TRUE);
10217f0b8309SEdward Pilatowicz 	(void) xenbus_printf(XBT_NULL, oename,
10227f0b8309SEdward Pilatowicz 	    XBP_MEDIA_REQ, "%s", XBV_MEDIA_REQ_NONE);
10237f0b8309SEdward Pilatowicz 	mutex_exit(&vdp->xs_cbmutex);
10247f0b8309SEdward Pilatowicz }
10257f0b8309SEdward Pilatowicz 
10267f0b8309SEdward Pilatowicz /*
10277f0b8309SEdward Pilatowicz  * If we're dealing with a cdrom device, let the frontend know that
10287f0b8309SEdward Pilatowicz  * we support media requests via XBP_MEDIA_REQ_SUP, and setup a watch
10297f0b8309SEdward Pilatowicz  * to handle those frontend media request changes, which modify the
10307f0b8309SEdward Pilatowicz  * following xenstore parameter:
10317f0b8309SEdward Pilatowicz  *	/local/domain/<domU_id>/device/vbd/<domU_dev>/media-req
10327f0b8309SEdward Pilatowicz  */
10337f0b8309SEdward Pilatowicz static boolean_t
xdb_media_req_init(xdb_t * vdp)10347f0b8309SEdward Pilatowicz xdb_media_req_init(xdb_t *vdp)
10357f0b8309SEdward Pilatowicz {
10367f0b8309SEdward Pilatowicz 	dev_info_t		*dip = vdp->xs_dip;
10377f0b8309SEdward Pilatowicz 	char			*xsname, *oename;
10387f0b8309SEdward Pilatowicz 
10397f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
10407f0b8309SEdward Pilatowicz 
10417f0b8309SEdward Pilatowicz 	if (((xsname = xvdi_get_xsname(dip)) == NULL) ||
10427f0b8309SEdward Pilatowicz 	    ((oename = xvdi_get_oename(dip)) == NULL))
10437f0b8309SEdward Pilatowicz 		return (B_FALSE);
10447f0b8309SEdward Pilatowicz 
10457f0b8309SEdward Pilatowicz 	if (!XDB_IS_FE_CD(vdp))
10467f0b8309SEdward Pilatowicz 		return (B_TRUE);
10477f0b8309SEdward Pilatowicz 
10487f0b8309SEdward Pilatowicz 	if (xenbus_printf(XBT_NULL, xsname, XBP_MEDIA_REQ_SUP, "%d", 1) != 0)
10497f0b8309SEdward Pilatowicz 		return (B_FALSE);
10507f0b8309SEdward Pilatowicz 
10517f0b8309SEdward Pilatowicz 	if (xvdi_add_xb_watch_handler(dip, oename,
10527f0b8309SEdward Pilatowicz 	    XBP_MEDIA_REQ, xdb_watch_media_req_cb, NULL) != DDI_SUCCESS) {
10537f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, EAGAIN,
10547f0b8309SEdward Pilatowicz 		    "Failed to register watch for cdrom media requests");
10557f0b8309SEdward Pilatowicz 		return (B_FALSE);
10567f0b8309SEdward Pilatowicz 	}
10577f0b8309SEdward Pilatowicz 
10587f0b8309SEdward Pilatowicz 	return (B_TRUE);
10597f0b8309SEdward Pilatowicz }
10607f0b8309SEdward Pilatowicz 
10617f0b8309SEdward Pilatowicz /*
10627f0b8309SEdward Pilatowicz  * Get our params value.  Also, if we're using "params" then setup a
10637f0b8309SEdward Pilatowicz  * watch to handle xm block-configure operations which modify the
10647f0b8309SEdward Pilatowicz  * following xenstore parameter:
10657f0b8309SEdward Pilatowicz  *	/local/domain/0/backend/vbd/<domU_id>/<domU_dev>/params
10667f0b8309SEdward Pilatowicz  */
10677f0b8309SEdward Pilatowicz static boolean_t
xdb_params_init(xdb_t * vdp)10687f0b8309SEdward Pilatowicz xdb_params_init(xdb_t *vdp)
10697f0b8309SEdward Pilatowicz {
10707f0b8309SEdward Pilatowicz 	dev_info_t		*dip = vdp->xs_dip;
10717f0b8309SEdward Pilatowicz 	char			*str, *xsname;
1072349b53ddSStuart Maybee 	int			err;
10737f0b8309SEdward Pilatowicz 
10747f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
10757f0b8309SEdward Pilatowicz 	ASSERT(vdp->xs_params_path == NULL);
10767f0b8309SEdward Pilatowicz 
10777f0b8309SEdward Pilatowicz 	if ((xsname = xvdi_get_xsname(dip)) == NULL)
10787f0b8309SEdward Pilatowicz 		return (B_FALSE);
10797f0b8309SEdward Pilatowicz 
1080349b53ddSStuart Maybee 	err = xenbus_read_str(xsname, "params", &str);
1081349b53ddSStuart Maybee 	if (err != 0) {
10827f0b8309SEdward Pilatowicz 		return (B_FALSE);
1083349b53ddSStuart Maybee 	}
10847f0b8309SEdward Pilatowicz 	vdp->xs_params_path = str;
10857f0b8309SEdward Pilatowicz 
10867f0b8309SEdward Pilatowicz 	if (xvdi_add_xb_watch_handler(dip, xsname, "params",
10877f0b8309SEdward Pilatowicz 	    xdb_watch_params_cb, NULL) != DDI_SUCCESS) {
10887f0b8309SEdward Pilatowicz 		strfree(vdp->xs_params_path);
10897f0b8309SEdward Pilatowicz 		vdp->xs_params_path = NULL;
10907f0b8309SEdward Pilatowicz 		return (B_FALSE);
10917f0b8309SEdward Pilatowicz 	}
10927f0b8309SEdward Pilatowicz 
10937f0b8309SEdward Pilatowicz 	return (B_TRUE);
10947f0b8309SEdward Pilatowicz }
10957f0b8309SEdward Pilatowicz 
1096843e1988Sjohnlev #define	LOFI_CTRL_NODE	"/dev/lofictl"
1097843e1988Sjohnlev #define	LOFI_DEV_NODE	"/devices/pseudo/lofi@0:"
10987f0b8309SEdward Pilatowicz #define	LOFI_MODE	(FREAD | FWRITE | FEXCL)
1099843e1988Sjohnlev 
1100843e1988Sjohnlev static int
xdb_setup_node(xdb_t * vdp,char * path)1101843e1988Sjohnlev xdb_setup_node(xdb_t *vdp, char *path)
1102843e1988Sjohnlev {
11037f0b8309SEdward Pilatowicz 	dev_info_t		*dip = vdp->xs_dip;
11047f0b8309SEdward Pilatowicz 	char			*xsname, *str;
11057f0b8309SEdward Pilatowicz 	ldi_handle_t		ldi_hdl;
11067f0b8309SEdward Pilatowicz 	struct lofi_ioctl	*li;
11077f0b8309SEdward Pilatowicz 	int			minor, err;
1108843e1988Sjohnlev 
11097f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
11107f0b8309SEdward Pilatowicz 
11117f0b8309SEdward Pilatowicz 	if ((xsname = xvdi_get_xsname(dip)) == NULL)
1112843e1988Sjohnlev 		return (DDI_FAILURE);
1113843e1988Sjohnlev 
11147f0b8309SEdward Pilatowicz 	if ((err = xenbus_read_str(xsname, "type", &str)) != 0) {
11157f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, err, "Getting type from backend device");
1116843e1988Sjohnlev 		return (DDI_FAILURE);
1117843e1988Sjohnlev 	}
11187f0b8309SEdward Pilatowicz 	if (strcmp(str, "file") == 0)
11197f0b8309SEdward Pilatowicz 		vdp->xs_type |= XDB_DEV_BE_LOFI;
11207f0b8309SEdward Pilatowicz 	strfree(str);
1121843e1988Sjohnlev 
11227f0b8309SEdward Pilatowicz 	if (!XDB_IS_BE_LOFI(vdp)) {
11237f0b8309SEdward Pilatowicz 		(void) strlcpy(path, vdp->xs_params_path, MAXPATHLEN);
11247f0b8309SEdward Pilatowicz 		ASSERT(vdp->xs_lofi_path == NULL);
1125843e1988Sjohnlev 		return (DDI_SUCCESS);
1126843e1988Sjohnlev 	}
1127843e1988Sjohnlev 
1128843e1988Sjohnlev 	do {
1129843e1988Sjohnlev 		err = ldi_open_by_name(LOFI_CTRL_NODE, LOFI_MODE, kcred,
1130843e1988Sjohnlev 		    &ldi_hdl, vdp->xs_ldi_li);
1131843e1988Sjohnlev 	} while (err == EBUSY);
1132843e1988Sjohnlev 	if (err != 0) {
1133843e1988Sjohnlev 		return (DDI_FAILURE);
1134843e1988Sjohnlev 	}
1135843e1988Sjohnlev 
1136843e1988Sjohnlev 	li = kmem_zalloc(sizeof (*li), KM_SLEEP);
11377f0b8309SEdward Pilatowicz 	(void) strlcpy(li->li_filename, vdp->xs_params_path,
11387f0b8309SEdward Pilatowicz 	    sizeof (li->li_filename));
11397f0b8309SEdward Pilatowicz 	err = ldi_ioctl(ldi_hdl, LOFI_MAP_FILE, (intptr_t)li,
11407f0b8309SEdward Pilatowicz 	    LOFI_MODE | FKIOCTL, kcred, &minor);
11417f0b8309SEdward Pilatowicz 	(void) ldi_close(ldi_hdl, LOFI_MODE, kcred);
11427f0b8309SEdward Pilatowicz 	kmem_free(li, sizeof (*li));
11437f0b8309SEdward Pilatowicz 
11447f0b8309SEdward Pilatowicz 	if (err != 0) {
1145843e1988Sjohnlev 		cmn_err(CE_WARN, "xdb@%s: Failed to create lofi dev for %s",
11467f0b8309SEdward Pilatowicz 		    ddi_get_name_addr(dip), vdp->xs_params_path);
1147843e1988Sjohnlev 		return (DDI_FAILURE);
1148843e1988Sjohnlev 	}
11497f0b8309SEdward Pilatowicz 
1150843e1988Sjohnlev 	/*
1151843e1988Sjohnlev 	 * return '/devices/...' instead of '/dev/lofi/...' since the
1152843e1988Sjohnlev 	 * former is available immediately after calling ldi_ioctl
1153843e1988Sjohnlev 	 */
11546f02aa44SDina K Nimeh 	(void) snprintf(path, MAXPATHLEN, LOFI_DEV_NODE "%d", minor);
11557f0b8309SEdward Pilatowicz 	(void) xenbus_printf(XBT_NULL, xsname, "node", "%s", path);
11567f0b8309SEdward Pilatowicz 
11577f0b8309SEdward Pilatowicz 	ASSERT(vdp->xs_lofi_path == NULL);
11587f0b8309SEdward Pilatowicz 	vdp->xs_lofi_path = strdup(path);
11597f0b8309SEdward Pilatowicz 
1160843e1988Sjohnlev 	return (DDI_SUCCESS);
1161843e1988Sjohnlev }
1162843e1988Sjohnlev 
1163843e1988Sjohnlev static void
xdb_teardown_node(xdb_t * vdp)1164843e1988Sjohnlev xdb_teardown_node(xdb_t *vdp)
1165843e1988Sjohnlev {
11667f0b8309SEdward Pilatowicz 	dev_info_t *dip = vdp->xs_dip;
1167843e1988Sjohnlev 	ldi_handle_t ldi_hdl;
1168843e1988Sjohnlev 	struct lofi_ioctl *li;
1169843e1988Sjohnlev 	int err;
1170843e1988Sjohnlev 
11717f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
1172843e1988Sjohnlev 
11737f0b8309SEdward Pilatowicz 	if (!XDB_IS_BE_LOFI(vdp))
1174843e1988Sjohnlev 		return;
1175843e1988Sjohnlev 
11767f0b8309SEdward Pilatowicz 	vdp->xs_type &= ~XDB_DEV_BE_LOFI;
11777f0b8309SEdward Pilatowicz 	ASSERT(vdp->xs_lofi_path != NULL);
1178843e1988Sjohnlev 
1179843e1988Sjohnlev 	li = kmem_zalloc(sizeof (*li), KM_SLEEP);
11807f0b8309SEdward Pilatowicz 	(void) strlcpy(li->li_filename, vdp->xs_params_path,
11817f0b8309SEdward Pilatowicz 	    sizeof (li->li_filename));
1182843e1988Sjohnlev 
1183843e1988Sjohnlev 	do {
1184843e1988Sjohnlev 		err = ldi_open_by_name(LOFI_CTRL_NODE, LOFI_MODE, kcred,
1185843e1988Sjohnlev 		    &ldi_hdl, vdp->xs_ldi_li);
1186843e1988Sjohnlev 	} while (err == EBUSY);
1187843e1988Sjohnlev 
1188843e1988Sjohnlev 	if (err != 0) {
1189843e1988Sjohnlev 		kmem_free(li, sizeof (*li));
1190843e1988Sjohnlev 		return;
1191843e1988Sjohnlev 	}
1192843e1988Sjohnlev 
1193843e1988Sjohnlev 	if (ldi_ioctl(ldi_hdl, LOFI_UNMAP_FILE, (intptr_t)li,
1194843e1988Sjohnlev 	    LOFI_MODE | FKIOCTL, kcred, NULL) != 0) {
1195843e1988Sjohnlev 		cmn_err(CE_WARN, "xdb@%s: Failed to delete lofi dev for %s",
1196843e1988Sjohnlev 		    ddi_get_name_addr(dip), li->li_filename);
1197843e1988Sjohnlev 	}
1198843e1988Sjohnlev 
1199843e1988Sjohnlev 	(void) ldi_close(ldi_hdl, LOFI_MODE, kcred);
1200843e1988Sjohnlev 	kmem_free(li, sizeof (*li));
12017f0b8309SEdward Pilatowicz 
12027f0b8309SEdward Pilatowicz 	strfree(vdp->xs_lofi_path);
12037f0b8309SEdward Pilatowicz 	vdp->xs_lofi_path = NULL;
1204843e1988Sjohnlev }
1205843e1988Sjohnlev 
1206843e1988Sjohnlev static int
xdb_open_device(xdb_t * vdp)1207843e1988Sjohnlev xdb_open_device(xdb_t *vdp)
1208843e1988Sjohnlev {
12097f0b8309SEdward Pilatowicz 	dev_info_t *dip = vdp->xs_dip;
1210843e1988Sjohnlev 	uint64_t devsize;
121165908c77Syu, larry liu - Sun Microsystems - Beijing China 	int blksize;
1212843e1988Sjohnlev 	char *nodepath;
1213349b53ddSStuart Maybee 	char *xsname;
1214349b53ddSStuart Maybee 	char *str;
1215349b53ddSStuart Maybee 	int err;
1216843e1988Sjohnlev 
12177f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
1218843e1988Sjohnlev 
12197f0b8309SEdward Pilatowicz 	if (strlen(vdp->xs_params_path) == 0) {
12207f0b8309SEdward Pilatowicz 		/*
12217f0b8309SEdward Pilatowicz 		 * it's possible to have no backing device when dealing
12227f0b8309SEdward Pilatowicz 		 * with a pv cdrom drive that has no virtual cd associated
12237f0b8309SEdward Pilatowicz 		 * with it.
12247f0b8309SEdward Pilatowicz 		 */
12257f0b8309SEdward Pilatowicz 		ASSERT(XDB_IS_FE_CD(vdp));
12267f0b8309SEdward Pilatowicz 		ASSERT(vdp->xs_sectors == 0);
12277f0b8309SEdward Pilatowicz 		ASSERT(vdp->xs_ldi_li == NULL);
12287f0b8309SEdward Pilatowicz 		ASSERT(vdp->xs_ldi_hdl == NULL);
12297f0b8309SEdward Pilatowicz 		return (DDI_SUCCESS);
1230843e1988Sjohnlev 	}
1231843e1988Sjohnlev 
1232349b53ddSStuart Maybee 	/*
1233349b53ddSStuart Maybee 	 * after the hotplug scripts have "connected" the device, check to see
1234349b53ddSStuart Maybee 	 * if we're using a dynamic device.  If so, replace the params path
1235349b53ddSStuart Maybee 	 * with the dynamic one.
1236349b53ddSStuart Maybee 	 */
1237349b53ddSStuart Maybee 	xsname = xvdi_get_xsname(dip);
1238349b53ddSStuart Maybee 	err = xenbus_read_str(xsname, "dynamic-device-path", &str);
1239349b53ddSStuart Maybee 	if (err == 0) {
1240349b53ddSStuart Maybee 		strfree(vdp->xs_params_path);
1241349b53ddSStuart Maybee 		vdp->xs_params_path = str;
1242349b53ddSStuart Maybee 	}
1243349b53ddSStuart Maybee 
1244843e1988Sjohnlev 	if (ldi_ident_from_dip(dip, &vdp->xs_ldi_li) != 0)
1245843e1988Sjohnlev 		return (DDI_FAILURE);
1246843e1988Sjohnlev 
12476f02aa44SDina K Nimeh 	nodepath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
12487f0b8309SEdward Pilatowicz 
12497f0b8309SEdward Pilatowicz 	/* try to open backend device */
12507f0b8309SEdward Pilatowicz 	if (xdb_setup_node(vdp, nodepath) != DDI_SUCCESS) {
12517f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, ENXIO,
1252843e1988Sjohnlev 		    "Getting device path of backend device");
1253843e1988Sjohnlev 		ldi_ident_release(vdp->xs_ldi_li);
12546f02aa44SDina K Nimeh 		kmem_free(nodepath, MAXPATHLEN);
1255843e1988Sjohnlev 		return (DDI_FAILURE);
1256843e1988Sjohnlev 	}
1257843e1988Sjohnlev 
1258843e1988Sjohnlev 	if (ldi_open_by_name(nodepath,
1259843e1988Sjohnlev 	    FREAD | (XDB_IS_RO(vdp) ? 0 : FWRITE),
1260843e1988Sjohnlev 	    kcred, &vdp->xs_ldi_hdl, vdp->xs_ldi_li) != 0) {
1261843e1988Sjohnlev 		xdb_teardown_node(vdp);
1262843e1988Sjohnlev 		ldi_ident_release(vdp->xs_ldi_li);
1263843e1988Sjohnlev 		cmn_err(CE_WARN, "xdb@%s: Failed to open: %s",
1264843e1988Sjohnlev 		    ddi_get_name_addr(dip), nodepath);
12656f02aa44SDina K Nimeh 		kmem_free(nodepath, MAXPATHLEN);
1266843e1988Sjohnlev 		return (DDI_FAILURE);
1267843e1988Sjohnlev 	}
1268843e1988Sjohnlev 
1269843e1988Sjohnlev 	if (ldi_get_size(vdp->xs_ldi_hdl, &devsize) != DDI_SUCCESS) {
1270843e1988Sjohnlev 		(void) ldi_close(vdp->xs_ldi_hdl,
1271843e1988Sjohnlev 		    FREAD | (XDB_IS_RO(vdp) ? 0 : FWRITE), kcred);
1272843e1988Sjohnlev 		xdb_teardown_node(vdp);
1273843e1988Sjohnlev 		ldi_ident_release(vdp->xs_ldi_li);
12746f02aa44SDina K Nimeh 		kmem_free(nodepath, MAXPATHLEN);
1275843e1988Sjohnlev 		return (DDI_FAILURE);
1276843e1988Sjohnlev 	}
127765908c77Syu, larry liu - Sun Microsystems - Beijing China 
127865908c77Syu, larry liu - Sun Microsystems - Beijing China 	blksize = ldi_prop_get_int64(vdp->xs_ldi_hdl,
127965908c77Syu, larry liu - Sun Microsystems - Beijing China 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
128065908c77Syu, larry liu - Sun Microsystems - Beijing China 	    "blksize", DEV_BSIZE);
128165908c77Syu, larry liu - Sun Microsystems - Beijing China 	if (blksize == DEV_BSIZE)
128265908c77Syu, larry liu - Sun Microsystems - Beijing China 		blksize = ldi_prop_get_int(vdp->xs_ldi_hdl,
128365908c77Syu, larry liu - Sun Microsystems - Beijing China 		    LDI_DEV_T_ANY | DDI_PROP_DONTPASS |
128465908c77Syu, larry liu - Sun Microsystems - Beijing China 		    DDI_PROP_NOTPROM, "device-blksize", DEV_BSIZE);
128565908c77Syu, larry liu - Sun Microsystems - Beijing China 
128665908c77Syu, larry liu - Sun Microsystems - Beijing China 	vdp->xs_sec_size = blksize;
128765908c77Syu, larry liu - Sun Microsystems - Beijing China 	vdp->xs_sectors = devsize / blksize;
1288843e1988Sjohnlev 
12897f0b8309SEdward Pilatowicz 	/* check if the underlying device is a CD/DVD disc */
12907f0b8309SEdward Pilatowicz 	if (ldi_prop_get_int(vdp->xs_ldi_hdl, LDI_DEV_T_ANY | DDI_PROP_DONTPASS,
12917f0b8309SEdward Pilatowicz 	    INQUIRY_DEVICE_TYPE, DTYPE_DIRECT) == DTYPE_RODIRECT)
12927f0b8309SEdward Pilatowicz 		vdp->xs_type |= XDB_DEV_BE_CD;
12937f0b8309SEdward Pilatowicz 
12947f0b8309SEdward Pilatowicz 	/* check if the underlying device is a removable disk */
12957f0b8309SEdward Pilatowicz 	if (ldi_prop_exists(vdp->xs_ldi_hdl,
12967f0b8309SEdward Pilatowicz 	    LDI_DEV_T_ANY | DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
12977f0b8309SEdward Pilatowicz 	    "removable-media"))
12987f0b8309SEdward Pilatowicz 		vdp->xs_type |= XDB_DEV_BE_RMB;
12997f0b8309SEdward Pilatowicz 
13006f02aa44SDina K Nimeh 	kmem_free(nodepath, MAXPATHLEN);
1301843e1988Sjohnlev 	return (DDI_SUCCESS);
1302843e1988Sjohnlev }
1303843e1988Sjohnlev 
1304843e1988Sjohnlev static void
xdb_close_device(xdb_t * vdp)1305843e1988Sjohnlev xdb_close_device(xdb_t *vdp)
1306843e1988Sjohnlev {
13077f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
13087f0b8309SEdward Pilatowicz 
13097f0b8309SEdward Pilatowicz 	if (strlen(vdp->xs_params_path) == 0) {
13107f0b8309SEdward Pilatowicz 		ASSERT(XDB_IS_FE_CD(vdp));
13117f0b8309SEdward Pilatowicz 		ASSERT(vdp->xs_sectors == 0);
13127f0b8309SEdward Pilatowicz 		ASSERT(vdp->xs_ldi_li == NULL);
13137f0b8309SEdward Pilatowicz 		ASSERT(vdp->xs_ldi_hdl == NULL);
13147f0b8309SEdward Pilatowicz 		return;
13157f0b8309SEdward Pilatowicz 	}
13167f0b8309SEdward Pilatowicz 
1317843e1988Sjohnlev 	(void) ldi_close(vdp->xs_ldi_hdl,
1318843e1988Sjohnlev 	    FREAD | (XDB_IS_RO(vdp) ? 0 : FWRITE), kcred);
1319843e1988Sjohnlev 	xdb_teardown_node(vdp);
1320843e1988Sjohnlev 	ldi_ident_release(vdp->xs_ldi_li);
13217f0b8309SEdward Pilatowicz 	vdp->xs_type &= ~(XDB_DEV_BE_CD | XDB_DEV_BE_RMB);
13227f0b8309SEdward Pilatowicz 	vdp->xs_sectors = 0;
1323843e1988Sjohnlev 	vdp->xs_ldi_li = NULL;
1324843e1988Sjohnlev 	vdp->xs_ldi_hdl = NULL;
1325843e1988Sjohnlev }
1326843e1988Sjohnlev 
1327843e1988Sjohnlev /*
1328843e1988Sjohnlev  * Kick-off connect process
13297f0b8309SEdward Pilatowicz  * If xs_fe_initialised == B_TRUE and xs_hp_connected == B_TRUE
13307f0b8309SEdward Pilatowicz  * the xs_if_connected will be changed to B_TRUE on success,
1331843e1988Sjohnlev  */
13327f0b8309SEdward Pilatowicz static void
xdb_start_connect(xdb_t * vdp)1333843e1988Sjohnlev xdb_start_connect(xdb_t *vdp)
1334843e1988Sjohnlev {
13357f0b8309SEdward Pilatowicz 	xenbus_transaction_t	xbt;
13367f0b8309SEdward Pilatowicz 	dev_info_t		*dip = vdp->xs_dip;
13377f0b8309SEdward Pilatowicz 	boolean_t		fb_exists;
13387f0b8309SEdward Pilatowicz 	int			err, instance = ddi_get_instance(dip);
13397f0b8309SEdward Pilatowicz 	uint64_t		sectors;
13407f0b8309SEdward Pilatowicz 	uint_t			dinfo, ssize;
13417f0b8309SEdward Pilatowicz 	char			*xsname;
13427f0b8309SEdward Pilatowicz 
13437f0b8309SEdward Pilatowicz 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
13447f0b8309SEdward Pilatowicz 
13457f0b8309SEdward Pilatowicz 	if (((xsname = xvdi_get_xsname(dip)) == NULL) ||
13467f0b8309SEdward Pilatowicz 	    ((vdp->xs_peer = xvdi_get_oeid(dip)) == (domid_t)-1))
13477f0b8309SEdward Pilatowicz 		return;
13487f0b8309SEdward Pilatowicz 
13497f0b8309SEdward Pilatowicz 	mutex_enter(&vdp->xs_iomutex);
13507f0b8309SEdward Pilatowicz 	/*
13517f0b8309SEdward Pilatowicz 	 * if the hotplug scripts haven't run or if the frontend is not
13527f0b8309SEdward Pilatowicz 	 * initialized, then we can't try to connect.
13537f0b8309SEdward Pilatowicz 	 */
13547f0b8309SEdward Pilatowicz 	if (!vdp->xs_hp_connected || !vdp->xs_fe_initialised) {
13557f0b8309SEdward Pilatowicz 		ASSERT(!vdp->xs_if_connected);
13567f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_iomutex);
13577f0b8309SEdward Pilatowicz 		return;
13587f0b8309SEdward Pilatowicz 	}
13597f0b8309SEdward Pilatowicz 
13607f0b8309SEdward Pilatowicz 	/* If we're already connected then there's nothing todo */
13617f0b8309SEdward Pilatowicz 	if (vdp->xs_if_connected) {
13627f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_iomutex);
13637f0b8309SEdward Pilatowicz 		return;
13647f0b8309SEdward Pilatowicz 	}
13657f0b8309SEdward Pilatowicz 	mutex_exit(&vdp->xs_iomutex);
1366843e1988Sjohnlev 
1367843e1988Sjohnlev 	/*
1368843e1988Sjohnlev 	 * Start connect to frontend only when backend device are ready
1369843e1988Sjohnlev 	 * and frontend has moved to XenbusStateInitialised, which means
13707f0b8309SEdward Pilatowicz 	 * ready to connect.
1371843e1988Sjohnlev 	 */
13727f0b8309SEdward Pilatowicz 	XDB_DBPRINT(XDB_DBG_INFO, (CE_NOTE,
13737f0b8309SEdward Pilatowicz 	    "xdb@%s: starting connection process", ddi_get_name_addr(dip)));
1374843e1988Sjohnlev 
13757f0b8309SEdward Pilatowicz 	if (xdb_open_device(vdp) != DDI_SUCCESS)
13767f0b8309SEdward Pilatowicz 		return;
1377843e1988Sjohnlev 
13787f0b8309SEdward Pilatowicz 	if (xdb_bindto_frontend(vdp) != DDI_SUCCESS) {
13797f0b8309SEdward Pilatowicz 		xdb_close_device(vdp);
13807f0b8309SEdward Pilatowicz 		return;
13817f0b8309SEdward Pilatowicz 	}
1382843e1988Sjohnlev 
1383843e1988Sjohnlev 	/* init i/o requests */
1384843e1988Sjohnlev 	xdb_init_ioreqs(vdp);
1385843e1988Sjohnlev 
1386843e1988Sjohnlev 	if (ddi_add_intr(dip, 0, NULL, NULL, xdb_intr, (caddr_t)vdp)
13877f0b8309SEdward Pilatowicz 	    != DDI_SUCCESS) {
13887f0b8309SEdward Pilatowicz 		xdb_uninit_ioreqs(vdp);
13897f0b8309SEdward Pilatowicz 		xdb_unbindfrom_frontend(vdp);
13907f0b8309SEdward Pilatowicz 		xdb_close_device(vdp);
13917f0b8309SEdward Pilatowicz 		return;
13927f0b8309SEdward Pilatowicz 	}
13937f0b8309SEdward Pilatowicz 
13947f0b8309SEdward Pilatowicz 	dinfo = 0;
13957f0b8309SEdward Pilatowicz 	if (XDB_IS_RO(vdp))
13967f0b8309SEdward Pilatowicz 		dinfo |= VDISK_READONLY;
13977f0b8309SEdward Pilatowicz 	if (XDB_IS_BE_RMB(vdp))
13987f0b8309SEdward Pilatowicz 		dinfo |= VDISK_REMOVABLE;
13997f0b8309SEdward Pilatowicz 	if (XDB_IS_BE_CD(vdp))
14007f0b8309SEdward Pilatowicz 		dinfo |= VDISK_CDROM;
14017f0b8309SEdward Pilatowicz 	if (XDB_IS_FE_CD(vdp))
14027f0b8309SEdward Pilatowicz 		dinfo |= VDISK_REMOVABLE | VDISK_CDROM;
1403843e1988Sjohnlev 
1404843e1988Sjohnlev 	/*
1405843e1988Sjohnlev 	 * we can recieve intr any time from now on
1406843e1988Sjohnlev 	 * mark that we're ready to take intr
1407843e1988Sjohnlev 	 */
1408843e1988Sjohnlev 	mutex_enter(&vdp->xs_iomutex);
14097f0b8309SEdward Pilatowicz 	ASSERT(vdp->xs_fe_initialised);
14107f0b8309SEdward Pilatowicz 	vdp->xs_if_connected = B_TRUE;
1411843e1988Sjohnlev 	mutex_exit(&vdp->xs_iomutex);
1412843e1988Sjohnlev 
1413843e1988Sjohnlev trans_retry:
14147f0b8309SEdward Pilatowicz 	/* write into xenstore the info needed by frontend */
14157f0b8309SEdward Pilatowicz 	if ((err = xenbus_transaction_start(&xbt)) != 0) {
14167f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, err, "connect transaction init");
14177f0b8309SEdward Pilatowicz 		goto errout;
1418843e1988Sjohnlev 	}
1419843e1988Sjohnlev 
14207f0b8309SEdward Pilatowicz 	/* If feature-barrier isn't present in xenstore, add it.  */
14217f0b8309SEdward Pilatowicz 	fb_exists = xenbus_exists(xsname, XBP_FB);
1422843e1988Sjohnlev 
142365908c77Syu, larry liu - Sun Microsystems - Beijing China 	ssize = (vdp->xs_sec_size == 0) ? DEV_BSIZE : vdp->xs_sec_size;
14247f0b8309SEdward Pilatowicz 	sectors = vdp->xs_sectors;
14257f0b8309SEdward Pilatowicz 	if (((!fb_exists &&
14267f0b8309SEdward Pilatowicz 	    (err = xenbus_printf(xbt, xsname, XBP_FB, "%d", 1)))) ||
14277f0b8309SEdward Pilatowicz 	    (err = xenbus_printf(xbt, xsname, XBP_INFO, "%u", dinfo)) ||
142865908c77Syu, larry liu - Sun Microsystems - Beijing China 	    (err = xenbus_printf(xbt, xsname, XBP_SECTOR_SIZE, "%u", ssize)) ||
14297f0b8309SEdward Pilatowicz 	    (err = xenbus_printf(xbt, xsname,
14307f0b8309SEdward Pilatowicz 	    XBP_SECTORS, "%"PRIu64, sectors)) ||
14317f0b8309SEdward Pilatowicz 	    (err = xenbus_printf(xbt, xsname, "instance", "%d", instance)) ||
14327f0b8309SEdward Pilatowicz 	    ((err = xvdi_switch_state(dip, xbt, XenbusStateConnected)) > 0)) {
14337f0b8309SEdward Pilatowicz 		(void) xenbus_transaction_end(xbt, 1);
14347f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, err, "connect transaction setup");
14357f0b8309SEdward Pilatowicz 		goto errout;
1436843e1988Sjohnlev 	}
1437843e1988Sjohnlev 
14387f0b8309SEdward Pilatowicz 	if ((err = xenbus_transaction_end(xbt, 0)) != 0) {
14397f0b8309SEdward Pilatowicz 		if (err == EAGAIN) {
1440843e1988Sjohnlev 			/* transaction is ended, don't need to abort it */
1441843e1988Sjohnlev 			goto trans_retry;
14427f0b8309SEdward Pilatowicz 		}
14437f0b8309SEdward Pilatowicz 		xvdi_dev_error(dip, err, "connect transaction commit");
14447f0b8309SEdward Pilatowicz 		goto errout;
1445843e1988Sjohnlev 	}
1446843e1988Sjohnlev 
14477f0b8309SEdward Pilatowicz 	return;
1448843e1988Sjohnlev 
14497f0b8309SEdward Pilatowicz errout:
14507f0b8309SEdward Pilatowicz 	xdb_close(dip);
1451843e1988Sjohnlev }
1452843e1988Sjohnlev 
1453843e1988Sjohnlev /*
1454843e1988Sjohnlev  * Disconnect from frontend and close backend device
1455843e1988Sjohnlev  */
1456843e1988Sjohnlev static void
xdb_close(dev_info_t * dip)1457843e1988Sjohnlev xdb_close(dev_info_t *dip)
1458843e1988Sjohnlev {
1459843e1988Sjohnlev 	xdb_t *vdp = (xdb_t *)ddi_get_driver_private(dip);
1460843e1988Sjohnlev 
1461843e1988Sjohnlev 	ASSERT(MUTEX_HELD(&vdp->xs_cbmutex));
1462843e1988Sjohnlev 	mutex_enter(&vdp->xs_iomutex);
1463843e1988Sjohnlev 
14647f0b8309SEdward Pilatowicz 	/*
14657f0b8309SEdward Pilatowicz 	 * if the hotplug scripts haven't run or if the frontend is not
14667f0b8309SEdward Pilatowicz 	 * initialized, then we can't be connected, so there's no
14677f0b8309SEdward Pilatowicz 	 * connection to close.
14687f0b8309SEdward Pilatowicz 	 */
14697f0b8309SEdward Pilatowicz 	if (!vdp->xs_hp_connected || !vdp->xs_fe_initialised) {
14707f0b8309SEdward Pilatowicz 		ASSERT(!vdp->xs_if_connected);
14717f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_iomutex);
14727f0b8309SEdward Pilatowicz 		return;
14737f0b8309SEdward Pilatowicz 	}
14747f0b8309SEdward Pilatowicz 
14757f0b8309SEdward Pilatowicz 	/* if we're not connected, there's nothing to do */
14767f0b8309SEdward Pilatowicz 	if (!vdp->xs_if_connected) {
1477843e1988Sjohnlev 		cv_broadcast(&vdp->xs_iocv);
1478843e1988Sjohnlev 		mutex_exit(&vdp->xs_iomutex);
1479843e1988Sjohnlev 		return;
1480843e1988Sjohnlev 	}
14817f0b8309SEdward Pilatowicz 
14827f0b8309SEdward Pilatowicz 	XDB_DBPRINT(XDB_DBG_INFO, (CE_NOTE, "closing while connected"));
14837f0b8309SEdward Pilatowicz 
14847f0b8309SEdward Pilatowicz 	vdp->xs_if_connected = B_FALSE;
1485843e1988Sjohnlev 	cv_broadcast(&vdp->xs_iocv);
1486843e1988Sjohnlev 
1487843e1988Sjohnlev 	mutex_exit(&vdp->xs_iomutex);
1488843e1988Sjohnlev 
1489843e1988Sjohnlev 	/* stop accepting I/O request from frontend */
1490843e1988Sjohnlev 	ddi_remove_intr(dip, 0, NULL);
14917f0b8309SEdward Pilatowicz 
1492843e1988Sjohnlev 	/* clear all on-going I/Os, if any */
1493843e1988Sjohnlev 	mutex_enter(&vdp->xs_iomutex);
1494843e1988Sjohnlev 	while (vdp->xs_ionum > 0)
1495843e1988Sjohnlev 		cv_wait(&vdp->xs_ionumcv, &vdp->xs_iomutex);
1496843e1988Sjohnlev 	mutex_exit(&vdp->xs_iomutex);
1497843e1988Sjohnlev 
1498843e1988Sjohnlev 	/* clean up resources and close this interface */
1499843e1988Sjohnlev 	xdb_uninit_ioreqs(vdp);
1500843e1988Sjohnlev 	xdb_unbindfrom_frontend(vdp);
1501843e1988Sjohnlev 	xdb_close_device(vdp);
1502843e1988Sjohnlev 	vdp->xs_peer = (domid_t)-1;
1503843e1988Sjohnlev }
1504843e1988Sjohnlev 
1505843e1988Sjohnlev static void
xdb_send_buf(void * arg)1506843e1988Sjohnlev xdb_send_buf(void *arg)
1507843e1988Sjohnlev {
15087f0b8309SEdward Pilatowicz 	xdb_t	*vdp = (xdb_t *)arg;
15097f0b8309SEdward Pilatowicz 	buf_t	*bp;
15107f0b8309SEdward Pilatowicz 	int	err;
1511843e1988Sjohnlev 
1512843e1988Sjohnlev 	mutex_enter(&vdp->xs_iomutex);
15137f0b8309SEdward Pilatowicz 	while (vdp->xs_send_buf) {
15147f0b8309SEdward Pilatowicz 		if ((bp = vdp->xs_f_iobuf) == NULL) {
15157f0b8309SEdward Pilatowicz 			/* wait for some io to send */
15167f0b8309SEdward Pilatowicz 			XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE,
15177f0b8309SEdward Pilatowicz 			    "send buf waiting for io"));
15187f0b8309SEdward Pilatowicz 			cv_wait(&vdp->xs_iocv, &vdp->xs_iomutex);
15197f0b8309SEdward Pilatowicz 			continue;
15207f0b8309SEdward Pilatowicz 		}
1521843e1988Sjohnlev 
15227f0b8309SEdward Pilatowicz 		vdp->xs_f_iobuf = bp->av_forw;
15237f0b8309SEdward Pilatowicz 		bp->av_forw = NULL;
15247f0b8309SEdward Pilatowicz 		vdp->xs_ionum++;
1525843e1988Sjohnlev 
15267f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_iomutex);
15277f0b8309SEdward Pilatowicz 		if (bp->b_bcount == 0) {
15287f0b8309SEdward Pilatowicz 			/* no I/O needs to be done */
15297f0b8309SEdward Pilatowicz 			(void) xdb_biodone(bp);
1530843e1988Sjohnlev 			mutex_enter(&vdp->xs_iomutex);
15317f0b8309SEdward Pilatowicz 			continue;
1532843e1988Sjohnlev 		}
1533843e1988Sjohnlev 
15347f0b8309SEdward Pilatowicz 		err = EIO;
15357f0b8309SEdward Pilatowicz 		if (vdp->xs_ldi_hdl != NULL)
15367f0b8309SEdward Pilatowicz 			err = ldi_strategy(vdp->xs_ldi_hdl, bp);
15377f0b8309SEdward Pilatowicz 		if (err != 0) {
15387f0b8309SEdward Pilatowicz 			bp->b_flags |= B_ERROR;
15397f0b8309SEdward Pilatowicz 			(void) xdb_biodone(bp);
15407f0b8309SEdward Pilatowicz 			XDB_DBPRINT(XDB_DBG_IO, (CE_WARN,
15417f0b8309SEdward Pilatowicz 			    "xdb@%s: sent buf to backend devfailed, err=%d",
15427f0b8309SEdward Pilatowicz 			    ddi_get_name_addr(vdp->xs_dip), err));
15437f0b8309SEdward Pilatowicz 		} else {
15447f0b8309SEdward Pilatowicz 			XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE,
15457f0b8309SEdward Pilatowicz 			    "sent buf to backend ok"));
15467f0b8309SEdward Pilatowicz 		}
15477f0b8309SEdward Pilatowicz 		mutex_enter(&vdp->xs_iomutex);
1548843e1988Sjohnlev 	}
15497f0b8309SEdward Pilatowicz 	XDB_DBPRINT(XDB_DBG_IO, (CE_NOTE, "send buf finishing"));
1550843e1988Sjohnlev 	mutex_exit(&vdp->xs_iomutex);
1551843e1988Sjohnlev }
1552843e1988Sjohnlev 
1553843e1988Sjohnlev /*ARGSUSED*/
1554843e1988Sjohnlev static void
xdb_hp_state_change(dev_info_t * dip,ddi_eventcookie_t id,void * arg,void * impl_data)1555843e1988Sjohnlev xdb_hp_state_change(dev_info_t *dip, ddi_eventcookie_t id, void *arg,
1556843e1988Sjohnlev     void *impl_data)
1557843e1988Sjohnlev {
1558843e1988Sjohnlev 	xendev_hotplug_state_t state = *(xendev_hotplug_state_t *)impl_data;
1559843e1988Sjohnlev 	xdb_t *vdp = (xdb_t *)ddi_get_driver_private(dip);
1560843e1988Sjohnlev 
1561843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_INFO, (CE_NOTE, "xdb@%s: "
1562843e1988Sjohnlev 	    "hotplug status change to %d!", ddi_get_name_addr(dip), state));
1563843e1988Sjohnlev 
15647f0b8309SEdward Pilatowicz 	if (state != Connected)
15657f0b8309SEdward Pilatowicz 		return;
15667f0b8309SEdward Pilatowicz 
1567843e1988Sjohnlev 	mutex_enter(&vdp->xs_cbmutex);
15687f0b8309SEdward Pilatowicz 
15697f0b8309SEdward Pilatowicz 	/* If hotplug script have already run, there's nothing todo */
15707f0b8309SEdward Pilatowicz 	if (vdp->xs_hp_connected) {
15717f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
15727f0b8309SEdward Pilatowicz 		return;
1573843e1988Sjohnlev 	}
15747f0b8309SEdward Pilatowicz 
15757f0b8309SEdward Pilatowicz 	vdp->xs_hp_connected = B_TRUE;
15767f0b8309SEdward Pilatowicz 	xdb_start_connect(vdp);
1577843e1988Sjohnlev 	mutex_exit(&vdp->xs_cbmutex);
1578843e1988Sjohnlev }
1579843e1988Sjohnlev 
1580843e1988Sjohnlev /*ARGSUSED*/
1581843e1988Sjohnlev static void
xdb_oe_state_change(dev_info_t * dip,ddi_eventcookie_t id,void * arg,void * impl_data)1582843e1988Sjohnlev xdb_oe_state_change(dev_info_t *dip, ddi_eventcookie_t id, void *arg,
1583843e1988Sjohnlev     void *impl_data)
1584843e1988Sjohnlev {
1585843e1988Sjohnlev 	XenbusState new_state = *(XenbusState *)impl_data;
1586843e1988Sjohnlev 	xdb_t *vdp = (xdb_t *)ddi_get_driver_private(dip);
1587843e1988Sjohnlev 
1588843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_INFO, (CE_NOTE, "xdb@%s: "
1589843e1988Sjohnlev 	    "otherend state change to %d!", ddi_get_name_addr(dip), new_state));
1590843e1988Sjohnlev 
1591843e1988Sjohnlev 	mutex_enter(&vdp->xs_cbmutex);
1592843e1988Sjohnlev 
15937f0b8309SEdward Pilatowicz 	/*
15947f0b8309SEdward Pilatowicz 	 * Now it'd really be nice if there was a well defined state
15957f0b8309SEdward Pilatowicz 	 * transition model for xen frontend drivers, but unfortunatly
15967f0b8309SEdward Pilatowicz 	 * there isn't.  So we're stuck with assuming that all state
15977f0b8309SEdward Pilatowicz 	 * transitions are possible, and we'll just have to deal with
15987f0b8309SEdward Pilatowicz 	 * them regardless of what state we're in.
15997f0b8309SEdward Pilatowicz 	 */
1600843e1988Sjohnlev 	switch (new_state) {
16017f0b8309SEdward Pilatowicz 	case XenbusStateUnknown:
16027f0b8309SEdward Pilatowicz 	case XenbusStateInitialising:
16037f0b8309SEdward Pilatowicz 	case XenbusStateInitWait:
16047f0b8309SEdward Pilatowicz 		/* tear down our connection to the frontend */
16057f0b8309SEdward Pilatowicz 		xdb_close(dip);
16067f0b8309SEdward Pilatowicz 		vdp->xs_fe_initialised = B_FALSE;
16077f0b8309SEdward Pilatowicz 		break;
1608843e1988Sjohnlev 
16097f0b8309SEdward Pilatowicz 	case XenbusStateInitialised:
16107f0b8309SEdward Pilatowicz 		/*
16117f0b8309SEdward Pilatowicz 		 * If we were conected, then we need to drop the connection
16127f0b8309SEdward Pilatowicz 		 * and re-negotiate it.
16137f0b8309SEdward Pilatowicz 		 */
16147f0b8309SEdward Pilatowicz 		xdb_close(dip);
16157f0b8309SEdward Pilatowicz 		vdp->xs_fe_initialised = B_TRUE;
16167f0b8309SEdward Pilatowicz 		xdb_start_connect(vdp);
16177f0b8309SEdward Pilatowicz 		break;
1618843e1988Sjohnlev 
16197f0b8309SEdward Pilatowicz 	case XenbusStateConnected:
16207f0b8309SEdward Pilatowicz 		/* nothing todo here other than congratulate the frontend */
1621843e1988Sjohnlev 		break;
16227f0b8309SEdward Pilatowicz 
1623843e1988Sjohnlev 	case XenbusStateClosing:
16247f0b8309SEdward Pilatowicz 		/* monkey see monkey do */
1625843e1988Sjohnlev 		(void) xvdi_switch_state(dip, XBT_NULL, XenbusStateClosing);
1626843e1988Sjohnlev 		break;
16277f0b8309SEdward Pilatowicz 
1628843e1988Sjohnlev 	case XenbusStateClosed:
16297f0b8309SEdward Pilatowicz 		/* tear down our connection to the frontend */
1630843e1988Sjohnlev 		xdb_close(dip);
16317f0b8309SEdward Pilatowicz 		vdp->xs_fe_initialised = B_FALSE;
16327f0b8309SEdward Pilatowicz 		(void) xvdi_switch_state(dip, XBT_NULL, new_state);
16337f0b8309SEdward Pilatowicz 		break;
1634843e1988Sjohnlev 	}
1635843e1988Sjohnlev 
1636843e1988Sjohnlev 	mutex_exit(&vdp->xs_cbmutex);
1637843e1988Sjohnlev }
1638843e1988Sjohnlev 
1639843e1988Sjohnlev static int
xdb_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1640843e1988Sjohnlev xdb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1641843e1988Sjohnlev {
16427f0b8309SEdward Pilatowicz 	ddi_iblock_cookie_t	ibc;
16437f0b8309SEdward Pilatowicz 	xdb_t			*vdp;
16447f0b8309SEdward Pilatowicz 	int			instance = ddi_get_instance(dip);
16457f0b8309SEdward Pilatowicz 	char			*xsname, *oename;
16467f0b8309SEdward Pilatowicz 	char			*str;
1647843e1988Sjohnlev 
1648843e1988Sjohnlev 	switch (cmd) {
1649843e1988Sjohnlev 	case DDI_RESUME:
1650843e1988Sjohnlev 		return (DDI_FAILURE);
1651843e1988Sjohnlev 	case DDI_ATTACH:
1652843e1988Sjohnlev 		break;
1653843e1988Sjohnlev 	default:
1654843e1988Sjohnlev 		return (DDI_FAILURE);
1655843e1988Sjohnlev 	}
1656843e1988Sjohnlev 	/* DDI_ATTACH */
16577f0b8309SEdward Pilatowicz 
16587f0b8309SEdward Pilatowicz 	if (((xsname = xvdi_get_xsname(dip)) == NULL) ||
16597f0b8309SEdward Pilatowicz 	    ((oename = xvdi_get_oename(dip)) == NULL))
1660843e1988Sjohnlev 		return (DDI_FAILURE);
1661843e1988Sjohnlev 
16627f0b8309SEdward Pilatowicz 	/*
16637f0b8309SEdward Pilatowicz 	 * Disable auto-detach.  This is necessary so that we don't get
16647f0b8309SEdward Pilatowicz 	 * detached while we're disconnected from the front end.
16657f0b8309SEdward Pilatowicz 	 */
16667f0b8309SEdward Pilatowicz 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1);
16677f0b8309SEdward Pilatowicz 
1668843e1988Sjohnlev 	if (ddi_get_iblock_cookie(dip, 0, &ibc) != DDI_SUCCESS)
16697f0b8309SEdward Pilatowicz 		return (DDI_FAILURE);
1670843e1988Sjohnlev 
16717f0b8309SEdward Pilatowicz 	if (ddi_soft_state_zalloc(xdb_statep, instance) != DDI_SUCCESS)
16727f0b8309SEdward Pilatowicz 		return (DDI_FAILURE);
1673843e1988Sjohnlev 
16747f0b8309SEdward Pilatowicz 	vdp = ddi_get_soft_state(xdb_statep, instance);
16757f0b8309SEdward Pilatowicz 	vdp->xs_dip = dip;
1676843e1988Sjohnlev 	mutex_init(&vdp->xs_iomutex, NULL, MUTEX_DRIVER, (void *)ibc);
1677843e1988Sjohnlev 	mutex_init(&vdp->xs_cbmutex, NULL, MUTEX_DRIVER, (void *)ibc);
1678843e1988Sjohnlev 	cv_init(&vdp->xs_iocv, NULL, CV_DRIVER, NULL);
1679843e1988Sjohnlev 	cv_init(&vdp->xs_ionumcv, NULL, CV_DRIVER, NULL);
1680843e1988Sjohnlev 	ddi_set_driver_private(dip, vdp);
1681843e1988Sjohnlev 
16827f0b8309SEdward Pilatowicz 	if (!xdb_kstat_init(vdp))
16837f0b8309SEdward Pilatowicz 		goto errout1;
16847f0b8309SEdward Pilatowicz 
16857f0b8309SEdward Pilatowicz 	/* Check if the frontend device is supposed to be a cdrom */
16867f0b8309SEdward Pilatowicz 	if (xenbus_read_str(oename, XBP_DEV_TYPE, &str) != 0)
16877f0b8309SEdward Pilatowicz 		return (DDI_FAILURE);
16887f0b8309SEdward Pilatowicz 	if (strcmp(str, XBV_DEV_TYPE_CD) == 0)
16897f0b8309SEdward Pilatowicz 		vdp->xs_type |= XDB_DEV_FE_CD;
16907f0b8309SEdward Pilatowicz 	strfree(str);
16917f0b8309SEdward Pilatowicz 
16927f0b8309SEdward Pilatowicz 	/* Check if the frontend device is supposed to be read only */
16937f0b8309SEdward Pilatowicz 	if (xenbus_read_str(xsname, "mode", &str) != 0)
16947f0b8309SEdward Pilatowicz 		return (DDI_FAILURE);
1695*2952f70aSToomas Soome 	if ((strcmp(str, "r") == 0) || (strcmp(str, "ro") == 0))
16967f0b8309SEdward Pilatowicz 		vdp->xs_type |= XDB_DEV_RO;
16977f0b8309SEdward Pilatowicz 	strfree(str);
16987f0b8309SEdward Pilatowicz 
16997f0b8309SEdward Pilatowicz 	mutex_enter(&vdp->xs_cbmutex);
17007f0b8309SEdward Pilatowicz 	if (!xdb_media_req_init(vdp) || !xdb_params_init(vdp)) {
17017f0b8309SEdward Pilatowicz 		xvdi_remove_xb_watch_handlers(dip);
17027f0b8309SEdward Pilatowicz 		mutex_exit(&vdp->xs_cbmutex);
17037f0b8309SEdward Pilatowicz 		goto errout2;
17047f0b8309SEdward Pilatowicz 	}
17057f0b8309SEdward Pilatowicz 	mutex_exit(&vdp->xs_cbmutex);
17067f0b8309SEdward Pilatowicz 
17077f0b8309SEdward Pilatowicz 	vdp->xs_send_buf = B_TRUE;
1708843e1988Sjohnlev 	vdp->xs_iotaskq = ddi_taskq_create(dip, "xdb_iotask", 1,
1709843e1988Sjohnlev 	    TASKQ_DEFAULTPRI, 0);
1710843e1988Sjohnlev 	(void) ddi_taskq_dispatch(vdp->xs_iotaskq, xdb_send_buf, vdp,
1711843e1988Sjohnlev 	    DDI_SLEEP);
1712843e1988Sjohnlev 
1713843e1988Sjohnlev 	/* Watch frontend and hotplug state change */
17147f0b8309SEdward Pilatowicz 	if ((xvdi_add_event_handler(dip, XS_OE_STATE, xdb_oe_state_change,
17157f0b8309SEdward Pilatowicz 	    NULL) != DDI_SUCCESS) ||
17167f0b8309SEdward Pilatowicz 	    (xvdi_add_event_handler(dip, XS_HP_STATE, xdb_hp_state_change,
17177f0b8309SEdward Pilatowicz 	    NULL) != DDI_SUCCESS))
1718843e1988Sjohnlev 		goto errout3;
1719843e1988Sjohnlev 
1720843e1988Sjohnlev 	/*
1721843e1988Sjohnlev 	 * Kick-off hotplug script
1722843e1988Sjohnlev 	 */
1723843e1988Sjohnlev 	if (xvdi_post_event(dip, XEN_HP_ADD) != DDI_SUCCESS) {
1724843e1988Sjohnlev 		cmn_err(CE_WARN, "xdb@%s: failed to start hotplug script",
1725843e1988Sjohnlev 		    ddi_get_name_addr(dip));
17267f0b8309SEdward Pilatowicz 		goto errout3;
1727843e1988Sjohnlev 	}
1728843e1988Sjohnlev 
1729843e1988Sjohnlev 	/*
1730843e1988Sjohnlev 	 * start waiting for hotplug event and otherend state event
1731843e1988Sjohnlev 	 * mainly for debugging, frontend will not take any op seeing this
1732843e1988Sjohnlev 	 */
1733843e1988Sjohnlev 	(void) xvdi_switch_state(dip, XBT_NULL, XenbusStateInitWait);
1734843e1988Sjohnlev 
1735843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_INFO, (CE_NOTE, "xdb@%s: attached!",
1736843e1988Sjohnlev 	    ddi_get_name_addr(dip)));
1737843e1988Sjohnlev 	return (DDI_SUCCESS);
1738843e1988Sjohnlev 
1739843e1988Sjohnlev errout3:
17407f0b8309SEdward Pilatowicz 	ASSERT(vdp->xs_hp_connected && vdp->xs_if_connected);
17417f0b8309SEdward Pilatowicz 
17427f0b8309SEdward Pilatowicz 	xvdi_remove_event_handler(dip, NULL);
17437f0b8309SEdward Pilatowicz 
17447f0b8309SEdward Pilatowicz 	/* Disconnect from the backend */
1745843e1988Sjohnlev 	mutex_enter(&vdp->xs_cbmutex);
1746843e1988Sjohnlev 	mutex_enter(&vdp->xs_iomutex);
17477f0b8309SEdward Pilatowicz 	vdp->xs_send_buf = B_FALSE;
1748843e1988Sjohnlev 	cv_broadcast(&vdp->xs_iocv);
1749843e1988Sjohnlev 	mutex_exit(&vdp->xs_iomutex);
1750843e1988Sjohnlev 	mutex_exit(&vdp->xs_cbmutex);
17517f0b8309SEdward Pilatowicz 
17527f0b8309SEdward Pilatowicz 	/* wait for all io to dtrain and destroy io taskq */
1753843e1988Sjohnlev 	ddi_taskq_destroy(vdp->xs_iotaskq);
17547f0b8309SEdward Pilatowicz 
17557f0b8309SEdward Pilatowicz 	/* tear down block-configure watch */
17567f0b8309SEdward Pilatowicz 	mutex_enter(&vdp->xs_cbmutex);
17577f0b8309SEdward Pilatowicz 	xvdi_remove_xb_watch_handlers(dip);
17587f0b8309SEdward Pilatowicz 	mutex_exit(&vdp->xs_cbmutex);
17597f0b8309SEdward Pilatowicz 
1760843e1988Sjohnlev errout2:
17617f0b8309SEdward Pilatowicz 	/* remove kstats */
17627f0b8309SEdward Pilatowicz 	kstat_delete(vdp->xs_kstats);
17637f0b8309SEdward Pilatowicz 
17647f0b8309SEdward Pilatowicz errout1:
17657f0b8309SEdward Pilatowicz 	/* free up driver state */
1766843e1988Sjohnlev 	ddi_set_driver_private(dip, NULL);
1767843e1988Sjohnlev 	cv_destroy(&vdp->xs_iocv);
1768843e1988Sjohnlev 	cv_destroy(&vdp->xs_ionumcv);
1769843e1988Sjohnlev 	mutex_destroy(&vdp->xs_cbmutex);
1770843e1988Sjohnlev 	mutex_destroy(&vdp->xs_iomutex);
1771843e1988Sjohnlev 	ddi_soft_state_free(xdb_statep, instance);
17727f0b8309SEdward Pilatowicz 
1773843e1988Sjohnlev 	return (DDI_FAILURE);
1774843e1988Sjohnlev }
1775843e1988Sjohnlev 
1776843e1988Sjohnlev /*ARGSUSED*/
1777843e1988Sjohnlev static int
xdb_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)1778843e1988Sjohnlev xdb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1779843e1988Sjohnlev {
1780843e1988Sjohnlev 	int instance = ddi_get_instance(dip);
1781843e1988Sjohnlev 	xdb_t *vdp = XDB_INST2SOFTS(instance);
1782843e1988Sjohnlev 
1783843e1988Sjohnlev 	switch (cmd) {
1784843e1988Sjohnlev 	case DDI_SUSPEND:
1785843e1988Sjohnlev 		return (DDI_FAILURE);
1786843e1988Sjohnlev 	case DDI_DETACH:
1787843e1988Sjohnlev 		break;
1788843e1988Sjohnlev 	default:
1789843e1988Sjohnlev 		return (DDI_FAILURE);
1790843e1988Sjohnlev 	}
1791843e1988Sjohnlev 
1792843e1988Sjohnlev 	/* DDI_DETACH handling */
1793843e1988Sjohnlev 
17947f0b8309SEdward Pilatowicz 	/* refuse to detach if we're still in use by the frontend */
1795843e1988Sjohnlev 	mutex_enter(&vdp->xs_iomutex);
17967f0b8309SEdward Pilatowicz 	if (vdp->xs_if_connected) {
1797843e1988Sjohnlev 		mutex_exit(&vdp->xs_iomutex);
1798843e1988Sjohnlev 		return (DDI_FAILURE);
1799843e1988Sjohnlev 	}
18007f0b8309SEdward Pilatowicz 	vdp->xs_send_buf = B_FALSE;
18017f0b8309SEdward Pilatowicz 	cv_broadcast(&vdp->xs_iocv);
1802843e1988Sjohnlev 	mutex_exit(&vdp->xs_iomutex);
1803843e1988Sjohnlev 
1804843e1988Sjohnlev 	xvdi_remove_event_handler(dip, NULL);
1805843e1988Sjohnlev 	(void) xvdi_post_event(dip, XEN_HP_REMOVE);
1806843e1988Sjohnlev 
1807843e1988Sjohnlev 	ddi_taskq_destroy(vdp->xs_iotaskq);
18087f0b8309SEdward Pilatowicz 
18097f0b8309SEdward Pilatowicz 	mutex_enter(&vdp->xs_cbmutex);
18107f0b8309SEdward Pilatowicz 	xvdi_remove_xb_watch_handlers(dip);
18117f0b8309SEdward Pilatowicz 	mutex_exit(&vdp->xs_cbmutex);
18127f0b8309SEdward Pilatowicz 
1813843e1988Sjohnlev 	cv_destroy(&vdp->xs_iocv);
1814843e1988Sjohnlev 	cv_destroy(&vdp->xs_ionumcv);
1815843e1988Sjohnlev 	mutex_destroy(&vdp->xs_cbmutex);
1816843e1988Sjohnlev 	mutex_destroy(&vdp->xs_iomutex);
1817843e1988Sjohnlev 	kstat_delete(vdp->xs_kstats);
1818843e1988Sjohnlev 	ddi_set_driver_private(dip, NULL);
1819843e1988Sjohnlev 	ddi_soft_state_free(xdb_statep, instance);
1820843e1988Sjohnlev 
1821843e1988Sjohnlev 	XDB_DBPRINT(XDB_DBG_INFO, (CE_NOTE, "xdb@%s: detached!",
1822843e1988Sjohnlev 	    ddi_get_name_addr(dip)));
1823843e1988Sjohnlev 	return (DDI_SUCCESS);
1824843e1988Sjohnlev }
1825843e1988Sjohnlev 
1826843e1988Sjohnlev static struct dev_ops xdb_dev_ops = {
1827843e1988Sjohnlev 	DEVO_REV,	/* devo_rev */
1828843e1988Sjohnlev 	0,		/* devo_refcnt */
1829843e1988Sjohnlev 	ddi_getinfo_1to1, /* devo_getinfo */
1830843e1988Sjohnlev 	nulldev,	/* devo_identify */
1831843e1988Sjohnlev 	nulldev,	/* devo_probe */
1832843e1988Sjohnlev 	xdb_attach,	/* devo_attach */
1833843e1988Sjohnlev 	xdb_detach,	/* devo_detach */
1834843e1988Sjohnlev 	nodev,		/* devo_reset */
1835843e1988Sjohnlev 	NULL,		/* devo_cb_ops */
1836843e1988Sjohnlev 	NULL,		/* devo_bus_ops */
183719397407SSherry Moore 	NULL,		/* power */
18387f0b8309SEdward Pilatowicz 	ddi_quiesce_not_needed, /* quiesce */
1839843e1988Sjohnlev };
1840843e1988Sjohnlev 
1841843e1988Sjohnlev /*
1842843e1988Sjohnlev  * Module linkage information for the kernel.
1843843e1988Sjohnlev  */
1844843e1988Sjohnlev static struct modldrv modldrv = {
1845843e1988Sjohnlev 	&mod_driverops,			/* Type of module. */
18467f0b8309SEdward Pilatowicz 	"vbd backend driver",		/* Name of the module */
1847843e1988Sjohnlev 	&xdb_dev_ops			/* driver ops */
1848843e1988Sjohnlev };
1849843e1988Sjohnlev 
1850843e1988Sjohnlev static struct modlinkage xdb_modlinkage = {
1851843e1988Sjohnlev 	MODREV_1,
1852843e1988Sjohnlev 	&modldrv,
1853843e1988Sjohnlev 	NULL
1854843e1988Sjohnlev };
1855843e1988Sjohnlev 
1856843e1988Sjohnlev int
_init(void)1857843e1988Sjohnlev _init(void)
1858843e1988Sjohnlev {
1859843e1988Sjohnlev 	int rv;
1860843e1988Sjohnlev 
1861843e1988Sjohnlev 	if ((rv = ddi_soft_state_init((void **)&xdb_statep,
1862843e1988Sjohnlev 	    sizeof (xdb_t), 0)) == 0)
1863843e1988Sjohnlev 		if ((rv = mod_install(&xdb_modlinkage)) != 0)
1864843e1988Sjohnlev 			ddi_soft_state_fini((void **)&xdb_statep);
1865843e1988Sjohnlev 	return (rv);
1866843e1988Sjohnlev }
1867843e1988Sjohnlev 
1868843e1988Sjohnlev int
_fini(void)1869843e1988Sjohnlev _fini(void)
1870843e1988Sjohnlev {
1871843e1988Sjohnlev 	int rv;
1872843e1988Sjohnlev 
1873843e1988Sjohnlev 	if ((rv = mod_remove(&xdb_modlinkage)) != 0)
1874843e1988Sjohnlev 		return (rv);
1875843e1988Sjohnlev 	ddi_soft_state_fini((void **)&xdb_statep);
1876843e1988Sjohnlev 	return (rv);
1877843e1988Sjohnlev }
1878843e1988Sjohnlev 
1879843e1988Sjohnlev int
_info(struct modinfo * modinfop)1880843e1988Sjohnlev _info(struct modinfo *modinfop)
1881843e1988Sjohnlev {
1882843e1988Sjohnlev 	return (mod_info(&xdb_modlinkage, modinfop));
1883843e1988Sjohnlev }
1884a576ab5bSrab 
1885a576ab5bSrab static int
xdb_get_request(xdb_t * vdp,blkif_request_t * req)1886a576ab5bSrab xdb_get_request(xdb_t *vdp, blkif_request_t *req)
1887a576ab5bSrab {
1888a576ab5bSrab 	void *src = xvdi_ring_get_request(vdp->xs_ring);
1889a576ab5bSrab 
1890a576ab5bSrab 	if (src == NULL)
1891a576ab5bSrab 		return (0);
1892a576ab5bSrab 
1893a576ab5bSrab 	switch (vdp->xs_blk_protocol) {
1894a576ab5bSrab 	case BLKIF_PROTOCOL_NATIVE:
1895a576ab5bSrab 		(void) memcpy(req, src, sizeof (*req));
1896a576ab5bSrab 		break;
1897a576ab5bSrab 	case BLKIF_PROTOCOL_X86_32:
1898a576ab5bSrab 		blkif_get_x86_32_req(req, src);
1899a576ab5bSrab 		break;
1900a576ab5bSrab 	case BLKIF_PROTOCOL_X86_64:
1901a576ab5bSrab 		blkif_get_x86_64_req(req, src);
1902a576ab5bSrab 		break;
1903a576ab5bSrab 	default:
1904a576ab5bSrab 		cmn_err(CE_PANIC, "xdb@%s: unrecognised protocol: %d",
1905a576ab5bSrab 		    ddi_get_name_addr(vdp->xs_dip),
1906a576ab5bSrab 		    vdp->xs_blk_protocol);
1907a576ab5bSrab 	}
1908a576ab5bSrab 	return (1);
1909a576ab5bSrab }
1910a576ab5bSrab 
1911a576ab5bSrab static int
xdb_push_response(xdb_t * vdp,uint64_t id,uint8_t op,uint16_t status)1912a576ab5bSrab xdb_push_response(xdb_t *vdp, uint64_t id, uint8_t op, uint16_t status)
1913a576ab5bSrab {
1914a576ab5bSrab 	ddi_acc_handle_t acchdl = vdp->xs_ring_hdl;
1915a576ab5bSrab 	blkif_response_t *rsp = xvdi_ring_get_response(vdp->xs_ring);
1916a576ab5bSrab 	blkif_x86_32_response_t *rsp_32 = (blkif_x86_32_response_t *)rsp;
1917a576ab5bSrab 	blkif_x86_64_response_t *rsp_64 = (blkif_x86_64_response_t *)rsp;
1918a576ab5bSrab 
1919a576ab5bSrab 	ASSERT(rsp);
1920a576ab5bSrab 
1921a576ab5bSrab 	switch (vdp->xs_blk_protocol) {
1922a576ab5bSrab 	case BLKIF_PROTOCOL_NATIVE:
1923a576ab5bSrab 		ddi_put64(acchdl, &rsp->id, id);
1924a576ab5bSrab 		ddi_put8(acchdl, &rsp->operation, op);
1925a576ab5bSrab 		ddi_put16(acchdl, (uint16_t *)&rsp->status,
1926a576ab5bSrab 		    status == 0 ? BLKIF_RSP_OKAY : BLKIF_RSP_ERROR);
1927a576ab5bSrab 		break;
1928a576ab5bSrab 	case BLKIF_PROTOCOL_X86_32:
1929a576ab5bSrab 		ddi_put64(acchdl, &rsp_32->id, id);
1930a576ab5bSrab 		ddi_put8(acchdl, &rsp_32->operation, op);
1931a576ab5bSrab 		ddi_put16(acchdl, (uint16_t *)&rsp_32->status,
1932a576ab5bSrab 		    status == 0 ? BLKIF_RSP_OKAY : BLKIF_RSP_ERROR);
1933a576ab5bSrab 		break;
1934a576ab5bSrab 	case BLKIF_PROTOCOL_X86_64:
1935a576ab5bSrab 		ddi_put64(acchdl, &rsp_64->id, id);
1936a576ab5bSrab 		ddi_put8(acchdl, &rsp_64->operation, op);
1937a576ab5bSrab 		ddi_put16(acchdl, (uint16_t *)&rsp_64->status,
1938a576ab5bSrab 		    status == 0 ? BLKIF_RSP_OKAY : BLKIF_RSP_ERROR);
1939a576ab5bSrab 		break;
1940a576ab5bSrab 	default:
1941a576ab5bSrab 		cmn_err(CE_PANIC, "xdb@%s: unrecognised protocol: %d",
1942a576ab5bSrab 		    ddi_get_name_addr(vdp->xs_dip),
1943a576ab5bSrab 		    vdp->xs_blk_protocol);
1944a576ab5bSrab 	}
1945a576ab5bSrab 
1946a576ab5bSrab 	return (xvdi_ring_push_response(vdp->xs_ring));
1947a576ab5bSrab }
1948a576ab5bSrab 
1949a576ab5bSrab static void
blkif_get_x86_32_req(blkif_request_t * dst,blkif_x86_32_request_t * src)1950a576ab5bSrab blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src)
1951a576ab5bSrab {
1952a576ab5bSrab 	int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST;
1953a576ab5bSrab 	dst->operation = src->operation;
1954a576ab5bSrab 	dst->nr_segments = src->nr_segments;
1955a576ab5bSrab 	dst->handle = src->handle;
1956a576ab5bSrab 	dst->id = src->id;
1957a576ab5bSrab 	dst->sector_number = src->sector_number;
1958a576ab5bSrab 	if (n > src->nr_segments)
1959a576ab5bSrab 		n = src->nr_segments;
1960a576ab5bSrab 	for (i = 0; i < n; i++)
1961a576ab5bSrab 		dst->seg[i] = src->seg[i];
1962a576ab5bSrab }
1963a576ab5bSrab 
1964a576ab5bSrab static void
blkif_get_x86_64_req(blkif_request_t * dst,blkif_x86_64_request_t * src)1965a576ab5bSrab blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src)
1966a576ab5bSrab {
1967a576ab5bSrab 	int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST;
1968a576ab5bSrab 	dst->operation = src->operation;
1969a576ab5bSrab 	dst->nr_segments = src->nr_segments;
1970a576ab5bSrab 	dst->handle = src->handle;
1971a576ab5bSrab 	dst->id = src->id;
1972a576ab5bSrab 	dst->sector_number = src->sector_number;
1973a576ab5bSrab 	if (n > src->nr_segments)
1974a576ab5bSrab 		n = src->nr_segments;
1975a576ab5bSrab 	for (i = 0; i < n; i++)
1976a576ab5bSrab 		dst->seg[i] = src->seg[i];
1977a576ab5bSrab }
1978