xref: /illumos-gate/usr/src/uts/common/xen/io/blk_common.c (revision 7eea693d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 #include <sys/errno.h>
29 #include <sys/types.h>
30 #include <sys/conf.h>
31 #include <sys/kmem.h>
32 #include <sys/ddi.h>
33 #include <sys/stat.h>
34 #include <sys/sunddi.h>
35 #include <sys/file.h>
36 #include <sys/open.h>
37 #include <sys/modctl.h>
38 #include <sys/ddi_impldefs.h>
39 #include <sys/sysmacros.h>
40 #include <sys/ddidevmap.h>
41 #include <sys/xendev.h>
42 #include <public/io/protocols.h>
43 #include <xen/io/blkif_impl.h>
44 
45 #include "blk_common.h"
46 
47 
48 /* blk interface status */
49 enum blk_if_state {
50 	/*
51 	 * initial state
52 	 */
53 	BLK_IF_UNKNOWN = 0,
54 	/*
55 	 * frontend xenbus state changed to XenbusStateConnected,
56 	 * we finally connect
57 	 */
58 	BLK_IF_CONNECTED,
59 	/*
60 	 * frontend xenbus state changed to XenbusStateClosed,
61 	 * interface disconnected
62 	 */
63 	BLK_IF_DISCONNECTED
64 };
65 
66 /* backend device status */
67 enum blk_be_state {
68 	/* initial state */
69 	BLK_BE_UNKNOWN = 0,
70 	/* backend device is ready (hotplug script finishes successfully) */
71 	BLK_BE_READY
72 };
73 
74 /* frontend status */
75 enum blk_fe_state {
76 	/* initial state */
77 	BLK_FE_UNKNOWN = 0,
78 	/*
79 	 * frontend's xenbus state has changed to
80 	 * XenbusStateInitialised, is ready for connecting
81 	 */
82 	BLK_FE_READY
83 };
84 
85 typedef struct blk_ring_state_s {
86 	kmutex_t		rs_mutex;
87 	boolean_t		rs_sleeping_on_ring;
88 	boolean_t		rs_ring_up;
89 	kcondvar_t		rs_cv;
90 } blk_ring_state_t;
91 
92 /* Disk Statistics */
93 static char *blk_stats[] = {
94 	"rd_reqs",
95 	"wr_reqs",
96 	"br_reqs",
97 	"fl_reqs",
98 	"oo_reqs"
99 };
100 
101 typedef struct blk_stats_s {
102 	uint64_t bs_req_reads;
103 	uint64_t bs_req_writes;
104 	uint64_t bs_req_barriers;
105 	uint64_t bs_req_flushes;
106 } blk_stats_t;
107 
108 struct blk_ring_s {
109 	kmutex_t		ri_mutex;
110 	dev_info_t		*ri_dip;
111 
112 	kstat_t			*ri_kstats;
113 	blk_stats_t		ri_stats;
114 
115 	blk_intr_t		ri_intr;
116 	caddr_t			ri_intr_arg;
117 	blk_ring_cb_t		ri_ringup;
118 	caddr_t			ri_ringup_arg;
119 	blk_ring_cb_t		ri_ringdown;
120 	caddr_t			ri_ringdown_arg;
121 
122 	/* blk interface, backend, and frontend status */
123 	enum blk_if_state	ri_if_status;
124 	enum blk_be_state	ri_be_status;
125 	enum blk_fe_state	ri_fe_status;
126 
127 	domid_t			ri_fe;
128 
129 	enum blkif_protocol	ri_protocol;
130 	size_t			ri_nentry;
131 	size_t			ri_entrysize;
132 
133 	xendev_ring_t		*ri_ring;
134 	blk_ring_state_t	ri_state;
135 };
136 
137 
138 static void blk_oe_state_change(dev_info_t *dip, ddi_eventcookie_t id,
139     void *arg, void *impl_data);
140 static void blk_hp_state_change(dev_info_t *dip, ddi_eventcookie_t id,
141     void *arg, void *impl_data);
142 static int blk_check_state_transition(blk_ring_t ring, XenbusState oestate);
143 static int blk_start_connect(blk_ring_t ring);
144 static void blk_start_disconnect(blk_ring_t ring);
145 static void blk_ring_close(blk_ring_t ring);
146 static int blk_bindto_frontend(blk_ring_t ring);
147 static void blk_unbindfrom_frontend(blk_ring_t ring);
148 static uint_t blk_intr(caddr_t arg);
149 
150 static int blk_kstat_init(blk_ring_t ring);
151 static void blk_kstat_fini(blk_ring_t ring);
152 static int blk_kstat_update(kstat_t *ksp, int flag);
153 
154 static void blk_ring_request_32(blkif_request_t *dst,
155     blkif_x86_32_request_t *src);
156 static void blk_ring_request_64(blkif_request_t *dst,
157     blkif_x86_64_request_t *src);
158 
159 static void blk_ring_response_32(blkif_x86_32_response_t *dst,
160     blkif_response_t *src);
161 static void blk_ring_response_64(blkif_x86_64_response_t *dst,
162     blkif_response_t *src);
163 
164 
165 /*
166  * blk_ring_init()
167  */
168 int
169 blk_ring_init(blk_ringinit_args_t *args, blk_ring_t *ringp)
170 {
171 	blk_ring_t ring;
172 	int e;
173 
174 
175 	ring = kmem_zalloc(sizeof (struct blk_ring_s), KM_SLEEP);
176 	mutex_init(&ring->ri_mutex, NULL, MUTEX_DRIVER, NULL);
177 	ring->ri_dip = args->ar_dip;
178 	ring->ri_intr = args->ar_intr;
179 	ring->ri_intr_arg = args->ar_intr_arg;
180 	ring->ri_ringup = args->ar_ringup;
181 	ring->ri_ringup_arg = args->ar_ringup_arg;
182 	ring->ri_ringdown = args->ar_ringdown;
183 	ring->ri_ringdown_arg = args->ar_ringdown_arg;
184 
185 	ring->ri_if_status = BLK_IF_UNKNOWN;
186 	ring->ri_be_status = BLK_BE_UNKNOWN;
187 	ring->ri_fe_status = BLK_FE_UNKNOWN;
188 	ring->ri_state.rs_sleeping_on_ring = B_FALSE;
189 	ring->ri_state.rs_ring_up = B_FALSE;
190 
191 	mutex_init(&ring->ri_state.rs_mutex, NULL, MUTEX_DRIVER, NULL);
192 	cv_init(&ring->ri_state.rs_cv, NULL, CV_DRIVER, NULL);
193 
194 	e = blk_kstat_init(ring);
195 	if (e != DDI_SUCCESS) {
196 		goto ringinitfail_kstat;
197 	}
198 
199 	/* Watch frontend and hotplug state change */
200 	if (xvdi_add_event_handler(ring->ri_dip, XS_OE_STATE,
201 	    blk_oe_state_change, ring) != DDI_SUCCESS) {
202 		goto ringinitfail_oestate;
203 	}
204 	if (xvdi_add_event_handler(ring->ri_dip, XS_HP_STATE,
205 	    blk_hp_state_change, ring) != DDI_SUCCESS) {
206 		goto ringinitfail_hpstate;
207 	}
208 
209 	/*
210 	 * Kick-off hotplug script
211 	 */
212 	if (xvdi_post_event(ring->ri_dip, XEN_HP_ADD) != DDI_SUCCESS) {
213 		cmn_err(CE_WARN, "blk@%s: failed to start hotplug script",
214 		    ddi_get_name_addr(ring->ri_dip));
215 		goto ringinitfail_postevent;
216 	}
217 
218 	/*
219 	 * start waiting for hotplug event and otherend state event
220 	 * mainly for debugging, frontend will not take any op seeing this
221 	 */
222 	(void) xvdi_switch_state(ring->ri_dip, XBT_NULL, XenbusStateInitWait);
223 
224 	*ringp = ring;
225 	return (DDI_SUCCESS);
226 
227 ringinitfail_postevent:
228 	xvdi_remove_event_handler(ring->ri_dip, XS_HP_STATE);
229 ringinitfail_hpstate:
230 	xvdi_remove_event_handler(ring->ri_dip, XS_OE_STATE);
231 ringinitfail_oestate:
232 	blk_kstat_fini(ring);
233 ringinitfail_kstat:
234 	cv_destroy(&ring->ri_state.rs_cv);
235 	mutex_destroy(&ring->ri_state.rs_mutex);
236 	mutex_destroy(&ring->ri_mutex);
237 	kmem_free(ring, sizeof (struct blk_ring_s));
238 	return (DDI_FAILURE);
239 }
240 
241 
242 /*
243  * blk_ring_fini()
244  */
245 void
246 blk_ring_fini(blk_ring_t *ringp)
247 {
248 	blk_ring_t ring;
249 
250 
251 	ring = *ringp;
252 
253 	mutex_enter(&ring->ri_mutex);
254 	if (ring->ri_if_status != BLK_IF_DISCONNECTED) {
255 		blk_ring_close(ring);
256 	}
257 	mutex_exit(&ring->ri_mutex);
258 
259 	xvdi_remove_event_handler(ring->ri_dip, NULL);
260 	blk_kstat_fini(ring);
261 	cv_destroy(&ring->ri_state.rs_cv);
262 	mutex_destroy(&ring->ri_state.rs_mutex);
263 	mutex_destroy(&ring->ri_mutex);
264 	kmem_free(ring, sizeof (struct blk_ring_s));
265 
266 	*ringp = NULL;
267 }
268 
269 
270 /*
271  * blk_kstat_init()
272  */
273 static int
274 blk_kstat_init(blk_ring_t ring)
275 {
276 	int nstat = sizeof (blk_stats) / sizeof (blk_stats[0]);
277 	char **cp = blk_stats;
278 	kstat_named_t *knp;
279 
280 	ring->ri_kstats = kstat_create(ddi_get_name(ring->ri_dip),
281 	    ddi_get_instance(ring->ri_dip), "req_statistics", "block",
282 	    KSTAT_TYPE_NAMED, nstat, 0);
283 	if (ring->ri_kstats == NULL) {
284 		return (DDI_FAILURE);
285 	}
286 
287 	ring->ri_kstats->ks_private = ring;
288 	ring->ri_kstats->ks_update = blk_kstat_update;
289 
290 	knp = ring->ri_kstats->ks_data;
291 	while (nstat > 0) {
292 		kstat_named_init(knp, *cp, KSTAT_DATA_UINT64);
293 		knp++;
294 		cp++;
295 		nstat--;
296 	}
297 
298 	kstat_install(ring->ri_kstats);
299 
300 	return (DDI_SUCCESS);
301 }
302 
303 
304 /*
305  * blk_kstat_fini()
306  */
307 static void
308 blk_kstat_fini(blk_ring_t ring)
309 {
310 	kstat_delete(ring->ri_kstats);
311 }
312 
313 
314 /*
315  * blk_kstat_update()
316  */
317 static int
318 blk_kstat_update(kstat_t *ksp, int flag)
319 {
320 	kstat_named_t *knp;
321 	blk_stats_t *stats;
322 	blk_ring_t ring;
323 
324 
325 	if (flag != KSTAT_READ) {
326 		return (EACCES);
327 	}
328 
329 	ring = ksp->ks_private;
330 	stats = &ring->ri_stats;
331 	knp = ksp->ks_data;
332 
333 	/*
334 	 * Assignment order should match that of the names in
335 	 * blk_stats.
336 	 */
337 	(knp++)->value.ui64 = stats->bs_req_reads;
338 	(knp++)->value.ui64 = stats->bs_req_writes;
339 	(knp++)->value.ui64 = stats->bs_req_barriers;
340 	(knp++)->value.ui64 = stats->bs_req_flushes;
341 	(knp++)->value.ui64 = 0; /* oo_req */
342 
343 	return (0);
344 }
345 
346 
347 /*
348  * blk_oe_state_change()
349  */
350 /*ARGSUSED*/
351 static void
352 blk_oe_state_change(dev_info_t *dip, ddi_eventcookie_t id, void *arg,
353     void *impl_data)
354 {
355 	XenbusState new_state;
356 	blk_ring_t ring;
357 
358 
359 	ring = (blk_ring_t)arg;
360 	new_state = *(XenbusState *)impl_data;
361 
362 	mutex_enter(&ring->ri_mutex);
363 
364 	if (blk_check_state_transition(ring, new_state) == DDI_FAILURE) {
365 		mutex_exit(&ring->ri_mutex);
366 		return;
367 	}
368 
369 	switch (new_state) {
370 	case XenbusStateInitialised:
371 		ASSERT(ring->ri_if_status == BLK_IF_UNKNOWN);
372 
373 		/* frontend is ready for connecting */
374 		ring->ri_fe_status = BLK_FE_READY;
375 
376 		if (ring->ri_be_status == BLK_BE_READY) {
377 			mutex_exit(&ring->ri_mutex);
378 			if (blk_start_connect(ring) != DDI_SUCCESS)
379 				(void) blk_start_disconnect(ring);
380 			mutex_enter(&ring->ri_mutex);
381 		}
382 		break;
383 	case XenbusStateClosing:
384 		(void) xvdi_switch_state(dip, XBT_NULL, XenbusStateClosing);
385 		break;
386 	case XenbusStateClosed:
387 		/* clean up */
388 		(void) xvdi_post_event(ring->ri_dip, XEN_HP_REMOVE);
389 		if (ring->ri_ringdown != NULL) {
390 			(*(ring->ri_ringdown))(ring->ri_ringdown_arg);
391 		}
392 		blk_ring_close(ring);
393 
394 		/* reset state in case of reconnect */
395 		ring->ri_if_status = BLK_IF_UNKNOWN;
396 		ring->ri_be_status = BLK_BE_UNKNOWN;
397 		ring->ri_fe_status = BLK_FE_UNKNOWN;
398 		ring->ri_state.rs_sleeping_on_ring = B_FALSE;
399 		ring->ri_state.rs_ring_up = B_FALSE;
400 
401 		break;
402 	default:
403 		ASSERT(0);
404 	}
405 
406 	mutex_exit(&ring->ri_mutex);
407 }
408 
409 
410 /*
411  * blk_hp_state_change()
412  */
413 /*ARGSUSED*/
414 static void
415 blk_hp_state_change(dev_info_t *dip, ddi_eventcookie_t id, void *arg,
416     void *impl_data)
417 {
418 	xendev_hotplug_state_t hpstate;
419 	blk_ring_t ring;
420 
421 
422 	ring = (blk_ring_t)arg;
423 	hpstate = *(xendev_hotplug_state_t *)impl_data;
424 
425 	mutex_enter(&ring->ri_mutex);
426 	if (hpstate == Connected) {
427 		/* Hotplug script has completed successfully */
428 		if (ring->ri_be_status == BLK_BE_UNKNOWN) {
429 			ring->ri_be_status = BLK_BE_READY;
430 			if (ring->ri_fe_status == BLK_FE_READY) {
431 				mutex_exit(&ring->ri_mutex);
432 				/* try to connect to frontend */
433 				if (blk_start_connect(ring) != DDI_SUCCESS)
434 					(void) blk_start_disconnect(ring);
435 				mutex_enter(&ring->ri_mutex);
436 			}
437 		}
438 	}
439 	mutex_exit(&ring->ri_mutex);
440 }
441 
442 
443 /*
444  * blk_check_state_transition()
445  *    check the XenbusState change to see if the change is a valid transition
446  *    or not. The new state is written by frontend domain, or by running
447  *    xenstore-write to change it manually in dom0.
448  */
449 static int
450 blk_check_state_transition(blk_ring_t ring, XenbusState oestate)
451 {
452 	switch (ring->ri_if_status) {
453 	case BLK_IF_UNKNOWN:
454 		if (ring->ri_fe_status == BLK_FE_UNKNOWN) {
455 			if ((oestate == XenbusStateUnknown)		||
456 			    (oestate == XenbusStateConnected))
457 				goto statechkfail_bug;
458 			else if ((oestate == XenbusStateInitialising)	||
459 			    (oestate == XenbusStateInitWait))
460 				goto statechkfail_nop;
461 		} else {
462 			if ((oestate == XenbusStateUnknown)		||
463 			    (oestate == XenbusStateInitialising)	||
464 			    (oestate == XenbusStateInitWait)		||
465 			    (oestate == XenbusStateConnected))
466 				goto statechkfail_bug;
467 			else if (oestate == XenbusStateInitialised)
468 				goto statechkfail_nop;
469 		}
470 		break;
471 
472 	case BLK_IF_CONNECTED:
473 		if ((oestate == XenbusStateUnknown)		||
474 		    (oestate == XenbusStateInitialising)	||
475 		    (oestate == XenbusStateInitWait)		||
476 		    (oestate == XenbusStateInitialised))
477 			goto statechkfail_bug;
478 		else if (oestate == XenbusStateConnected)
479 			goto statechkfail_nop;
480 		break;
481 
482 	case BLK_IF_DISCONNECTED:
483 	default:
484 		goto statechkfail_bug;
485 	}
486 
487 	return (DDI_SUCCESS);
488 
489 statechkfail_bug:
490 	cmn_err(CE_NOTE, "blk@%s: unexpected otherend "
491 	    "state change to %d!, when status is %d",
492 	    ddi_get_name_addr(ring->ri_dip), oestate,
493 	    ring->ri_if_status);
494 
495 statechkfail_nop:
496 	return (DDI_FAILURE);
497 }
498 
499 
500 /*
501  * blk_start_connect()
502  *    Kick-off connect process
503  *    If ri_fe_status == BLK_FE_READY and ri_be_status == BLK_BE_READY
504  *    the ri_if_status will be changed to BLK_IF_CONNECTED on success,
505  *    otherwise, ri_if_status will not be changed
506  */
507 static int
508 blk_start_connect(blk_ring_t ring)
509 {
510 	xenbus_transaction_t xbt;
511 	dev_info_t *dip;
512 	char *barrier;
513 	char *xsnode;
514 	uint_t len;
515 	int e;
516 
517 
518 	dip = ring->ri_dip;
519 
520 	/*
521 	 * Start connect to frontend only when backend device are ready
522 	 * and frontend has moved to XenbusStateInitialised, which means
523 	 * ready to connect
524 	 */
525 	ASSERT(ring->ri_fe_status == BLK_FE_READY);
526 	ASSERT(ring->ri_be_status == BLK_BE_READY);
527 
528 	xsnode = xvdi_get_xsname(dip);
529 	if (xsnode == NULL) {
530 		goto startconnectfail_get_xsname;
531 	}
532 
533 	ring->ri_fe = xvdi_get_oeid(dip);
534 	if (ring->ri_fe == (domid_t)-1) {
535 		goto startconnectfail_get_oeid;
536 	}
537 
538 	e =  xvdi_switch_state(dip, XBT_NULL, XenbusStateInitialised);
539 	if (e > 0) {
540 		goto startconnectfail_switch_init;
541 	}
542 
543 	e = blk_bindto_frontend(ring);
544 	if (e != DDI_SUCCESS) {
545 		goto startconnectfail_bindto_frontend;
546 	}
547 	ring->ri_if_status = BLK_IF_CONNECTED;
548 
549 	e = ddi_add_intr(dip, 0, NULL, NULL, blk_intr, (caddr_t)ring);
550 	if (e != DDI_SUCCESS) {
551 		goto startconnectfail_add_intr;
552 	}
553 
554 trans_retry:
555 	e = xenbus_transaction_start(&xbt);
556 	if (e != 0) {
557 		xvdi_fatal_error(dip, e, "transaction start");
558 		goto startconnectfail_transaction_start;
559 	}
560 
561 	/* If feature-barrier isn't present in xenstore, add it */
562 	e = xenbus_read(xbt, xsnode, "feature-barrier", (void **)&barrier,
563 	    &len);
564 	if (e != 0) {
565 		e = xenbus_printf(xbt, xsnode, "feature-barrier", "%d", 1);
566 		if (e != 0) {
567 			cmn_err(CE_WARN, "xdb@%s: failed to write "
568 			    "'feature-barrier'", ddi_get_name_addr(dip));
569 			xvdi_fatal_error(dip, e, "writing 'feature-barrier'");
570 			(void) xenbus_transaction_end(xbt, 1);
571 			goto startconnectfail_xenbus_printf;
572 		}
573 	} else {
574 		kmem_free(barrier, len);
575 	}
576 
577 	e = xvdi_switch_state(dip, xbt, XenbusStateConnected);
578 	if (e > 0) {
579 		xvdi_fatal_error(dip, e, "writing 'state'");
580 		(void) xenbus_transaction_end(xbt, 1);
581 		goto startconnectfail_switch_connected;
582 	}
583 
584 	e = xenbus_transaction_end(xbt, 0);
585 	if (e != 0) {
586 		if (e == EAGAIN) {
587 			/* transaction is ended, don't need to abort it */
588 			goto trans_retry;
589 		}
590 		xvdi_fatal_error(dip, e, "completing transaction");
591 		goto startconnectfail_transaction_end;
592 	}
593 
594 	mutex_enter(&ring->ri_state.rs_mutex);
595 	ring->ri_state.rs_ring_up = B_TRUE;
596 	if (ring->ri_state.rs_sleeping_on_ring) {
597 		ring->ri_state.rs_sleeping_on_ring = B_FALSE;
598 		cv_signal(&ring->ri_state.rs_cv);
599 	}
600 	mutex_exit(&ring->ri_state.rs_mutex);
601 
602 	if (ring->ri_ringup != NULL) {
603 		(*(ring->ri_ringup))(ring->ri_ringup_arg);
604 	}
605 
606 	return (DDI_SUCCESS);
607 
608 
609 startconnectfail_transaction_end:
610 startconnectfail_switch_connected:
611 startconnectfail_xenbus_printf:
612 startconnectfail_transaction_start:
613 	ddi_remove_intr(dip, 0, NULL);
614 startconnectfail_add_intr:
615 	blk_unbindfrom_frontend(ring);
616 	ring->ri_fe = (domid_t)-1;
617 startconnectfail_bindto_frontend:
618 	(void) xvdi_switch_state(dip, XBT_NULL, XenbusStateClosed);
619 startconnectfail_switch_init:
620 startconnectfail_get_oeid:
621 startconnectfail_get_xsname:
622 	return (DDI_FAILURE);
623 }
624 
625 
626 /*
627  * blk_start_disconnect()
628  *    Kick-off disconnect process. ri_if_status will not be changed
629  */
630 static void
631 blk_start_disconnect(blk_ring_t ring)
632 {
633 	/* Kick-off disconnect process */
634 	(void) xvdi_switch_state(ring->ri_dip, XBT_NULL, XenbusStateClosing);
635 }
636 
637 
638 /*
639  * blk_ring_close()
640  *    Disconnect from frontend and close backend device
641  *    ifstatus will be changed to BLK_DISCONNECTED
642  *    Xenbus state will be changed to XenbusStateClosed
643  */
644 static void
645 blk_ring_close(blk_ring_t ring)
646 {
647 	dev_info_t *dip;
648 
649 
650 	/* mutex protect ri_if_status only here */
651 	ASSERT(MUTEX_HELD(&ring->ri_mutex));
652 
653 	dip = ring->ri_dip;
654 
655 	if (ring->ri_if_status != BLK_IF_CONNECTED) {
656 		return;
657 	}
658 
659 	ring->ri_if_status = BLK_IF_DISCONNECTED;
660 	mutex_exit(&ring->ri_mutex);
661 
662 	/* stop accepting I/O request from frontend */
663 	ddi_remove_intr(dip, 0, NULL);
664 
665 	blk_unbindfrom_frontend(ring);
666 	ring->ri_fe = (domid_t)-1;
667 	(void) xvdi_switch_state(dip, XBT_NULL, XenbusStateClosed);
668 	mutex_enter(&ring->ri_mutex);
669 }
670 
671 
672 /*
673  * blk_bindto_frontend()
674  */
675 static int
676 blk_bindto_frontend(blk_ring_t ring)
677 {
678 	evtchn_port_t evtchn;
679 	char protocol[64];
680 	grant_ref_t gref;
681 	dev_info_t *dip;
682 	char *oename;
683 	int e;
684 
685 
686 	dip = ring->ri_dip;
687 	protocol[0] = 0x0;
688 
689 	/*
690 	 * Gather info from frontend
691 	 */
692 	oename = xvdi_get_oename(dip);
693 	if (oename == NULL) {
694 		return (DDI_FAILURE);
695 	}
696 
697 	e = xenbus_gather(XBT_NULL, oename, "ring-ref", "%lu", &gref,
698 	    "event-channel", "%u", &evtchn, NULL);
699 	if (e != 0) {
700 		xvdi_fatal_error(dip, e,
701 		    "Getting ring-ref and evtchn from frontend");
702 		return (DDI_FAILURE);
703 	}
704 
705 	e = xenbus_gather(XBT_NULL, oename, "protocol", "%63s",
706 	    protocol, NULL);
707 	if (e != 0) {
708 		(void) strcpy(protocol, "unspecified, assuming native");
709 	} else if (strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) {
710 		ring->ri_protocol = BLKIF_PROTOCOL_NATIVE;
711 		ring->ri_nentry = BLKIF_RING_SIZE;
712 		ring->ri_entrysize = sizeof (union blkif_sring_entry);
713 	} else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
714 		ring->ri_protocol = BLKIF_PROTOCOL_X86_32;
715 		ring->ri_nentry = BLKIF_X86_32_RING_SIZE;
716 		ring->ri_entrysize = sizeof (union blkif_x86_32_sring_entry);
717 	} else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
718 		ring->ri_protocol = BLKIF_PROTOCOL_X86_64;
719 		ring->ri_nentry = BLKIF_X86_64_RING_SIZE;
720 		ring->ri_entrysize = sizeof (union blkif_x86_64_sring_entry);
721 	} else {
722 		xvdi_fatal_error(dip, e, "unknown fe protocol");
723 		return (DDI_FAILURE);
724 	}
725 
726 	/*
727 	 * map and init ring
728 	 */
729 	e = xvdi_map_ring(dip, ring->ri_nentry, ring->ri_entrysize, gref,
730 	    &ring->ri_ring);
731 	if (e != DDI_SUCCESS) {
732 		return (DDI_FAILURE);
733 	}
734 
735 	/*
736 	 * bind event channel
737 	 */
738 	e = xvdi_bind_evtchn(dip, evtchn);
739 	if (e != DDI_SUCCESS) {
740 		xvdi_unmap_ring(ring->ri_ring);
741 		return (DDI_FAILURE);
742 	}
743 
744 
745 	return (DDI_SUCCESS);
746 }
747 
748 
749 /*
750  * blk_unbindfrom_frontend()
751  */
752 static void
753 blk_unbindfrom_frontend(blk_ring_t ring)
754 {
755 	xvdi_free_evtchn(ring->ri_dip);
756 	xvdi_unmap_ring(ring->ri_ring);
757 }
758 
759 
760 /*
761  * blk_intr()
762  */
763 static uint_t
764 blk_intr(caddr_t arg)
765 {
766 	blk_ring_t ring;
767 
768 	ring = (blk_ring_t)arg;
769 	if (ring->ri_if_status != BLK_IF_CONNECTED) {
770 		return (DDI_INTR_CLAIMED);
771 	}
772 
773 	(void) (*ring->ri_intr)(ring->ri_intr_arg);
774 	return (DDI_INTR_CLAIMED);
775 }
776 
777 
778 /*
779  * blk_ring_request_get()
780  */
781 boolean_t
782 blk_ring_request_get(blk_ring_t ring, blkif_request_t *req)
783 {
784 	blkif_request_t *src;
785 	blk_stats_t *stats;
786 
787 
788 	mutex_enter(&ring->ri_mutex);
789 	src = xvdi_ring_get_request(ring->ri_ring);
790 	if (src == NULL) {
791 		mutex_exit(&ring->ri_mutex);
792 		return (B_FALSE);
793 	}
794 
795 	switch (ring->ri_protocol) {
796 	case BLKIF_PROTOCOL_NATIVE:
797 		bcopy(src, req, sizeof (*req));
798 		break;
799 	case BLKIF_PROTOCOL_X86_32:
800 		blk_ring_request_32(req, (blkif_x86_32_request_t *)src);
801 		break;
802 	case BLKIF_PROTOCOL_X86_64:
803 		blk_ring_request_64(req, (blkif_x86_64_request_t *)src);
804 		break;
805 	default:
806 		cmn_err(CE_WARN, "blkif@%s: unrecognised protocol: %d",
807 		    ddi_get_name_addr(ring->ri_dip),
808 		    ring->ri_protocol);
809 	}
810 	mutex_exit(&ring->ri_mutex);
811 
812 	stats = &ring->ri_stats;
813 	switch (req->operation) {
814 	case BLKIF_OP_READ:
815 		stats->bs_req_reads++;
816 		break;
817 	case BLKIF_OP_WRITE:
818 		stats->bs_req_writes++;
819 		break;
820 	case BLKIF_OP_WRITE_BARRIER:
821 		stats->bs_req_barriers++;
822 		break;
823 	case BLKIF_OP_FLUSH_DISKCACHE:
824 		stats->bs_req_flushes++;
825 		break;
826 	}
827 
828 	return (B_TRUE);
829 }
830 
831 
832 /*
833  * blk_ring_request_requeue()
834  *    if a request is requeued, caller will have to poll for request
835  *    later.
836  */
837 void
838 blk_ring_request_requeue(blk_ring_t ring)
839 {
840 	ring->ri_ring->xr_sring.br.req_cons--;
841 }
842 
843 
844 /*
845  * blk_ring_response_put()
846  */
847 void
848 blk_ring_response_put(blk_ring_t ring, blkif_response_t *src)
849 {
850 	blkif_response_t *rsp = xvdi_ring_get_response(ring->ri_ring);
851 	int e;
852 
853 	ASSERT(rsp);
854 
855 	switch (ring->ri_protocol) {
856 	case BLKIF_PROTOCOL_NATIVE:
857 		bcopy(src, rsp, sizeof (*rsp));
858 		break;
859 	case BLKIF_PROTOCOL_X86_32:
860 		blk_ring_response_32((blkif_x86_32_response_t *)rsp, src);
861 		break;
862 	case BLKIF_PROTOCOL_X86_64:
863 		blk_ring_response_64((blkif_x86_64_response_t *)rsp, src);
864 		break;
865 	default:
866 		cmn_err(CE_WARN, "blk@%s: unrecognised protocol: %d",
867 		    ddi_get_name_addr(ring->ri_dip),
868 		    ring->ri_protocol);
869 	}
870 
871 	e = xvdi_ring_push_response(ring->ri_ring);
872 	if (e != 0) {
873 		xvdi_notify_oe(ring->ri_dip);
874 	}
875 }
876 
877 
878 /*
879  * blk_ring_request_32()
880  */
881 static void
882 blk_ring_request_32(blkif_request_t *dst, blkif_x86_32_request_t *src)
883 {
884 	int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST;
885 	dst->operation = src->operation;
886 	dst->nr_segments = src->nr_segments;
887 	dst->handle = src->handle;
888 	dst->id = src->id;
889 	dst->sector_number = src->sector_number;
890 	if (n > src->nr_segments)
891 		n = src->nr_segments;
892 	for (i = 0; i < n; i++)
893 		dst->seg[i] = src->seg[i];
894 }
895 
896 
897 /*
898  * blk_ring_request_64()
899  */
900 static void
901 blk_ring_request_64(blkif_request_t *dst, blkif_x86_64_request_t *src)
902 {
903 	int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST;
904 	dst->operation = src->operation;
905 	dst->nr_segments = src->nr_segments;
906 	dst->handle = src->handle;
907 	dst->id = src->id;
908 	dst->sector_number = src->sector_number;
909 	if (n > src->nr_segments)
910 		n = src->nr_segments;
911 	for (i = 0; i < n; i++)
912 		dst->seg[i] = src->seg[i];
913 }
914 
915 
916 /*
917  * blk_ring_response_32()
918  */
919 static void
920 blk_ring_response_32(blkif_x86_32_response_t *dst, blkif_response_t *src)
921 {
922 	dst->id = src->id;
923 	dst->operation = src->operation;
924 	dst->status = src->status;
925 }
926 
927 
928 /*
929  * blk_ring_response_64()
930  */
931 static void
932 blk_ring_response_64(blkif_x86_64_response_t *dst, blkif_response_t *src)
933 {
934 	dst->id = src->id;
935 	dst->operation = src->operation;
936 	dst->status = src->status;
937 }
938 
939 
940 /*
941  * blk_ring_request_dump()
942  */
943 void
944 blk_ring_request_dump(blkif_request_t *req)
945 {
946 	int i;
947 
948 	/*
949 	 * Exploit the public interface definitions for BLKIF_OP_READ
950 	 * etc..
951 	 */
952 	char *op_name[] = { "read", "write", "barrier", "flush" };
953 
954 	cmn_err(CE_NOTE, "   op=%s", op_name[req->operation]);
955 	cmn_err(CE_NOTE, "   num of segments=%d", req->nr_segments);
956 	cmn_err(CE_NOTE, "   handle=%d", req->handle);
957 	cmn_err(CE_NOTE, "   id=0x%llx", (unsigned long long)req->id);
958 	cmn_err(CE_NOTE, "   start sector=%llu",
959 	    (unsigned long long)req->sector_number);
960 	for (i = 0; i < req->nr_segments; i++) {
961 		cmn_err(CE_NOTE, "   gref=%d, first sec=%d,"
962 		    "last sec=%d", req->seg[i].gref, req->seg[i].first_sect,
963 		    req->seg[i].last_sect);
964 	}
965 }
966 
967 
968 /*
969  * blk_ring_response_dump()
970  */
971 void
972 blk_ring_response_dump(blkif_response_t *resp)
973 {
974 	/*
975 	 * Exploit the public interface definitions for BLKIF_OP_READ
976 	 * etc..
977 	 */
978 	char *op_name[] = { "read", "write", "barrier", "flush" };
979 
980 	cmn_err(CE_NOTE, "   op=%d:%s", resp->operation,
981 	    op_name[resp->operation]);
982 	cmn_err(CE_NOTE, "   op=%d", resp->operation);
983 	cmn_err(CE_NOTE, "   status=%d", resp->status);
984 }
985