xref: /freebsd/sys/dev/xen/blkfront/blkfront.c (revision b834eea6971bf6792440f01332a44d356951eb64)
189e0f4d2SKip Macy /*
28698b76cSKip Macy  * XenBSD block device driver
38698b76cSKip Macy  *
433eebb6aSJustin T. Gibbs  * Copyright (c) 2010-2013 Spectra Logic Corporation
5e4808c4bSKip Macy  * Copyright (c) 2009 Scott Long, Yahoo!
68698b76cSKip Macy  * Copyright (c) 2009 Frank Suchomel, Citrix
79999d2cbSKip Macy  * Copyright (c) 2009 Doug F. Rabson, Citrix
89999d2cbSKip Macy  * Copyright (c) 2005 Kip Macy
99999d2cbSKip Macy  * Copyright (c) 2003-2004, Keir Fraser & Steve Hand
109999d2cbSKip Macy  * Modifications by Mark A. Williamson are (c) Intel Research Cambridge
119999d2cbSKip Macy  *
129999d2cbSKip Macy  *
139999d2cbSKip Macy  * Permission is hereby granted, free of charge, to any person obtaining a copy
149999d2cbSKip Macy  * of this software and associated documentation files (the "Software"), to
159999d2cbSKip Macy  * deal in the Software without restriction, including without limitation the
169999d2cbSKip Macy  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
179999d2cbSKip Macy  * sell copies of the Software, and to permit persons to whom the Software is
189999d2cbSKip Macy  * furnished to do so, subject to the following conditions:
199999d2cbSKip Macy  *
209999d2cbSKip Macy  * The above copyright notice and this permission notice shall be included in
219999d2cbSKip Macy  * all copies or substantial portions of the Software.
229999d2cbSKip Macy  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
239999d2cbSKip Macy  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
249999d2cbSKip Macy  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
259999d2cbSKip Macy  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
269999d2cbSKip Macy  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
279999d2cbSKip Macy  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
289999d2cbSKip Macy  * DEALINGS IN THE SOFTWARE.
2989e0f4d2SKip Macy  */
3089e0f4d2SKip Macy 
3189e0f4d2SKip Macy #include <sys/cdefs.h>
3289e0f4d2SKip Macy __FBSDID("$FreeBSD$");
3389e0f4d2SKip Macy 
3489e0f4d2SKip Macy #include <sys/param.h>
3589e0f4d2SKip Macy #include <sys/systm.h>
3689e0f4d2SKip Macy #include <sys/malloc.h>
3789e0f4d2SKip Macy #include <sys/kernel.h>
3889e0f4d2SKip Macy #include <vm/vm.h>
3989e0f4d2SKip Macy #include <vm/pmap.h>
4089e0f4d2SKip Macy 
4189e0f4d2SKip Macy #include <sys/bio.h>
4289e0f4d2SKip Macy #include <sys/bus.h>
4389e0f4d2SKip Macy #include <sys/conf.h>
4489e0f4d2SKip Macy #include <sys/module.h>
458b8bfa35SJustin T. Gibbs #include <sys/sysctl.h>
4689e0f4d2SKip Macy 
4789e0f4d2SKip Macy #include <machine/bus.h>
4889e0f4d2SKip Macy #include <sys/rman.h>
4989e0f4d2SKip Macy #include <machine/resource.h>
5089e0f4d2SKip Macy #include <machine/intr_machdep.h>
5189e0f4d2SKip Macy #include <machine/vmparam.h>
52e4808c4bSKip Macy #include <sys/bus_dma.h>
5389e0f4d2SKip Macy 
54ff662b5cSJustin T. Gibbs #include <machine/_inttypes.h>
5589e0f4d2SKip Macy #include <machine/xen/xen-os.h>
562913e88cSRobert Watson #include <machine/xen/xenvar.h>
5712678024SDoug Rabson #include <machine/xen/xenfunc.h>
58ff662b5cSJustin T. Gibbs 
5912678024SDoug Rabson #include <xen/hypervisor.h>
603a6d1fcfSKip Macy #include <xen/xen_intr.h>
613a6d1fcfSKip Macy #include <xen/evtchn.h>
6212678024SDoug Rabson #include <xen/gnttab.h>
6389e0f4d2SKip Macy #include <xen/interface/grant_table.h>
6423dc5621SKip Macy #include <xen/interface/io/protocols.h>
6523dc5621SKip Macy #include <xen/xenbus/xenbusvar.h>
6689e0f4d2SKip Macy 
6789e0f4d2SKip Macy #include <geom/geom_disk.h>
6889e0f4d2SKip Macy 
6989e0f4d2SKip Macy #include <dev/xen/blkfront/block.h>
7089e0f4d2SKip Macy 
7123dc5621SKip Macy #include "xenbus_if.h"
7223dc5621SKip Macy 
73fac3fd80SJustin T. Gibbs /*--------------------------- Forward Declarations ---------------------------*/
7433eebb6aSJustin T. Gibbs static void xbd_closing(device_t);
75fac3fd80SJustin T. Gibbs static void xbd_startio(struct xbd_softc *sc);
7689e0f4d2SKip Macy 
77fac3fd80SJustin T. Gibbs /*---------------------------------- Macros ----------------------------------*/
78fac3fd80SJustin T. Gibbs #if 0
79fac3fd80SJustin T. Gibbs #define DPRINTK(fmt, args...) printf("[XEN] %s:%d: " fmt ".\n", __func__, __LINE__, ##args)
80fac3fd80SJustin T. Gibbs #else
81fac3fd80SJustin T. Gibbs #define DPRINTK(fmt, args...)
82fac3fd80SJustin T. Gibbs #endif
83fac3fd80SJustin T. Gibbs 
84fac3fd80SJustin T. Gibbs #define XBD_SECTOR_SHFT		9
85ff662b5cSJustin T. Gibbs 
86fac3fd80SJustin T. Gibbs /*---------------------------- Global Static Data ----------------------------*/
87fac3fd80SJustin T. Gibbs static MALLOC_DEFINE(M_XENBLOCKFRONT, "xbd", "Xen Block Front driver data");
8889e0f4d2SKip Macy 
89fac3fd80SJustin T. Gibbs /*---------------------------- Command Processing ----------------------------*/
90127a9483SJustin T. Gibbs static void
91127a9483SJustin T. Gibbs xbd_freeze(struct xbd_softc *sc, xbd_flag_t xbd_flag)
92127a9483SJustin T. Gibbs {
93127a9483SJustin T. Gibbs 	if (xbd_flag != XBDF_NONE && (sc->xbd_flags & xbd_flag) != 0)
94127a9483SJustin T. Gibbs 		return;
95127a9483SJustin T. Gibbs 
96127a9483SJustin T. Gibbs 	sc->xbd_flags |= xbd_flag;
97127a9483SJustin T. Gibbs 	sc->xbd_qfrozen_cnt++;
98127a9483SJustin T. Gibbs }
99127a9483SJustin T. Gibbs 
100127a9483SJustin T. Gibbs static void
101127a9483SJustin T. Gibbs xbd_thaw(struct xbd_softc *sc, xbd_flag_t xbd_flag)
102127a9483SJustin T. Gibbs {
103127a9483SJustin T. Gibbs 	if (xbd_flag != XBDF_NONE && (sc->xbd_flags & xbd_flag) == 0)
104127a9483SJustin T. Gibbs 		return;
105127a9483SJustin T. Gibbs 
106*b834eea6SJustin T. Gibbs 	if (sc->xbd_qfrozen_cnt == 0)
107127a9483SJustin T. Gibbs 		panic("%s: Thaw with flag 0x%x while not frozen.",
108127a9483SJustin T. Gibbs 		    __func__, xbd_flag);
109127a9483SJustin T. Gibbs 
110127a9483SJustin T. Gibbs 	sc->xbd_flags &= ~xbd_flag;
111127a9483SJustin T. Gibbs 	sc->xbd_qfrozen_cnt--;
112127a9483SJustin T. Gibbs }
113127a9483SJustin T. Gibbs 
114fac3fd80SJustin T. Gibbs static inline void
115cdf5d66fSJustin T. Gibbs xbd_flush_requests(struct xbd_softc *sc)
116fac3fd80SJustin T. Gibbs {
117fac3fd80SJustin T. Gibbs 	int notify;
118e4808c4bSKip Macy 
119fac3fd80SJustin T. Gibbs 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&sc->xbd_ring, notify);
12089e0f4d2SKip Macy 
121fac3fd80SJustin T. Gibbs 	if (notify)
122fac3fd80SJustin T. Gibbs 		notify_remote_via_irq(sc->xbd_irq);
123fac3fd80SJustin T. Gibbs }
12489e0f4d2SKip Macy 
12523dc5621SKip Macy static void
126fac3fd80SJustin T. Gibbs xbd_free_command(struct xbd_command *cm)
12723dc5621SKip Macy {
12823dc5621SKip Macy 
129e2c1fe90SJustin T. Gibbs 	KASSERT((cm->cm_flags & XBDCF_Q_MASK) == XBD_Q_NONE,
130e2c1fe90SJustin T. Gibbs 	    ("Freeing command that is still on queue %d.",
131e2c1fe90SJustin T. Gibbs 	    cm->cm_flags & XBDCF_Q_MASK));
13223dc5621SKip Macy 
133e2c1fe90SJustin T. Gibbs 	cm->cm_flags = XBDCF_INITIALIZER;
134fac3fd80SJustin T. Gibbs 	cm->cm_bp = NULL;
135fac3fd80SJustin T. Gibbs 	cm->cm_complete = NULL;
136e2c1fe90SJustin T. Gibbs 	xbd_enqueue_cm(cm, XBD_Q_FREE);
137127a9483SJustin T. Gibbs 	xbd_thaw(cm->cm_sc, XBDF_CM_SHORTAGE);
13823dc5621SKip Macy }
13923dc5621SKip Macy 
14089e0f4d2SKip Macy static void
141fac3fd80SJustin T. Gibbs xbd_queue_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
14289e0f4d2SKip Macy {
143fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc;
144fac3fd80SJustin T. Gibbs 	struct xbd_command *cm;
145fac3fd80SJustin T. Gibbs 	blkif_request_t	*ring_req;
146fac3fd80SJustin T. Gibbs 	struct blkif_request_segment *sg;
147fac3fd80SJustin T. Gibbs 	struct blkif_request_segment *last_block_sg;
148fac3fd80SJustin T. Gibbs 	grant_ref_t *sg_ref;
149fac3fd80SJustin T. Gibbs 	vm_paddr_t buffer_ma;
150fac3fd80SJustin T. Gibbs 	uint64_t fsect, lsect;
151fac3fd80SJustin T. Gibbs 	int ref;
152fac3fd80SJustin T. Gibbs 	int op;
153fac3fd80SJustin T. Gibbs 	int block_segs;
15489e0f4d2SKip Macy 
155fac3fd80SJustin T. Gibbs 	cm = arg;
156fac3fd80SJustin T. Gibbs 	sc = cm->cm_sc;
157fac3fd80SJustin T. Gibbs 
158fac3fd80SJustin T. Gibbs 	if (error) {
159fac3fd80SJustin T. Gibbs 		printf("error %d in xbd_queue_cb\n", error);
160fac3fd80SJustin T. Gibbs 		cm->cm_bp->bio_error = EIO;
161fac3fd80SJustin T. Gibbs 		biodone(cm->cm_bp);
162fac3fd80SJustin T. Gibbs 		xbd_free_command(cm);
16389e0f4d2SKip Macy 		return;
16489e0f4d2SKip Macy 	}
16589e0f4d2SKip Macy 
166fac3fd80SJustin T. Gibbs 	/* Fill out a communications ring structure. */
167fac3fd80SJustin T. Gibbs 	ring_req = RING_GET_REQUEST(&sc->xbd_ring, sc->xbd_ring.req_prod_pvt);
168fac3fd80SJustin T. Gibbs 	sc->xbd_ring.req_prod_pvt++;
169fac3fd80SJustin T. Gibbs 	ring_req->id = cm->cm_id;
170fac3fd80SJustin T. Gibbs 	ring_req->operation = cm->cm_operation;
171fac3fd80SJustin T. Gibbs 	ring_req->sector_number = cm->cm_sector_number;
172fac3fd80SJustin T. Gibbs 	ring_req->handle = (blkif_vdev_t)(uintptr_t)sc->xbd_disk;
173fac3fd80SJustin T. Gibbs 	ring_req->nr_segments = nsegs;
174fac3fd80SJustin T. Gibbs 	cm->cm_nseg = nsegs;
175fac3fd80SJustin T. Gibbs 
176fac3fd80SJustin T. Gibbs 	block_segs    = MIN(nsegs, BLKIF_MAX_SEGMENTS_PER_HEADER_BLOCK);
177fac3fd80SJustin T. Gibbs 	sg            = ring_req->seg;
178fac3fd80SJustin T. Gibbs 	last_block_sg = sg + block_segs;
179fac3fd80SJustin T. Gibbs 	sg_ref        = cm->cm_sg_refs;
180fac3fd80SJustin T. Gibbs 
181fac3fd80SJustin T. Gibbs 	while (1) {
182fac3fd80SJustin T. Gibbs 
183fac3fd80SJustin T. Gibbs 		while (sg < last_block_sg) {
184fac3fd80SJustin T. Gibbs 			buffer_ma = segs->ds_addr;
185fac3fd80SJustin T. Gibbs 			fsect = (buffer_ma & PAGE_MASK) >> XBD_SECTOR_SHFT;
186fac3fd80SJustin T. Gibbs 			lsect = fsect + (segs->ds_len  >> XBD_SECTOR_SHFT) - 1;
187fac3fd80SJustin T. Gibbs 
188fac3fd80SJustin T. Gibbs 			KASSERT(lsect <= 7, ("XEN disk driver data cannot "
189fac3fd80SJustin T. Gibbs 			    "cross a page boundary"));
190fac3fd80SJustin T. Gibbs 
191fac3fd80SJustin T. Gibbs 			/* install a grant reference. */
192fac3fd80SJustin T. Gibbs 			ref = gnttab_claim_grant_reference(&cm->cm_gref_head);
193fac3fd80SJustin T. Gibbs 
194e4808c4bSKip Macy 			/*
195fac3fd80SJustin T. Gibbs 			 * GNTTAB_LIST_END == 0xffffffff, but it is private
196fac3fd80SJustin T. Gibbs 			 * to gnttab.c.
197e4808c4bSKip Macy 			 */
198fac3fd80SJustin T. Gibbs 			KASSERT(ref != ~0, ("grant_reference failed"));
199fac3fd80SJustin T. Gibbs 
200fac3fd80SJustin T. Gibbs 			gnttab_grant_foreign_access_ref(
201fac3fd80SJustin T. Gibbs 			    ref,
202fac3fd80SJustin T. Gibbs 			    xenbus_get_otherend_id(sc->xbd_dev),
203fac3fd80SJustin T. Gibbs 			    buffer_ma >> PAGE_SHIFT,
204fac3fd80SJustin T. Gibbs 			    ring_req->operation == BLKIF_OP_WRITE);
205fac3fd80SJustin T. Gibbs 
206fac3fd80SJustin T. Gibbs 			*sg_ref = ref;
207fac3fd80SJustin T. Gibbs 			*sg = (struct blkif_request_segment) {
208fac3fd80SJustin T. Gibbs 				.gref       = ref,
209fac3fd80SJustin T. Gibbs 				.first_sect = fsect,
210fac3fd80SJustin T. Gibbs 				.last_sect  = lsect
211fac3fd80SJustin T. Gibbs 			};
212fac3fd80SJustin T. Gibbs 			sg++;
213fac3fd80SJustin T. Gibbs 			sg_ref++;
214fac3fd80SJustin T. Gibbs 			segs++;
215fac3fd80SJustin T. Gibbs 			nsegs--;
216fac3fd80SJustin T. Gibbs 		}
217fac3fd80SJustin T. Gibbs 		block_segs = MIN(nsegs, BLKIF_MAX_SEGMENTS_PER_SEGMENT_BLOCK);
218fac3fd80SJustin T. Gibbs 		if (block_segs == 0)
219fac3fd80SJustin T. Gibbs 			break;
220fac3fd80SJustin T. Gibbs 
221fac3fd80SJustin T. Gibbs 		sg = BLKRING_GET_SEG_BLOCK(&sc->xbd_ring,
222fac3fd80SJustin T. Gibbs 		    sc->xbd_ring.req_prod_pvt);
223fac3fd80SJustin T. Gibbs 		sc->xbd_ring.req_prod_pvt++;
224fac3fd80SJustin T. Gibbs 		last_block_sg = sg + block_segs;
225fac3fd80SJustin T. Gibbs 	}
226fac3fd80SJustin T. Gibbs 
227fac3fd80SJustin T. Gibbs 	if (cm->cm_operation == BLKIF_OP_READ)
228fac3fd80SJustin T. Gibbs 		op = BUS_DMASYNC_PREREAD;
229fac3fd80SJustin T. Gibbs 	else if (cm->cm_operation == BLKIF_OP_WRITE)
230fac3fd80SJustin T. Gibbs 		op = BUS_DMASYNC_PREWRITE;
231fac3fd80SJustin T. Gibbs 	else
232fac3fd80SJustin T. Gibbs 		op = 0;
233fac3fd80SJustin T. Gibbs 	bus_dmamap_sync(sc->xbd_io_dmat, cm->cm_map, op);
234fac3fd80SJustin T. Gibbs 
235fac3fd80SJustin T. Gibbs 	gnttab_free_grant_references(cm->cm_gref_head);
236fac3fd80SJustin T. Gibbs 
237e2c1fe90SJustin T. Gibbs 	xbd_enqueue_cm(cm, XBD_Q_BUSY);
238fac3fd80SJustin T. Gibbs 
239fac3fd80SJustin T. Gibbs 	/*
240127a9483SJustin T. Gibbs 	 * If bus dma had to asynchronously call us back to dispatch
241127a9483SJustin T. Gibbs 	 * this command, we are no longer executing in the context of
242127a9483SJustin T. Gibbs 	 * xbd_startio().  Thus we cannot rely on xbd_startio()'s call to
243127a9483SJustin T. Gibbs 	 * xbd_flush_requests() to publish this command to the backend
244127a9483SJustin T. Gibbs 	 * along with any other commands that it could batch.
245fac3fd80SJustin T. Gibbs 	 */
246127a9483SJustin T. Gibbs 	if ((cm->cm_flags & XBDCF_ASYNC_MAPPING) != 0)
247cdf5d66fSJustin T. Gibbs 		xbd_flush_requests(sc);
248fac3fd80SJustin T. Gibbs 
249fac3fd80SJustin T. Gibbs 	return;
250fac3fd80SJustin T. Gibbs }
251fac3fd80SJustin T. Gibbs 
252fac3fd80SJustin T. Gibbs static int
253fac3fd80SJustin T. Gibbs xbd_queue_request(struct xbd_softc *sc, struct xbd_command *cm)
254fac3fd80SJustin T. Gibbs {
255fac3fd80SJustin T. Gibbs 	int error;
256fac3fd80SJustin T. Gibbs 
257fac3fd80SJustin T. Gibbs 	error = bus_dmamap_load(sc->xbd_io_dmat, cm->cm_map, cm->cm_data,
258fac3fd80SJustin T. Gibbs 	    cm->cm_datalen, xbd_queue_cb, cm, 0);
259fac3fd80SJustin T. Gibbs 	if (error == EINPROGRESS) {
260127a9483SJustin T. Gibbs 		/*
261127a9483SJustin T. Gibbs 		 * Maintain queuing order by freezing the queue.  The next
262127a9483SJustin T. Gibbs 		 * command may not require as many resources as the command
263127a9483SJustin T. Gibbs 		 * we just attempted to map, so we can't rely on bus dma
264127a9483SJustin T. Gibbs 		 * blocking for it too.
265127a9483SJustin T. Gibbs 		 */
266127a9483SJustin T. Gibbs 		xbd_freeze(sc, XBDF_NONE);
267127a9483SJustin T. Gibbs 		cm->cm_flags |= XBDCF_FROZEN|XBDCF_ASYNC_MAPPING;
268fac3fd80SJustin T. Gibbs 		return (0);
269fac3fd80SJustin T. Gibbs 	}
270fac3fd80SJustin T. Gibbs 
271fac3fd80SJustin T. Gibbs 	return (error);
272fac3fd80SJustin T. Gibbs }
273fac3fd80SJustin T. Gibbs 
274fac3fd80SJustin T. Gibbs static void
275fac3fd80SJustin T. Gibbs xbd_restart_queue_callback(void *arg)
276fac3fd80SJustin T. Gibbs {
277fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = arg;
278fac3fd80SJustin T. Gibbs 
27933eebb6aSJustin T. Gibbs 	mtx_lock(&sc->xbd_io_lock);
280e4808c4bSKip Macy 
281127a9483SJustin T. Gibbs 	xbd_thaw(sc, XBDF_GNT_SHORTAGE);
282127a9483SJustin T. Gibbs 
28333eebb6aSJustin T. Gibbs 	xbd_startio(sc);
284e4808c4bSKip Macy 
28533eebb6aSJustin T. Gibbs 	mtx_unlock(&sc->xbd_io_lock);
286fac3fd80SJustin T. Gibbs }
287fac3fd80SJustin T. Gibbs 
288fac3fd80SJustin T. Gibbs static struct xbd_command *
289fac3fd80SJustin T. Gibbs xbd_bio_command(struct xbd_softc *sc)
290fac3fd80SJustin T. Gibbs {
291fac3fd80SJustin T. Gibbs 	struct xbd_command *cm;
292fac3fd80SJustin T. Gibbs 	struct bio *bp;
293fac3fd80SJustin T. Gibbs 
294e2c1fe90SJustin T. Gibbs 	if (unlikely(sc->xbd_state != XBD_STATE_CONNECTED))
295fac3fd80SJustin T. Gibbs 		return (NULL);
296fac3fd80SJustin T. Gibbs 
297fac3fd80SJustin T. Gibbs 	bp = xbd_dequeue_bio(sc);
298fac3fd80SJustin T. Gibbs 	if (bp == NULL)
299fac3fd80SJustin T. Gibbs 		return (NULL);
300fac3fd80SJustin T. Gibbs 
301e2c1fe90SJustin T. Gibbs 	if ((cm = xbd_dequeue_cm(sc, XBD_Q_FREE)) == NULL) {
302127a9483SJustin T. Gibbs 		xbd_freeze(sc, XBDF_CM_SHORTAGE);
303fac3fd80SJustin T. Gibbs 		xbd_requeue_bio(sc, bp);
304fac3fd80SJustin T. Gibbs 		return (NULL);
305fac3fd80SJustin T. Gibbs 	}
306fac3fd80SJustin T. Gibbs 
307fac3fd80SJustin T. Gibbs 	if (gnttab_alloc_grant_references(sc->xbd_max_request_segments,
308fac3fd80SJustin T. Gibbs 	    &cm->cm_gref_head) != 0) {
309fac3fd80SJustin T. Gibbs 		gnttab_request_free_callback(&sc->xbd_callback,
310fac3fd80SJustin T. Gibbs 		    xbd_restart_queue_callback, sc,
311fac3fd80SJustin T. Gibbs 		    sc->xbd_max_request_segments);
312127a9483SJustin T. Gibbs 		xbd_freeze(sc, XBDF_GNT_SHORTAGE);
313fac3fd80SJustin T. Gibbs 		xbd_requeue_bio(sc, bp);
314e2c1fe90SJustin T. Gibbs 		xbd_enqueue_cm(cm, XBD_Q_FREE);
315fac3fd80SJustin T. Gibbs 		return (NULL);
316fac3fd80SJustin T. Gibbs 	}
317fac3fd80SJustin T. Gibbs 
318fac3fd80SJustin T. Gibbs 	cm->cm_bp = bp;
319fac3fd80SJustin T. Gibbs 	cm->cm_data = bp->bio_data;
320fac3fd80SJustin T. Gibbs 	cm->cm_datalen = bp->bio_bcount;
321fac3fd80SJustin T. Gibbs 	cm->cm_operation = (bp->bio_cmd == BIO_READ) ?
322fac3fd80SJustin T. Gibbs 	    BLKIF_OP_READ : BLKIF_OP_WRITE;
323fac3fd80SJustin T. Gibbs 	cm->cm_sector_number = (blkif_sector_t)bp->bio_pblkno;
324fac3fd80SJustin T. Gibbs 
325fac3fd80SJustin T. Gibbs 	return (cm);
326fac3fd80SJustin T. Gibbs }
327fac3fd80SJustin T. Gibbs 
328fac3fd80SJustin T. Gibbs /*
329fac3fd80SJustin T. Gibbs  * Dequeue buffers and place them in the shared communication ring.
330fac3fd80SJustin T. Gibbs  * Return when no more requests can be accepted or all buffers have
331fac3fd80SJustin T. Gibbs  * been queued.
332fac3fd80SJustin T. Gibbs  *
333fac3fd80SJustin T. Gibbs  * Signal XEN once the ring has been filled out.
334fac3fd80SJustin T. Gibbs  */
335fac3fd80SJustin T. Gibbs static void
336fac3fd80SJustin T. Gibbs xbd_startio(struct xbd_softc *sc)
337fac3fd80SJustin T. Gibbs {
338fac3fd80SJustin T. Gibbs 	struct xbd_command *cm;
339fac3fd80SJustin T. Gibbs 	int error, queued = 0;
340fac3fd80SJustin T. Gibbs 
341fac3fd80SJustin T. Gibbs 	mtx_assert(&sc->xbd_io_lock, MA_OWNED);
342fac3fd80SJustin T. Gibbs 
343e2c1fe90SJustin T. Gibbs 	if (sc->xbd_state != XBD_STATE_CONNECTED)
344e4808c4bSKip Macy 		return;
345fac3fd80SJustin T. Gibbs 
346fac3fd80SJustin T. Gibbs 	while (RING_FREE_REQUESTS(&sc->xbd_ring) >=
347fac3fd80SJustin T. Gibbs 	    sc->xbd_max_request_blocks) {
348127a9483SJustin T. Gibbs 		if (sc->xbd_qfrozen_cnt != 0)
349fac3fd80SJustin T. Gibbs 			break;
350fac3fd80SJustin T. Gibbs 
351e2c1fe90SJustin T. Gibbs 		cm = xbd_dequeue_cm(sc, XBD_Q_READY);
352fac3fd80SJustin T. Gibbs 
353fac3fd80SJustin T. Gibbs 		if (cm == NULL)
354fac3fd80SJustin T. Gibbs 		    cm = xbd_bio_command(sc);
355fac3fd80SJustin T. Gibbs 
356fac3fd80SJustin T. Gibbs 		if (cm == NULL)
357fac3fd80SJustin T. Gibbs 			break;
358fac3fd80SJustin T. Gibbs 
359fac3fd80SJustin T. Gibbs 		if ((error = xbd_queue_request(sc, cm)) != 0) {
360fac3fd80SJustin T. Gibbs 			printf("xbd_queue_request returned %d\n", error);
361fac3fd80SJustin T. Gibbs 			break;
362fac3fd80SJustin T. Gibbs 		}
363fac3fd80SJustin T. Gibbs 		queued++;
364fac3fd80SJustin T. Gibbs 	}
365fac3fd80SJustin T. Gibbs 
366fac3fd80SJustin T. Gibbs 	if (queued != 0)
367cdf5d66fSJustin T. Gibbs 		xbd_flush_requests(sc);
368e4808c4bSKip Macy }
369e4808c4bSKip Macy 
370e4808c4bSKip Macy static void
37133eebb6aSJustin T. Gibbs xbd_bio_complete(struct xbd_softc *sc, struct xbd_command *cm)
372e4808c4bSKip Macy {
373e4808c4bSKip Macy 	struct bio *bp;
374e4808c4bSKip Macy 
37533eebb6aSJustin T. Gibbs 	bp = cm->cm_bp;
376e4808c4bSKip Macy 
37733eebb6aSJustin T. Gibbs 	if (unlikely(cm->cm_status != BLKIF_RSP_OKAY)) {
378e4808c4bSKip Macy 		disk_err(bp, "disk error" , -1, 0);
37933eebb6aSJustin T. Gibbs 		printf(" status: %x\n", cm->cm_status);
380e4808c4bSKip Macy 		bp->bio_flags |= BIO_ERROR;
381e4808c4bSKip Macy 	}
382e4808c4bSKip Macy 
383e4808c4bSKip Macy 	if (bp->bio_flags & BIO_ERROR)
384e4808c4bSKip Macy 		bp->bio_error = EIO;
385e4808c4bSKip Macy 	else
386e4808c4bSKip Macy 		bp->bio_resid = 0;
387e4808c4bSKip Macy 
38833eebb6aSJustin T. Gibbs 	xbd_free_command(cm);
389e4808c4bSKip Macy 	biodone(bp);
390e4808c4bSKip Macy }
391e4808c4bSKip Macy 
392fac3fd80SJustin T. Gibbs static int
393fac3fd80SJustin T. Gibbs xbd_completion(struct xbd_command *cm)
394fac3fd80SJustin T. Gibbs {
395fac3fd80SJustin T. Gibbs 	gnttab_end_foreign_access_references(cm->cm_nseg, cm->cm_sg_refs);
396fac3fd80SJustin T. Gibbs 	return (BLKIF_SEGS_TO_BLOCKS(cm->cm_nseg));
397fac3fd80SJustin T. Gibbs }
398fac3fd80SJustin T. Gibbs 
399fac3fd80SJustin T. Gibbs static void
400fac3fd80SJustin T. Gibbs xbd_int(void *xsc)
401fac3fd80SJustin T. Gibbs {
402fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = xsc;
403fac3fd80SJustin T. Gibbs 	struct xbd_command *cm;
404fac3fd80SJustin T. Gibbs 	blkif_response_t *bret;
405fac3fd80SJustin T. Gibbs 	RING_IDX i, rp;
406fac3fd80SJustin T. Gibbs 	int op;
407fac3fd80SJustin T. Gibbs 
408fac3fd80SJustin T. Gibbs 	mtx_lock(&sc->xbd_io_lock);
409fac3fd80SJustin T. Gibbs 
410e2c1fe90SJustin T. Gibbs 	if (unlikely(sc->xbd_state == XBD_STATE_DISCONNECTED)) {
411fac3fd80SJustin T. Gibbs 		mtx_unlock(&sc->xbd_io_lock);
412fac3fd80SJustin T. Gibbs 		return;
413fac3fd80SJustin T. Gibbs 	}
414fac3fd80SJustin T. Gibbs 
415fac3fd80SJustin T. Gibbs  again:
416fac3fd80SJustin T. Gibbs 	rp = sc->xbd_ring.sring->rsp_prod;
417fac3fd80SJustin T. Gibbs 	rmb(); /* Ensure we see queued responses up to 'rp'. */
418fac3fd80SJustin T. Gibbs 
419fac3fd80SJustin T. Gibbs 	for (i = sc->xbd_ring.rsp_cons; i != rp;) {
420fac3fd80SJustin T. Gibbs 		bret = RING_GET_RESPONSE(&sc->xbd_ring, i);
421fac3fd80SJustin T. Gibbs 		cm   = &sc->xbd_shadow[bret->id];
422fac3fd80SJustin T. Gibbs 
423e2c1fe90SJustin T. Gibbs 		xbd_remove_cm(cm, XBD_Q_BUSY);
424fac3fd80SJustin T. Gibbs 		i += xbd_completion(cm);
425fac3fd80SJustin T. Gibbs 
426fac3fd80SJustin T. Gibbs 		if (cm->cm_operation == BLKIF_OP_READ)
427fac3fd80SJustin T. Gibbs 			op = BUS_DMASYNC_POSTREAD;
428fac3fd80SJustin T. Gibbs 		else if (cm->cm_operation == BLKIF_OP_WRITE)
429fac3fd80SJustin T. Gibbs 			op = BUS_DMASYNC_POSTWRITE;
430fac3fd80SJustin T. Gibbs 		else
431fac3fd80SJustin T. Gibbs 			op = 0;
432fac3fd80SJustin T. Gibbs 		bus_dmamap_sync(sc->xbd_io_dmat, cm->cm_map, op);
433fac3fd80SJustin T. Gibbs 		bus_dmamap_unload(sc->xbd_io_dmat, cm->cm_map);
434fac3fd80SJustin T. Gibbs 
435fac3fd80SJustin T. Gibbs 		/*
436127a9483SJustin T. Gibbs 		 * Release any hold this command has on future command
437127a9483SJustin T. Gibbs 		 * dispatch.
438fac3fd80SJustin T. Gibbs 		 */
439127a9483SJustin T. Gibbs 		if ((cm->cm_flags & XBDCF_FROZEN) != 0) {
440127a9483SJustin T. Gibbs 			xbd_thaw(sc, XBDF_NONE);
441127a9483SJustin T. Gibbs 			cm->cm_flags &= ~XBDCF_FROZEN;
442127a9483SJustin T. Gibbs 		}
443fac3fd80SJustin T. Gibbs 
444fac3fd80SJustin T. Gibbs 		/*
445fac3fd80SJustin T. Gibbs 		 * Directly call the i/o complete routine to save an
446fac3fd80SJustin T. Gibbs 		 * an indirection in the common case.
447fac3fd80SJustin T. Gibbs 		 */
448fac3fd80SJustin T. Gibbs 		cm->cm_status = bret->status;
449fac3fd80SJustin T. Gibbs 		if (cm->cm_bp)
450fac3fd80SJustin T. Gibbs 			xbd_bio_complete(sc, cm);
451fac3fd80SJustin T. Gibbs 		else if (cm->cm_complete != NULL)
452fac3fd80SJustin T. Gibbs 			cm->cm_complete(cm);
453fac3fd80SJustin T. Gibbs 		else
454fac3fd80SJustin T. Gibbs 			xbd_free_command(cm);
455fac3fd80SJustin T. Gibbs 	}
456fac3fd80SJustin T. Gibbs 
457fac3fd80SJustin T. Gibbs 	sc->xbd_ring.rsp_cons = i;
458fac3fd80SJustin T. Gibbs 
459fac3fd80SJustin T. Gibbs 	if (i != sc->xbd_ring.req_prod_pvt) {
460fac3fd80SJustin T. Gibbs 		int more_to_do;
461fac3fd80SJustin T. Gibbs 		RING_FINAL_CHECK_FOR_RESPONSES(&sc->xbd_ring, more_to_do);
462fac3fd80SJustin T. Gibbs 		if (more_to_do)
463fac3fd80SJustin T. Gibbs 			goto again;
464fac3fd80SJustin T. Gibbs 	} else {
465fac3fd80SJustin T. Gibbs 		sc->xbd_ring.sring->rsp_event = i + 1;
466fac3fd80SJustin T. Gibbs 	}
467fac3fd80SJustin T. Gibbs 
468fac3fd80SJustin T. Gibbs 	xbd_startio(sc);
469fac3fd80SJustin T. Gibbs 
470e2c1fe90SJustin T. Gibbs 	if (unlikely(sc->xbd_state == XBD_STATE_SUSPENDED))
471e2c1fe90SJustin T. Gibbs 		wakeup(&sc->xbd_cm_q[XBD_Q_BUSY]);
472fac3fd80SJustin T. Gibbs 
473fac3fd80SJustin T. Gibbs 	mtx_unlock(&sc->xbd_io_lock);
474fac3fd80SJustin T. Gibbs }
475fac3fd80SJustin T. Gibbs 
476fac3fd80SJustin T. Gibbs /*------------------------------- Dump Support -------------------------------*/
477fac3fd80SJustin T. Gibbs /**
478fac3fd80SJustin T. Gibbs  * Quiesce the disk writes for a dump file before allowing the next buffer.
479fac3fd80SJustin T. Gibbs  */
4808698b76cSKip Macy static void
48133eebb6aSJustin T. Gibbs xbd_quiesce(struct xbd_softc *sc)
4828698b76cSKip Macy {
4838698b76cSKip Macy 	int mtd;
4848698b76cSKip Macy 
4858698b76cSKip Macy 	// While there are outstanding requests
486e2c1fe90SJustin T. Gibbs 	while (!TAILQ_EMPTY(&sc->xbd_cm_q[XBD_Q_BUSY].q_tailq)) {
48733eebb6aSJustin T. Gibbs 		RING_FINAL_CHECK_FOR_RESPONSES(&sc->xbd_ring, mtd);
4888698b76cSKip Macy 		if (mtd) {
489e4808c4bSKip Macy 			/* Recieved request completions, update queue. */
49033eebb6aSJustin T. Gibbs 			xbd_int(sc);
4918698b76cSKip Macy 		}
492e2c1fe90SJustin T. Gibbs 		if (!TAILQ_EMPTY(&sc->xbd_cm_q[XBD_Q_BUSY].q_tailq)) {
493e4808c4bSKip Macy 			/*
494e4808c4bSKip Macy 			 * Still pending requests, wait for the disk i/o
495e4808c4bSKip Macy 			 * to complete.
496e4808c4bSKip Macy 			 */
497be7747b4SKip Macy 			HYPERVISOR_yield();
4988698b76cSKip Macy 		}
4998698b76cSKip Macy 	}
5008698b76cSKip Macy }
5018698b76cSKip Macy 
502e4808c4bSKip Macy /* Kernel dump function for a paravirtualized disk device */
503e4808c4bSKip Macy static void
50433eebb6aSJustin T. Gibbs xbd_dump_complete(struct xbd_command *cm)
505e4808c4bSKip Macy {
5068698b76cSKip Macy 
507e2c1fe90SJustin T. Gibbs 	xbd_enqueue_cm(cm, XBD_Q_COMPLETE);
508e4808c4bSKip Macy }
509e4808c4bSKip Macy 
5108698b76cSKip Macy static int
51133eebb6aSJustin T. Gibbs xbd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset,
5128698b76cSKip Macy     size_t length)
5138698b76cSKip Macy {
5148698b76cSKip Macy 	struct disk *dp = arg;
51533eebb6aSJustin T. Gibbs 	struct xbd_softc *sc = dp->d_drv1;
51633eebb6aSJustin T. Gibbs 	struct xbd_command *cm;
517e4808c4bSKip Macy 	size_t chunk;
518e4808c4bSKip Macy 	int sbp;
5198698b76cSKip Macy 	int rc = 0;
5208698b76cSKip Macy 
521e4808c4bSKip Macy 	if (length <= 0)
522e4808c4bSKip Macy 		return (rc);
5238698b76cSKip Macy 
52433eebb6aSJustin T. Gibbs 	xbd_quiesce(sc);	/* All quiet on the western front. */
525e4808c4bSKip Macy 
526e4808c4bSKip Macy 	/*
527e4808c4bSKip Macy 	 * If this lock is held, then this module is failing, and a
528e4808c4bSKip Macy 	 * successful kernel dump is highly unlikely anyway.
529e4808c4bSKip Macy 	 */
53033eebb6aSJustin T. Gibbs 	mtx_lock(&sc->xbd_io_lock);
531e4808c4bSKip Macy 
532e4808c4bSKip Macy 	/* Split the 64KB block as needed */
533e4808c4bSKip Macy 	for (sbp=0; length > 0; sbp++) {
534e2c1fe90SJustin T. Gibbs 		cm = xbd_dequeue_cm(sc, XBD_Q_FREE);
535e4808c4bSKip Macy 		if (cm == NULL) {
53633eebb6aSJustin T. Gibbs 			mtx_unlock(&sc->xbd_io_lock);
53733eebb6aSJustin T. Gibbs 			device_printf(sc->xbd_dev, "dump: no more commands?\n");
538e4808c4bSKip Macy 			return (EBUSY);
539e4808c4bSKip Macy 		}
540e4808c4bSKip Macy 
54133eebb6aSJustin T. Gibbs 		if (gnttab_alloc_grant_references(sc->xbd_max_request_segments,
54233eebb6aSJustin T. Gibbs 		    &cm->cm_gref_head) != 0) {
54333eebb6aSJustin T. Gibbs 			xbd_free_command(cm);
54433eebb6aSJustin T. Gibbs 			mtx_unlock(&sc->xbd_io_lock);
54533eebb6aSJustin T. Gibbs 			device_printf(sc->xbd_dev, "no more grant allocs?\n");
546e4808c4bSKip Macy 			return (EBUSY);
547e4808c4bSKip Macy 		}
548e4808c4bSKip Macy 
54933eebb6aSJustin T. Gibbs 		chunk = length > sc->xbd_max_request_size ?
55033eebb6aSJustin T. Gibbs 		    sc->xbd_max_request_size : length;
55133eebb6aSJustin T. Gibbs 		cm->cm_data = virtual;
55233eebb6aSJustin T. Gibbs 		cm->cm_datalen = chunk;
55333eebb6aSJustin T. Gibbs 		cm->cm_operation = BLKIF_OP_WRITE;
55433eebb6aSJustin T. Gibbs 		cm->cm_sector_number = offset / dp->d_sectorsize;
55533eebb6aSJustin T. Gibbs 		cm->cm_complete = xbd_dump_complete;
556e4808c4bSKip Macy 
557e2c1fe90SJustin T. Gibbs 		xbd_enqueue_cm(cm, XBD_Q_READY);
5588698b76cSKip Macy 
5598698b76cSKip Macy 		length -= chunk;
5608698b76cSKip Macy 		offset += chunk;
5618698b76cSKip Macy 		virtual = (char *) virtual + chunk;
5628698b76cSKip Macy 	}
5638698b76cSKip Macy 
564e4808c4bSKip Macy 	/* Tell DOM0 to do the I/O */
56533eebb6aSJustin T. Gibbs 	xbd_startio(sc);
56633eebb6aSJustin T. Gibbs 	mtx_unlock(&sc->xbd_io_lock);
567e4808c4bSKip Macy 
568e4808c4bSKip Macy 	/* Poll for the completion. */
56933eebb6aSJustin T. Gibbs 	xbd_quiesce(sc);	/* All quite on the eastern front */
570e4808c4bSKip Macy 
571e4808c4bSKip Macy 	/* If there were any errors, bail out... */
572e2c1fe90SJustin T. Gibbs 	while ((cm = xbd_dequeue_cm(sc, XBD_Q_COMPLETE)) != NULL) {
57333eebb6aSJustin T. Gibbs 		if (cm->cm_status != BLKIF_RSP_OKAY) {
57433eebb6aSJustin T. Gibbs 			device_printf(sc->xbd_dev,
575e4808c4bSKip Macy 			    "Dump I/O failed at sector %jd\n",
57633eebb6aSJustin T. Gibbs 			    cm->cm_sector_number);
577e4808c4bSKip Macy 			rc = EIO;
5788698b76cSKip Macy 		}
57933eebb6aSJustin T. Gibbs 		xbd_free_command(cm);
5808698b76cSKip Macy 	}
581e4808c4bSKip Macy 
5828698b76cSKip Macy 	return (rc);
5838698b76cSKip Macy }
5848698b76cSKip Macy 
585fac3fd80SJustin T. Gibbs /*----------------------------- Disk Entrypoints -----------------------------*/
58623dc5621SKip Macy static int
587fac3fd80SJustin T. Gibbs xbd_open(struct disk *dp)
58889e0f4d2SKip Macy {
589fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = dp->d_drv1;
59023dc5621SKip Macy 
591fac3fd80SJustin T. Gibbs 	if (sc == NULL) {
592fac3fd80SJustin T. Gibbs 		printf("xb%d: not found", sc->xbd_unit);
59323dc5621SKip Macy 		return (ENXIO);
59423dc5621SKip Macy 	}
59523dc5621SKip Macy 
596e2c1fe90SJustin T. Gibbs 	sc->xbd_flags |= XBDF_OPEN;
597fac3fd80SJustin T. Gibbs 	sc->xbd_users++;
598fac3fd80SJustin T. Gibbs 	return (0);
599fac3fd80SJustin T. Gibbs }
600fac3fd80SJustin T. Gibbs 
601fac3fd80SJustin T. Gibbs static int
602fac3fd80SJustin T. Gibbs xbd_close(struct disk *dp)
603fac3fd80SJustin T. Gibbs {
604fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = dp->d_drv1;
605fac3fd80SJustin T. Gibbs 
606fac3fd80SJustin T. Gibbs 	if (sc == NULL)
607fac3fd80SJustin T. Gibbs 		return (ENXIO);
608e2c1fe90SJustin T. Gibbs 	sc->xbd_flags &= ~XBDF_OPEN;
609fac3fd80SJustin T. Gibbs 	if (--(sc->xbd_users) == 0) {
610fac3fd80SJustin T. Gibbs 		/*
611fac3fd80SJustin T. Gibbs 		 * Check whether we have been instructed to close.  We will
612fac3fd80SJustin T. Gibbs 		 * have ignored this request initially, as the device was
613fac3fd80SJustin T. Gibbs 		 * still mounted.
614fac3fd80SJustin T. Gibbs 		 */
615fac3fd80SJustin T. Gibbs 		if (xenbus_get_otherend_state(sc->xbd_dev) ==
616fac3fd80SJustin T. Gibbs 		    XenbusStateClosing)
617fac3fd80SJustin T. Gibbs 			xbd_closing(sc->xbd_dev);
618fac3fd80SJustin T. Gibbs 	}
619fac3fd80SJustin T. Gibbs 	return (0);
620fac3fd80SJustin T. Gibbs }
621fac3fd80SJustin T. Gibbs 
622fac3fd80SJustin T. Gibbs static int
623fac3fd80SJustin T. Gibbs xbd_ioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td)
624fac3fd80SJustin T. Gibbs {
625fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = dp->d_drv1;
626fac3fd80SJustin T. Gibbs 
627fac3fd80SJustin T. Gibbs 	if (sc == NULL)
628fac3fd80SJustin T. Gibbs 		return (ENXIO);
629fac3fd80SJustin T. Gibbs 
630fac3fd80SJustin T. Gibbs 	return (ENOTTY);
631fac3fd80SJustin T. Gibbs }
632fac3fd80SJustin T. Gibbs 
633fac3fd80SJustin T. Gibbs /*
634fac3fd80SJustin T. Gibbs  * Read/write routine for a buffer.  Finds the proper unit, place it on
635fac3fd80SJustin T. Gibbs  * the sortq and kick the controller.
636fac3fd80SJustin T. Gibbs  */
637fac3fd80SJustin T. Gibbs static void
638fac3fd80SJustin T. Gibbs xbd_strategy(struct bio *bp)
639fac3fd80SJustin T. Gibbs {
640fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = bp->bio_disk->d_drv1;
641fac3fd80SJustin T. Gibbs 
642fac3fd80SJustin T. Gibbs 	/* bogus disk? */
643fac3fd80SJustin T. Gibbs 	if (sc == NULL) {
644fac3fd80SJustin T. Gibbs 		bp->bio_error = EINVAL;
645fac3fd80SJustin T. Gibbs 		bp->bio_flags |= BIO_ERROR;
646fac3fd80SJustin T. Gibbs 		bp->bio_resid = bp->bio_bcount;
647fac3fd80SJustin T. Gibbs 		biodone(bp);
648fac3fd80SJustin T. Gibbs 		return;
649fac3fd80SJustin T. Gibbs 	}
650fac3fd80SJustin T. Gibbs 
651fac3fd80SJustin T. Gibbs 	/*
652fac3fd80SJustin T. Gibbs 	 * Place it in the queue of disk activities for this disk
653fac3fd80SJustin T. Gibbs 	 */
654fac3fd80SJustin T. Gibbs 	mtx_lock(&sc->xbd_io_lock);
655fac3fd80SJustin T. Gibbs 
656fac3fd80SJustin T. Gibbs 	xbd_enqueue_bio(sc, bp);
657fac3fd80SJustin T. Gibbs 	xbd_startio(sc);
658fac3fd80SJustin T. Gibbs 
659fac3fd80SJustin T. Gibbs 	mtx_unlock(&sc->xbd_io_lock);
660fac3fd80SJustin T. Gibbs 	return;
661fac3fd80SJustin T. Gibbs }
662fac3fd80SJustin T. Gibbs 
663fac3fd80SJustin T. Gibbs /*------------------------------ Ring Management -----------------------------*/
664fac3fd80SJustin T. Gibbs static int
665cdf5d66fSJustin T. Gibbs xbd_alloc_ring(struct xbd_softc *sc)
666fac3fd80SJustin T. Gibbs {
667fac3fd80SJustin T. Gibbs 	blkif_sring_t *sring;
668fac3fd80SJustin T. Gibbs 	uintptr_t sring_page_addr;
669fac3fd80SJustin T. Gibbs 	int error;
670fac3fd80SJustin T. Gibbs 	int i;
671fac3fd80SJustin T. Gibbs 
672fac3fd80SJustin T. Gibbs 	sring = malloc(sc->xbd_ring_pages * PAGE_SIZE, M_XENBLOCKFRONT,
673fac3fd80SJustin T. Gibbs 	    M_NOWAIT|M_ZERO);
674fac3fd80SJustin T. Gibbs 	if (sring == NULL) {
675fac3fd80SJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, ENOMEM, "allocating shared ring");
676fac3fd80SJustin T. Gibbs 		return (ENOMEM);
677fac3fd80SJustin T. Gibbs 	}
678fac3fd80SJustin T. Gibbs 	SHARED_RING_INIT(sring);
679fac3fd80SJustin T. Gibbs 	FRONT_RING_INIT(&sc->xbd_ring, sring, sc->xbd_ring_pages * PAGE_SIZE);
680fac3fd80SJustin T. Gibbs 
681fac3fd80SJustin T. Gibbs 	for (i = 0, sring_page_addr = (uintptr_t)sring;
682fac3fd80SJustin T. Gibbs 	     i < sc->xbd_ring_pages;
683fac3fd80SJustin T. Gibbs 	     i++, sring_page_addr += PAGE_SIZE) {
684fac3fd80SJustin T. Gibbs 
685fac3fd80SJustin T. Gibbs 		error = xenbus_grant_ring(sc->xbd_dev,
686fac3fd80SJustin T. Gibbs 		    (vtomach(sring_page_addr) >> PAGE_SHIFT),
687fac3fd80SJustin T. Gibbs 		    &sc->xbd_ring_ref[i]);
688fac3fd80SJustin T. Gibbs 		if (error) {
689fac3fd80SJustin T. Gibbs 			xenbus_dev_fatal(sc->xbd_dev, error,
690fac3fd80SJustin T. Gibbs 			    "granting ring_ref(%d)", i);
691fac3fd80SJustin T. Gibbs 			return (error);
692fac3fd80SJustin T. Gibbs 		}
693fac3fd80SJustin T. Gibbs 	}
694fac3fd80SJustin T. Gibbs 	if (sc->xbd_ring_pages == 1) {
695fac3fd80SJustin T. Gibbs 		error = xs_printf(XST_NIL, xenbus_get_node(sc->xbd_dev),
696fac3fd80SJustin T. Gibbs 		    "ring-ref", "%u", sc->xbd_ring_ref[0]);
697fac3fd80SJustin T. Gibbs 		if (error) {
698fac3fd80SJustin T. Gibbs 			xenbus_dev_fatal(sc->xbd_dev, error,
699fac3fd80SJustin T. Gibbs 			    "writing %s/ring-ref",
700fac3fd80SJustin T. Gibbs 			    xenbus_get_node(sc->xbd_dev));
701fac3fd80SJustin T. Gibbs 			return (error);
702fac3fd80SJustin T. Gibbs 		}
703fac3fd80SJustin T. Gibbs 	} else {
704fac3fd80SJustin T. Gibbs 		for (i = 0; i < sc->xbd_ring_pages; i++) {
705fac3fd80SJustin T. Gibbs 			char ring_ref_name[]= "ring_refXX";
706fac3fd80SJustin T. Gibbs 
707fac3fd80SJustin T. Gibbs 			snprintf(ring_ref_name, sizeof(ring_ref_name),
708fac3fd80SJustin T. Gibbs 			    "ring-ref%u", i);
709fac3fd80SJustin T. Gibbs 			error = xs_printf(XST_NIL, xenbus_get_node(sc->xbd_dev),
710fac3fd80SJustin T. Gibbs 			     ring_ref_name, "%u", sc->xbd_ring_ref[i]);
711fac3fd80SJustin T. Gibbs 			if (error) {
712fac3fd80SJustin T. Gibbs 				xenbus_dev_fatal(sc->xbd_dev, error,
713fac3fd80SJustin T. Gibbs 				    "writing %s/%s",
714fac3fd80SJustin T. Gibbs 				    xenbus_get_node(sc->xbd_dev),
715fac3fd80SJustin T. Gibbs 				    ring_ref_name);
716fac3fd80SJustin T. Gibbs 				return (error);
717fac3fd80SJustin T. Gibbs 			}
718fac3fd80SJustin T. Gibbs 		}
719fac3fd80SJustin T. Gibbs 	}
720fac3fd80SJustin T. Gibbs 
721fac3fd80SJustin T. Gibbs 	error = bind_listening_port_to_irqhandler(
722fac3fd80SJustin T. Gibbs 	    xenbus_get_otherend_id(sc->xbd_dev),
723fac3fd80SJustin T. Gibbs 	    "xbd", (driver_intr_t *)xbd_int, sc,
724fac3fd80SJustin T. Gibbs 	    INTR_TYPE_BIO | INTR_MPSAFE, &sc->xbd_irq);
725fac3fd80SJustin T. Gibbs 	if (error) {
726fac3fd80SJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, error,
727fac3fd80SJustin T. Gibbs 		    "bind_evtchn_to_irqhandler failed");
728fac3fd80SJustin T. Gibbs 		return (error);
729fac3fd80SJustin T. Gibbs 	}
730fac3fd80SJustin T. Gibbs 
731fac3fd80SJustin T. Gibbs 	return (0);
732fac3fd80SJustin T. Gibbs }
733fac3fd80SJustin T. Gibbs 
734d9fab01dSJustin T. Gibbs static void
735d9fab01dSJustin T. Gibbs xbd_free_ring(struct xbd_softc *sc)
736d9fab01dSJustin T. Gibbs {
737d9fab01dSJustin T. Gibbs 	int i;
738d9fab01dSJustin T. Gibbs 
739d9fab01dSJustin T. Gibbs 	if (sc->xbd_ring.sring == NULL)
740d9fab01dSJustin T. Gibbs 		return;
741d9fab01dSJustin T. Gibbs 
742d9fab01dSJustin T. Gibbs 	for (i = 0; i < sc->xbd_ring_pages; i++) {
743d9fab01dSJustin T. Gibbs 		if (sc->xbd_ring_ref[i] != GRANT_REF_INVALID) {
744d9fab01dSJustin T. Gibbs 			gnttab_end_foreign_access_ref(sc->xbd_ring_ref[i]);
745d9fab01dSJustin T. Gibbs 			sc->xbd_ring_ref[i] = GRANT_REF_INVALID;
746d9fab01dSJustin T. Gibbs 		}
747d9fab01dSJustin T. Gibbs 	}
748d9fab01dSJustin T. Gibbs 	free(sc->xbd_ring.sring, M_XENBLOCKFRONT);
749d9fab01dSJustin T. Gibbs 	sc->xbd_ring.sring = NULL;
750d9fab01dSJustin T. Gibbs }
751d9fab01dSJustin T. Gibbs 
752fac3fd80SJustin T. Gibbs /*-------------------------- Initialization/Teardown -------------------------*/
7538b8bfa35SJustin T. Gibbs static void
75433eebb6aSJustin T. Gibbs xbd_setup_sysctl(struct xbd_softc *xbd)
7558b8bfa35SJustin T. Gibbs {
7568b8bfa35SJustin T. Gibbs 	struct sysctl_ctx_list *sysctl_ctx = NULL;
7578b8bfa35SJustin T. Gibbs 	struct sysctl_oid *sysctl_tree = NULL;
7588b8bfa35SJustin T. Gibbs 
75933eebb6aSJustin T. Gibbs 	sysctl_ctx = device_get_sysctl_ctx(xbd->xbd_dev);
7608b8bfa35SJustin T. Gibbs 	if (sysctl_ctx == NULL)
7618b8bfa35SJustin T. Gibbs 		return;
7628b8bfa35SJustin T. Gibbs 
76333eebb6aSJustin T. Gibbs 	sysctl_tree = device_get_sysctl_tree(xbd->xbd_dev);
7648b8bfa35SJustin T. Gibbs 	if (sysctl_tree == NULL)
7658b8bfa35SJustin T. Gibbs 		return;
7668b8bfa35SJustin T. Gibbs 
7678b8bfa35SJustin T. Gibbs 	SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
76833eebb6aSJustin T. Gibbs 	    "max_requests", CTLFLAG_RD, &xbd->xbd_max_requests, -1,
7698b8bfa35SJustin T. Gibbs 	    "maximum outstanding requests (negotiated)");
7708b8bfa35SJustin T. Gibbs 
7718b8bfa35SJustin T. Gibbs 	SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
7728b8bfa35SJustin T. Gibbs 	    "max_request_segments", CTLFLAG_RD,
77333eebb6aSJustin T. Gibbs 	    &xbd->xbd_max_request_segments, 0,
7748b8bfa35SJustin T. Gibbs 	    "maximum number of pages per requests (negotiated)");
7758b8bfa35SJustin T. Gibbs 
7768b8bfa35SJustin T. Gibbs 	SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
77733eebb6aSJustin T. Gibbs 	    "max_request_size", CTLFLAG_RD, &xbd->xbd_max_request_size, 0,
7788b8bfa35SJustin T. Gibbs 	    "maximum size in bytes of a request (negotiated)");
7798b8bfa35SJustin T. Gibbs 
7808b8bfa35SJustin T. Gibbs 	SYSCTL_ADD_UINT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
78133eebb6aSJustin T. Gibbs 	    "ring_pages", CTLFLAG_RD, &xbd->xbd_ring_pages, 0,
7828b8bfa35SJustin T. Gibbs 	    "communication channel pages (negotiated)");
7838b8bfa35SJustin T. Gibbs }
7848b8bfa35SJustin T. Gibbs 
78523dc5621SKip Macy /*
786fac3fd80SJustin T. Gibbs  * Translate Linux major/minor to an appropriate name and unit
787fac3fd80SJustin T. Gibbs  * number. For HVM guests, this allows us to use the same drive names
788fac3fd80SJustin T. Gibbs  * with blkfront as the emulated drives, easing transition slightly.
78923dc5621SKip Macy  */
790fac3fd80SJustin T. Gibbs static void
791fac3fd80SJustin T. Gibbs xbd_vdevice_to_unit(uint32_t vdevice, int *unit, const char **name)
79223dc5621SKip Macy {
793fac3fd80SJustin T. Gibbs 	static struct vdev_info {
794fac3fd80SJustin T. Gibbs 		int major;
795fac3fd80SJustin T. Gibbs 		int shift;
796fac3fd80SJustin T. Gibbs 		int base;
79723dc5621SKip Macy 		const char *name;
798fac3fd80SJustin T. Gibbs 	} info[] = {
799fac3fd80SJustin T. Gibbs 		{3,	6,	0,	"ada"},	/* ide0 */
800fac3fd80SJustin T. Gibbs 		{22,	6,	2,	"ada"},	/* ide1 */
801fac3fd80SJustin T. Gibbs 		{33,	6,	4,	"ada"},	/* ide2 */
802fac3fd80SJustin T. Gibbs 		{34,	6,	6,	"ada"},	/* ide3 */
803fac3fd80SJustin T. Gibbs 		{56,	6,	8,	"ada"},	/* ide4 */
804fac3fd80SJustin T. Gibbs 		{57,	6,	10,	"ada"},	/* ide5 */
805fac3fd80SJustin T. Gibbs 		{88,	6,	12,	"ada"},	/* ide6 */
806fac3fd80SJustin T. Gibbs 		{89,	6,	14,	"ada"},	/* ide7 */
807fac3fd80SJustin T. Gibbs 		{90,	6,	16,	"ada"},	/* ide8 */
808fac3fd80SJustin T. Gibbs 		{91,	6,	18,	"ada"},	/* ide9 */
80989e0f4d2SKip Macy 
810fac3fd80SJustin T. Gibbs 		{8,	4,	0,	"da"},	/* scsi disk0 */
811fac3fd80SJustin T. Gibbs 		{65,	4,	16,	"da"},	/* scsi disk1 */
812fac3fd80SJustin T. Gibbs 		{66,	4,	32,	"da"},	/* scsi disk2 */
813fac3fd80SJustin T. Gibbs 		{67,	4,	48,	"da"},	/* scsi disk3 */
814fac3fd80SJustin T. Gibbs 		{68,	4,	64,	"da"},	/* scsi disk4 */
815fac3fd80SJustin T. Gibbs 		{69,	4,	80,	"da"},	/* scsi disk5 */
816fac3fd80SJustin T. Gibbs 		{70,	4,	96,	"da"},	/* scsi disk6 */
817fac3fd80SJustin T. Gibbs 		{71,	4,	112,	"da"},	/* scsi disk7 */
818fac3fd80SJustin T. Gibbs 		{128,	4,	128,	"da"},	/* scsi disk8 */
819fac3fd80SJustin T. Gibbs 		{129,	4,	144,	"da"},	/* scsi disk9 */
820fac3fd80SJustin T. Gibbs 		{130,	4,	160,	"da"},	/* scsi disk10 */
821fac3fd80SJustin T. Gibbs 		{131,	4,	176,	"da"},	/* scsi disk11 */
822fac3fd80SJustin T. Gibbs 		{132,	4,	192,	"da"},	/* scsi disk12 */
823fac3fd80SJustin T. Gibbs 		{133,	4,	208,	"da"},	/* scsi disk13 */
824fac3fd80SJustin T. Gibbs 		{134,	4,	224,	"da"},	/* scsi disk14 */
825fac3fd80SJustin T. Gibbs 		{135,	4,	240,	"da"},	/* scsi disk15 */
826fac3fd80SJustin T. Gibbs 
827fac3fd80SJustin T. Gibbs 		{202,	4,	0,	"xbd"},	/* xbd */
828fac3fd80SJustin T. Gibbs 
829fac3fd80SJustin T. Gibbs 		{0,	0,	0,	NULL},
830fac3fd80SJustin T. Gibbs 	};
831fac3fd80SJustin T. Gibbs 	int major = vdevice >> 8;
832fac3fd80SJustin T. Gibbs 	int minor = vdevice & 0xff;
833fac3fd80SJustin T. Gibbs 	int i;
834fac3fd80SJustin T. Gibbs 
835fac3fd80SJustin T. Gibbs 	if (vdevice & (1 << 28)) {
836fac3fd80SJustin T. Gibbs 		*unit = (vdevice & ((1 << 28) - 1)) >> 8;
837fac3fd80SJustin T. Gibbs 		*name = "xbd";
838fac3fd80SJustin T. Gibbs 		return;
83989e0f4d2SKip Macy 	}
84089e0f4d2SKip Macy 
841fac3fd80SJustin T. Gibbs 	for (i = 0; info[i].major; i++) {
842fac3fd80SJustin T. Gibbs 		if (info[i].major == major) {
843fac3fd80SJustin T. Gibbs 			*unit = info[i].base + (minor >> info[i].shift);
844fac3fd80SJustin T. Gibbs 			*name = info[i].name;
845fac3fd80SJustin T. Gibbs 			return;
846fac3fd80SJustin T. Gibbs 		}
847fac3fd80SJustin T. Gibbs 	}
848fac3fd80SJustin T. Gibbs 
849fac3fd80SJustin T. Gibbs 	*unit = minor >> 4;
850fac3fd80SJustin T. Gibbs 	*name = "xbd";
851fac3fd80SJustin T. Gibbs }
852fac3fd80SJustin T. Gibbs 
853fac3fd80SJustin T. Gibbs int
854fac3fd80SJustin T. Gibbs xbd_instance_create(struct xbd_softc *sc, blkif_sector_t sectors,
855fac3fd80SJustin T. Gibbs     int vdevice, uint16_t vdisk_info, unsigned long sector_size)
856fac3fd80SJustin T. Gibbs {
857fac3fd80SJustin T. Gibbs 	int unit, error = 0;
858fac3fd80SJustin T. Gibbs 	const char *name;
859fac3fd80SJustin T. Gibbs 
86033eebb6aSJustin T. Gibbs 	xbd_vdevice_to_unit(vdevice, &unit, &name);
86123dc5621SKip Macy 
862fac3fd80SJustin T. Gibbs 	sc->xbd_unit = unit;
86389e0f4d2SKip Macy 
864fac3fd80SJustin T. Gibbs 	if (strcmp(name, "xbd"))
865fac3fd80SJustin T. Gibbs 		device_printf(sc->xbd_dev, "attaching as %s%d\n", name, unit);
86689e0f4d2SKip Macy 
867fac3fd80SJustin T. Gibbs 	sc->xbd_disk = disk_alloc();
868fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_unit = sc->xbd_unit;
869fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_open = xbd_open;
870fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_close = xbd_close;
871fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_ioctl = xbd_ioctl;
872fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_strategy = xbd_strategy;
873fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_dump = xbd_dump;
874fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_name = name;
875fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_drv1 = sc;
876fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_sectorsize = sector_size;
8778b8bfa35SJustin T. Gibbs 
878fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_mediasize = sectors * sector_size;
879fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_maxsize = sc->xbd_max_request_size;
880fac3fd80SJustin T. Gibbs 	sc->xbd_disk->d_flags = 0;
881fac3fd80SJustin T. Gibbs 	disk_create(sc->xbd_disk, DISK_VERSION);
88212678024SDoug Rabson 
883fac3fd80SJustin T. Gibbs 	return error;
88412678024SDoug Rabson }
88512678024SDoug Rabson 
886fac3fd80SJustin T. Gibbs static void
887fac3fd80SJustin T. Gibbs xbd_free(struct xbd_softc *sc)
88812678024SDoug Rabson {
889fac3fd80SJustin T. Gibbs 	int i;
89012678024SDoug Rabson 
89112678024SDoug Rabson 	/* Prevent new requests being issued until we fix things up. */
89233eebb6aSJustin T. Gibbs 	mtx_lock(&sc->xbd_io_lock);
893e2c1fe90SJustin T. Gibbs 	sc->xbd_state = XBD_STATE_DISCONNECTED;
89433eebb6aSJustin T. Gibbs 	mtx_unlock(&sc->xbd_io_lock);
89589e0f4d2SKip Macy 
896fac3fd80SJustin T. Gibbs 	/* Free resources associated with old device channel. */
897d9fab01dSJustin T. Gibbs 	xbd_free_ring(sc);
898fac3fd80SJustin T. Gibbs 	if (sc->xbd_shadow) {
89989e0f4d2SKip Macy 
900fac3fd80SJustin T. Gibbs 		for (i = 0; i < sc->xbd_max_requests; i++) {
901fac3fd80SJustin T. Gibbs 			struct xbd_command *cm;
90289e0f4d2SKip Macy 
903fac3fd80SJustin T. Gibbs 			cm = &sc->xbd_shadow[i];
904fac3fd80SJustin T. Gibbs 			if (cm->cm_sg_refs != NULL) {
905fac3fd80SJustin T. Gibbs 				free(cm->cm_sg_refs, M_XENBLOCKFRONT);
906fac3fd80SJustin T. Gibbs 				cm->cm_sg_refs = NULL;
90789e0f4d2SKip Macy 			}
90889e0f4d2SKip Macy 
909fac3fd80SJustin T. Gibbs 			bus_dmamap_destroy(sc->xbd_io_dmat, cm->cm_map);
910fac3fd80SJustin T. Gibbs 		}
911fac3fd80SJustin T. Gibbs 		free(sc->xbd_shadow, M_XENBLOCKFRONT);
912fac3fd80SJustin T. Gibbs 		sc->xbd_shadow = NULL;
913fac3fd80SJustin T. Gibbs 
914fac3fd80SJustin T. Gibbs 		bus_dma_tag_destroy(sc->xbd_io_dmat);
915fac3fd80SJustin T. Gibbs 
916e2c1fe90SJustin T. Gibbs 		xbd_initq_cm(sc, XBD_Q_FREE);
917e2c1fe90SJustin T. Gibbs 		xbd_initq_cm(sc, XBD_Q_READY);
918e2c1fe90SJustin T. Gibbs 		xbd_initq_cm(sc, XBD_Q_COMPLETE);
919fac3fd80SJustin T. Gibbs 	}
920fac3fd80SJustin T. Gibbs 
921fac3fd80SJustin T. Gibbs 	if (sc->xbd_irq) {
922fac3fd80SJustin T. Gibbs 		unbind_from_irqhandler(sc->xbd_irq);
923fac3fd80SJustin T. Gibbs 		sc->xbd_irq = 0;
924fac3fd80SJustin T. Gibbs 	}
925fac3fd80SJustin T. Gibbs }
926fac3fd80SJustin T. Gibbs 
927fac3fd80SJustin T. Gibbs /*--------------------------- State Change Handlers --------------------------*/
928ff662b5cSJustin T. Gibbs static void
92933eebb6aSJustin T. Gibbs xbd_initialize(struct xbd_softc *sc)
93089e0f4d2SKip Macy {
931ff662b5cSJustin T. Gibbs 	const char *otherend_path;
932ff662b5cSJustin T. Gibbs 	const char *node_path;
9338b8bfa35SJustin T. Gibbs 	uint32_t max_ring_page_order;
934ff662b5cSJustin T. Gibbs 	int error;
935ff662b5cSJustin T. Gibbs 	int i;
93689e0f4d2SKip Macy 
93733eebb6aSJustin T. Gibbs 	if (xenbus_get_state(sc->xbd_dev) != XenbusStateInitialising) {
93806a630f6SJustin T. Gibbs 		/* Initialization has already been performed. */
939ff662b5cSJustin T. Gibbs 		return;
94006a630f6SJustin T. Gibbs 	}
94189e0f4d2SKip Macy 
942ff662b5cSJustin T. Gibbs 	/*
943ff662b5cSJustin T. Gibbs 	 * Protocol defaults valid even if negotiation for a
944ff662b5cSJustin T. Gibbs 	 * setting fails.
945ff662b5cSJustin T. Gibbs 	 */
9468b8bfa35SJustin T. Gibbs 	max_ring_page_order = 0;
94733eebb6aSJustin T. Gibbs 	sc->xbd_ring_pages = 1;
94833eebb6aSJustin T. Gibbs 	sc->xbd_max_request_segments = BLKIF_MAX_SEGMENTS_PER_HEADER_BLOCK;
94933eebb6aSJustin T. Gibbs 	sc->xbd_max_request_size =
95033eebb6aSJustin T. Gibbs 	    XBD_SEGS_TO_SIZE(sc->xbd_max_request_segments);
95133eebb6aSJustin T. Gibbs 	sc->xbd_max_request_blocks =
95233eebb6aSJustin T. Gibbs 	    BLKIF_SEGS_TO_BLOCKS(sc->xbd_max_request_segments);
953ff662b5cSJustin T. Gibbs 
954ff662b5cSJustin T. Gibbs 	/*
955ff662b5cSJustin T. Gibbs 	 * Protocol negotiation.
956ff662b5cSJustin T. Gibbs 	 *
957ff662b5cSJustin T. Gibbs 	 * \note xs_gather() returns on the first encountered error, so
958ff662b5cSJustin T. Gibbs 	 *       we must use independant calls in order to guarantee
959ff662b5cSJustin T. Gibbs 	 *       we don't miss information in a sparsly populated back-end
960ff662b5cSJustin T. Gibbs 	 *       tree.
9618b8bfa35SJustin T. Gibbs 	 *
9628b8bfa35SJustin T. Gibbs 	 * \note xs_scanf() does not update variables for unmatched
9638b8bfa35SJustin T. Gibbs 	 *	 fields.
964ff662b5cSJustin T. Gibbs 	 */
96533eebb6aSJustin T. Gibbs 	otherend_path = xenbus_get_otherend_path(sc->xbd_dev);
96633eebb6aSJustin T. Gibbs 	node_path = xenbus_get_node(sc->xbd_dev);
9678b8bfa35SJustin T. Gibbs 
9688b8bfa35SJustin T. Gibbs 	/* Support both backend schemes for relaying ring page limits. */
9698b8bfa35SJustin T. Gibbs 	(void)xs_scanf(XST_NIL, otherend_path,
9708b8bfa35SJustin T. Gibbs 	    "max-ring-page-order", NULL, "%" PRIu32,
9718b8bfa35SJustin T. Gibbs 	    &max_ring_page_order);
97233eebb6aSJustin T. Gibbs 	sc->xbd_ring_pages = 1 << max_ring_page_order;
973ff662b5cSJustin T. Gibbs 	(void)xs_scanf(XST_NIL, otherend_path,
974ff662b5cSJustin T. Gibbs 	    "max-ring-pages", NULL, "%" PRIu32,
97533eebb6aSJustin T. Gibbs 	    &sc->xbd_ring_pages);
97633eebb6aSJustin T. Gibbs 	if (sc->xbd_ring_pages < 1)
97733eebb6aSJustin T. Gibbs 		sc->xbd_ring_pages = 1;
978ff662b5cSJustin T. Gibbs 
97933eebb6aSJustin T. Gibbs 	sc->xbd_max_requests =
98033eebb6aSJustin T. Gibbs 	    BLKIF_MAX_RING_REQUESTS(sc->xbd_ring_pages * PAGE_SIZE);
981ff662b5cSJustin T. Gibbs 	(void)xs_scanf(XST_NIL, otherend_path,
982ff662b5cSJustin T. Gibbs 	    "max-requests", NULL, "%" PRIu32,
98333eebb6aSJustin T. Gibbs 	    &sc->xbd_max_requests);
984ff662b5cSJustin T. Gibbs 
985ff662b5cSJustin T. Gibbs 	(void)xs_scanf(XST_NIL, otherend_path,
986ff662b5cSJustin T. Gibbs 	    "max-request-segments", NULL, "%" PRIu32,
98733eebb6aSJustin T. Gibbs 	    &sc->xbd_max_request_segments);
988ff662b5cSJustin T. Gibbs 
989ff662b5cSJustin T. Gibbs 	(void)xs_scanf(XST_NIL, otherend_path,
990ff662b5cSJustin T. Gibbs 	    "max-request-size", NULL, "%" PRIu32,
99133eebb6aSJustin T. Gibbs 	    &sc->xbd_max_request_size);
992ff662b5cSJustin T. Gibbs 
99333eebb6aSJustin T. Gibbs 	if (sc->xbd_ring_pages > XBD_MAX_RING_PAGES) {
99433eebb6aSJustin T. Gibbs 		device_printf(sc->xbd_dev,
99533eebb6aSJustin T. Gibbs 		    "Back-end specified ring-pages of %u "
99633eebb6aSJustin T. Gibbs 		    "limited to front-end limit of %zu.\n",
99733eebb6aSJustin T. Gibbs 		    sc->xbd_ring_pages, XBD_MAX_RING_PAGES);
99833eebb6aSJustin T. Gibbs 		sc->xbd_ring_pages = XBD_MAX_RING_PAGES;
99989e0f4d2SKip Macy 	}
100089e0f4d2SKip Macy 
100133eebb6aSJustin T. Gibbs 	if (powerof2(sc->xbd_ring_pages) == 0) {
10028b8bfa35SJustin T. Gibbs 		uint32_t new_page_limit;
10038b8bfa35SJustin T. Gibbs 
100433eebb6aSJustin T. Gibbs 		new_page_limit = 0x01 << (fls(sc->xbd_ring_pages) - 1);
100533eebb6aSJustin T. Gibbs 		device_printf(sc->xbd_dev,
100633eebb6aSJustin T. Gibbs 		    "Back-end specified ring-pages of %u "
100733eebb6aSJustin T. Gibbs 		    "is not a power of 2. Limited to %u.\n",
100833eebb6aSJustin T. Gibbs 		    sc->xbd_ring_pages, new_page_limit);
100933eebb6aSJustin T. Gibbs 		sc->xbd_ring_pages = new_page_limit;
10108b8bfa35SJustin T. Gibbs 	}
10118b8bfa35SJustin T. Gibbs 
101233eebb6aSJustin T. Gibbs 	if (sc->xbd_max_requests > XBD_MAX_REQUESTS) {
101333eebb6aSJustin T. Gibbs 		device_printf(sc->xbd_dev,
101433eebb6aSJustin T. Gibbs 		    "Back-end specified max_requests of %u "
101533eebb6aSJustin T. Gibbs 		    "limited to front-end limit of %u.\n",
101633eebb6aSJustin T. Gibbs 		    sc->xbd_max_requests, XBD_MAX_REQUESTS);
101733eebb6aSJustin T. Gibbs 		sc->xbd_max_requests = XBD_MAX_REQUESTS;
101889e0f4d2SKip Macy 	}
1019ff662b5cSJustin T. Gibbs 
102033eebb6aSJustin T. Gibbs 	if (sc->xbd_max_request_segments > XBD_MAX_SEGMENTS_PER_REQUEST) {
102133eebb6aSJustin T. Gibbs 		device_printf(sc->xbd_dev,
102233eebb6aSJustin T. Gibbs 		    "Back-end specified max_request_segments of %u "
102333eebb6aSJustin T. Gibbs 		    "limited to front-end limit of %u.\n",
102433eebb6aSJustin T. Gibbs 		    sc->xbd_max_request_segments,
102533eebb6aSJustin T. Gibbs 		    XBD_MAX_SEGMENTS_PER_REQUEST);
102633eebb6aSJustin T. Gibbs 		sc->xbd_max_request_segments = XBD_MAX_SEGMENTS_PER_REQUEST;
102789e0f4d2SKip Macy 	}
1028ff662b5cSJustin T. Gibbs 
102933eebb6aSJustin T. Gibbs 	if (sc->xbd_max_request_size > XBD_MAX_REQUEST_SIZE) {
103033eebb6aSJustin T. Gibbs 		device_printf(sc->xbd_dev,
103133eebb6aSJustin T. Gibbs 		    "Back-end specified max_request_size of %u "
103233eebb6aSJustin T. Gibbs 		    "limited to front-end limit of %u.\n",
103333eebb6aSJustin T. Gibbs 		    sc->xbd_max_request_size,
103433eebb6aSJustin T. Gibbs 		    XBD_MAX_REQUEST_SIZE);
103533eebb6aSJustin T. Gibbs 		sc->xbd_max_request_size = XBD_MAX_REQUEST_SIZE;
1036ff662b5cSJustin T. Gibbs 	}
1037443cc4d4SJustin T. Gibbs 
103833eebb6aSJustin T. Gibbs  	if (sc->xbd_max_request_size >
103933eebb6aSJustin T. Gibbs 	    XBD_SEGS_TO_SIZE(sc->xbd_max_request_segments)) {
104033eebb6aSJustin T. Gibbs  		device_printf(sc->xbd_dev,
104133eebb6aSJustin T. Gibbs 		    "Back-end specified max_request_size of %u "
104233eebb6aSJustin T. Gibbs 		    "limited to front-end limit of %u.  (Too few segments.)\n",
104333eebb6aSJustin T. Gibbs 		    sc->xbd_max_request_size,
104433eebb6aSJustin T. Gibbs 		    XBD_SEGS_TO_SIZE(sc->xbd_max_request_segments));
104533eebb6aSJustin T. Gibbs  		sc->xbd_max_request_size =
104633eebb6aSJustin T. Gibbs  		    XBD_SEGS_TO_SIZE(sc->xbd_max_request_segments);
1047443cc4d4SJustin T. Gibbs  	}
1048443cc4d4SJustin T. Gibbs 
104933eebb6aSJustin T. Gibbs 	sc->xbd_max_request_blocks =
105033eebb6aSJustin T. Gibbs 	    BLKIF_SEGS_TO_BLOCKS(sc->xbd_max_request_segments);
1051ff662b5cSJustin T. Gibbs 
1052ff662b5cSJustin T. Gibbs 	/* Allocate datastructures based on negotiated values. */
105333eebb6aSJustin T. Gibbs 	error = bus_dma_tag_create(
105433eebb6aSJustin T. Gibbs 	    bus_get_dma_tag(sc->xbd_dev),	/* parent */
1055ff662b5cSJustin T. Gibbs 	    512, PAGE_SIZE,			/* algnmnt, boundary */
1056ff662b5cSJustin T. Gibbs 	    BUS_SPACE_MAXADDR,			/* lowaddr */
1057ff662b5cSJustin T. Gibbs 	    BUS_SPACE_MAXADDR,			/* highaddr */
1058ff662b5cSJustin T. Gibbs 	    NULL, NULL,				/* filter, filterarg */
105933eebb6aSJustin T. Gibbs 	    sc->xbd_max_request_size,
106033eebb6aSJustin T. Gibbs 	    sc->xbd_max_request_segments,
1061ff662b5cSJustin T. Gibbs 	    PAGE_SIZE,				/* maxsegsize */
1062ff662b5cSJustin T. Gibbs 	    BUS_DMA_ALLOCNOW,			/* flags */
1063ff662b5cSJustin T. Gibbs 	    busdma_lock_mutex,			/* lockfunc */
106433eebb6aSJustin T. Gibbs 	    &sc->xbd_io_lock,			/* lockarg */
106533eebb6aSJustin T. Gibbs 	    &sc->xbd_io_dmat);
1066ff662b5cSJustin T. Gibbs 	if (error != 0) {
106733eebb6aSJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, error,
1068ff662b5cSJustin T. Gibbs 		    "Cannot allocate parent DMA tag\n");
1069ff662b5cSJustin T. Gibbs 		return;
1070ff662b5cSJustin T. Gibbs 	}
1071ff662b5cSJustin T. Gibbs 
1072ff662b5cSJustin T. Gibbs 	/* Per-transaction data allocation. */
107333eebb6aSJustin T. Gibbs 	sc->xbd_shadow = malloc(sizeof(*sc->xbd_shadow) * sc->xbd_max_requests,
1074ff662b5cSJustin T. Gibbs 	    M_XENBLOCKFRONT, M_NOWAIT|M_ZERO);
107533eebb6aSJustin T. Gibbs 	if (sc->xbd_shadow == NULL) {
107633eebb6aSJustin T. Gibbs 		bus_dma_tag_destroy(sc->xbd_io_dmat);
107733eebb6aSJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, error,
1078ff662b5cSJustin T. Gibbs 		    "Cannot allocate request structures\n");
107906a630f6SJustin T. Gibbs 		return;
1080ff662b5cSJustin T. Gibbs 	}
1081ff662b5cSJustin T. Gibbs 
108233eebb6aSJustin T. Gibbs 	for (i = 0; i < sc->xbd_max_requests; i++) {
108333eebb6aSJustin T. Gibbs 		struct xbd_command *cm;
1084ff662b5cSJustin T. Gibbs 
108533eebb6aSJustin T. Gibbs 		cm = &sc->xbd_shadow[i];
108633eebb6aSJustin T. Gibbs 		cm->cm_sg_refs = malloc(
108733eebb6aSJustin T. Gibbs 		    sizeof(grant_ref_t) * sc->xbd_max_request_segments,
1088ff662b5cSJustin T. Gibbs 		    M_XENBLOCKFRONT, M_NOWAIT);
108933eebb6aSJustin T. Gibbs 		if (cm->cm_sg_refs == NULL)
1090ff662b5cSJustin T. Gibbs 			break;
109133eebb6aSJustin T. Gibbs 		cm->cm_id = i;
1092e2c1fe90SJustin T. Gibbs 		cm->cm_flags = XBDCF_INITIALIZER;
1093ff662b5cSJustin T. Gibbs 		cm->cm_sc = sc;
109433eebb6aSJustin T. Gibbs 		if (bus_dmamap_create(sc->xbd_io_dmat, 0, &cm->cm_map) != 0)
1095ff662b5cSJustin T. Gibbs 			break;
109633eebb6aSJustin T. Gibbs 		xbd_free_command(cm);
1097ff662b5cSJustin T. Gibbs 	}
1098ff662b5cSJustin T. Gibbs 
1099cdf5d66fSJustin T. Gibbs 	if (xbd_alloc_ring(sc) != 0)
1100ff662b5cSJustin T. Gibbs 		return;
1101ff662b5cSJustin T. Gibbs 
11028b8bfa35SJustin T. Gibbs 	/* Support both backend schemes for relaying ring page limits. */
110333eebb6aSJustin T. Gibbs 	if (sc->xbd_ring_pages > 1) {
1104ff662b5cSJustin T. Gibbs 		error = xs_printf(XST_NIL, node_path,
110533eebb6aSJustin T. Gibbs 		    "num-ring-pages","%u",
110633eebb6aSJustin T. Gibbs 		    sc->xbd_ring_pages);
1107ff662b5cSJustin T. Gibbs 		if (error) {
110833eebb6aSJustin T. Gibbs 			xenbus_dev_fatal(sc->xbd_dev, error,
11098b8bfa35SJustin T. Gibbs 			    "writing %s/num-ring-pages",
11108b8bfa35SJustin T. Gibbs 			    node_path);
11118b8bfa35SJustin T. Gibbs 			return;
11128b8bfa35SJustin T. Gibbs 		}
11130d172324SJustin T. Gibbs 
11148b8bfa35SJustin T. Gibbs 		error = xs_printf(XST_NIL, node_path,
11150d172324SJustin T. Gibbs 		    "ring-page-order", "%u",
111633eebb6aSJustin T. Gibbs 		    fls(sc->xbd_ring_pages) - 1);
11178b8bfa35SJustin T. Gibbs 		if (error) {
111833eebb6aSJustin T. Gibbs 			xenbus_dev_fatal(sc->xbd_dev, error,
11198b8bfa35SJustin T. Gibbs 			    "writing %s/ring-page-order",
1120ff662b5cSJustin T. Gibbs 			    node_path);
1121ff662b5cSJustin T. Gibbs 			return;
1122ff662b5cSJustin T. Gibbs 		}
11230d172324SJustin T. Gibbs 	}
1124ff662b5cSJustin T. Gibbs 
1125ff662b5cSJustin T. Gibbs 	error = xs_printf(XST_NIL, node_path,
112633eebb6aSJustin T. Gibbs 	    "max-requests","%u",
112733eebb6aSJustin T. Gibbs 	    sc->xbd_max_requests);
1128ff662b5cSJustin T. Gibbs 	if (error) {
112933eebb6aSJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, error,
1130ff662b5cSJustin T. Gibbs 		    "writing %s/max-requests",
1131ff662b5cSJustin T. Gibbs 		    node_path);
1132ff662b5cSJustin T. Gibbs 		return;
1133ff662b5cSJustin T. Gibbs 	}
1134ff662b5cSJustin T. Gibbs 
1135ff662b5cSJustin T. Gibbs 	error = xs_printf(XST_NIL, node_path,
113633eebb6aSJustin T. Gibbs 	    "max-request-segments","%u",
113733eebb6aSJustin T. Gibbs 	    sc->xbd_max_request_segments);
1138ff662b5cSJustin T. Gibbs 	if (error) {
113933eebb6aSJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, error,
1140ff662b5cSJustin T. Gibbs 		    "writing %s/max-request-segments",
1141ff662b5cSJustin T. Gibbs 		    node_path);
1142ff662b5cSJustin T. Gibbs 		return;
1143ff662b5cSJustin T. Gibbs 	}
1144ff662b5cSJustin T. Gibbs 
1145ff662b5cSJustin T. Gibbs 	error = xs_printf(XST_NIL, node_path,
114633eebb6aSJustin T. Gibbs 	    "max-request-size","%u",
114733eebb6aSJustin T. Gibbs 	    sc->xbd_max_request_size);
1148ff662b5cSJustin T. Gibbs 	if (error) {
114933eebb6aSJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, error,
1150ff662b5cSJustin T. Gibbs 		    "writing %s/max-request-size",
1151ff662b5cSJustin T. Gibbs 		    node_path);
1152ff662b5cSJustin T. Gibbs 		return;
1153ff662b5cSJustin T. Gibbs 	}
1154ff662b5cSJustin T. Gibbs 
1155ff662b5cSJustin T. Gibbs 	error = xs_printf(XST_NIL, node_path, "event-channel",
115633eebb6aSJustin T. Gibbs 	    "%u", irq_to_evtchn_port(sc->xbd_irq));
1157ff662b5cSJustin T. Gibbs 	if (error) {
115833eebb6aSJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, error,
1159ff662b5cSJustin T. Gibbs 		    "writing %s/event-channel",
1160ff662b5cSJustin T. Gibbs 		    node_path);
1161ff662b5cSJustin T. Gibbs 		return;
1162ff662b5cSJustin T. Gibbs 	}
1163ff662b5cSJustin T. Gibbs 
116433eebb6aSJustin T. Gibbs 	error = xs_printf(XST_NIL, node_path, "protocol",
116533eebb6aSJustin T. Gibbs 	    "%s", XEN_IO_PROTO_ABI_NATIVE);
1166ff662b5cSJustin T. Gibbs 	if (error) {
116733eebb6aSJustin T. Gibbs 		xenbus_dev_fatal(sc->xbd_dev, error,
1168ff662b5cSJustin T. Gibbs 		    "writing %s/protocol",
1169ff662b5cSJustin T. Gibbs 		    node_path);
1170ff662b5cSJustin T. Gibbs 		return;
117123dc5621SKip Macy 	}
117212678024SDoug Rabson 
117333eebb6aSJustin T. Gibbs 	xenbus_set_state(sc->xbd_dev, XenbusStateInitialised);
117489e0f4d2SKip Macy }
117589e0f4d2SKip Macy 
117689e0f4d2SKip Macy /*
117733eebb6aSJustin T. Gibbs  * Invoked when the backend is finally 'ready' (and has published
117833eebb6aSJustin T. Gibbs  * the details about the physical device - #sectors, size, etc).
117989e0f4d2SKip Macy  */
118089e0f4d2SKip Macy static void
118133eebb6aSJustin T. Gibbs xbd_connect(struct xbd_softc *sc)
118289e0f4d2SKip Macy {
118333eebb6aSJustin T. Gibbs 	device_t dev = sc->xbd_dev;
118489e0f4d2SKip Macy 	unsigned long sectors, sector_size;
118589e0f4d2SKip Macy 	unsigned int binfo;
1186e4808c4bSKip Macy 	int err, feature_barrier;
118789e0f4d2SKip Macy 
1188e2c1fe90SJustin T. Gibbs 	if (sc->xbd_state == XBD_STATE_CONNECTED ||
1189e2c1fe90SJustin T. Gibbs 	    sc->xbd_state == XBD_STATE_SUSPENDED)
119089e0f4d2SKip Macy 		return;
119189e0f4d2SKip Macy 
119223dc5621SKip Macy 	DPRINTK("blkfront.c:connect:%s.\n", xenbus_get_otherend_path(dev));
119389e0f4d2SKip Macy 
1194ff662b5cSJustin T. Gibbs 	err = xs_gather(XST_NIL, xenbus_get_otherend_path(dev),
119589e0f4d2SKip Macy 	    "sectors", "%lu", &sectors,
119689e0f4d2SKip Macy 	    "info", "%u", &binfo,
119789e0f4d2SKip Macy 	    "sector-size", "%lu", &sector_size,
119889e0f4d2SKip Macy 	    NULL);
119989e0f4d2SKip Macy 	if (err) {
120023dc5621SKip Macy 		xenbus_dev_fatal(dev, err,
120189e0f4d2SKip Macy 		    "reading backend fields at %s",
120223dc5621SKip Macy 		    xenbus_get_otherend_path(dev));
120389e0f4d2SKip Macy 		return;
120489e0f4d2SKip Macy 	}
1205ff662b5cSJustin T. Gibbs 	err = xs_gather(XST_NIL, xenbus_get_otherend_path(dev),
1206e4808c4bSKip Macy 	     "feature-barrier", "%lu", &feature_barrier,
120789e0f4d2SKip Macy 	     NULL);
1208e4808c4bSKip Macy 	if (!err || feature_barrier)
1209e2c1fe90SJustin T. Gibbs 		sc->xbd_flags |= XBDF_BARRIER;
121089e0f4d2SKip Macy 
121133eebb6aSJustin T. Gibbs 	if (sc->xbd_disk == NULL) {
121223dc5621SKip Macy 		device_printf(dev, "%juMB <%s> at %s",
121323dc5621SKip Macy 		    (uintmax_t) sectors / (1048576 / sector_size),
121423dc5621SKip Macy 		    device_get_desc(dev),
121523dc5621SKip Macy 		    xenbus_get_node(dev));
121623dc5621SKip Macy 		bus_print_child_footer(device_get_parent(dev), dev);
121789e0f4d2SKip Macy 
121833eebb6aSJustin T. Gibbs 		xbd_instance_create(sc, sectors, sc->xbd_vdevice, binfo,
121933eebb6aSJustin T. Gibbs 		    sector_size);
122006a630f6SJustin T. Gibbs 	}
122123dc5621SKip Macy 
122223dc5621SKip Macy 	(void)xenbus_set_state(dev, XenbusStateConnected);
122389e0f4d2SKip Macy 
122489e0f4d2SKip Macy 	/* Kick pending requests. */
122533eebb6aSJustin T. Gibbs 	mtx_lock(&sc->xbd_io_lock);
1226e2c1fe90SJustin T. Gibbs 	sc->xbd_state = XBD_STATE_CONNECTED;
122733eebb6aSJustin T. Gibbs 	xbd_startio(sc);
1228e2c1fe90SJustin T. Gibbs 	sc->xbd_flags |= XBDF_READY;
122933eebb6aSJustin T. Gibbs 	mtx_unlock(&sc->xbd_io_lock);
123089e0f4d2SKip Macy }
123189e0f4d2SKip Macy 
123289e0f4d2SKip Macy /**
123389e0f4d2SKip Macy  * Handle the change of state of the backend to Closing.  We must delete our
123489e0f4d2SKip Macy  * device-layer structures now, to ensure that writes are flushed through to
1235a4660d59SJustin T. Gibbs  * the backend.  Once this is done, we can switch to Closed in
123689e0f4d2SKip Macy  * acknowledgement.
123789e0f4d2SKip Macy  */
123823dc5621SKip Macy static void
123933eebb6aSJustin T. Gibbs xbd_closing(device_t dev)
124089e0f4d2SKip Macy {
124133eebb6aSJustin T. Gibbs 	struct xbd_softc *sc = device_get_softc(dev);
124289e0f4d2SKip Macy 
1243ff662b5cSJustin T. Gibbs 	xenbus_set_state(dev, XenbusStateClosing);
1244ff662b5cSJustin T. Gibbs 
124533eebb6aSJustin T. Gibbs 	DPRINTK("xbd_closing: %s removed\n", xenbus_get_node(dev));
124689e0f4d2SKip Macy 
124733eebb6aSJustin T. Gibbs 	if (sc->xbd_disk != NULL) {
124833eebb6aSJustin T. Gibbs 		disk_destroy(sc->xbd_disk);
124933eebb6aSJustin T. Gibbs 		sc->xbd_disk = NULL;
125089e0f4d2SKip Macy 	}
125189e0f4d2SKip Macy 
125223dc5621SKip Macy 	xenbus_set_state(dev, XenbusStateClosed);
125389e0f4d2SKip Macy }
125489e0f4d2SKip Macy 
1255fac3fd80SJustin T. Gibbs /*---------------------------- NewBus Entrypoints ----------------------------*/
1256fac3fd80SJustin T. Gibbs static int
1257fac3fd80SJustin T. Gibbs xbd_probe(device_t dev)
1258fac3fd80SJustin T. Gibbs {
1259fac3fd80SJustin T. Gibbs 
1260fac3fd80SJustin T. Gibbs 	if (!strcmp(xenbus_get_type(dev), "vbd")) {
1261fac3fd80SJustin T. Gibbs 		device_set_desc(dev, "Virtual Block Device");
1262fac3fd80SJustin T. Gibbs 		device_quiet(dev);
1263fac3fd80SJustin T. Gibbs 		return (0);
1264fac3fd80SJustin T. Gibbs 	}
1265fac3fd80SJustin T. Gibbs 
1266fac3fd80SJustin T. Gibbs 	return (ENXIO);
1267fac3fd80SJustin T. Gibbs }
1268fac3fd80SJustin T. Gibbs 
1269fac3fd80SJustin T. Gibbs /*
1270fac3fd80SJustin T. Gibbs  * Setup supplies the backend dir, virtual device.  We place an event
1271fac3fd80SJustin T. Gibbs  * channel and shared frame entries.  We watch backend to wait if it's
1272fac3fd80SJustin T. Gibbs  * ok.
1273fac3fd80SJustin T. Gibbs  */
1274fac3fd80SJustin T. Gibbs static int
1275fac3fd80SJustin T. Gibbs xbd_attach(device_t dev)
1276fac3fd80SJustin T. Gibbs {
1277fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc;
1278fac3fd80SJustin T. Gibbs 	const char *name;
1279fac3fd80SJustin T. Gibbs 	uint32_t vdevice;
1280fac3fd80SJustin T. Gibbs 	int error;
1281fac3fd80SJustin T. Gibbs 	int i;
1282fac3fd80SJustin T. Gibbs 	int unit;
1283fac3fd80SJustin T. Gibbs 
1284fac3fd80SJustin T. Gibbs 	/* FIXME: Use dynamic device id if this is not set. */
1285fac3fd80SJustin T. Gibbs 	error = xs_scanf(XST_NIL, xenbus_get_node(dev),
1286fac3fd80SJustin T. Gibbs 	    "virtual-device", NULL, "%" PRIu32, &vdevice);
1287fac3fd80SJustin T. Gibbs 	if (error) {
1288fac3fd80SJustin T. Gibbs 		xenbus_dev_fatal(dev, error, "reading virtual-device");
1289fac3fd80SJustin T. Gibbs 		device_printf(dev, "Couldn't determine virtual device.\n");
1290fac3fd80SJustin T. Gibbs 		return (error);
1291fac3fd80SJustin T. Gibbs 	}
1292fac3fd80SJustin T. Gibbs 
1293fac3fd80SJustin T. Gibbs 	xbd_vdevice_to_unit(vdevice, &unit, &name);
1294fac3fd80SJustin T. Gibbs 	if (!strcmp(name, "xbd"))
1295fac3fd80SJustin T. Gibbs 		device_set_unit(dev, unit);
1296fac3fd80SJustin T. Gibbs 
1297fac3fd80SJustin T. Gibbs 	sc = device_get_softc(dev);
1298fac3fd80SJustin T. Gibbs 	mtx_init(&sc->xbd_io_lock, "blkfront i/o lock", NULL, MTX_DEF);
1299e2c1fe90SJustin T. Gibbs 	xbd_initqs(sc);
1300fac3fd80SJustin T. Gibbs 	for (i = 0; i < XBD_MAX_RING_PAGES; i++)
1301d9fab01dSJustin T. Gibbs 		sc->xbd_ring_ref[i] = GRANT_REF_INVALID;
1302fac3fd80SJustin T. Gibbs 
1303fac3fd80SJustin T. Gibbs 	sc->xbd_dev = dev;
1304fac3fd80SJustin T. Gibbs 	sc->xbd_vdevice = vdevice;
1305e2c1fe90SJustin T. Gibbs 	sc->xbd_state = XBD_STATE_DISCONNECTED;
1306fac3fd80SJustin T. Gibbs 
1307fac3fd80SJustin T. Gibbs 	xbd_setup_sysctl(sc);
1308fac3fd80SJustin T. Gibbs 
1309fac3fd80SJustin T. Gibbs 	/* Wait for backend device to publish its protocol capabilities. */
1310fac3fd80SJustin T. Gibbs 	xenbus_set_state(dev, XenbusStateInitialising);
1311fac3fd80SJustin T. Gibbs 
1312fac3fd80SJustin T. Gibbs 	return (0);
1313fac3fd80SJustin T. Gibbs }
131489e0f4d2SKip Macy 
131523dc5621SKip Macy static int
131633eebb6aSJustin T. Gibbs xbd_detach(device_t dev)
131789e0f4d2SKip Macy {
131833eebb6aSJustin T. Gibbs 	struct xbd_softc *sc = device_get_softc(dev);
131989e0f4d2SKip Macy 
1320e2c1fe90SJustin T. Gibbs 	DPRINTK("%s: %s removed\n", __func__, xenbus_get_node(dev));
132189e0f4d2SKip Macy 
132233eebb6aSJustin T. Gibbs 	xbd_free(sc);
132333eebb6aSJustin T. Gibbs 	mtx_destroy(&sc->xbd_io_lock);
132489e0f4d2SKip Macy 
132589e0f4d2SKip Macy 	return 0;
132689e0f4d2SKip Macy }
132789e0f4d2SKip Macy 
132889e0f4d2SKip Macy static int
1329fac3fd80SJustin T. Gibbs xbd_suspend(device_t dev)
133089e0f4d2SKip Macy {
1331fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = device_get_softc(dev);
1332fac3fd80SJustin T. Gibbs 	int retval;
1333fac3fd80SJustin T. Gibbs 	int saved_state;
133489e0f4d2SKip Macy 
133589e0f4d2SKip Macy 	/* Prevent new requests being issued until we fix things up. */
133633eebb6aSJustin T. Gibbs 	mtx_lock(&sc->xbd_io_lock);
1337e2c1fe90SJustin T. Gibbs 	saved_state = sc->xbd_state;
1338e2c1fe90SJustin T. Gibbs 	sc->xbd_state = XBD_STATE_SUSPENDED;
1339fac3fd80SJustin T. Gibbs 
1340fac3fd80SJustin T. Gibbs 	/* Wait for outstanding I/O to drain. */
1341fac3fd80SJustin T. Gibbs 	retval = 0;
1342e2c1fe90SJustin T. Gibbs 	while (TAILQ_EMPTY(&sc->xbd_cm_q[XBD_Q_BUSY].q_tailq) == 0) {
1343e2c1fe90SJustin T. Gibbs 		if (msleep(&sc->xbd_cm_q[XBD_Q_BUSY], &sc->xbd_io_lock,
1344fac3fd80SJustin T. Gibbs 		    PRIBIO, "blkf_susp", 30 * hz) == EWOULDBLOCK) {
1345fac3fd80SJustin T. Gibbs 			retval = EBUSY;
1346fac3fd80SJustin T. Gibbs 			break;
1347fac3fd80SJustin T. Gibbs 		}
1348fac3fd80SJustin T. Gibbs 	}
134933eebb6aSJustin T. Gibbs 	mtx_unlock(&sc->xbd_io_lock);
135089e0f4d2SKip Macy 
1351fac3fd80SJustin T. Gibbs 	if (retval != 0)
1352e2c1fe90SJustin T. Gibbs 		sc->xbd_state = saved_state;
135333eebb6aSJustin T. Gibbs 
1354fac3fd80SJustin T. Gibbs 	return (retval);
135589e0f4d2SKip Macy }
135689e0f4d2SKip Macy 
1357ff662b5cSJustin T. Gibbs static int
1358fac3fd80SJustin T. Gibbs xbd_resume(device_t dev)
135989e0f4d2SKip Macy {
1360fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = device_get_softc(dev);
1361fac3fd80SJustin T. Gibbs 
1362fac3fd80SJustin T. Gibbs 	DPRINTK("xbd_resume: %s\n", xenbus_get_node(dev));
1363fac3fd80SJustin T. Gibbs 
1364fac3fd80SJustin T. Gibbs 	xbd_free(sc);
1365fac3fd80SJustin T. Gibbs 	xbd_initialize(sc);
1366fac3fd80SJustin T. Gibbs 	return (0);
136789e0f4d2SKip Macy }
136889e0f4d2SKip Macy 
1369fac3fd80SJustin T. Gibbs /**
1370fac3fd80SJustin T. Gibbs  * Callback received when the backend's state changes.
1371fac3fd80SJustin T. Gibbs  */
1372fac3fd80SJustin T. Gibbs static void
1373fac3fd80SJustin T. Gibbs xbd_backend_changed(device_t dev, XenbusState backend_state)
1374fac3fd80SJustin T. Gibbs {
1375fac3fd80SJustin T. Gibbs 	struct xbd_softc *sc = device_get_softc(dev);
1376fac3fd80SJustin T. Gibbs 
1377fac3fd80SJustin T. Gibbs 	DPRINTK("backend_state=%d\n", backend_state);
1378fac3fd80SJustin T. Gibbs 
1379fac3fd80SJustin T. Gibbs 	switch (backend_state) {
1380fac3fd80SJustin T. Gibbs 	case XenbusStateUnknown:
1381fac3fd80SJustin T. Gibbs 	case XenbusStateInitialising:
1382fac3fd80SJustin T. Gibbs 	case XenbusStateReconfigured:
1383fac3fd80SJustin T. Gibbs 	case XenbusStateReconfiguring:
1384fac3fd80SJustin T. Gibbs 	case XenbusStateClosed:
1385fac3fd80SJustin T. Gibbs 		break;
1386fac3fd80SJustin T. Gibbs 
1387fac3fd80SJustin T. Gibbs 	case XenbusStateInitWait:
1388fac3fd80SJustin T. Gibbs 	case XenbusStateInitialised:
1389fac3fd80SJustin T. Gibbs 		xbd_initialize(sc);
1390fac3fd80SJustin T. Gibbs 		break;
1391fac3fd80SJustin T. Gibbs 
1392fac3fd80SJustin T. Gibbs 	case XenbusStateConnected:
1393fac3fd80SJustin T. Gibbs 		xbd_initialize(sc);
1394fac3fd80SJustin T. Gibbs 		xbd_connect(sc);
1395fac3fd80SJustin T. Gibbs 		break;
1396fac3fd80SJustin T. Gibbs 
1397fac3fd80SJustin T. Gibbs 	case XenbusStateClosing:
1398fac3fd80SJustin T. Gibbs 		if (sc->xbd_users > 0)
1399fac3fd80SJustin T. Gibbs 			xenbus_dev_error(dev, -EBUSY,
1400fac3fd80SJustin T. Gibbs 			    "Device in use; refusing to close");
1401fac3fd80SJustin T. Gibbs 		else
1402fac3fd80SJustin T. Gibbs 			xbd_closing(dev);
1403fac3fd80SJustin T. Gibbs 		break;
1404fac3fd80SJustin T. Gibbs 	}
1405fac3fd80SJustin T. Gibbs }
1406fac3fd80SJustin T. Gibbs 
1407fac3fd80SJustin T. Gibbs /*---------------------------- NewBus Registration ---------------------------*/
140833eebb6aSJustin T. Gibbs static device_method_t xbd_methods[] = {
140923dc5621SKip Macy 	/* Device interface */
141033eebb6aSJustin T. Gibbs 	DEVMETHOD(device_probe,         xbd_probe),
141133eebb6aSJustin T. Gibbs 	DEVMETHOD(device_attach,        xbd_attach),
141233eebb6aSJustin T. Gibbs 	DEVMETHOD(device_detach,        xbd_detach),
141323dc5621SKip Macy 	DEVMETHOD(device_shutdown,      bus_generic_shutdown),
141433eebb6aSJustin T. Gibbs 	DEVMETHOD(device_suspend,       xbd_suspend),
141533eebb6aSJustin T. Gibbs 	DEVMETHOD(device_resume,        xbd_resume),
141689e0f4d2SKip Macy 
141723dc5621SKip Macy 	/* Xenbus interface */
141833eebb6aSJustin T. Gibbs 	DEVMETHOD(xenbus_otherend_changed, xbd_backend_changed),
141989e0f4d2SKip Macy 
142023dc5621SKip Macy 	{ 0, 0 }
142189e0f4d2SKip Macy };
142289e0f4d2SKip Macy 
142333eebb6aSJustin T. Gibbs static driver_t xbd_driver = {
142423dc5621SKip Macy 	"xbd",
142533eebb6aSJustin T. Gibbs 	xbd_methods,
142633eebb6aSJustin T. Gibbs 	sizeof(struct xbd_softc),
142789e0f4d2SKip Macy };
142833eebb6aSJustin T. Gibbs devclass_t xbd_devclass;
142989e0f4d2SKip Macy 
143033eebb6aSJustin T. Gibbs DRIVER_MODULE(xbd, xenbusb_front, xbd_driver, xbd_devclass, 0, 0);
1431