static int xen_pcibk_attach(struct xen_pcibk_device *pdev) { int err = 0; int gnt_ref, remote_evtchn; char *magic = NULL; mutex_lock(&pdev->dev_lock); /* Make sure we only do this setup once */ if (xenbus_read_driver_state(pdev->xdev->nodename) != XenbusStateInitialised) goto out; /* Wait for frontend to state that it has published the configuration */ if (xenbus_read_driver_state(pdev->xdev->otherend) != XenbusStateInitialised) goto out; dev_dbg(&pdev->xdev->dev, "Reading frontend config\n"); err = xenbus_gather(XBT_NIL, pdev->xdev->otherend, "pci-op-ref", "%u", &gnt_ref, "event-channel", "%u", &remote_evtchn, "magic", NULL, &magic, NULL); if (err) { /* If configuration didn't get read correctly, wait longer */ xenbus_dev_fatal(pdev->xdev, err, "Error reading configuration from frontend"); goto out; } if (magic == NULL || strcmp(magic, XEN_PCI_MAGIC) != 0) { xenbus_dev_fatal(pdev->xdev, -EFAULT, "version mismatch (%s/%s) with pcifront - " "halting " DRV_NAME, magic, XEN_PCI_MAGIC); err = -EFAULT; goto out; } err = xen_pcibk_do_attach(pdev, gnt_ref, remote_evtchn); if (err) goto out; dev_dbg(&pdev->xdev->dev, "Connecting...\n"); err = xenbus_switch_state(pdev->xdev, XenbusStateConnected); if (err) xenbus_dev_fatal(pdev->xdev, err, "Error switching to connected state!"); dev_dbg(&pdev->xdev->dev, "Connected? %d\n", err); out: mutex_unlock(&pdev->dev_lock); kfree(magic); return err; }
/*ARGSUSED2*/ static void otherend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len) { struct xenbus_device *dev = watch->dev; XenbusState state; /* * Protect us against watches firing on old details when the otherend * details change, say immediately after a resume. */ if (!dev->otherend || strncmp(dev->otherend, vec[XS_WATCH_PATH], strlen(dev->otherend))) { #if 0 printf("Ignoring watch at %s", vec[XS_WATCH_PATH]); #endif return; } state = xenbus_read_driver_state(dev->otherend); #if 0 printf("state is %d, %s, %s", state, dev->otherend_watch.node, vec[XS_WATCH_PATH]); #endif if (dev->otherend_changed) dev->otherend_changed(dev, state); }
static int __devinit pcifront_attach_devices(struct pcifront_device *pdev) { int err = -EFAULT; int i, num_roots, len; unsigned int domain, bus; char str[64]; spin_lock(&pdev->dev_lock); if (xenbus_read_driver_state(pdev->xdev->nodename) != XenbusStateReconfiguring) goto out; err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, "root_num", "%d", &num_roots); if (err == -ENOENT) { xenbus_dev_error(pdev->xdev, err, "No PCI Roots found, trying 0000:00"); err = pcifront_rescan_root(pdev, 0, 0); num_roots = 0; } else if (err != 1) { if (err == 0) err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error reading number of PCI roots"); goto out; } for (i = 0; i < num_roots; i++) { len = snprintf(str, sizeof(str), "root-%d", i); if (unlikely(len >= (sizeof(str) - 1))) { err = -ENOMEM; goto out; } err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, "%x:%x", &domain, &bus); if (err != 2) { if (err >= 0) err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error reading PCI root %d", i); goto out; } err = pcifront_rescan_root(pdev, domain, bus); if (err) { xenbus_dev_fatal(pdev->xdev, err, "Error scanning PCI root %04x:%02x", domain, bus); goto out; } } xenbus_switch_state(pdev->xdev, XenbusStateConnected); out: spin_unlock(&pdev->dev_lock); return err; }
int xenbus_probe_node(struct xen_bus_type *bus, const char *type, const char *nodename) { char devname[XEN_BUS_ID_SIZE]; int err; struct xenbus_device *xendev; size_t stringlen; char *tmpstring; enum xenbus_state state = xenbus_read_driver_state(nodename); if (state != XenbusStateInitialising) { /* Device is not new, so ignore it. This can happen if a device is going away after switching to Closed. */ return 0; } stringlen = strlen(nodename) + 1 + strlen(type) + 1; xendev = kzalloc(sizeof(*xendev) + stringlen, GFP_KERNEL); if (!xendev) return -ENOMEM; xendev->state = XenbusStateInitialising; /* Copy the strings into the extra space. */ tmpstring = (char *)(xendev + 1); strcpy(tmpstring, nodename); xendev->nodename = tmpstring; tmpstring += strlen(tmpstring) + 1; strcpy(tmpstring, type); xendev->devicetype = tmpstring; init_completion(&xendev->down); xendev->dev.bus = &bus->bus; xendev->dev.release = xenbus_dev_release; err = bus->get_bus_id(devname, xendev->nodename); if (err) goto fail; dev_set_name(&xendev->dev, "%s", devname); /* Register with generic device framework. */ err = device_register(&xendev->dev); if (err) goto fail; return 0; fail: kfree(xendev); return err; }
static void xen_pcibk_be_watch(struct xenbus_watch *watch, const char **vec, unsigned int len) { struct xen_pcibk_device *pdev = container_of(watch, struct xen_pcibk_device, be_watch); switch (xenbus_read_driver_state(pdev->xdev->nodename)) { case XenbusStateInitWait: xen_pcibk_setup_backend(pdev); break; default: break; } }
/* add for vmdq migrate.When the device is vmdq_vnic ,return */ if(0 == strcmp(xendev->devicetype, VMDQ_VNIC)){ return 0; } int err = xenbus_gather(XBT_NIL, xendev->nodename, id_node, "%i", &xendev->otherend_id, path_node, NULL, &xendev->otherend, NULL); if (err) { xenbus_dev_fatal(xendev, err, "reading other end details from %s", xendev->nodename); return err; } if (strlen(xendev->otherend) == 0 || !xenbus_exists(XBT_NIL, xendev->otherend, "")) { xenbus_dev_fatal(xendev, -ENOENT, "unable to read other end from %s. " "missing or inaccessible.", xendev->nodename); free_otherend_details(xendev); return -ENOENT; } return 0; } PARAVIRT_EXPORT_SYMBOL(xenbus_read_otherend_details); #if defined(CONFIG_XEN) || defined(MODULE) static int read_backend_details(struct xenbus_device *xendev) { return xenbus_read_otherend_details(xendev, "backend-id", "backend"); } static void otherend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len) #else /* !CONFIG_XEN && !MODULE */ void xenbus_otherend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len, int ignore_on_shutdown) #endif /* CONFIG_XEN || MODULE */ { struct xenbus_device *dev = container_of(watch, struct xenbus_device, otherend_watch); struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); enum xenbus_state state; /* Protect us against watches firing on old details when the otherend details change, say immediately after a resume. */ if (!dev->otherend || strncmp(dev->otherend, vec[XS_WATCH_PATH], strlen(dev->otherend))) { dev_dbg(&dev->dev, "Ignoring watch at %s", vec[XS_WATCH_PATH]); return; } state = xenbus_read_driver_state(dev->otherend); dev_dbg(&dev->dev, "state is %d (%s), %s, %s", state, xenbus_strstate(state), dev->otherend_watch.node, vec[XS_WATCH_PATH]); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) /* * Ignore xenbus transitions during shutdown. This prevents us doing * work that can fail e.g., when the rootfs is gone. */ if (system_state > SYSTEM_RUNNING) { /* If we're frontend, drive the state machine to Closed. */ /* This should cause the backend to release our resources. */ # if defined(CONFIG_XEN) || defined(MODULE) const struct xen_bus_type *bus = container_of(dev->dev.bus, struct xen_bus_type, bus); int ignore_on_shutdown = (bus->levels == 2); # endif if (ignore_on_shutdown && (state == XenbusStateClosing)) xenbus_frontend_closed(dev); return; } #endif if (drv->otherend_changed) drv->otherend_changed(dev, state); }
/* The backend is closing so process a disconnect */ static int pcifront_disconnect(struct pcifront_device *pdev) { int err = 0; XenbusState prev_state; prev_state = xenbus_read_driver_state(pdev->xdev->nodename); if (prev_state < XenbusStateClosing) { err = xenbus_switch_state(pdev->xdev, NULL, XenbusStateClosing); if (!err && prev_state == XenbusStateConnected) { /* TODO - need to detach the newbus devices */ } } return err; }
static void otherend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len) { struct xenbus_device *dev = container_of(watch, struct xenbus_device, otherend_watch); struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); enum xenbus_state state; /* Protect us against watches firing on old details when the otherend details change, say immediately after a resume. */ if (!dev->otherend || strncmp(dev->otherend, vec[XS_WATCH_PATH], strlen(dev->otherend))) { dev_dbg(&dev->dev, "Ignoring watch at %s\n", vec[XS_WATCH_PATH]); return; } state = xenbus_read_driver_state(dev->otherend); dev_dbg(&dev->dev, "state is %d, (%s), %s, %s\n", state, xenbus_strstate(state), dev->otherend_watch.node, vec[XS_WATCH_PATH]); /* * Ignore xenbus transitions during shutdown. This prevents us doing * work that can fail e.g., when the rootfs is gone. */ if (system_state > SYSTEM_RUNNING) { struct xen_bus_type *bus = bus; bus = container_of(dev->dev.bus, struct xen_bus_type, bus); /* If we're frontend, drive the state machine to Closed. */ /* This should cause the backend to release our resources. */ if ((bus == &xenbus_frontend) && (state == XenbusStateClosing)) xenbus_frontend_closed(dev); return; } if (drv->otherend_changed) drv->otherend_changed(dev, state); }
static int print_device_status(struct device *dev, void *data) { struct xenbus_device *xendev = to_xenbus_device(dev); struct device_driver *drv = data; /* Is this operation limited to a particular driver? */ if (drv && (dev->driver != drv)) return 0; if (!dev->driver) { /* Information only: is this too noisy? */ pr_info("Device with no driver: %s\n", xendev->nodename); } else if (xendev->state < XenbusStateConnected) { enum xenbus_state rstate = XenbusStateUnknown; if (xendev->otherend) rstate = xenbus_read_driver_state(xendev->otherend); pr_warn("Timeout connecting to device: %s (local state %d, remote state %d)\n", xendev->nodename, xendev->state, rstate); } return 0; }
/** * XenBus watch callback registered against the "state" XenStore * node of the other-end of a split device connection. * * This callback is invoked whenever the state of a device instance's * peer changes. * * \param watch The xs_watch object used to register this callback * function. * \param vec An array of pointers to NUL terminated strings containing * watch event data. The vector should be indexed via the * xs_watch_type enum in xs_wire.h. * \param vec_size The number of elements in vec. */ static void xenbusb_otherend_watch_cb(struct xs_watch *watch, const char **vec, unsigned int vec_size __unused) { struct xenbus_device_ivars *ivars; device_t child; device_t bus; const char *path; enum xenbus_state newstate; ivars = (struct xenbus_device_ivars *)watch->callback_data; child = ivars->xd_dev; bus = device_get_parent(child); path = vec[XS_WATCH_PATH]; if (ivars->xd_otherend_path == NULL || strncmp(ivars->xd_otherend_path, path, ivars->xd_otherend_path_len)) return; newstate = xenbus_read_driver_state(ivars->xd_otherend_path); XENBUSB_OTHEREND_CHANGED(bus, child, newstate); }
void xenbus_otherend_changed(struct xenbus_watch *watch, const char **vec, unsigned int len, int ignore_on_shutdown) { struct xenbus_device *dev = container_of(watch, struct xenbus_device, otherend_watch); struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); enum xenbus_state state; /* Protect us against watches firing on old details when the otherend details change, say immediately after a resume. */ if (!dev->otherend || strncmp(dev->otherend, vec[XS_WATCH_PATH], strlen(dev->otherend))) { dev_dbg(&dev->dev, "Ignoring watch at %s\n", vec[XS_WATCH_PATH]); return; } state = xenbus_read_driver_state(dev->otherend); dev_dbg(&dev->dev, "state is %d, (%s), %s, %s\n", state, xenbus_strstate(state), dev->otherend_watch.node, vec[XS_WATCH_PATH]); /* * Ignore xenbus transitions during shutdown. This prevents us doing * work that can fail e.g., when the rootfs is gone. */ if (system_state > SYSTEM_RUNNING) { if (ignore_on_shutdown && (state == XenbusStateClosing)) xenbus_frontend_closed(dev); return; } if (drv->otherend_changed) drv->otherend_changed(dev, state); }
static int pcifront_try_disconnect(struct pcifront_device *pdev) { int err = 0; enum xenbus_state prev_state; spin_lock(&pdev->dev_lock); prev_state = xenbus_read_driver_state(pdev->xdev->nodename); if (prev_state >= XenbusStateClosing) goto out; if(prev_state == XenbusStateConnected) { pcifront_free_roots(pdev); pcifront_disconnect(pdev); } err = xenbus_switch_state(pdev->xdev, XenbusStateClosed); out: spin_unlock(&pdev->dev_lock); return err; }
static int print_device_status(struct device *dev, void *data) { struct xenbus_device *xendev = to_xenbus_device(dev); struct device_driver *drv = data; /* */ if (drv && (dev->driver != drv)) return 0; if (!dev->driver) { /* */ printk(KERN_INFO "XENBUS: Device with no driver: %s\n", xendev->nodename); } else if (xendev->state < XenbusStateConnected) { enum xenbus_state rstate = XenbusStateUnknown; if (xendev->otherend) rstate = xenbus_read_driver_state(xendev->otherend); printk(KERN_WARNING "XENBUS: Timeout connecting " "to device: %s (local state %d, remote state %d)\n", xendev->nodename, xendev->state, rstate); } return 0; }
static int pcifront_detach_devices(struct pcifront_device *pdev) { int err = 0; int i, num_devs; unsigned int domain, bus, slot, func; struct pci_bus *pci_bus; struct pci_dev *pci_dev; char str[64]; spin_lock(&pdev->dev_lock); if (xenbus_read_driver_state(pdev->xdev->nodename) != XenbusStateConnected) goto out; err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, "num_devs", "%d", &num_devs); if (err != 1) { if (err >= 0) err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error reading number of PCI devices"); goto out; } /* Find devices being detached and remove them. */ for (i = 0; i < num_devs; i++) { int l, state; l = snprintf(str, sizeof(str), "state-%d", i); if (unlikely(l >= (sizeof(str) - 1))) { err = -ENOMEM; goto out; } err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, "%d", &state); if (err != 1) state = XenbusStateUnknown; if (state != XenbusStateClosing) continue; /* Remove device. */ l = snprintf(str, sizeof(str), "vdev-%d", i); if (unlikely(l >= (sizeof(str) - 1))) { err = -ENOMEM; goto out; } err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, "%x:%x:%x.%x", &domain, &bus, &slot, &func); if (err != 4) { if (err >= 0) err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error reading PCI device %d", i); goto out; } pci_bus = pci_find_bus(domain, bus); if(!pci_bus) { dev_dbg(&pdev->xdev->dev, "Cannot get bus %04x:%02x\n", domain, bus); continue; } pci_dev = pci_get_slot(pci_bus, PCI_DEVFN(slot, func)); if(!pci_dev) { dev_dbg(&pdev->xdev->dev, "Cannot get PCI device %04x:%02x:%02x.%02x\n", domain, bus, slot, func); continue; } pci_remove_bus_device(pci_dev); pci_dev_put(pci_dev); dev_dbg(&pdev->xdev->dev, "PCI device %04x:%02x:%02x.%02x removed.\n", domain, bus, slot, func); } err = xenbus_switch_state(pdev->xdev, XenbusStateReconfiguring); out: spin_unlock(&pdev->dev_lock); return err; }
static int xen_pcibk_setup_backend(struct xen_pcibk_device *pdev) { /* Get configuration from xend (if available now) */ int domain, bus, slot, func; int err = 0; int i, num_devs; char dev_str[64]; char state_str[64]; mutex_lock(&pdev->dev_lock); /* It's possible we could get the call to setup twice, so make sure * we're not already connected. */ if (xenbus_read_driver_state(pdev->xdev->nodename) != XenbusStateInitWait) goto out; dev_dbg(&pdev->xdev->dev, "getting be setup\n"); err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d", &num_devs); if (err != 1) { if (err >= 0) err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error reading number of devices"); goto out; } for (i = 0; i < num_devs; i++) { int l = snprintf(dev_str, sizeof(dev_str), "dev-%d", i); if (unlikely(l >= (sizeof(dev_str) - 1))) { err = -ENOMEM; xenbus_dev_fatal(pdev->xdev, err, "String overflow while reading " "configuration"); goto out; } err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, dev_str, "%x:%x:%x.%x", &domain, &bus, &slot, &func); if (err < 0) { xenbus_dev_fatal(pdev->xdev, err, "Error reading device configuration"); goto out; } if (err != 4) { err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error parsing pci device " "configuration"); goto out; } err = xen_pcibk_export_device(pdev, domain, bus, slot, func, i); if (err) goto out; /* Switch substate of this device. */ l = snprintf(state_str, sizeof(state_str), "state-%d", i); if (unlikely(l >= (sizeof(state_str) - 1))) { err = -ENOMEM; xenbus_dev_fatal(pdev->xdev, err, "String overflow while reading " "configuration"); goto out; } err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, state_str, "%d", XenbusStateInitialised); if (err) { xenbus_dev_fatal(pdev->xdev, err, "Error switching " "substate of dev-%d\n", i); goto out; } } err = xen_pcibk_publish_pci_roots(pdev, xen_pcibk_publish_pci_root); if (err) { xenbus_dev_fatal(pdev->xdev, err, "Error while publish PCI root buses " "for frontend"); goto out; } err = xenbus_switch_state(pdev->xdev, XenbusStateInitialised); if (err) xenbus_dev_fatal(pdev->xdev, err, "Error switching to initialised state!"); out: mutex_unlock(&pdev->dev_lock); if (!err) /* see if pcifront is already configured (if not, we'll wait) */ xen_pcibk_attach(pdev); return err; }
static int xen_pcibk_reconfigure(struct xen_pcibk_device *pdev) { int err = 0; int num_devs; int domain, bus, slot, func; int substate; int i, len; char state_str[64]; char dev_str[64]; dev_dbg(&pdev->xdev->dev, "Reconfiguring device ...\n"); mutex_lock(&pdev->dev_lock); /* Make sure we only reconfigure once */ if (xenbus_read_driver_state(pdev->xdev->nodename) != XenbusStateReconfiguring) goto out; err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d", &num_devs); if (err != 1) { if (err >= 0) err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error reading number of devices"); goto out; } for (i = 0; i < num_devs; i++) { len = snprintf(state_str, sizeof(state_str), "state-%d", i); if (unlikely(len >= (sizeof(state_str) - 1))) { err = -ENOMEM; xenbus_dev_fatal(pdev->xdev, err, "String overflow while reading " "configuration"); goto out; } err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, state_str, "%d", &substate); if (err != 1) substate = XenbusStateUnknown; switch (substate) { case XenbusStateInitialising: dev_dbg(&pdev->xdev->dev, "Attaching dev-%d ...\n", i); len = snprintf(dev_str, sizeof(dev_str), "dev-%d", i); if (unlikely(len >= (sizeof(dev_str) - 1))) { err = -ENOMEM; xenbus_dev_fatal(pdev->xdev, err, "String overflow while " "reading configuration"); goto out; } err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, dev_str, "%x:%x:%x.%x", &domain, &bus, &slot, &func); if (err < 0) { xenbus_dev_fatal(pdev->xdev, err, "Error reading device " "configuration"); goto out; } if (err != 4) { err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error parsing pci device " "configuration"); goto out; } err = xen_pcibk_export_device(pdev, domain, bus, slot, func, i); if (err) goto out; /* Publish pci roots. */ err = xen_pcibk_publish_pci_roots(pdev, xen_pcibk_publish_pci_root); if (err) { xenbus_dev_fatal(pdev->xdev, err, "Error while publish PCI root" "buses for frontend"); goto out; } err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, state_str, "%d", XenbusStateInitialised); if (err) { xenbus_dev_fatal(pdev->xdev, err, "Error switching substate of " "dev-%d\n", i); goto out; } break; case XenbusStateClosing: dev_dbg(&pdev->xdev->dev, "Detaching dev-%d ...\n", i); len = snprintf(dev_str, sizeof(dev_str), "vdev-%d", i); if (unlikely(len >= (sizeof(dev_str) - 1))) { err = -ENOMEM; xenbus_dev_fatal(pdev->xdev, err, "String overflow while " "reading configuration"); goto out; } err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, dev_str, "%x:%x:%x.%x", &domain, &bus, &slot, &func); if (err < 0) { xenbus_dev_fatal(pdev->xdev, err, "Error reading device " "configuration"); goto out; } if (err != 4) { err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Error parsing pci device " "configuration"); goto out; } err = xen_pcibk_remove_device(pdev, domain, bus, slot, func); if (err) goto out; /* TODO: If at some point we implement support for pci * root hot-remove on pcifront side, we'll need to * remove unnecessary xenstore nodes of pci roots here. */ break; default: break; } } err = xenbus_switch_state(pdev->xdev, XenbusStateReconfigured); if (err) { xenbus_dev_fatal(pdev->xdev, err, "Error switching to reconfigured state!"); goto out; } out: mutex_unlock(&pdev->dev_lock); return 0; }
static enum xenbus_state xenbus_otherend_state(struct xenbus_device_ivars *ivars) { return (xenbus_read_driver_state(ivars->xd_otherend_path)); }
static int xenbus_add_device(device_t dev, const char *bus, const char *type, const char *id) { device_t child; struct xenbus_device_ivars *ivars; enum xenbus_state state; char *statepath; int error; ivars = malloc(sizeof(struct xenbus_device_ivars), M_DEVBUF, M_ZERO|M_WAITOK); ivars->xd_node = kasprintf("%s/%s/%s", bus, type, id); if (xenbus_device_exists(dev, ivars->xd_node)) { /* * We are already tracking this node */ free(ivars->xd_node, M_DEVBUF); free(ivars, M_DEVBUF); return (0); } state = xenbus_read_driver_state(ivars->xd_node); if (state != XenbusStateInitialising) { /* * Device is not new, so ignore it. This can * happen if a device is going away after * switching to Closed. */ free(ivars->xd_node, M_DEVBUF); free(ivars, M_DEVBUF); return (0); } /* * Find the backend details */ error = xenbus_gather(XBT_NIL, ivars->xd_node, "backend-id", "%i", &ivars->xd_otherend_id, "backend", NULL, &ivars->xd_otherend_path, NULL); if (error) return (error); sx_init(&ivars->xd_lock, "xdlock"); ivars->xd_type = strdup(type, M_DEVBUF); ivars->xd_state = XenbusStateInitialising; statepath = malloc(strlen(ivars->xd_otherend_path) + strlen("/state") + 1, M_DEVBUF, M_WAITOK); sprintf(statepath, "%s/state", ivars->xd_otherend_path); ivars->xd_otherend_watch.node = statepath; ivars->xd_otherend_watch.callback = xenbus_backend_changed; child = device_add_child(dev, NULL, -1); ivars->xd_dev = child; device_set_ivars(child, ivars); return (0); }
static void scsifront_free(struct vscsifrnt_info *info) { struct Scsi_Host *host = info->host; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) if (host->shost_state != SHOST_DEL) { #else if (!test_bit(SHOST_DEL, &host->shost_state)) { #endif scsi_remove_host(info->host); } if (info->ring_ref != GRANT_INVALID_REF) { gnttab_end_foreign_access(info->ring_ref, (unsigned long)info->ring.sring); info->ring_ref = GRANT_INVALID_REF; info->ring.sring = NULL; } if (info->irq) unbind_from_irqhandler(info->irq, info); info->irq = 0; scsi_host_put(info->host); } static int scsifront_alloc_ring(struct vscsifrnt_info *info) { struct xenbus_device *dev = info->dev; struct vscsiif_sring *sring; int err = -ENOMEM; info->ring_ref = GRANT_INVALID_REF; /***** Frontend to Backend ring start *****/ sring = (struct vscsiif_sring *) __get_free_page(GFP_KERNEL); if (!sring) { xenbus_dev_fatal(dev, err, "fail to allocate shared ring (Front to Back)"); return err; } SHARED_RING_INIT(sring); FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); err = xenbus_grant_ring(dev, virt_to_mfn(sring)); if (err < 0) { free_page((unsigned long) sring); info->ring.sring = NULL; xenbus_dev_fatal(dev, err, "fail to grant shared ring (Front to Back)"); goto free_sring; } info->ring_ref = err; err = bind_listening_port_to_irqhandler( dev->otherend_id, scsifront_intr, SA_SAMPLE_RANDOM, "scsifront", info); if (err <= 0) { xenbus_dev_fatal(dev, err, "bind_listening_port_to_irqhandler"); goto free_sring; } info->irq = err; return 0; /* free resource */ free_sring: scsifront_free(info); return err; } static int scsifront_init_ring(struct vscsifrnt_info *info) { struct xenbus_device *dev = info->dev; struct xenbus_transaction xbt; int err; DPRINTK("%s\n",__FUNCTION__); err = scsifront_alloc_ring(info); if (err) return err; DPRINTK("%u %u\n", info->ring_ref, info->evtchn); again: err = xenbus_transaction_start(&xbt); if (err) { xenbus_dev_fatal(dev, err, "starting transaction"); } err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u", info->ring_ref); if (err) { xenbus_dev_fatal(dev, err, "%s", "writing ring-ref"); goto fail; } err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", irq_to_evtchn_port(info->irq)); if (err) { xenbus_dev_fatal(dev, err, "%s", "writing event-channel"); goto fail; } err = xenbus_transaction_end(xbt, 0); if (err) { if (err == -EAGAIN) goto again; xenbus_dev_fatal(dev, err, "completing transaction"); goto free_sring; } return 0; fail: xenbus_transaction_end(xbt, 1); free_sring: /* free resource */ scsifront_free(info); return err; } static int scsifront_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { struct vscsifrnt_info *info; struct Scsi_Host *host; int i, err = -ENOMEM; char name[DEFAULT_TASK_COMM_LEN]; host = scsi_host_alloc(&scsifront_sht, sizeof(*info)); if (!host) { xenbus_dev_fatal(dev, err, "fail to allocate scsi host"); return err; } info = (struct vscsifrnt_info *) host->hostdata; info->host = host; dev->dev.driver_data = info; info->dev = dev; for (i = 0; i < VSCSIIF_MAX_REQS; i++) { info->shadow[i].next_free = i + 1; init_waitqueue_head(&(info->shadow[i].wq_reset)); info->shadow[i].wait_reset = 0; } info->shadow[VSCSIIF_MAX_REQS - 1].next_free = 0x0fff; err = scsifront_init_ring(info); if (err) { scsi_host_put(host); return err; } init_waitqueue_head(&info->wq); spin_lock_init(&info->io_lock); spin_lock_init(&info->shadow_lock); snprintf(name, DEFAULT_TASK_COMM_LEN, "vscsiif.%d", info->host->host_no); info->kthread = kthread_run(scsifront_schedule, info, name); if (IS_ERR(info->kthread)) { err = PTR_ERR(info->kthread); info->kthread = NULL; printk(KERN_ERR "scsifront: kthread start err %d\n", err); goto free_sring; } host->max_id = VSCSIIF_MAX_TARGET; host->max_channel = 0; host->max_lun = VSCSIIF_MAX_LUN; host->max_sectors = (VSCSIIF_SG_TABLESIZE - 1) * PAGE_SIZE / 512; err = scsi_add_host(host, &dev->dev); if (err) { printk(KERN_ERR "scsifront: fail to add scsi host %d\n", err); goto free_sring; } xenbus_switch_state(dev, XenbusStateInitialised); return 0; free_sring: /* free resource */ scsifront_free(info); return err; } static int scsifront_remove(struct xenbus_device *dev) { struct vscsifrnt_info *info = dev->dev.driver_data; DPRINTK("%s: %s removed\n",__FUNCTION__ ,dev->nodename); if (info->kthread) { kthread_stop(info->kthread); info->kthread = NULL; } scsifront_free(info); return 0; } static int scsifront_disconnect(struct vscsifrnt_info *info) { struct xenbus_device *dev = info->dev; struct Scsi_Host *host = info->host; DPRINTK("%s: %s disconnect\n",__FUNCTION__ ,dev->nodename); /* When this function is executed, all devices of Frontend have been deleted. Therefore, it need not block I/O before remove_host. */ scsi_remove_host(host); xenbus_frontend_closed(dev); return 0; } #define VSCSIFRONT_OP_ADD_LUN 1 #define VSCSIFRONT_OP_DEL_LUN 2 static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op) { struct xenbus_device *dev = info->dev; int i, err = 0; char str[64], state_str[64]; char **dir; unsigned int dir_n = 0; unsigned int device_state; unsigned int hst, chn, tgt, lun; struct scsi_device *sdev; dir = xenbus_directory(XBT_NIL, dev->otherend, "vscsi-devs", &dir_n); if (IS_ERR(dir)) return; for (i = 0; i < dir_n; i++) { /* read status */ snprintf(str, sizeof(str), "vscsi-devs/%s/state", dir[i]); err = xenbus_scanf(XBT_NIL, dev->otherend, str, "%u", &device_state); if (XENBUS_EXIST_ERR(err)) continue; /* virtual SCSI device */ snprintf(str, sizeof(str), "vscsi-devs/%s/v-dev", dir[i]); err = xenbus_scanf(XBT_NIL, dev->otherend, str, "%u:%u:%u:%u", &hst, &chn, &tgt, &lun); if (XENBUS_EXIST_ERR(err)) continue; /* front device state path */ snprintf(state_str, sizeof(state_str), "vscsi-devs/%s/state", dir[i]); switch (op) { case VSCSIFRONT_OP_ADD_LUN: if (device_state == XenbusStateInitialised) { sdev = scsi_device_lookup(info->host, chn, tgt, lun); if (sdev) { printk(KERN_ERR "scsifront: Device already in use.\n"); scsi_device_put(sdev); xenbus_printf(XBT_NIL, dev->nodename, state_str, "%d", XenbusStateClosed); } else { scsi_add_device(info->host, chn, tgt, lun); xenbus_printf(XBT_NIL, dev->nodename, state_str, "%d", XenbusStateConnected); } } break; case VSCSIFRONT_OP_DEL_LUN: if (device_state == XenbusStateClosing) { sdev = scsi_device_lookup(info->host, chn, tgt, lun); if (sdev) { scsi_remove_device(sdev); scsi_device_put(sdev); xenbus_printf(XBT_NIL, dev->nodename, state_str, "%d", XenbusStateClosed); } } break; default: break; } } kfree(dir); return; } static void scsifront_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { struct vscsifrnt_info *info = dev->dev.driver_data; DPRINTK("%p %u %u\n", dev, dev->state, backend_state); switch (backend_state) { case XenbusStateUnknown: case XenbusStateInitialising: case XenbusStateInitWait: case XenbusStateClosed: break; case XenbusStateInitialised: break; case XenbusStateConnected: if (xenbus_read_driver_state(dev->nodename) == XenbusStateInitialised) { scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN); } if (dev->state == XenbusStateConnected) break; xenbus_switch_state(dev, XenbusStateConnected); break; case XenbusStateClosing: scsifront_disconnect(info); break; case XenbusStateReconfiguring: scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_DEL_LUN); xenbus_switch_state(dev, XenbusStateReconfiguring); break; case XenbusStateReconfigured: scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN); xenbus_switch_state(dev, XenbusStateConnected); break; } } static struct xenbus_device_id scsifront_ids[] = { { "vscsi" }, { "" } }; MODULE_ALIAS("xen:vscsi"); static struct xenbus_driver scsifront_driver = { .name = "vscsi", .owner = THIS_MODULE, .ids = scsifront_ids, .probe = scsifront_probe, .remove = scsifront_remove, /* .resume = scsifront_resume, */ .otherend_changed = scsifront_backend_changed, }; int scsifront_xenbus_init(void) { return xenbus_register_frontend(&scsifront_driver); } void scsifront_xenbus_unregister(void) { xenbus_unregister_driver(&scsifront_driver); }
int xenbus_probe_node(struct xen_bus_type *bus, const char *type, const char *nodename) { int err; struct xenbus_device *xendev; size_t stringlen; char *tmpstring; enum xenbus_state state = xenbus_read_driver_state(nodename); if (bus->error) return bus->error; if (state != XenbusStateInitialising) { /* Device is not new, so ignore it. This can happen if a device is going away after switching to Closed. */ return 0; } stringlen = strlen(nodename) + 1 + strlen(type) + 1; xendev = kzalloc(sizeof(*xendev) + stringlen, GFP_KERNEL); if (!xendev) return -ENOMEM; xendev->state = XenbusStateInitialising; /* Copy the strings into the extra space. */ tmpstring = (char *)(xendev + 1); strcpy(tmpstring, nodename); xendev->nodename = tmpstring; tmpstring += strlen(tmpstring) + 1; strcpy(tmpstring, type); xendev->devicetype = tmpstring; init_completion(&xendev->down); #if defined(CONFIG_XEN) || defined(MODULE) xendev->dev.parent = &bus->dev; #endif xendev->dev.bus = &bus->bus; xendev->dev.release = xenbus_dev_release; err = bus->get_bus_id(xendev->dev.bus_id, xendev->nodename); if (err) goto fail; /* Register with generic device framework. */ err = device_register(&xendev->dev); if (err) goto fail; err = device_create_file(&xendev->dev, &dev_attr_nodename); if (err) goto fail_unregister; err = device_create_file(&xendev->dev, &dev_attr_devtype); if (err) goto fail_remove_nodename; err = device_create_file(&xendev->dev, &dev_attr_modalias); if (err) goto fail_remove_devtype; return 0; fail_remove_devtype: device_remove_file(&xendev->dev, &dev_attr_devtype); fail_remove_nodename: device_remove_file(&xendev->dev, &dev_attr_nodename); fail_unregister: device_unregister(&xendev->dev); fail: kfree(xendev); return err; }