# HG changeset patch # User t.horikoshi@jp.fujitsu.com # Date 1193730190 -32400 # Node ID 374aa5b731056b2182ff60d2efa9f3524232d8d8 # Parent 24e0cb9fe9df6f4a6df226823562bec3089072e6 [LINUX][scsiback] add scsi backend driver Signed-off-by: Tomonari Horikoshi Signed-off-by: Tsunehisa Doi Signed-off-by: Jun Kamada Signed-off-by: Akira Hayakawa diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/Makefile Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,8 @@ +ifeq ($(CONFIG_XEN_FC),y) + EXTRA_CFLAGS += -DCONFIG_XEN_FC + obj-$(CONFIG_XEN_SCSI_BACKEND) += xen-fcscsibk.o + xen-fcscsibk-y += interface.o scsiback.o xenbus.o fcback.o comback.o traceback.o +else + obj-$(CONFIG_XEN_SCSI_BACKEND) += xen-scsibk.o + xen-scsibk-y += interface.o scsiback.o xenbus.o comback.o traceback.o +endif diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/comback.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/comback.c Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,272 @@ +/* + * Xen SCSI backend driver + * + * Copyright (c) 2007, FUJITSU Limited + * + * Based on the blkback driver code. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#ifdef CONFIG_XEN_FC +#include +#include +#endif + +#include "comback.h" + +extern struct list_head pending_free; +extern int vscsiif_reqs; + +static DEFINE_SPINLOCK(pending_free_lock); +static DECLARE_WAIT_QUEUE_HEAD(pending_free_wq); + +extern void scsiback_cmd_exec(pending_req_t *); +extern int copy_request_ring_info(struct comback_info *, + struct vscsiif_ftb_request *, pending_req_t *); +extern void scsiback_reset_exec(pending_req_t *); +extern void scsi_trace(unsigned long, unsigned char *, unsigned int); + +#ifdef CONFIG_XEN_FC +extern void fcback_cmd_exec(pending_req_t *); +#endif + +static void read_btf_ring_resp_cons(struct comback_info *info); + +/* ------------------------------------------------------------ */ +/* for frontend to backend communication */ +/* ------------------------------------------------------------ */ + +static pending_req_t * alloc_req(void) +{ + pending_req_t *req = NULL; + unsigned long flags; + + spin_lock_irqsave(&pending_free_lock, flags); + if (!list_empty(&pending_free)) { + req = list_entry(pending_free.next, pending_req_t, u.scsi.free_list); + list_del(&req->u.scsi.free_list); + } + spin_unlock_irqrestore(&pending_free_lock, flags); + return req; +} + +void free_req(pending_req_t *req) +{ + unsigned long flags; + int was_empty; + + spin_lock_irqsave(&pending_free_lock, flags); + was_empty = list_empty(&pending_free); + list_add(&req->u.scsi.free_list, &pending_free); + spin_unlock_irqrestore(&pending_free_lock, flags); + if (was_empty) + wake_up(&pending_free_wq); +} + +static void comback_notify_work(struct comback_info *info) +{ + info->waiting_reqs = 1; + wake_up(&info->wq); +} + +irqreturn_t comback_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + comback_notify_work((struct comback_info *)dev_id); + return IRQ_HANDLED; +} + +static int __copy_request_ring_info(struct comback_info *info, + struct vscsiif_ftb_request *ftb_req, pending_req_t *pending_req) +{ + pending_req->cmnd = ftb_req->cmnd; + pending_req->info = info; + + if (ftb_req->cmnd == CMND_SCSI || ftb_req->cmnd == CMND_SCSI_RESET) { + return copy_request_ring_info(info, ftb_req, pending_req); + } else { +#ifdef CONFIG_XEN_FC + pending_req->u.fc.ftb_req = *ftb_req; +#else + BUG(); +#endif + } + return 0; +} + +static int do_comback_cmd_fn(struct comback_info *info) +{ + struct vscsiif_ftb_back_ring *ftb_ring = &info->ftb_ring; + struct vscsiif_ftb_request *ftb_req; + struct vscsiif_ftb_response *ftb_resp; + + pending_req_t *pending_req[vscsiif_reqs]; + RING_IDX rc, rp; + int i, err, more_to_do = 0; + int queued = 0; + int notify; + + DPRINTK("%s\n",__FUNCTION__); + + rc = ftb_ring->req_cons; + rp = ftb_ring->sring->req_prod; + rmb(); + + while ((rc != rp) && !RING_REQUEST_CONS_OVERFLOW(ftb_ring, rc)) { + pending_req[queued] = alloc_req(); + if (NULL == pending_req[queued]) { + more_to_do = 1; + break; + } + + /***** Front to Back request consume *****/ + + ftb_req = RING_GET_REQUEST(ftb_ring, rc); + ftb_ring->req_cons = ++rc; + + err = __copy_request_ring_info(info, ftb_req, pending_req[queued]); + + scsi_trace(0, (unsigned char *)(pending_req[queued]), + sizeof(pending_req_t)); + + /* It responds immediately after the command is issued. */ + ftb_resp = RING_GET_RESPONSE(ftb_ring, + ftb_ring->rsp_prod_pvt); + + if (!RING_FULL(&info->btf_ring)) { + ftb_resp->status = SCSIIF_REQ_OKAY; + } else { + ftb_resp->status = SCSIIF_BTFRING_BUSY; + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->btf_ring, notify); + if (notify) + notify_remote_via_irq(info->irq); + } + + ftb_ring->rsp_prod_pvt++; + RING_PUSH_RESPONSES(ftb_ring); + + queued++; + } + + for (i = 0; i < queued; i++) { + if (pending_req[i]->cmnd == CMND_SCSI) { + scsiback_cmd_exec(pending_req[i]); + } else if (pending_req[i]->cmnd == CMND_SCSI_RESET) { + scsiback_reset_exec(pending_req[i]); +#ifndef CONFIG_XEN_FC + } +#else + } else { + fcback_cmd_exec(pending_req[i]); + } +#endif + } + + if (ftb_ring->rsp_prod_pvt == rc) { + RING_FINAL_CHECK_FOR_REQUESTS(ftb_ring, more_to_do); + + } else if (RING_HAS_UNCONSUMED_REQUESTS(ftb_ring)) { + more_to_do = 1; + } + + return more_to_do; +} + +int comback_schedule(void *data) +{ + struct comback_info *info = (struct comback_info *)data; + + DPRINTK("%s\n",__FUNCTION__); + + scsiback_get(info); + + while (!kthread_should_stop()) { + wait_event_interruptible( + info->wq, + info->waiting_reqs || kthread_should_stop()); + wait_event_interruptible( + pending_free_wq, + !list_empty(&pending_free) || kthread_should_stop()); + + info->waiting_reqs = 0; + smp_mb(); + + if (do_comback_cmd_fn(info)) + info->waiting_reqs = 1; + + /***** Back to Front response consume *****/ + read_btf_ring_resp_cons(info); + } + + info->kthread = NULL; + scsiback_put(info); + + return 0; +} + +/* ------------------------------------------------------------ */ +/* for backend to frontend communication */ +/* ------------------------------------------------------------ */ + +struct vscsiif_btf_request *comback_pre_reply(struct comback_info *info) +{ + struct vscsiif_btf_request *btf_req; + + rmb(); + btf_req = RING_GET_REQUEST(&info->btf_ring, info->btf_ring.req_prod_pvt); + info->btf_ring.req_prod_pvt++; + + return btf_req; +} + +static void read_btf_ring_resp_cons(struct comback_info *info) +{ + RING_IDX rp; + + rp = info->btf_ring.sring->rsp_prod; + rmb(); + + if (info->btf_ring.rsp_cons != rp) + info->btf_ring.rsp_cons = rp; +} + +void comback_do_reply(struct comback_info *info) +{ + int notify; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->btf_ring, notify); + if (notify) + notify_remote_via_irq(info->irq); + + /***** Back to Front response consume *****/ + read_btf_ring_resp_cons(info); + + if (RING_HAS_UNCONSUMED_REQUESTS(&info->ftb_ring)) { + comback_notify_work(info); + } +} diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/comback.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/comback.h Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2007, FUJITSU Limited + * + * Based on the blkback driver code. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __SCSIIF__BACKEND__COMMON_H__ +#define __SCSIIF__BACKEND__COMMON_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DPRINTK(_f, _a...) \ + pr_debug("(file=%s, line=%d) " _f, \ + __FILE__ , __LINE__ , ## _a ) + +struct comback_info { + struct xenbus_device *dev; + struct Scsi_Host *host; + + domid_t domid; + unsigned int evtchn; + unsigned int irq; + + unsigned short host_no; + + struct vscsiif_ftb_back_ring ftb_ring; + struct vscsiif_btf_front_ring btf_ring; + struct vm_struct *ftb_ring_area; + struct vm_struct *btf_ring_area; + + grant_handle_t ftb_shmem_handle; + grant_ref_t ftb_shmem_ref; + grant_handle_t btf_shmem_handle; + grant_ref_t btf_shmem_ref; + + struct work_struct scsiback_work; + + spinlock_t ring_lock_ftb; + spinlock_t ring_lock_btf; + atomic_t refcnt; + + struct task_struct *kthread; + wait_queue_head_t waiting_to_free; + wait_queue_head_t wq; + unsigned int waiting_reqs; +#ifdef CONFIG_XEN_FC + void *fcinfo; +#endif + struct page **mmap_pages; + +}; + +typedef struct { + unsigned int cmnd; + struct comback_info *info; + union { + struct scsi_pending_req { + struct scsi_device *sdev; + unsigned short data_dir; + uint32_t rqid; + int use_sg; + int request_bufflen; + atomic_t pendcnt; + struct request *rq; + struct scsiback_request_segment{ + grant_ref_t gref; + uint32_t offset; + uint32_t length; + } pend_seg[SG_TABLESIZE]; + struct list_head free_list; + } scsi; +#ifdef CONFIG_XEN_FC + struct { + struct vscsiif_ftb_request ftb_req; + } fc; +#endif + } u; +} pending_req_t; + +typedef struct scsi_pending_req scsi_pending_req_t; + +irqreturn_t scsiback_intr(int, void *, struct pt_regs *); +int scsiback_init_sring(struct comback_info *, + unsigned long, unsigned long, unsigned int); +int scsiback_schedule(void *data); + + +#define scsiback_get(_b) (atomic_inc(&(_b)->refcnt)) +#define scsiback_put(_b) \ + do { \ + if (atomic_dec_and_test(&(_b)->refcnt)) \ + wake_up(&(_b)->waiting_to_free);\ + } while (0) + +struct comback_info *scsiinfo_alloc(domid_t domid); +void scsiback_free(struct comback_info *info); +void scsiback_disconnect(struct comback_info *info); +void __init scsiback_interface_init(void); +void __exit scsiback_interface_exit(void); +int scsiif_xenbus_init(void); +void scsiif_xenbus_unregister(void); + + +#endif /* __SCSIIF__BACKEND__COMMON_H__ */ diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/fcback.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/fcback.c Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,610 @@ +/* + * Xen SCSI FC backend driver + * + * Copyright (c) 2007, FUJITSU Limited + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comback.h" +#include "fcback.h" + +#define SET_FT(x, z, y) \ + if ((x)->z == 0) { \ + (y)->z = 0; \ + } else { \ + (y)->z = 1; \ + } + + +extern void unbind_from_irqhandler(unsigned int, void *); +extern struct fc_function_template *fc_shost2ft(struct Scsi_Host *); +extern struct vscsiif_btf_request *comback_pre_reply(struct comback_info *); +extern void comback_do_reply(struct comback_info *); + + +/* ---------------------------------------------------------------------- */ + +static struct scsi_target *shost_to_starget(struct Scsi_Host *shost, + unsigned int channel, + unsigned int id) +{ + struct scsi_target *starget, *found_starget = NULL; + + list_for_each_entry(starget, &(shost->__targets), siblings) { + if ((starget->channel == channel) && (starget->id == id)) { + found_starget = starget; + break; + } + } + + return found_starget; +} + +static struct fc_rport *shost_to_rport(struct Scsi_Host *shost, u64 node_name, + u64 port_name, u32 port_id, u32 roles) +{ + struct fc_rport *rport, *found_rport = NULL; + + list_for_each_entry(rport, &fc_host_rports(shost), peers) { + if ((rport->node_name == node_name) && + (rport->port_name == port_name) && + (rport->port_id == port_id) && + (rport->roles == roles)) { + found_rport = rport; + break; + } + } + + return found_rport; +} + + +/* ---------------------------------------------------------------------- */ + +static int fcback_main(struct fcback_info *info, + struct vscsiif_ftb_request *ring_req_tmp) +{ + struct comback_info *cominfo = info->cominfo; + struct vscsiif_ftb_request *ring_req = &(info->ftb_req); + struct vscsiif_btf_request *ring_res; + struct fc_function_template *ft = fc_shost2ft(cominfo->host); + struct scsi_target *starget; + struct fc_rport *rport; + + /* Must check by class or something ... */ + if ((strcmp(cominfo->host->hostt->name, "qla2xxx") != 0) && + (strcmp(cominfo->host->hostt->name, "lpfc") != 0)) { + printk(KERN_ERR + "fcback: %s: not a supported fibre channel card>>>\n", + __FUNCTION__); + return -1; + } + + memcpy(ring_req, ring_req_tmp, sizeof(struct vscsiif_ftb_request)); + + /* + Perform specified function and send response to DomU + */ + + ring_res = comback_pre_reply(cominfo); + + ring_res->rslt = -1; + + switch (ring_req->cmnd) { + case CMND_GET_HOST_PORT_ID: + if ((ft->show_host_port_id != 0) && + (ft->get_host_port_id != NULL)) { + ft->get_host_port_id(cominfo->host); + ring_res->u.ghpi.port_id = ((struct fc_host_attrs *) + (cominfo->host->shost_data))->port_id; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_HOST_PORT_TYPE: + if ((ft->show_host_port_type != 0) && + (ft->get_host_port_type != NULL)) { + ft->get_host_port_type(cominfo->host); + ring_res->u.ghpt.port_type = ((struct fc_host_attrs *) + (cominfo->host->shost_data))->port_type; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_HOST_PORT_STATE: + if ((ft->show_host_port_state != 0) && + (ft->get_host_port_state != NULL)) { + ft->get_host_port_state(cominfo->host); + ring_res->u.ghps.port_state = ((struct fc_host_attrs *) + (cominfo->host->shost_data))->port_state; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_HOST_ACTIVE_FC4S: + if ((ft->show_host_active_fc4s != 0) && + (ft->get_host_active_fc4s != NULL)) { + ft->get_host_active_fc4s(cominfo->host); + memcpy(ring_res->u.ghaf.active_fc4s, + ((struct fc_host_attrs *) + (cominfo->host->shost_data))->active_fc4s, + sizeof(ring_res->u.ghaf.active_fc4s)); + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_HOST_SPEED: + if ((ft->show_host_speed != 0) && + (ft->get_host_speed != NULL)) { + ft->get_host_speed(cominfo->host); + ring_res->u.ghsp.speed = ((struct fc_host_attrs *) + (cominfo->host->shost_data))->speed; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_HOST_FABRIC_NAME: + if ((ft->show_host_fabric_name != 0) && + (ft->get_host_fabric_name != NULL)) { + ft->get_host_fabric_name(cominfo->host); + ring_res->u.ghfn.fabric_name = + ((struct fc_host_attrs *) + (cominfo->host->shost_data))->fabric_name; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_HOST_STATS: { + struct fc_host_statistics *hs; + + if (ft->get_fc_host_stats != NULL) { + if ((hs = ft->get_fc_host_stats( + cominfo->host)) == NULL) { + break; + } + ring_res->u.ghst.stats = *hs; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; } + + case CMND_RESET_HOST_STATS: + if (ft->reset_fc_host_stats != NULL) { + ft->reset_fc_host_stats(cominfo->host); + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_ISSUE_HOST_LIP: + if (ft->issue_fc_host_lip != NULL) { + ring_res->rslt = ft->issue_fc_host_lip(cominfo->host); + ring_res->rqid = ring_req->rqid; + } + break; + + case CMND_GET_STARGET_PORT_ID: + if ((ft->show_starget_port_id != 0) && + (ft->get_starget_port_id != NULL)) { + if ((starget = shost_to_starget(cominfo->host, + ring_req->u.gtpi.channel, + ring_req->u.gtpi.id)) == NULL) { + break; + } + ft->get_starget_port_id(starget); + ring_res->u.gtpi.port_id = ((struct fc_starget_attrs *) + (&(starget->starget_data)))->port_id; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_STARGET_NODE_NAME: + if ((ft->show_starget_node_name != 0) && + (ft->get_starget_node_name != NULL)) { + if ((starget = shost_to_starget(cominfo->host, + ring_req->u.gtnn.channel, + ring_req->u.gtnn.id)) == NULL) { + break; + } + ft->get_starget_node_name(starget); + ring_res->u.gtnn.node_name = + ((struct fc_starget_attrs *) + (&(starget->starget_data)))->node_name; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_STARGET_PORT_NAME: + if ((ft->show_starget_port_name != 0) && + (ft->get_starget_port_name != NULL)) { + if ((starget = shost_to_starget(cominfo->host, + ring_req->u.gtpn.channel, + ring_req->u.gtpn.id)) == NULL) { + break; + } + ft->get_starget_port_name(starget); + ring_res->u.gtpn.port_name = + ((struct fc_starget_attrs *) + (&(starget->starget_data)))->port_name; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_RPORT_LOSS_TMO: + if ((ft->show_rport_dev_loss_tmo != 0) && + (ft->get_rport_dev_loss_tmo != NULL)) { + if ((rport = shost_to_rport(cominfo->host, + ring_req->u.gplt.node_name, + ring_req->u.gplt.port_name, + ring_req->u.gplt.port_id, + ring_req->u.gplt.roles)) == NULL) { + break; + } + ft->get_rport_dev_loss_tmo(rport); + ring_res->u.gplt.timeout = rport->dev_loss_tmo; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_SET_RPORT_LOSS_TMO: + if (ft->set_rport_dev_loss_tmo != NULL) { + if ((rport = shost_to_rport(cominfo->host, + ring_req->u.splt.node_name, + ring_req->u.splt.port_name, + ring_req->u.splt.port_id, + ring_req->u.splt.roles)) == NULL) { + break; + } + ft->set_rport_dev_loss_tmo(rport, + ring_req->u.splt.timeout); + ring_res->u.splt.timeout = rport->dev_loss_tmo; + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + } + break; + + case CMND_GET_INITIAL_SHOST_ATTRIB: { + struct fc_host_attrs *host_attrs = (struct fc_host_attrs *) + (cominfo->host->shost_data); + struct giha *giha = (struct giha *)(info->gnt_area->addr); + + memcpy(giha->active_fc4s, host_attrs->active_fc4s, + sizeof(giha->active_fc4s)); + giha->fabric_name = host_attrs->fabric_name; + giha->maxframe_size = host_attrs->maxframe_size; + giha->node_name = host_attrs->node_name; + giha->permanent_port_name + = host_attrs->permanent_port_name; + giha->port_id = host_attrs->port_id; + giha->port_name = host_attrs->port_name; + giha->port_state = host_attrs->port_state; + giha->port_type = host_attrs->port_type; + memcpy(giha->serial_number, host_attrs->serial_number, + sizeof(giha->serial_number)); + giha->speed = host_attrs->speed; + { + struct fc_host_statistics *hs; + + if (ft->get_fc_host_stats != NULL) { + if ((hs = ft->get_fc_host_stats( + cominfo->host)) != NULL) { + giha->stats = *hs; + } else { + memset(&(giha->stats), 0, + sizeof(giha->stats)); + } + } + } + giha->supported_classes = host_attrs->supported_classes; + memcpy(giha->supported_fc4s, host_attrs->supported_fc4s, + sizeof(giha->supported_fc4s)); + giha->supported_speeds = host_attrs->supported_speeds; + memcpy(giha->symbolic_name, host_attrs->symbolic_name, + sizeof(giha->symbolic_name)); + giha->tgtid_bind_type = host_attrs->tgtid_bind_type; + + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + + break; } + + case CMND_GET_INITIAL_STARGET_ATTRIB: { + struct scsi_target *starget; + struct fc_starget_attrs *starget_attrs; + unsigned int i = 0; + struct gita *gita = (struct gita *)(info->gnt_area->addr); + + list_for_each_entry(starget, + &(cominfo->host->__targets), siblings) { + starget_attrs = (struct fc_starget_attrs *) + (&(starget->starget_data)); + gita->e[i].channel = starget->channel; + gita->e[i].id = starget->id; + gita->e[i].node_name = starget_attrs->node_name; + gita->e[i].port_id = starget_attrs->port_id; + gita->e[i].port_name = starget_attrs->port_name; + i++; + if (i > SCSI_FC_MAX_STARGET) { + ring_res->rslt = -1; + goto out_gita; + } + } + gita->num = i; + + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + +out_gita: + break; } + + case CMND_GET_INITIAL_RPORT_ATTRIB: { + struct fc_rport *found_rport; + unsigned int i = 0; + struct gipa *gipa = (struct gipa *)(info->gnt_area->addr); + + list_for_each_entry(found_rport, + &fc_host_rports(cominfo->host), peers) { + gipa->e[i].dev_loss_tmo = found_rport->dev_loss_tmo; + gipa->e[i].maxframe_size = found_rport->maxframe_size; + gipa->e[i].node_name = found_rport->node_name; + gipa->e[i].port_id = found_rport->port_id; + gipa->e[i].port_name = found_rport->port_name; + gipa->e[i].port_state = found_rport->port_state; + gipa->e[i].roles = found_rport->roles; + gipa->e[i].scsi_target_id + = found_rport->scsi_target_id; + gipa->e[i].supported_classes + = found_rport->supported_classes; + gipa->e[i].channel = found_rport->channel; + i++; + if (i > SCSI_FC_MAX_RPORT) { + ring_res->rslt = -1; + goto out_gipa; + } + } + gipa->num = i; + + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + +out_gipa: + break; } + + case CMND_GET_FUNCTION_TEMPLATE: { + struct fc_function_template *ft; + struct gftp *gftp = (struct gftp *)(info->gnt_area->addr); + struct fc_function_template_shared *fts = &(gftp->ft); + + ft = fc_shost2ft(cominfo->host); + + SET_FT(ft, get_rport_dev_loss_tmo, fts); + SET_FT(ft, set_rport_dev_loss_tmo, fts); + SET_FT(ft, get_starget_node_name, fts); + SET_FT(ft, get_starget_port_name, fts); + SET_FT(ft, get_starget_port_id, fts); + SET_FT(ft, get_host_port_id, fts); + SET_FT(ft, get_host_port_type, fts); + SET_FT(ft, get_host_port_state, fts); + SET_FT(ft, get_host_active_fc4s, fts); + SET_FT(ft, get_host_speed, fts); + SET_FT(ft, get_host_fabric_name, fts); + SET_FT(ft, get_fc_host_stats, fts); + SET_FT(ft, reset_fc_host_stats, fts); + SET_FT(ft, issue_fc_host_lip, fts); + + fts->dd_fcrport_size = ft->dd_fcrport_size; + + SET_FT(ft, show_rport_maxframe_size, fts); + SET_FT(ft, show_rport_supported_classes, fts); + SET_FT(ft, show_rport_dev_loss_tmo, fts); + SET_FT(ft, show_starget_node_name, fts); + SET_FT(ft, show_starget_port_name, fts); + SET_FT(ft, show_starget_port_id, fts); + SET_FT(ft, show_host_node_name, fts); + SET_FT(ft, show_host_port_name, fts); + SET_FT(ft, show_host_permanent_port_name, fts); + SET_FT(ft, show_host_supported_classes, fts); + SET_FT(ft, show_host_supported_fc4s, fts); + SET_FT(ft, show_host_symbolic_name, fts); + SET_FT(ft, show_host_supported_speeds, fts); + SET_FT(ft, show_host_maxframe_size, fts); + SET_FT(ft, show_host_serial_number, fts); + SET_FT(ft, show_host_port_id, fts); + SET_FT(ft, show_host_port_type, fts); + SET_FT(ft, show_host_port_state, fts); + SET_FT(ft, show_host_active_fc4s, fts); + SET_FT(ft, show_host_speed, fts); + SET_FT(ft, show_host_fabric_name, fts); + + ring_res->rqid = ring_req->rqid; + ring_res->rslt = 0; + + break; } + + default: + printk(KERN_ERR "fcback: %s: unknown command>>>\n", + __FUNCTION__); + ring_res->rslt = -1; + break; + } + + comback_do_reply(cominfo); + + return 0; +} + +void fcback_cmd_exec(pending_req_t *pending_req) +{ + (void)fcback_main((struct fcback_info *)(pending_req->info->fcinfo), + &(pending_req->u.fc.ftb_req)); +} + + +/* ---------------------------------------------------------------------- */ +static int __setup_xenstore(struct fcback_info *info) +{ + struct xenbus_device *dev = info->cominfo->dev; + int gntref; + int ret = 0; + + if ((ret = xenbus_gather(XBT_NIL, dev->otherend, + "fc_gntref", "%d", &gntref, NULL)) != 0) { + printk(KERN_ERR "fcback: %s: xenbus_gather() error>>>\n", + __FUNCTION__); + return ret; + } + + info->gntref = gntref; + + return 0; +} + +static void __unsetup_xenstore(struct fcback_info *info) +{ + /* currently, nothing to do */ +} + +static int __setup_grant_table(struct fcback_info *info) +{ + struct gnttab_map_grant_ref op; + struct vm_struct *gnt_area; + int err; + + if ((gnt_area = alloc_vm_area(PAGE_SIZE)) == NULL) { + printk(KERN_ERR "fcback: %s: alloc_vm_area() error>>>\n", + __FUNCTION__); + return -ENOMEM; + } + + gnttab_set_map_op(&op, (unsigned long)(gnt_area->addr), + GNTMAP_host_map, + info->gntref, info->cominfo->dev->otherend_id); + + err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1); + if (op.status) { + printk(KERN_ERR + "fcback: %s: HYPERVISOR_grant_table_op() error>>>\n", + __FUNCTION__); + return op.status; + } + + info->gnt_area = gnt_area; + + return 0; +} + +static void __unsetup_grant_table(struct fcback_info *info) +{ + /* currently, nothing to do */ +} + +int fcback_connection_setup(struct comback_info *cominfo) +{ + struct fcback_info *info; + int ret = 0; + +#if 0 /* for DEBUG */ + printk(KERN_ERR "%s: giha=%ld\n", sizeof(struct giha), __FUNCTION__); + printk(KERN_ERR "%s: gita=%ld\n", sizeof(struct gita), __FUNCTION__); + printk(KERN_ERR "%s: gipa=%ld\n", sizeof(struct gipa), __FUNCTION__); + printk(KERN_ERR "%s: gftp=%ld\n", sizeof(struct gftp), __FUNCTION__); +#endif + + { + cominfo->fcinfo = kmalloc(sizeof(struct fcback_info), + GFP_KERNEL); + if (cominfo->fcinfo == NULL) { + ret = -ENOMEM; + goto out0; + } + memset(cominfo->fcinfo, 0, sizeof(struct fcback_info)); + info = (struct fcback_info *)(cominfo->fcinfo); + info->cominfo = cominfo; + } + + /* exchange various parameters through xenstore */ + if ((ret = __setup_xenstore(info)) != 0) { + printk(KERN_ERR "fcback: %s: __setup_xenstore() error>>>\n", + __FUNCTION__); + goto out1; + } + + /* prepare for grant table */ + if ((ret = __setup_grant_table(info)) != 0) { + printk(KERN_ERR "fcback: %s: __setup_grant_table() error>>>\n", + __FUNCTION__); + goto out2; + } + + return 0; + +out2: + __unsetup_xenstore(info); + +out1: + kfree(cominfo->fcinfo); + +out0: + return ret; +} + +void +fcback_connection_unsetup(struct comback_info *cominfo) +{ + struct fcback_info *info = (struct fcback_info *)(cominfo->fcinfo); + + __unsetup_grant_table(info); + __unsetup_xenstore(info); + + kfree(cominfo->fcinfo); +} + + +MODULE_DESCRIPTION("Xen Para-Virtual SCSI Fibre Channel Backend Driver"); +MODULE_LICENSE("GPL"); diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/fcback.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/fcback.h Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,191 @@ +/* + * Xen SCSI frontend driver + * + * Copyright (c) 2007, FUJITSU Limited + * + * Based on the scsifront driver code by FUJITA Tomonori + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#ifndef __XEN_DRIVERS_FCFRONT_H__ +#define __XEN_DRIVERS_FCFRONT_H__ + +#include +#include + +/* ---------------------------------------------------------------------- + Definition of Grant Table Structures + ---------------------------------------------------------------------- */ + +#define SCSI_FC_MAX_STARGET 32 /* limited by PAGE_SIZE */ +#define SCSI_FC_MAX_RPORT 32 /* limited by PAGE_SIZE */ + +struct giha { + u8 active_fc4s[FC_FC4_LIST_SIZE]; + u64 fabric_name; + u64 node_name; + u64 permanent_port_name; + u32 maxframe_size; + u32 port_id; + u64 port_name; + enum fc_port_state port_state; + enum fc_port_type port_type; + char serial_number[FC_SERIAL_NUMBER_SIZE]; + struct fc_host_statistics stats; + u32 speed; + u32 supported_classes; + u8 supported_fc4s[FC_FC4_LIST_SIZE]; + char symbolic_name[FC_SYMBOLIC_NAME_SIZE]; + u32 supported_speeds; + enum fc_tgtid_binding_type tgtid_bind_type; +}; + +struct gita { + u32 num; + u32 padding1; + struct { + u64 node_name; + u64 port_name; + u32 port_id; + u32 channel; + u32 id; + u32 padding2; + } e[SCSI_FC_MAX_STARGET]; +}; + +struct gipa { + u32 num; + u32 padding1; + struct { + u32 dev_loss_tmo; + u32 maxframe_size; + u64 node_name; + u64 port_name; + u32 port_id; + enum fc_port_state port_state; + u32 roles; + u32 scsi_target_id; + u32 supported_classes; + u32 channel; + } e[SCSI_FC_MAX_RPORT]; +}; + +struct fc_function_template_shared { + u64 get_rport_dev_loss_tmo:1; + u64 set_rport_dev_loss_tmo:1; + u64 get_starget_node_name:1; + u64 get_starget_port_name:1; + u64 get_starget_port_id:1; + u64 get_host_port_id:1; + u64 get_host_port_type:1; + u64 get_host_port_state:1; + u64 get_host_active_fc4s:1; + u64 get_host_speed:1; + u64 get_host_fabric_name:1; + u64 get_fc_host_stats:1; + u64 reset_fc_host_stats:1; + u64 issue_fc_host_lip:1; + u64 padding1:50; + + u64 show_rport_maxframe_size:1; + u64 show_rport_supported_classes:1; + u64 show_rport_dev_loss_tmo:1; + u64 show_starget_node_name:1; + u64 show_starget_port_name:1; + u64 show_starget_port_id:1; + u64 show_host_node_name:1; + u64 show_host_port_name:1; + u64 show_host_permanent_port_name:1; + u64 show_host_supported_classes:1; + u64 show_host_supported_fc4s:1; + u64 show_host_symbolic_name:1; + u64 show_host_supported_speeds:1; + u64 show_host_maxframe_size:1; + u64 show_host_serial_number:1; + u64 show_host_port_id:1; + u64 show_host_port_type:1; + u64 show_host_port_state:1; + u64 show_host_active_fc4s:1; + u64 show_host_speed:1; + u64 show_host_fabric_name:1; + u64 padding2:43; + + u32 dd_fcrport_size; + u32 padding3; +}; + +struct gftp { + + struct fc_function_template_shared ft; + +}; + + +/* ---------------------------------------------------------------------- + Definition of Internal Information Structures + ---------------------------------------------------------------------- */ + +struct fcfront_info { + struct comfront_info *cominfo; + + int gntref; + void *addr; + + /* On backend, "hs" is stored in ***_hba */ + struct fc_host_statistics hs; +}; + +struct fcback_info { + struct comback_info *cominfo; + + int gntref; + struct vm_struct *gnt_area; + + struct vscsiif_ftb_request ftb_req; +}; + + +/* ---------------------------------------------------------------------- + Definition of Front/Back common functions + ---------------------------------------------------------------------- */ + +struct fc_internal_head { + struct scsi_transport_template t; + struct fc_function_template *f; +}; + + +static struct fc_function_template * +fc_shost2ft(struct Scsi_Host *shost) +{ + struct fc_internal_head *i; + + i = container_of(shost->transportt, struct fc_internal_head, t); + return i->f; +} + +#endif /*__XEN_DRIVERS_FCFRONT_H__*/ diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/interface.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/interface.c Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,215 @@ +/* + * interface management. + * + * Copyright (c) 2007, FUJITSU Limited + * + * Based on the blkback driver code. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#ifdef CONFIG_XEN_FC +#include +#include +#endif + +#include "comback.h" + +#include +#include + +extern irqreturn_t comback_intr(int, void *, struct pt_regs *); + + +static kmem_cache_t *scsiback_cachep; + +struct comback_info *scsiinfo_alloc(domid_t domid) +{ + struct comback_info *info; + + info = kmem_cache_alloc(scsiback_cachep, GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + memset(info, 0, sizeof(*info)); + info->domid = domid; +/* spin_lock_init(&info->ring_lock_ftb);*/ + spin_lock_init(&info->ring_lock_btf); + atomic_set(&info->refcnt, 1); + init_waitqueue_head(&info->wq); + init_waitqueue_head(&info->waiting_to_free); + + return info; +} + +static int map_frontend_page( struct comback_info *info, + unsigned long ftb_ring_ref, unsigned long btf_ring_ref) +{ + struct gnttab_map_grant_ref op; + int err; + + gnttab_set_map_op(&op, (unsigned long)info->ftb_ring_area->addr, + GNTMAP_host_map, ftb_ring_ref, + info->domid); + + err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1); + BUG_ON(err); + + if (op.status) { + printk(KERN_ERR "scsiback: Grant table operation failure !\n"); + return op.status; + } + + info->ftb_shmem_ref = ftb_ring_ref; + info->ftb_shmem_handle = op.handle; + + gnttab_set_map_op(&op, (unsigned long)info->btf_ring_area->addr, + GNTMAP_host_map, btf_ring_ref, info->domid); + + err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1); + BUG_ON(err); + + if (op.status) { + printk(KERN_ERR "scsiback: Grant table operation failure !\n"); + return op.status; + } + + info->btf_shmem_ref = btf_ring_ref; + info->btf_shmem_handle = op.handle; + + return 0; +} + +static void unmap_frontend_page(struct comback_info *info) +{ + struct gnttab_unmap_grant_ref op; + int err; + + gnttab_set_unmap_op(&op, (unsigned long)info->ftb_ring_area->addr, + GNTMAP_host_map, info->ftb_shmem_handle); + + err = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1); + BUG_ON(err); + + gnttab_set_unmap_op(&op, (unsigned long)info->btf_ring_area->addr, + GNTMAP_host_map, info->btf_shmem_handle); + + err = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1); + BUG_ON(err); +} + +int scsiback_init_sring(struct comback_info *info, + unsigned long ftb_ring_ref, unsigned long btf_ring_ref, unsigned int evtchn) +{ + struct vscsiif_ftb_sring *ftb_sring; + struct vscsiif_btf_sring *btf_sring; + int err; + + if (info->irq) { + printk(KERN_ERR "scsiback: Already connected through?\n"); + return 0; + } + + info->ftb_ring_area = alloc_vm_area(PAGE_SIZE); + if (!info) + return -ENOMEM; + info->btf_ring_area = alloc_vm_area(PAGE_SIZE); + if (!info) + return -ENOMEM; + + err = map_frontend_page(info, ftb_ring_ref, btf_ring_ref); + if (err) + goto free_vm; + + ftb_sring = (struct vscsiif_ftb_sring *) info->ftb_ring_area->addr; + BACK_RING_INIT(&info->ftb_ring, ftb_sring, PAGE_SIZE); + + btf_sring = (struct vscsiif_btf_sring *) info->btf_ring_area->addr; + FRONT_RING_INIT(&info->btf_ring, btf_sring, PAGE_SIZE); + + err = bind_interdomain_evtchn_to_irqhandler( + info->domid, evtchn, + comback_intr, 0, "scsiif-backend", info); + + if (err < 0) + goto unmap_page; + + info->irq = err; + + return 0; + +unmap_page: + unmap_frontend_page(info); +free_vm: + free_vm_area(info->ftb_ring_area); + free_vm_area(info->btf_ring_area); + return err; +} + +void scsiback_disconnect(struct comback_info *info) +{ + if (info->kthread) { + kthread_stop(info->kthread); + info->kthread = NULL; + } + + atomic_dec(&info->refcnt); + wait_event(info->waiting_to_free, atomic_read(&info->refcnt) == 0); + atomic_inc(&info->refcnt); + + if (info->irq) { + unbind_from_irqhandler(info->irq, info); + info->irq = 0; + } + + if (info->ftb_ring.sring || info->btf_ring.sring) { + unmap_frontend_page(info); + free_vm_area(info->ftb_ring_area); + free_vm_area(info->btf_ring_area); + info->ftb_ring.sring = NULL; + info->btf_ring.sring = NULL; + } +} + +void scsiback_free(struct comback_info *info) +{ + if (!atomic_dec_and_test(&info->refcnt)) + BUG(); + kmem_cache_free(scsiback_cachep, info); +} + +void __init scsiback_interface_init(void) +{ + scsiback_cachep = kmem_cache_create("scsiif_cache", + sizeof(struct comback_info), 0, 0, NULL, NULL); +} + +void __exit scsiback_interface_exit(void) +{ + kmem_cache_destroy(scsiback_cachep); +} diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/scsiback.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/scsiback.c Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,556 @@ +/* + * Xen SCSI backend driver + * + * Copyright (c) 2007, FUJITSU Limited + * + * Based on the blkback driver code. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_XEN_FC +#include +#include +#endif + +#include "comback.h" + +extern struct vscsiif_btf_request * comback_pre_reply(struct comback_info *); +extern void comback_do_reply(struct comback_info *); +extern void free_req(pending_req_t *req); +extern int __init scsi_trace_init(void); +extern void __exit scsi_trace_exit(void); + +int vscsiif_reqs = DEFAULT_CAN_QUEUE; +module_param_named(reqs, vscsiif_reqs, int, 0); +MODULE_PARM_DESC(reqs, "Number of scsiback requests to allocate"); + + +#define INVALID_GRANT_HANDLE 0xFFFF +#define SCSIBACK_INVALID_HANDLE (~0) + +static pending_req_t *pending_reqs; +struct list_head pending_free; +static struct page **pending_pages; +static grant_handle_t *pending_grant_handles; + +static inline int vaddr_pagenr(pending_req_t *req, int seg) +{ + return (req - pending_reqs) * SG_TABLESIZE + seg; +} + +static inline unsigned long vaddr(pending_req_t *req, int seg) +{ + unsigned long pfn = page_to_pfn(pending_pages[vaddr_pagenr(req, seg)]); + return (unsigned long)pfn_to_kaddr(pfn); +} + +#define pending_handle(_req, _seg) \ + (pending_grant_handles[vaddr_pagenr(_req, _seg)]) + + +static void fast_flush_area(pending_req_t *req) +{ + struct gnttab_unmap_grant_ref unmap[SG_TABLESIZE]; + unsigned int i, invcount = 0; + grant_handle_t handle; + int err; + + if (req->u.scsi.use_sg) { + for (i = 0; i < req->u.scsi.use_sg; i++) { + handle = pending_handle(req, i); + if (handle == SCSIBACK_INVALID_HANDLE) + continue; + gnttab_set_unmap_op(&unmap[i], vaddr(req, i), + GNTMAP_host_map, handle); + pending_handle(req, i) = SCSIBACK_INVALID_HANDLE; + invcount++; + } + + err = HYPERVISOR_grant_table_op( + GNTTABOP_unmap_grant_ref, unmap, invcount); + BUG_ON(err); + } else if (req->u.scsi.request_bufflen) { + handle = pending_handle(req, 0); + if (handle == SCSIBACK_INVALID_HANDLE) + return; + gnttab_set_unmap_op(&unmap[0], vaddr(req, 0), + GNTMAP_host_map, handle); + pending_handle(req, 0) = SCSIBACK_INVALID_HANDLE; + + err = HYPERVISOR_grant_table_op( + GNTTABOP_unmap_grant_ref, unmap, 1); + BUG_ON(err); + } + + return; +} + + +static void make_sense(struct comback_info *info, struct request *req, + int32_t result, uint64_t rqid) +{ + struct vscsiif_btf_request *btf_req; + + DPRINTK("%s\n",__FUNCTION__); + + btf_req = comback_pre_reply(info); + + memset(btf_req->u.scsi.sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); + + btf_req->rslt = result; + btf_req->rqid = rqid; + + if (result != 0 && req != NULL ) { + memcpy(btf_req->u.scsi.sense_buffer, req->sense, req->sense_len); + btf_req->u.scsi.sense_len = req->sense_len; + } else + btf_req->u.scsi.sense_len = 0; + + comback_do_reply(info); + +} + + +static void scsiback_end_cmd_fn(struct request *req, int error) +{ + unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; + pending_req_t *pending_req = req->end_io_data; + pending_req->u.scsi.rq = req; + + DPRINTK("%s\n",__FUNCTION__); + + if (req->errors != 0) { + + printk("scsiback: SCSI CMD return. req->errors = 0x%08x\n", + req->errors); + printk("scsiback: status = 0x%02x, message = 0x%02x, host = 0x%02x, driver = 0x%02x\n", + status_byte(req->errors), msg_byte(req->errors), + host_byte(req->errors), driver_byte(req->errors)); + memcpy(sense_buffer, req->sense, req->sense_len); + __scsi_print_sense("scsiback", sense_buffer, req->sense_len); + + } + + if (atomic_dec_and_test(&pending_req->u.scsi.pendcnt)) { + fast_flush_area(pending_req); + make_sense(pending_req->info, pending_req->u.scsi.rq, + req->errors, pending_req->u.scsi.rqid); + scsiback_put(pending_req->info); + free_req(pending_req); + } + + __blk_put_request(req->q, req); + +} + + +/* quoted scsi_lib.c/scsi_merge_bio */ +static int scsiback_merge_bio(struct request *rq, struct bio *bio) +{ + struct request_queue *q = rq->q; + + bio->bi_flags &= ~(1 << BIO_SEG_VALID); + if (rq_data_dir(rq) == WRITE) + bio->bi_rw |= (1 << BIO_RW); + + blk_queue_bounce(q, &bio); + + if (!rq->bio) + blk_rq_bio_prep(q, rq, bio); + else if (!q->back_merge_fn(q, rq, bio)) + return -EINVAL; + else { + rq->biotail->bi_next = bio; + rq->biotail = bio; + rq->hard_nr_sectors += bio_sectors(bio); + rq->nr_sectors = rq->hard_nr_sectors; + } + + return 0; +} + + +/* quoted scsi_lib.c/scsi_bi_endio */ +static int scsiback_bi_endio(struct bio *bio, unsigned int bytes_done, int error) +{ + if (bio->bi_size) + return 1; + + bio_put(bio); + return 0; +} + + +/* quoted scsi_lib.c/scsi_req_map_sg . */ +static int requset_map_sg(pending_req_t *pending_req, int count) +{ + struct request *rq = pending_req->u.scsi.rq; + struct request_queue *q = pending_req->u.scsi.rq->q; + int nr_pages; + int nsegs = count; + + unsigned int data_len = 0, len, bytes, off; + struct page *page; + struct bio *bio = NULL; + int i, err, nr_vecs = 0; + + for (i = 0; i < nsegs; i++) { + page = virt_to_page(vaddr(pending_req, i)); + + off = pending_req->u.scsi.pend_seg[i].offset; + len = pending_req->u.scsi.pend_seg[i].length; + data_len += len; + + nr_pages = (len + off + PAGE_SIZE - 1) >> PAGE_SHIFT; + + while (len > 0) { + bytes = min_t(unsigned int, len, PAGE_SIZE - off); + + if (!bio) { + nr_vecs = min_t(int, BIO_MAX_PAGES, nr_pages); + nr_pages -= nr_vecs; + bio = bio_alloc(GFP_KERNEL, nr_vecs); + if (!bio) { + err = -ENOMEM; + goto free_bios; + } + bio->bi_end_io = scsiback_bi_endio; + } + + if (bio_add_pc_page(q, bio, page, bytes, off) != + bytes) { + bio_put(bio); + err = -EINVAL; + goto free_bios; + } + + if (bio->bi_vcnt >= nr_vecs) { + err = scsiback_merge_bio(rq, bio); + if (err) { + bio_endio(bio, bio->bi_size, 0); + goto free_bios; + } + bio = NULL; + } + + page++; + len -= bytes; + off = 0; + } + } + + rq->buffer = rq->data = NULL; + rq->data_len = data_len; + return 0; + +free_bios: + while ((bio = rq->bio) != NULL) { + rq->bio = bio->bi_next; + /* + * call endio instead of bio_put incase it was bounced + */ + bio_endio(bio, bio->bi_size, 0); + } + + return err; +} + +int copy_request_ring_info(struct comback_info *info, + struct vscsiif_ftb_request *ftb_req, pending_req_t *pending_req) +{ + int write; + char sense[SCSI_SENSE_BUFFERSIZE]; + int i; + + DPRINTK("%s\n",__FUNCTION__); + + pending_req->u.scsi.rqid = ftb_req->rqid; + pending_req->u.scsi.sdev = scsi_device_lookup(info->host, + ftb_req->u.scsi.channel, ftb_req->u.scsi.id, ftb_req->u.scsi.lun); + if (!pending_req->u.scsi.sdev) { + goto fail_response; + } + + write = (ftb_req->u.scsi.sc_data_direction == DMA_TO_DEVICE); + pending_req->u.scsi.data_dir = ftb_req->u.scsi.sc_data_direction; + pending_req->u.scsi.rq = + blk_get_request(pending_req->u.scsi.sdev->request_queue, + write, GFP_KERNEL); + pending_req->info = info; + pending_req->u.scsi.use_sg = ftb_req->u.scsi.use_sg; + pending_req->u.scsi.request_bufflen = ftb_req->u.scsi.request_bufflen; + + + pending_req->u.scsi.rq->flags |= REQ_BLOCK_PC; + pending_req->u.scsi.rq->cmd_len = ftb_req->u.scsi.cmd_len; + memcpy(pending_req->u.scsi.rq->cmd, ftb_req->u.scsi.cmnd, + ftb_req->u.scsi.cmd_len); + + memset(sense, 0, sizeof(sense)); /*FIXME*/ + pending_req->u.scsi.rq->sense = sense; + pending_req->u.scsi.rq->sense_len = SCSI_SENSE_BUFFERSIZE; + +/* pending_req->u.scsi.rq->retries = ftb_req->u.scsi.retries;*/ + pending_req->u.scsi.rq->retries = 0; + pending_req->u.scsi.rq->timeout = ftb_req->u.scsi.timeout_per_command; + + pending_req->u.scsi.rq->end_io_data = pending_req; + + if (ftb_req->u.scsi.use_sg) { + for (i = 0; i < ftb_req->u.scsi.use_sg; i++) { + pending_req->u.scsi.pend_seg[i].gref = ftb_req->u.scsi.seg[i].gref; + pending_req->u.scsi.pend_seg[i].offset = ftb_req->u.scsi.seg[i].offset; + pending_req->u.scsi.pend_seg[i].length = ftb_req->u.scsi.seg[i].length; + } + } else if (ftb_req->u.scsi.request_bufflen) { + pending_req->u.scsi.pend_seg[0].gref = ftb_req->u.scsi.seg[0].gref; + pending_req->u.scsi.pend_seg[0].offset = ftb_req->u.scsi.seg[0].offset; + pending_req->u.scsi.pend_seg[0].length = ftb_req->u.scsi.seg[0].length; + } + + return 0; + +fail_response: + return 1; +} + + +void scsiback_cmd_exec(pending_req_t *pending_req) +{ + + struct gnttab_map_grant_ref map[SG_TABLESIZE]; + struct comback_info *info = pending_req->info; + + int write = (pending_req->u.scsi.data_dir == DMA_TO_DEVICE); + u32 flags; + int i, err = 0; + + DPRINTK("%s\n",__FUNCTION__); + + if (!pending_req->u.scsi.sdev) { + goto fail_response; + } + + if (pending_req->u.scsi.use_sg) { + + for (i = 0; i < pending_req->u.scsi.use_sg; i++) { + flags = GNTMAP_host_map; + if (write) + flags |= GNTMAP_readonly; + gnttab_set_map_op(&map[i], vaddr(pending_req, i), flags, + pending_req->u.scsi.pend_seg[i].gref, + info->domid); + } + + err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, + map, pending_req->u.scsi.use_sg); + BUG_ON(err); + + for (i = 0; i < pending_req->u.scsi.use_sg; i++) { + if (unlikely(map[i].status != 0)) { + printk(KERN_ERR "scsiback: invalid buffer -- could not remap it\n"); + map[i].handle = SCSIBACK_INVALID_HANDLE; + err |= 1; + } + + pending_handle(pending_req, i) = map[i].handle; + + if (err) + continue; + + set_phys_to_machine(__pa(vaddr( + pending_req, i)) >> PAGE_SHIFT, + FOREIGN_FRAME(map[i].dev_bus_addr >> PAGE_SHIFT)); + } + + if (err) + goto fail_flush; + + if (requset_map_sg(pending_req, pending_req->u.scsi.use_sg)) { + printk(KERN_ERR "scsiback: SG Request Map Error\n"); + goto fail_map; + } + + } else if (pending_req->u.scsi.request_bufflen) { + + flags = GNTMAP_host_map; + if (write) + flags |= GNTMAP_readonly; + gnttab_set_map_op(&map[0], vaddr(pending_req, 0), flags, + pending_req->u.scsi.pend_seg[0].gref, + info->domid); + + err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map, 1); + BUG_ON(err); + + if (unlikely(map[0].status != 0)) { + printk(KERN_ERR "scsiback: invalid buffer single -- could not remap it\n"); + map[0].handle = SCSIBACK_INVALID_HANDLE; + err |= 1; + } + + pending_handle(pending_req, 0) = map[0].handle; + + set_phys_to_machine(__pa(vaddr( + pending_req, 0)) >> PAGE_SHIFT, + FOREIGN_FRAME(map[0].dev_bus_addr >> PAGE_SHIFT)); + + if (err) + goto fail_flush; + + if (requset_map_sg(pending_req, 1)) { + printk(KERN_ERR "scsiback: SG Request Map Error\n"); + goto fail_map; + } + } + + atomic_set(&pending_req->u.scsi.pendcnt, 1); + scsiback_get(info); + + blk_execute_rq_nowait(pending_req->u.scsi.rq->q, NULL, + pending_req->u.scsi.rq, 1, scsiback_end_cmd_fn); + + return ; + +fail_map: +fail_flush: + fast_flush_area(pending_req); +fail_response: + make_sense(info, NULL, (DID_NO_CONNECT << 16), pending_req->u.scsi.rqid); + free_req(pending_req); + +} + + +void scsiback_reset_exec(pending_req_t *pending_req) +{ + struct scsi_device *sdev = pending_req->u.scsi.sdev; + struct comback_info *info = pending_req->info; + struct vscsiif_btf_request *ring_res; + int err; + + err = scsi_reset_provider(sdev, SCSI_TRY_RESET_HOST); + if (err != SUCCESS) + err = scsi_reset_provider(sdev, SCSI_TRY_RESET_BUS); + + ring_res = comback_pre_reply(info); + + ring_res->rqid = pending_req->u.scsi.rqid; + ring_res->rslt = err; + + comback_do_reply(info); + +} + + +static int __init scsiback_init(void) +{ + int i, mmap_pages; + + if (!is_running_on_xen()) + return -ENODEV; + + mmap_pages = vscsiif_reqs * SG_TABLESIZE; + + pending_reqs = kmalloc(sizeof(pending_reqs[0]) * + vscsiif_reqs, GFP_KERNEL); + pending_grant_handles = kmalloc(sizeof(pending_grant_handles[0]) * + mmap_pages, GFP_KERNEL); + pending_pages = alloc_empty_pages_and_pagevec(mmap_pages); + + if (!pending_reqs || !pending_grant_handles || !pending_pages) + goto out_of_memory; + + for (i = 0; i < mmap_pages; i++) + pending_grant_handles[i] = SCSIBACK_INVALID_HANDLE; + + scsiback_interface_init(); + + memset(pending_reqs, 0, sizeof(pending_reqs)); + INIT_LIST_HEAD(&pending_free); + + for (i = 0; i < vscsiif_reqs; i++) + list_add_tail(&pending_reqs[i].u.scsi.free_list, &pending_free); + + if (scsiif_xenbus_init()) + goto out_of_memory; + + (void)scsi_trace_init(); + +#if 0 /*DEBUG*/ + printk(KERN_ERR "%s: ftb_req=%ld\n", __FUNCTION__, + sizeof(struct vscsiif_ftb_request)); + printk(KERN_ERR "%s: ftb_res=%ld\n", __FUNCTION__, + sizeof(struct vscsiif_ftb_response)); + printk(KERN_ERR "%s: btf_req=%ld\n", __FUNCTION__, + sizeof(struct vscsiif_btf_request)); + printk(KERN_ERR "%s: btf_res=%ld\n", __FUNCTION__, + sizeof(struct vscsiif_btf_response)); + printk("%s SCSI_RING_SIZE_ftb=%ld\n", __FUNCTION__, + __RING_SIZE((struct vscsiif_ftb_sring *)0, PAGE_SIZE)); + printk("%s SCSI_RING_SIZE_btf=%ld\n", __FUNCTION__, + __RING_SIZE((struct vscsiif_btf_sring *)0, PAGE_SIZE)); + +#endif /*DEBUG*/ + + + return 0; + + out_of_memory: + kfree(pending_reqs); + kfree(pending_grant_handles); + free_empty_pages_and_pagevec(pending_pages, mmap_pages); + printk(KERN_ERR "scsiback: %s: out of memory\n", __FUNCTION__); + return -ENOMEM; +} + +static void __exit scsiback_exit(void) +{ + scsiif_xenbus_unregister(); + scsiback_interface_exit(); + scsi_trace_exit(); + kfree(pending_reqs); + kfree(pending_grant_handles); + free_empty_pages_and_pagevec(pending_pages, (vscsiif_reqs * SG_TABLESIZE)); +} + +module_init(scsiback_init); +module_exit(scsiback_exit); + +MODULE_DESCRIPTION("Xen SCSI backend driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/traceback.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/traceback.c Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,109 @@ +#include +#include +#include + +#include + + +#define MAX_DATA_SIZE 472 /* should be modified */ + /* according to data */ + /* to be recorded */ +#define ENTRY_NUM (16 * 1024) + +struct scsi_trace_entry { + unsigned long dir; /* direction */ + /* 0: pending(BE) */ + /* 1: request(FE) */ + /* 2: response(FE) */ + unsigned long serial; /* incremental counter */ + /* starts at 0 */ + struct timeval tv; /* timeofday */ + unsigned int size; /* data size */ + unsigned char data[MAX_DATA_SIZE]; /* data body */ +}; + +static struct scsi_trace_entry *scsi_trace_buf; +EXPORT_SYMBOL(scsi_trace_buf); /* for DUMP */ + +static DEFINE_SPINLOCK(scsi_trace_lock); + +void +scsi_trace(unsigned long dir, unsigned char *data, unsigned int size) +{ + static unsigned long index = 0; + static unsigned long serial = 0; + unsigned long flags; + + if (size > MAX_DATA_SIZE) { + printk(KERN_ERR "%s: data given is too big.\n" + "must be smaller than %d bytes.\n", + __FUNCTION__, MAX_DATA_SIZE); + return; + } + + spin_lock_irqsave(&scsi_trace_lock, flags); + + scsi_trace_buf[index].dir = dir; + do_gettimeofday(&(scsi_trace_buf[index].tv)); + scsi_trace_buf[index].size = size; + memcpy(scsi_trace_buf[index].data, data, size); + + /* This indicates that the record is successfully recorded */ + scsi_trace_buf[index].serial = serial++; + + index = (index + 1) % ENTRY_NUM; + + spin_unlock_irqrestore(&scsi_trace_lock, flags); + +} + +static int +scsi_trace_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = sizeof(struct scsi_trace_entry) * ENTRY_NUM; + unsigned long flags; + + spin_lock_irqsave(&scsi_trace_lock, flags); + + if (len <= off + count) { + *eof = 1; + } + *start = page; + len -= off; + if (len > count) { + len = count; + } + if (len < 0) { + len = 0; + } else { + memcpy(page, ((unsigned char *)scsi_trace_buf) + off, len); + } + + spin_unlock_irqrestore(&scsi_trace_lock, flags); + + return len; +} + +int __init scsi_trace_init(void) +{ + unsigned int scsi_trace_buf_size; + + scsi_trace_buf_size = sizeof(struct scsi_trace_entry) * ENTRY_NUM; + if ((scsi_trace_buf = vmalloc(scsi_trace_buf_size)) == NULL) { + return -1; + } + memset(scsi_trace_buf, 0, scsi_trace_buf_size); + + create_proc_read_entry("scsi_trace", 0, NULL, + scsi_trace_read_proc, NULL); + + return 0; +} + +void __exit scsi_trace_exit(void) +{ + vfree(scsi_trace_buf); + remove_proc_entry("scsi_trace", NULL); +} + diff -r 24e0cb9fe9df -r 374aa5b73105 drivers/xen/scsiback/xenbus.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsiback/xenbus.c Tue Oct 30 16:43:10 2007 +0900 @@ -0,0 +1,300 @@ +/* + * Xen SCSI backend driver + * + * Copyright (c) 2007, FUJITSU Limited + * + * Based on the blkback driver code. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_XEN_FC +#include +#include +#endif + +#include "comback.h" + +extern int comback_schedule(void *); +#ifdef CONFIG_XEN_FC +extern int fcback_connection_setup(struct comback_info *cominfo); +#endif + +struct backend_info +{ + struct xenbus_device *dev; + struct comback_info *info; + struct xenbus_watch backend_watch; +}; + + +static int scsiback_remove(struct xenbus_device *dev) +{ + struct backend_info *be = dev->dev.driver_data; + + if (be->backend_watch.node) { + unregister_xenbus_watch(&be->backend_watch); + kfree(be->backend_watch.node); + be->backend_watch.node = NULL; + } + + if (be->info) { + scsiback_disconnect(be->info); + scsiback_free(be->info); + be->info = NULL; + } + + kfree(be); + dev->dev.driver_data = NULL; + + return 0; +} + +static int scsiif_name(struct backend_info *be, char *buf) +{ + char *devpath; + struct xenbus_device *dev = be->dev; + + devpath = xenbus_read(XBT_NIL, dev->nodename, "hostno", NULL); + if (IS_ERR(devpath)) + return PTR_ERR(devpath); + + snprintf(buf, TASK_COMM_LEN, "scsiif.%d.%s", be->info->domid, devpath); + kfree(devpath); + + return 0; +} + +static int scsiback_connect(struct backend_info *be) +{ + struct xenbus_device *dev = be->dev; + unsigned long ftb_ring_ref, btf_ring_ref; + unsigned int evtchn; + int err; + char name[TASK_COMM_LEN]; + + err = xenbus_gather(XBT_NIL, dev->otherend, + "ftb-ring-ref", "%lu", &ftb_ring_ref, + "btf-ring-ref", "%lu", &btf_ring_ref, + "event-channel", "%u", &evtchn, NULL); + if (err) { + xenbus_dev_fatal(dev, err, "reading %s ring", dev->otherend); + return err; + } + + err = scsiback_init_sring(be->info, + ftb_ring_ref, btf_ring_ref, evtchn); + if (err) + return err; + + err = scsiif_name(be, name); + if (err) { + xenbus_dev_error(dev, err, "get scsiback dev name"); + return err; + } + + be->info->kthread = kthread_run(comback_schedule, be->info, name); + if (IS_ERR(be->info->kthread)) { + err = PTR_ERR(be->info->kthread); + be->info->kthread = NULL; + xenbus_dev_error(be->dev, err, "start scsiif"); + return err; + } + +#ifdef CONFIG_XEN_FC + { + int ret; + + if ((ret = fcback_connection_setup(be->info)) != 0) { + printk(KERN_ERR "scsiback: %s: fcback_setup() error>>>\n", + __FUNCTION__); + return ret; + } + } +#endif + + return 0; +} + +static void scsiback_frontend_changed(struct xenbus_device *dev, + enum xenbus_state frontend_state) +{ + struct backend_info *be = dev->dev.driver_data; + int err; + + switch (frontend_state) { + case XenbusStateInitialising: + break; + + case XenbusStateInitialised: + case XenbusStateConnected: + if (dev->state == XenbusStateConnected) + break; + + err = scsiback_connect(be); + if (err) + break; + + err = xenbus_switch_state(dev, XenbusStateConnected); + if (err) + xenbus_dev_fatal(dev, err, "switching to Connected state", + dev->nodename); + break; + + case XenbusStateClosing: + scsiback_disconnect(be->info); + xenbus_switch_state(dev, XenbusStateClosing); + break; + + case XenbusStateClosed: + xenbus_switch_state(dev, XenbusStateClosed); + if (xenbus_dev_is_online(dev)) + break; + + case XenbusStateUnknown: + device_unregister(&dev->dev); + break; + default: + xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend", + frontend_state); + break; + } +} + + +static void scsiback_backend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + int err; + struct backend_info *be = + container_of(watch, struct backend_info, backend_watch); + struct xenbus_device *dev = be->dev; + + printk("%s SCSI_RING_SIZE_ftb=%ld\n", __FUNCTION__, + __RING_SIZE((struct vscsiif_ftb_sring *)0, PAGE_SIZE)); + printk("%s SCSI_RING_SIZE_btf=%ld\n", __FUNCTION__, + __RING_SIZE((struct vscsiif_btf_sring *)0, PAGE_SIZE)); + + be->info->host = scsi_host_lookup(be->info->host_no); + if (IS_ERR(be->info->host)) { + err = PTR_ERR(be->info->host); + xenbus_dev_fatal(dev, err, "no lookup scsi host"); + goto fail; + } + + return; +fail: + scsiback_remove(dev); +} + + +static int scsiback_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err; + int hostno; + struct backend_info *be = kzalloc(sizeof(struct backend_info), + GFP_KERNEL); + + DPRINTK("%p %d\n", dev, dev->otherend_id); + + if (!be) { + xenbus_dev_fatal(dev, -ENOMEM, + "allocating backend structure"); + return -ENOMEM; + } + be->dev = dev; + dev->dev.driver_data = be; + + be->info = scsiinfo_alloc(dev->otherend_id); + if (IS_ERR(be->info)) { + err = PTR_ERR(be->info); + be->info = NULL; + xenbus_dev_fatal(dev, err, "creating scsihost interface"); + goto fail; + } + + be->info->dev = dev; + be->info->irq = 0; + + err = xenbus_scanf(XBT_NIL, dev->nodename, "hostno", "%d", &hostno); + if (err == -ENOENT ) { + printk(KERN_WARNING "scsiback: error xenbus_scanf hostno=%d\n",hostno); + goto fail; + } + + be->info->host_no = (unsigned short)hostno; + + err = xenbus_watch_path2(dev, dev->nodename, + "scsi-host", + &be->backend_watch, + scsiback_backend_changed); + if (err) + goto fail; + + err = xenbus_switch_state(dev, XenbusStateInitWait); + if (err) + goto fail; + + return 0; + +fail: + printk(KERN_WARNING "scsiback: %s failed\n",__FUNCTION__); + scsiback_remove(dev); + + return err; +} + + +static struct xenbus_device_id scsiback_ids[] = { + { "scsihost" }, + { "" } +}; + +static struct xenbus_driver scsiback = { + .name = "scsihost", + .owner = THIS_MODULE, + .ids = scsiback_ids, + .probe = scsiback_probe, + .remove = scsiback_remove, + .otherend_changed = scsiback_frontend_changed +}; + +int scsiif_xenbus_init(void) +{ + return xenbus_register_backend(&scsiback); +} + +void scsiif_xenbus_unregister(void) +{ + xenbus_unregister_driver(&scsiback); +}