diff options
Diffstat (limited to 'drivers/xen')
-rw-r--r-- | drivers/xen/gntalloc.c | 4 | ||||
-rw-r--r-- | drivers/xen/gntdev-dmabuf.c | 13 | ||||
-rw-r--r-- | drivers/xen/grant-table.c | 12 | ||||
-rw-r--r-- | drivers/xen/xen-front-pgdir-shbuf.c | 18 | ||||
-rw-r--r-- | drivers/xen/xen-scsiback.c | 82 | ||||
-rw-r--r-- | drivers/xen/xenbus/xenbus_client.c | 82 | ||||
-rw-r--r-- | drivers/xen/xenbus/xenbus_probe.c | 91 |
7 files changed, 228 insertions, 74 deletions
diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c index 4849f94372a4..55acb32842a3 100644 --- a/drivers/xen/gntalloc.c +++ b/drivers/xen/gntalloc.c @@ -178,9 +178,9 @@ static void __del_gref(struct gntalloc_gref *gref) unsigned long addr; if (gref->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { - uint8_t *tmp = kmap(gref->page); + uint8_t *tmp = kmap_local_page(gref->page); tmp[gref->notify.pgoff] = 0; - kunmap(gref->page); + kunmap_local(tmp); } if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT) { notify_remote_via_evtchn(gref->notify.event); diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c index d5bfd7b867fc..91073b4e4a20 100644 --- a/drivers/xen/gntdev-dmabuf.c +++ b/drivers/xen/gntdev-dmabuf.c @@ -24,15 +24,6 @@ MODULE_IMPORT_NS(DMA_BUF); -#ifndef GRANT_INVALID_REF -/* - * Note on usage of grant reference 0 as invalid grant reference: - * grant reference 0 is valid, but never exposed to a driver, - * because of the fact it is already in use/reserved by the PV console. - */ -#define GRANT_INVALID_REF 0 -#endif - struct gntdev_dmabuf { struct gntdev_dmabuf_priv *priv; struct dma_buf *dmabuf; @@ -532,7 +523,7 @@ static void dmabuf_imp_end_foreign_access(u32 *refs, int count) int i; for (i = 0; i < count; i++) - if (refs[i] != GRANT_INVALID_REF) + if (refs[i] != INVALID_GRANT_REF) gnttab_end_foreign_access(refs[i], 0UL); } @@ -567,7 +558,7 @@ static struct gntdev_dmabuf *dmabuf_imp_alloc_storage(int count) gntdev_dmabuf->nr_pages = count; for (i = 0; i < count; i++) - gntdev_dmabuf->u.imp.refs[i] = GRANT_INVALID_REF; + gntdev_dmabuf->u.imp.refs[i] = INVALID_GRANT_REF; return gntdev_dmabuf; diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 8ccccace2a4f..1a1aec0a88a1 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -66,8 +66,6 @@ #include <asm/sync_bitops.h> -/* External tools reserve first few grant table entries. */ -#define NR_RESERVED_ENTRIES 8 #define GNTTAB_LIST_END 0xffffffff static grant_ref_t **gnttab_list; @@ -209,6 +207,10 @@ static inline void check_free_callbacks(void) static void put_free_entry(grant_ref_t ref) { unsigned long flags; + + if (unlikely(ref < GNTTAB_NR_RESERVED_ENTRIES)) + return; + spin_lock_irqsave(&gnttab_list_lock, flags); gnttab_entry(ref) = gnttab_free_head; gnttab_free_head = ref; @@ -1465,12 +1467,12 @@ int gnttab_init(void) nr_init_grefs = nr_grant_frames * gnttab_interface->grefs_per_grant_frame; - for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++) + for (i = GNTTAB_NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++) gnttab_entry(i) = i + 1; gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END; - gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES; - gnttab_free_head = NR_RESERVED_ENTRIES; + gnttab_free_count = nr_init_grefs - GNTTAB_NR_RESERVED_ENTRIES; + gnttab_free_head = GNTTAB_NR_RESERVED_ENTRIES; printk("Grant table initialized\n"); return 0; diff --git a/drivers/xen/xen-front-pgdir-shbuf.c b/drivers/xen/xen-front-pgdir-shbuf.c index a959dee21134..b6433761d42c 100644 --- a/drivers/xen/xen-front-pgdir-shbuf.c +++ b/drivers/xen/xen-front-pgdir-shbuf.c @@ -21,15 +21,6 @@ #include <xen/xen-front-pgdir-shbuf.h> -#ifndef GRANT_INVALID_REF -/* - * FIXME: usage of grant reference 0 as invalid grant reference: - * grant reference 0 is valid, but never exposed to a PV driver, - * because of the fact it is already in use/reserved by the PV console. - */ -#define GRANT_INVALID_REF 0 -#endif - /** * This structure represents the structure of a shared page * that contains grant references to the pages of the shared @@ -38,6 +29,7 @@ */ struct xen_page_directory { grant_ref_t gref_dir_next_page; +#define XEN_GREF_LIST_END 0 grant_ref_t gref[1]; /* Variable length */ }; @@ -83,7 +75,7 @@ grant_ref_t xen_front_pgdir_shbuf_get_dir_start(struct xen_front_pgdir_shbuf *buf) { if (!buf->grefs) - return GRANT_INVALID_REF; + return INVALID_GRANT_REF; return buf->grefs[0]; } @@ -142,7 +134,7 @@ void xen_front_pgdir_shbuf_free(struct xen_front_pgdir_shbuf *buf) int i; for (i = 0; i < buf->num_grefs; i++) - if (buf->grefs[i] != GRANT_INVALID_REF) + if (buf->grefs[i] != INVALID_GRANT_REF) gnttab_end_foreign_access(buf->grefs[i], 0UL); } kfree(buf->grefs); @@ -355,7 +347,7 @@ static void backend_fill_page_dir(struct xen_front_pgdir_shbuf *buf) } /* Last page must say there is no more pages. */ page_dir = (struct xen_page_directory *)ptr; - page_dir->gref_dir_next_page = GRANT_INVALID_REF; + page_dir->gref_dir_next_page = XEN_GREF_LIST_END; } /** @@ -384,7 +376,7 @@ static void guest_fill_page_dir(struct xen_front_pgdir_shbuf *buf) if (grefs_left <= XEN_NUM_GREFS_PER_PAGE) { to_copy = grefs_left; - page_dir->gref_dir_next_page = GRANT_INVALID_REF; + page_dir->gref_dir_next_page = XEN_GREF_LIST_END; } else { to_copy = XEN_NUM_GREFS_PER_PAGE; page_dir->gref_dir_next_page = buf->grefs[i + 1]; diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 0c5e565aa8cf..7a0c93acc2c5 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -280,6 +280,82 @@ static void scsiback_free_translation_entry(struct kref *kref) kfree(entry); } +static int32_t scsiback_result(int32_t result) +{ + int32_t host_status; + + switch (XEN_VSCSIIF_RSLT_HOST(result)) { + case DID_OK: + host_status = XEN_VSCSIIF_RSLT_HOST_OK; + break; + case DID_NO_CONNECT: + host_status = XEN_VSCSIIF_RSLT_HOST_NO_CONNECT; + break; + case DID_BUS_BUSY: + host_status = XEN_VSCSIIF_RSLT_HOST_BUS_BUSY; + break; + case DID_TIME_OUT: + host_status = XEN_VSCSIIF_RSLT_HOST_TIME_OUT; + break; + case DID_BAD_TARGET: + host_status = XEN_VSCSIIF_RSLT_HOST_BAD_TARGET; + break; + case DID_ABORT: + host_status = XEN_VSCSIIF_RSLT_HOST_ABORT; + break; + case DID_PARITY: + host_status = XEN_VSCSIIF_RSLT_HOST_PARITY; + break; + case DID_ERROR: + host_status = XEN_VSCSIIF_RSLT_HOST_ERROR; + break; + case DID_RESET: + host_status = XEN_VSCSIIF_RSLT_HOST_RESET; + break; + case DID_BAD_INTR: + host_status = XEN_VSCSIIF_RSLT_HOST_BAD_INTR; + break; + case DID_PASSTHROUGH: + host_status = XEN_VSCSIIF_RSLT_HOST_PASSTHROUGH; + break; + case DID_SOFT_ERROR: + host_status = XEN_VSCSIIF_RSLT_HOST_SOFT_ERROR; + break; + case DID_IMM_RETRY: + host_status = XEN_VSCSIIF_RSLT_HOST_IMM_RETRY; + break; + case DID_REQUEUE: + host_status = XEN_VSCSIIF_RSLT_HOST_REQUEUE; + break; + case DID_TRANSPORT_DISRUPTED: + host_status = XEN_VSCSIIF_RSLT_HOST_TRANSPORT_DISRUPTED; + break; + case DID_TRANSPORT_FAILFAST: + host_status = XEN_VSCSIIF_RSLT_HOST_TRANSPORT_FAILFAST; + break; + case DID_TARGET_FAILURE: + host_status = XEN_VSCSIIF_RSLT_HOST_TARGET_FAILURE; + break; + case DID_NEXUS_FAILURE: + host_status = XEN_VSCSIIF_RSLT_HOST_NEXUS_FAILURE; + break; + case DID_ALLOC_FAILURE: + host_status = XEN_VSCSIIF_RSLT_HOST_ALLOC_FAILURE; + break; + case DID_MEDIUM_ERROR: + host_status = XEN_VSCSIIF_RSLT_HOST_MEDIUM_ERROR; + break; + case DID_TRANSPORT_MARGINAL: + host_status = XEN_VSCSIIF_RSLT_HOST_TRANSPORT_MARGINAL; + break; + default: + host_status = XEN_VSCSIIF_RSLT_HOST_ERROR; + break; + } + + return (host_status << 16) | (result & 0x00ffff); +} + static void scsiback_send_response(struct vscsibk_info *info, char *sense_buffer, int32_t result, uint32_t resid, uint16_t rqid) @@ -295,7 +371,7 @@ static void scsiback_send_response(struct vscsibk_info *info, ring_res = RING_GET_RESPONSE(&info->ring, info->ring.rsp_prod_pvt); info->ring.rsp_prod_pvt++; - ring_res->rslt = result; + ring_res->rslt = scsiback_result(result); ring_res->rqid = rqid; if (sense_buffer != NULL && @@ -555,7 +631,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req, struct scsiback_nexus *nexus = tpg->tpg_nexus; struct se_cmd *se_cmd = &pending_req->se_cmd; u64 unpacked_lun = pending_req->v2p->lun; - int rc, err = FAILED; + int rc, err = XEN_VSCSIIF_RSLT_RESET_FAILED; init_completion(&pending_req->tmr_done); @@ -569,7 +645,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req, wait_for_completion(&pending_req->tmr_done); err = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ? - SUCCESS : FAILED; + XEN_VSCSIIF_RSLT_RESET_SUCCESS : XEN_VSCSIIF_RSLT_RESET_FAILED; scsiback_do_resp_with_sense(NULL, err, 0, pending_req); transport_generic_free_cmd(&pending_req->se_cmd, 0); diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index df6890681231..d6fdd2d209d3 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -363,50 +363,92 @@ static void xenbus_switch_fatal(struct xenbus_device *dev, int depth, int err, __xenbus_switch_state(dev, XenbusStateClosing, 1); } -/** - * xenbus_grant_ring +/* + * xenbus_setup_ring * @dev: xenbus device - * @vaddr: starting virtual address of the ring + * @vaddr: pointer to starting virtual address of the ring * @nr_pages: number of pages to be granted * @grefs: grant reference array to be filled in * - * Grant access to the given @vaddr to the peer of the given device. - * Then fill in @grefs with grant references. Return 0 on success, or - * -errno on error. On error, the device will switch to - * XenbusStateClosing, and the error will be saved in the store. + * Allocate physically contiguous pages for a shared ring buffer and grant it + * to the peer of the given device. The ring buffer is initially filled with + * zeroes. The virtual address of the ring is stored at @vaddr and the + * grant references are stored in the @grefs array. In case of error @vaddr + * will be set to NULL and @grefs will be filled with INVALID_GRANT_REF. */ -int xenbus_grant_ring(struct xenbus_device *dev, void *vaddr, +int xenbus_setup_ring(struct xenbus_device *dev, gfp_t gfp, void **vaddr, unsigned int nr_pages, grant_ref_t *grefs) { - int err; - unsigned int i; + unsigned long ring_size = nr_pages * XEN_PAGE_SIZE; grant_ref_t gref_head; + unsigned int i; + int ret; - err = gnttab_alloc_grant_references(nr_pages, &gref_head); - if (err) { - xenbus_dev_fatal(dev, err, "granting access to ring page"); - return err; + *vaddr = alloc_pages_exact(ring_size, gfp | __GFP_ZERO); + if (!*vaddr) { + ret = -ENOMEM; + goto err; + } + + ret = gnttab_alloc_grant_references(nr_pages, &gref_head); + if (ret) { + xenbus_dev_fatal(dev, ret, "granting access to %u ring pages", + nr_pages); + goto err; } for (i = 0; i < nr_pages; i++) { unsigned long gfn; - if (is_vmalloc_addr(vaddr)) - gfn = pfn_to_gfn(vmalloc_to_pfn(vaddr)); + if (is_vmalloc_addr(*vaddr)) + gfn = pfn_to_gfn(vmalloc_to_pfn(vaddr[i])); else - gfn = virt_to_gfn(vaddr); + gfn = virt_to_gfn(vaddr[i]); grefs[i] = gnttab_claim_grant_reference(&gref_head); gnttab_grant_foreign_access_ref(grefs[i], dev->otherend_id, gfn, 0); - - vaddr = vaddr + XEN_PAGE_SIZE; } return 0; + + err: + if (*vaddr) + free_pages_exact(*vaddr, ring_size); + for (i = 0; i < nr_pages; i++) + grefs[i] = INVALID_GRANT_REF; + *vaddr = NULL; + + return ret; } -EXPORT_SYMBOL_GPL(xenbus_grant_ring); +EXPORT_SYMBOL_GPL(xenbus_setup_ring); +/* + * xenbus_teardown_ring + * @vaddr: starting virtual address of the ring + * @nr_pages: number of pages + * @grefs: grant reference array + * + * Remove grants for the shared ring buffer and free the associated memory. + * On return the grant reference array is filled with INVALID_GRANT_REF. + */ +void xenbus_teardown_ring(void **vaddr, unsigned int nr_pages, + grant_ref_t *grefs) +{ + unsigned int i; + + for (i = 0; i < nr_pages; i++) { + if (grefs[i] != INVALID_GRANT_REF) { + gnttab_end_foreign_access(grefs[i], 0); + grefs[i] = INVALID_GRANT_REF; + } + } + + if (*vaddr) + free_pages_exact(*vaddr, nr_pages * XEN_PAGE_SIZE); + *vaddr = NULL; +} +EXPORT_SYMBOL_GPL(xenbus_teardown_ring); /** * Allocate an event channel for the given xenbus_device, assigning the newly diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index fe360c33ce71..d367f2bd2b93 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -65,6 +65,7 @@ #include "xenbus.h" +static int xs_init_irq; int xen_store_evtchn; EXPORT_SYMBOL_GPL(xen_store_evtchn); @@ -750,6 +751,20 @@ static void xenbus_probe(void) { xenstored_ready = 1; + if (!xen_store_interface) { + xen_store_interface = xen_remap(xen_store_gfn << XEN_PAGE_SHIFT, + XEN_PAGE_SIZE); + /* + * Now it is safe to free the IRQ used for xenstore late + * initialization. No need to unbind: it is about to be + * bound again from xb_init_comms. Note that calling + * unbind_from_irqhandler now would result in xen_evtchn_close() + * being called and the event channel not being enabled again + * afterwards, resulting in missed event notifications. + */ + free_irq(xs_init_irq, &xb_waitq); + } + /* * In the HVM case, xenbus_init() deferred its call to * xs_init() in case callbacks were not operational yet. @@ -798,20 +813,22 @@ static int __init xenbus_probe_initcall(void) { /* * Probe XenBus here in the XS_PV case, and also XS_HVM unless we - * need to wait for the platform PCI device to come up. + * need to wait for the platform PCI device to come up or + * xen_store_interface is not ready. */ if (xen_store_domain_type == XS_PV || (xen_store_domain_type == XS_HVM && - !xs_hvm_defer_init_for_callback())) + !xs_hvm_defer_init_for_callback() && + xen_store_interface != NULL)) xenbus_probe(); /* - * For XS_LOCAL, spawn a thread which will wait for xenstored - * or a xenstore-stubdom to be started, then probe. It will be - * triggered when communication starts happening, by waiting - * on xb_waitq. + * For XS_LOCAL or when xen_store_interface is not ready, spawn a + * thread which will wait for xenstored or a xenstore-stubdom to be + * started, then probe. It will be triggered when communication + * starts happening, by waiting on xb_waitq. */ - if (xen_store_domain_type == XS_LOCAL) { + if (xen_store_domain_type == XS_LOCAL || xen_store_interface == NULL) { struct task_struct *probe_task; probe_task = kthread_run(xenbus_probe_thread, NULL, @@ -907,10 +924,25 @@ static struct notifier_block xenbus_resume_nb = { .notifier_call = xenbus_resume_cb, }; +static irqreturn_t xenbus_late_init(int irq, void *unused) +{ + int err; + uint64_t v = 0; + + err = hvm_get_parameter(HVM_PARAM_STORE_PFN, &v); + if (err || !v || !~v) + return IRQ_HANDLED; + xen_store_gfn = (unsigned long)v; + + wake_up(&xb_waitq); + return IRQ_HANDLED; +} + static int __init xenbus_init(void) { int err; uint64_t v = 0; + bool wait = false; xen_store_domain_type = XS_UNKNOWN; if (!xen_domain()) @@ -957,25 +989,44 @@ static int __init xenbus_init(void) * been properly initialized. Instead of attempting to map a * wrong guest physical address return error. * - * Also recognize all bits set as an invalid value. + * Also recognize all bits set as an invalid/uninitialized value. */ - if (!v || !~v) { + if (!v) { err = -ENOENT; goto out_error; } - /* Avoid truncation on 32-bit. */ + if (v == ~0ULL) { + wait = true; + } else { + /* Avoid truncation on 32-bit. */ #if BITS_PER_LONG == 32 - if (v > ULONG_MAX) { - pr_err("%s: cannot handle HVM_PARAM_STORE_PFN=%llx > ULONG_MAX\n", - __func__, v); - err = -EINVAL; - goto out_error; - } + if (v > ULONG_MAX) { + pr_err("%s: cannot handle HVM_PARAM_STORE_PFN=%llx > ULONG_MAX\n", + __func__, v); + err = -EINVAL; + goto out_error; + } #endif - xen_store_gfn = (unsigned long)v; - xen_store_interface = - xen_remap(xen_store_gfn << XEN_PAGE_SHIFT, - XEN_PAGE_SIZE); + xen_store_gfn = (unsigned long)v; + xen_store_interface = + xen_remap(xen_store_gfn << XEN_PAGE_SHIFT, + XEN_PAGE_SIZE); + if (xen_store_interface->connection != XENSTORE_CONNECTED) + wait = true; + } + if (wait) { + err = bind_evtchn_to_irqhandler(xen_store_evtchn, + xenbus_late_init, + 0, "xenstore_late_init", + &xb_waitq); + if (err < 0) { + pr_err("xenstore_late_init couldn't bind irq err=%d\n", + err); + return err; + } + + xs_init_irq = err; + } break; default: pr_warn("Xenstore state unknown\n"); |