blk_common.c revision 349b53dd4e695e3d833b5380540385145b2d3ae8
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * CDDL HEADER START
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * The contents of this file are subject to the terms of the
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Common Development and Distribution License (the "License").
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * You may not use this file except in compliance with the License.
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * See the License for the specific language governing permissions
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * and limitations under the License.
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * When distributing Covered Code, include this CDDL HEADER in each
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * If applicable, add the following below this CDDL HEADER, with the
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * fields enclosed by brackets "[]" replaced with your own identifying
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * information: Portions Copyright [yyyy] [name of copyright owner]
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * CDDL HEADER END
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Use is subject to license terms.
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson/* blk interface status */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * initial state
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * frontend xenbus state changed to XenbusStateConnected,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * we finally connect
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * frontend xenbus state changed to XenbusStateClosed,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * interface disconnected
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson/* backend device status */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* initial state */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* backend device is ready (hotplug script finishes successfully) */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson/* frontend status */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* initial state */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * frontend's xenbus state has changed to
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * XenbusStateInitialised, is ready for connecting
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsontypedef struct blk_ring_state_s {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson/* Disk Statistics */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic char *blk_stats[] = {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsontypedef struct blk_stats_s {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* blk interface, backend, and frontend status */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic void blk_oe_state_change(dev_info_t *dip, ddi_eventcookie_t id,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic void blk_hp_state_change(dev_info_t *dip, ddi_eventcookie_t id,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic int blk_check_state_transition(blk_ring_t ring, XenbusState oestate);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic void blk_start_disconnect(blk_ring_t ring);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic int blk_bindto_frontend(blk_ring_t ring);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic void blk_unbindfrom_frontend(blk_ring_t ring);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic int blk_kstat_update(kstat_t *ksp, int flag);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic void blk_ring_request_32(blkif_request_t *dst,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic void blk_ring_request_64(blkif_request_t *dst,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic void blk_ring_response_32(blkif_x86_32_response_t *dst,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonstatic void blk_ring_response_64(blkif_x86_64_response_t *dst,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_init()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_ring_init(blk_ringinit_args_t *args, blk_ring_t *ringp)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson ring = kmem_zalloc(sizeof (struct blk_ring_s), KM_SLEEP);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson mutex_init(&ring->ri_mutex, NULL, MUTEX_DRIVER, NULL);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson mutex_init(&ring->ri_state.rs_mutex, NULL, MUTEX_DRIVER, NULL);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson cv_init(&ring->ri_state.rs_cv, NULL, CV_DRIVER, NULL);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* Watch frontend and hotplug state change */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (xvdi_add_event_handler(ring->ri_dip, XS_OE_STATE,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (xvdi_add_event_handler(ring->ri_dip, XS_HP_STATE,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Kick-off hotplug script
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (xvdi_post_event(ring->ri_dip, XEN_HP_ADD) != DDI_SUCCESS) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson cmn_err(CE_WARN, "blk@%s: failed to start hotplug script",
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * start waiting for hotplug event and otherend state event
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * mainly for debugging, frontend will not take any op seeing this
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson (void) xvdi_switch_state(ring->ri_dip, XBT_NULL, XenbusStateInitWait);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson xvdi_remove_event_handler(ring->ri_dip, XS_HP_STATE);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson xvdi_remove_event_handler(ring->ri_dip, XS_OE_STATE);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_fini()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (ring->ri_if_status != BLK_IF_DISCONNECTED) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_kstat_init()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson int nstat = sizeof (blk_stats) / sizeof (blk_stats[0]);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson ring->ri_kstats = kstat_create(ddi_get_name(ring->ri_dip),
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson ddi_get_instance(ring->ri_dip), "req_statistics", "block",
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson while (nstat > 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_kstat_fini()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_kstat_update()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Assignment order should match that of the names in
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_stats.
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_oe_state_change()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_oe_state_change(dev_info_t *dip, ddi_eventcookie_t id, void *arg,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (blk_check_state_transition(ring, new_state) == DDI_FAILURE) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* frontend is ready for connecting */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson (void) xvdi_switch_state(dip, XBT_NULL, XenbusStateClosing);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* clean up */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson (void) xvdi_post_event(ring->ri_dip, XEN_HP_REMOVE);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* reset state in case of reconnect */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_hp_state_change()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_hp_state_change(dev_info_t *dip, ddi_eventcookie_t id, void *arg,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson hpstate = *(xendev_hotplug_state_t *)impl_data;
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* Hotplug script has completed successfully */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* try to connect to frontend */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_check_state_transition()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * check the XenbusState change to see if the change is a valid transition
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * or not. The new state is written by frontend domain, or by running
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * xenstore-write to change it manually in dom0.
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_check_state_transition(blk_ring_t ring, XenbusState oestate)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson else if ((oestate == XenbusStateInitialising) ||
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson cmn_err(CE_NOTE, "blk@%s: unexpected otherend "
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson "state change to %d!, when status is %d",
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_start_connect()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Kick-off connect process
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * If ri_fe_status == BLK_FE_READY and ri_be_status == BLK_BE_READY
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * the ri_if_status will be changed to BLK_IF_CONNECTED on success,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * otherwise, ri_if_status will not be changed
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Start connect to frontend only when backend device are ready
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * and frontend has moved to XenbusStateInitialised, which means
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * ready to connect
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson e = xvdi_switch_state(dip, XBT_NULL, XenbusStateInitialised);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e > 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson e = ddi_add_intr(dip, 0, NULL, NULL, blk_intr, (caddr_t)ring);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e != 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* If feature-barrier isn't present in xenstore, add it */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson e = xenbus_read(xbt, xsnode, "feature-barrier", (void **)&barrier,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e != 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson e = xenbus_printf(xbt, xsnode, "feature-barrier", "%d", 1);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e != 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson xvdi_fatal_error(dip, e, "writing 'feature-barrier'");
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson e = xvdi_switch_state(dip, xbt, XenbusStateConnected);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e > 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e != 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* transaction is ended, don't need to abort it */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson xvdi_fatal_error(dip, e, "completing transaction");
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson (void) xvdi_switch_state(dip, XBT_NULL, XenbusStateClosed);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_start_disconnect()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Kick-off disconnect process. ri_if_status will not be changed
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* Kick-off disconnect process */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson (void) xvdi_switch_state(ring->ri_dip, XBT_NULL, XenbusStateClosing);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_close()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Disconnect from frontend and close backend device
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * ifstatus will be changed to BLK_DISCONNECTED
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Xenbus state will be changed to XenbusStateClosed
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* mutex protect ri_if_status only here */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson /* stop accepting I/O request from frontend */
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson (void) xvdi_switch_state(dip, XBT_NULL, XenbusStateClosed);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_bindto_frontend()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Gather info from frontend
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson e = xenbus_gather(XBT_NULL, oename, "ring-ref", "%lu", &gref,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e != 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson "Getting ring-ref and evtchn from frontend");
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson e = xenbus_gather(XBT_NULL, oename, "protocol", "%63s",
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e != 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson (void) strcpy(protocol, "unspecified, assuming native");
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson } else if (strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson ring->ri_entrysize = sizeof (union blkif_sring_entry);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson ring->ri_entrysize = sizeof (union blkif_x86_32_sring_entry);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson ring->ri_entrysize = sizeof (union blkif_x86_64_sring_entry);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson xvdi_fatal_error(dip, e, "unknown fe protocol");
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * map and init ring
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson e = xvdi_map_ring(dip, ring->ri_nentry, ring->ri_entrysize, gref,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * bind event channel
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_unbindfrom_frontend()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_intr()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_request_get()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_ring_request_get(blk_ring_t ring, blkif_request_t *req)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson blk_ring_request_32(req, (blkif_x86_32_request_t *)src);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson blk_ring_request_64(req, (blkif_x86_64_request_t *)src);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson cmn_err(CE_WARN, "blkif@%s: unrecognised protocol: %d",
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_request_requeue()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * if a request is requeued, caller will have to poll for request
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_response_put()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_ring_response_put(blk_ring_t ring, blkif_response_t *src)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson blk_ring_response_32((blkif_x86_32_response_t *)rsp, src);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson blk_ring_response_64((blkif_x86_64_response_t *)rsp, src);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson cmn_err(CE_WARN, "blk@%s: unrecognised protocol: %d",
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson if (e != 0) {
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_request_32()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_ring_request_32(blkif_request_t *dst, blkif_x86_32_request_t *src)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson for (i = 0; i < n; i++)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_request_64()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_ring_request_64(blkif_request_t *dst, blkif_x86_64_request_t *src)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson for (i = 0; i < n; i++)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_response_32()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_ring_response_32(blkif_x86_32_response_t *dst, blkif_response_t *src)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_response_64()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnsonblk_ring_response_64(blkif_x86_64_response_t *dst, blkif_response_t *src)
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_request_dump()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Exploit the public interface definitions for BLKIF_OP_READ
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson char *op_name[] = { "read", "write", "barrier", "flush" };
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson cmn_err(CE_NOTE, " op=%s", op_name[req->operation]);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson cmn_err(CE_NOTE, " num of segments=%d", req->nr_segments);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson cmn_err(CE_NOTE, " id=0x%llx", (unsigned long long)req->id);
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson "last sec=%d", req->seg[i].gref, req->seg[i].first_sect,
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * blk_ring_response_dump()
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson * Exploit the public interface definitions for BLKIF_OP_READ
7eea693d6b672899726e75993fddc4e95b52647fMark Johnson char *op_name[] = { "read", "write", "barrier", "flush" };