xref: /titanic_41/usr/src/uts/common/xen/io/blk_common.c (revision 23294c7da48c2eb5222bccedbefb1e06cf5c4df3)
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 2009 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 
790 	if (ring->ri_if_status != BLK_IF_CONNECTED) {
791 		mutex_exit(&ring->ri_mutex);
792 		return (B_FALSE);
793 	}
794 
795 	src = xvdi_ring_get_request(ring->ri_ring);
796 	if (src == NULL) {
797 		mutex_exit(&ring->ri_mutex);
798 		return (B_FALSE);
799 	}
800 
801 	switch (ring->ri_protocol) {
802 	case BLKIF_PROTOCOL_NATIVE:
803 		bcopy(src, req, sizeof (*req));
804 		break;
805 	case BLKIF_PROTOCOL_X86_32:
806 		blk_ring_request_32(req, (blkif_x86_32_request_t *)src);
807 		break;
808 	case BLKIF_PROTOCOL_X86_64:
809 		blk_ring_request_64(req, (blkif_x86_64_request_t *)src);
810 		break;
811 	default:
812 		cmn_err(CE_WARN, "blkif@%s: unrecognised protocol: %d",
813 		    ddi_get_name_addr(ring->ri_dip),
814 		    ring->ri_protocol);
815 	}
816 	mutex_exit(&ring->ri_mutex);
817 
818 	stats = &ring->ri_stats;
819 	switch (req->operation) {
820 	case BLKIF_OP_READ:
821 		stats->bs_req_reads++;
822 		break;
823 	case BLKIF_OP_WRITE:
824 		stats->bs_req_writes++;
825 		break;
826 	case BLKIF_OP_WRITE_BARRIER:
827 		stats->bs_req_barriers++;
828 		break;
829 	case BLKIF_OP_FLUSH_DISKCACHE:
830 		stats->bs_req_flushes++;
831 		break;
832 	}
833 
834 	return (B_TRUE);
835 }
836 
837 
838 /*
839  * blk_ring_request_requeue()
840  *    if a request is requeued, caller will have to poll for request
841  *    later.
842  */
843 void
844 blk_ring_request_requeue(blk_ring_t ring)
845 {
846 	mutex_enter(&ring->ri_mutex);
847 
848 	if (ring->ri_if_status != BLK_IF_CONNECTED) {
849 		mutex_exit(&ring->ri_mutex);
850 		return;
851 	}
852 
853 	ring->ri_ring->xr_sring.br.req_cons--;
854 
855 	mutex_exit(&ring->ri_mutex);
856 }
857 
858 
859 /*
860  * blk_ring_response_put()
861  */
862 void
863 blk_ring_response_put(blk_ring_t ring, blkif_response_t *src)
864 {
865 	blkif_response_t *rsp;
866 	int e;
867 
868 
869 	mutex_enter(&ring->ri_mutex);
870 
871 	if (ring->ri_if_status != BLK_IF_CONNECTED) {
872 		mutex_exit(&ring->ri_mutex);
873 		return;
874 	}
875 
876 	rsp = xvdi_ring_get_response(ring->ri_ring);
877 	ASSERT(rsp);
878 
879 	switch (ring->ri_protocol) {
880 	case BLKIF_PROTOCOL_NATIVE:
881 		bcopy(src, rsp, sizeof (*rsp));
882 		break;
883 	case BLKIF_PROTOCOL_X86_32:
884 		blk_ring_response_32((blkif_x86_32_response_t *)rsp, src);
885 		break;
886 	case BLKIF_PROTOCOL_X86_64:
887 		blk_ring_response_64((blkif_x86_64_response_t *)rsp, src);
888 		break;
889 	default:
890 		cmn_err(CE_WARN, "blk@%s: unrecognised protocol: %d",
891 		    ddi_get_name_addr(ring->ri_dip),
892 		    ring->ri_protocol);
893 	}
894 
895 	e = xvdi_ring_push_response(ring->ri_ring);
896 	if (e != 0) {
897 		xvdi_notify_oe(ring->ri_dip);
898 	}
899 
900 	mutex_exit(&ring->ri_mutex);
901 }
902 
903 
904 /*
905  * blk_ring_request_32()
906  */
907 static void
908 blk_ring_request_32(blkif_request_t *dst, blkif_x86_32_request_t *src)
909 {
910 	int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST;
911 	dst->operation = src->operation;
912 	dst->nr_segments = src->nr_segments;
913 	dst->handle = src->handle;
914 	dst->id = src->id;
915 	dst->sector_number = src->sector_number;
916 	if (n > src->nr_segments)
917 		n = src->nr_segments;
918 	for (i = 0; i < n; i++)
919 		dst->seg[i] = src->seg[i];
920 }
921 
922 
923 /*
924  * blk_ring_request_64()
925  */
926 static void
927 blk_ring_request_64(blkif_request_t *dst, blkif_x86_64_request_t *src)
928 {
929 	int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST;
930 	dst->operation = src->operation;
931 	dst->nr_segments = src->nr_segments;
932 	dst->handle = src->handle;
933 	dst->id = src->id;
934 	dst->sector_number = src->sector_number;
935 	if (n > src->nr_segments)
936 		n = src->nr_segments;
937 	for (i = 0; i < n; i++)
938 		dst->seg[i] = src->seg[i];
939 }
940 
941 
942 /*
943  * blk_ring_response_32()
944  */
945 static void
946 blk_ring_response_32(blkif_x86_32_response_t *dst, blkif_response_t *src)
947 {
948 	dst->id = src->id;
949 	dst->operation = src->operation;
950 	dst->status = src->status;
951 }
952 
953 
954 /*
955  * blk_ring_response_64()
956  */
957 static void
958 blk_ring_response_64(blkif_x86_64_response_t *dst, blkif_response_t *src)
959 {
960 	dst->id = src->id;
961 	dst->operation = src->operation;
962 	dst->status = src->status;
963 }
964 
965 
966 /*
967  * blk_ring_request_dump()
968  */
969 void
970 blk_ring_request_dump(blkif_request_t *req)
971 {
972 	int i;
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=%s", op_name[req->operation]);
981 	cmn_err(CE_NOTE, "   num of segments=%d", req->nr_segments);
982 	cmn_err(CE_NOTE, "   handle=%d", req->handle);
983 	cmn_err(CE_NOTE, "   id=0x%llx", (unsigned long long)req->id);
984 	cmn_err(CE_NOTE, "   start sector=%llu",
985 	    (unsigned long long)req->sector_number);
986 	for (i = 0; i < req->nr_segments; i++) {
987 		cmn_err(CE_NOTE, "   gref=%d, first sec=%d,"
988 		    "last sec=%d", req->seg[i].gref, req->seg[i].first_sect,
989 		    req->seg[i].last_sect);
990 	}
991 }
992 
993 
994 /*
995  * blk_ring_response_dump()
996  */
997 void
998 blk_ring_response_dump(blkif_response_t *resp)
999 {
1000 	/*
1001 	 * Exploit the public interface definitions for BLKIF_OP_READ
1002 	 * etc..
1003 	 */
1004 	char *op_name[] = { "read", "write", "barrier", "flush" };
1005 
1006 	cmn_err(CE_NOTE, "   op=%d:%s", resp->operation,
1007 	    op_name[resp->operation]);
1008 	cmn_err(CE_NOTE, "   op=%d", resp->operation);
1009 	cmn_err(CE_NOTE, "   status=%d", resp->status);
1010 }
1011