# HG changeset patch # User t.horikoshi@jp.fujitsu.com # Date 1193730096 -32400 # Node ID 24e0cb9fe9df6f4a6df226823562bec3089072e6 # Parent 709db80c03c3d7606744a71f48f5186358a5e659 [LINUX][scsifront] add scsi frontend driver Signed-off-by: Tomonari Horikoshi Signed-off-by: Tsunehisa Doi Signed-off-by: Jun Kamada Signed-off-by: Akira Hayakawa diff -r 709db80c03c3 -r 24e0cb9fe9df drivers/xen/scsifront/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/Makefile Tue Oct 30 16:41:36 2007 +0900 @@ -0,0 +1,8 @@ +ifeq ($(CONFIG_XEN_FC),y) + EXTRA_CFLAGS += -DCONFIG_XEN_FC + obj-$(CONFIG_XEN_SCSI_FRONTEND) := xenfcscsi.o + xenfcscsi-objs := scsifront.o fcfront.o comfront.o tracefront.o +else + obj-$(CONFIG_XEN_SCSI_FRONTEND) := xenscsi.o + xenscsi-objs := scsifront.o comfront.o tracefront.o +endif diff -r 709db80c03c3 -r 24e0cb9fe9df drivers/xen/scsifront/comfront.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/comfront.c Tue Oct 30 16:41:36 2007 +0900 @@ -0,0 +1,265 @@ +/* + * 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. + */ + + +#include +#include "comfront.h" + +extern int scsifront_cmd_done(struct comfront_info *, struct vscsiif_btf_request *); +static void comfront_notify_work(struct comfront_info *info); +#ifdef CONFIG_XEN_FC +extern void fcfront_cmd_done(struct comfront_info *, struct vscsiif_btf_request *); +#endif + +extern void scsi_trace(unsigned long, unsigned char *, unsigned int); + +static inline int GET_ID_FROM_FREELIST(struct comfront_info *info) +{ + unsigned long flags; + uint32_t free; + + spin_lock_irqsave(&info->shadow_lock, flags); + + free = info->shadow_free; + BUG_ON(free > DEFAULT_CAN_QUEUE); + info->shadow_free = info->shadow[free].rqid; + info->shadow[free].rqid = 0x0fffffee; /* debug */ + + info->shadow[free].cond = 0; + + spin_unlock_irqrestore(&info->shadow_lock, flags); + + return free; +} + +void ADD_ID_TO_FREELIST(struct comfront_info *info, uint32_t id) +{ + unsigned long flags; + + spin_lock_irqsave(&info->shadow_lock, flags); + + info->shadow[id].rqid = info->shadow_free; + info->shadow[id].u.scsi.req_scsi_cmnd = 0; + info->shadow_free = id; + + spin_unlock_irqrestore(&info->shadow_lock, flags); +} + + +/* ------------------------------------------------------------ */ +/* for frontend to backend communication */ +/* ------------------------------------------------------------ */ + +int read_ftb_ring_resp_cons(struct comfront_info *info) +{ + RING_IDX i, ftb_rp; + struct vscsiif_ftb_response *ring_res; + int err; + + ftb_rp = info->ftb_ring.sring->rsp_prod; + err = SCSIIF_REQ_OKAY; + rmb(); + + for (i = info->ftb_ring.rsp_cons; i != ftb_rp; i++) { + ring_res = RING_GET_RESPONSE(&info->ftb_ring, i); + + if (ring_res->status == SCSIIF_BTFRING_BUSY) { + err = SCSIIF_BTFRING_BUSY; + comfront_notify_work(info); + } + } + + info->ftb_ring.rsp_cons = i; + + return err; +} + +struct vscsiif_ftb_request * comfront_pre_request(struct comfront_info *info) +{ + struct vscsiif_ftb_front_ring *ring = &(info->ftb_ring); + struct vscsiif_ftb_request *ring_req; + uint32_t id; + + ring_req = RING_GET_REQUEST(&(info->ftb_ring), ring->req_prod_pvt); + + id = GET_ID_FROM_FREELIST(info); /* use id by response */ + ring_req->rqid = id; + + return ring_req; +} + +void comfront_do_request(struct comfront_info *info) +{ + struct vscsiif_ftb_front_ring *ring = &(info->ftb_ring); + unsigned int irq = info->irq; + int notify; + + ring->req_prod_pvt++; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(ring, notify); + + if (notify) + notify_remote_via_irq(irq); + + /***** Front to Back response consume *****/ + read_ftb_ring_resp_cons(info); +} + + +/* ------------------------------------------------------------ */ +/* for backend to frontend communication */ +/* ------------------------------------------------------------ */ + +static void comfront_notify_work(struct comfront_info *info) +{ + + info->waiting_resp = 1; + wake_up(&info->wq); +} + +irqreturn_t comfront_intr(int irq, void *dev_id, struct pt_regs *ptregs) +{ + comfront_notify_work((struct comfront_info *)dev_id); + return IRQ_HANDLED; +} + +static void __sync_cmd_done(struct comfront_info *info, + struct vscsiif_btf_request *btf_req) +{ + uint32_t id = btf_req->rqid; + + info->btf_req = *btf_req; + + info->shadow[id].cond++; + wake_up(&(info->shadow[id].wq)); +} + +int do_comfront_cmd_done(struct comfront_info *info) +{ + struct vscsiif_btf_back_ring *btf_ring = &info->btf_ring; + struct vscsiif_btf_request *btf_req; + + int btf_rc, btf_rp; + int more_to_do = 0; + + if (info->dev->state != XenbusStateConnected) + return 0; + + /***** Back to Front request consume *****/ + DPRINTK("%s\n",__FUNCTION__); + + btf_rc = btf_ring->req_cons; + btf_rp = btf_ring->sring->req_prod; + + while ((btf_rc != btf_rp) && + !RING_REQUEST_CONS_OVERFLOW(btf_ring, btf_rc)) { + + rmb(); + btf_req = RING_GET_REQUEST(btf_ring, btf_rc); + btf_ring->req_cons = ++btf_rc; + + if (info->shadow[btf_req->rqid].cmnd == CMND_SCSI) { + scsi_trace(2, (unsigned char *)btf_req, + sizeof(struct vscsiif_btf_request)); + + if (scsifront_cmd_done(info, btf_req)) { + BUG(); + } + + } else { + __sync_cmd_done(info, btf_req); + } + + btf_ring->rsp_prod_pvt++; + RING_PUSH_RESPONSES(btf_ring); + } + + if (btf_ring->rsp_prod_pvt == btf_rc) { + RING_FINAL_CHECK_FOR_REQUESTS(btf_ring, more_to_do); + + } else if (RING_HAS_UNCONSUMED_REQUESTS(btf_ring)) { + more_to_do = 1; + } + + return more_to_do; +} + +int comfront_schedule(void *data) +{ + struct comfront_info *info = (struct comfront_info *)data; + + while (!kthread_should_stop()) { + wait_event_interruptible( + info->wq, + info->waiting_resp || kthread_should_stop()); + + info->waiting_resp = 0; + smp_mb(); + + if (do_comfront_cmd_done(info)) + info->waiting_resp = 1; + + read_ftb_ring_resp_cons(info); + + } + + info->kthread = NULL; + + return 0; +} + +struct vscsiif_btf_request *comfront_do_request_and_wait_response( + struct comfront_info *info, + struct vscsiif_ftb_request *ring_req) +{ + struct vscsiif_btf_request *ring_res; + uint32_t rqid = ring_req->rqid; + + info->shadow[rqid].cmnd = ring_req->cmnd; + + scsi_trace(1, (unsigned char *)ring_req, + sizeof(struct vscsiif_ftb_request)); + + comfront_do_request(info); + wait_event_interruptible(info->shadow[rqid].wq, + info->shadow[rqid].cond); + info->shadow[rqid].cond--; + + ADD_ID_TO_FREELIST(info, rqid); + + ring_res = &(info->btf_req); + + scsi_trace(2, (unsigned char *)ring_res, + sizeof(struct vscsiif_btf_request)); + + return ring_res; +} diff -r 709db80c03c3 -r 24e0cb9fe9df drivers/xen/scsifront/comfront.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/comfront.h Tue Oct 30 16:41:36 2007 +0900 @@ -0,0 +1,130 @@ +/* + * 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_SCSIFRONT_H__ +#define __XEN_DRIVERS_SCSIFRONT_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_XEN_FC +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + + +#define GRANT_INVALID_REF 0 + +struct comfront_shadow { + uint32_t rqid; + unsigned int cmnd; + wait_queue_head_t wq; + int cond; + union { + struct { + unsigned int sc_data_direction; + unsigned int use_sg; + unsigned int request_bufflen; + unsigned long req_scsi_cmnd; + int gref[SG_TABLESIZE]; + } scsi; +#ifdef CONFIG_XEN_FC + struct { + } fc; +#endif + } u; +}; + +struct comfront_info { + struct xenbus_device *dev; + struct Scsi_Host *host; + spinlock_t io_lock; + spinlock_t shadow_lock; + unsigned int evtchn; + unsigned int irq; + int ftb_ring_ref; + int btf_ring_ref; + struct vscsiif_ftb_front_ring ftb_ring; + struct vscsiif_btf_back_ring btf_ring; + struct comfront_shadow shadow[DEFAULT_CAN_QUEUE]; + uint32_t shadow_free; + struct vscsiif_btf_request btf_req; + + struct task_struct *kthread; + wait_queue_head_t wq; + unsigned int waiting_resp; + + void *fcinfo; +}; + +#define DPRINTK(_f, _a...) \ + pr_debug("(file=%s, line=%d) " _f, \ + __FILE__ , __LINE__ , ## _a ) + + +void ADD_ID_TO_FREELIST(struct comfront_info *info, uint32_t id); + +#ifdef CONFIG_XEN_FC +extern int fcfront_get_initial_attribute(struct comfront_info *cominfo); +extern int fcfront_get_function_template(struct comfront_info *cominfo); +extern int fcfront_connection_setup(struct comfront_info *cominfo); +extern void fcfront_connection_unsetup(struct comfront_info *cominfo); +#endif + + +struct vscsiif_ftb_request * comfront_pre_request(struct comfront_info *); +struct vscsiif_btf_request *comfront_do_request_and_wait_response( + struct comfront_info *info, + struct vscsiif_ftb_request *ring_req); +void comfront_do_request(struct comfront_info *); +irqreturn_t comfront_intr(int, void *, struct pt_regs *); +int comfront_schedule(void *); + +#endif /* __XEN_DRIVERS_SCSIFRONT_H__ */ diff -r 709db80c03c3 -r 24e0cb9fe9df drivers/xen/scsifront/fcfront.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/fcfront.c Tue Oct 30 16:41:36 2007 +0900 @@ -0,0 +1,777 @@ +/* + * Xen SCSI FC 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. + */ + +#include +#include "comfront.h" +#include "fcfront.h" + + +extern void notify_remote_via_irq(int); +extern int irq_to_evtchn_port(int); +extern int bind_listening_port_to_irqhandler(unsigned int, + irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long, const char *, void *); +extern void unbind_from_irqhandler(unsigned int, void *); +extern int gnttab_grant_foreign_access(domid_t, unsigned long, int); +extern int gnttab_end_foreign_access_ref(grant_ref_t, int); +extern void gnttab_end_foreign_access(grant_ref_t, int, unsigned long); + +extern struct vscsiif_ftb_request *comfront_pre_request(struct comfront_info *); +extern void comfront_do_request(struct comfront_info *); + +extern struct vscsiif_btf_request *comfront_do_request_and_wait_response( + struct comfront_info *, + struct vscsiif_ftb_request *); + + + +/* ---------------------------------------------------------------------- */ + +static void fc_get_host_port_id(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_HOST_PORT_ID; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + fc_host_port_id(shost) = ring_res->u.ghpi.port_id; + } else { + fc_host_port_id(shost) = (u32)0xffffffff; + } + +} + +static void fc_get_host_port_type(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_HOST_PORT_TYPE; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + fc_host_port_type(shost) = ring_res->u.ghpt.port_type; + } else { + fc_host_port_type(shost) = (u32)0xffffffff; + } + +} + +static void fc_get_host_port_state(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_HOST_PORT_STATE; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + fc_host_port_state(shost) = ring_res->u.ghps.port_state; + } else { + fc_host_port_state(shost) = (u32)0xffffffff; + } + +} + +static void fc_get_host_active_fc4s(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_HOST_ACTIVE_FC4S; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + memcpy(fc_host_active_fc4s(shost), + ring_res->u.ghaf.active_fc4s, + sizeof(fc_host_active_fc4s(shost))); + } else { + memset(fc_host_active_fc4s(shost), 0, + sizeof(fc_host_active_fc4s(shost))); + } + +} + +static void fc_get_host_speed(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_HOST_SPEED; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + fc_host_speed(shost) = ring_res->u.ghsp.speed; + } else { + fc_host_speed(shost) = (u32)0xffffffff; + } + +} + +static void fc_get_host_fabric_name(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_HOST_FABRIC_NAME; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + fc_host_fabric_name(shost) = ring_res->u.ghfn.fabric_name; + } else { + fc_host_fabric_name(shost) = (u32)0xffffffff; + } + +} + +static struct fc_host_statistics * fc_get_stats(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct fcfront_info *fcinfo = (struct fcfront_info *) + (cominfo->fcinfo); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + struct fc_host_statistics *ret; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_HOST_STATS; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + /* "info->hs" is appropriate position ? */ + memcpy(&(fcinfo->hs), &(ring_res->u.ghst.stats), + sizeof(fcinfo->hs)); + ret = &(ring_res->u.ghst.stats); + } else { + ret = NULL; + } + + return ret; +} + +static void fc_reset_stats(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_RESET_HOST_STATS; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + +} + +static int fc_issue_lip(struct Scsi_Host *shost) +{ + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_ISSUE_HOST_LIP; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + return ring_res->rslt; +} + +static void fc_get_starget_port_id(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_STARGET_PORT_ID; + ring_req->u.gtpi.channel = starget->channel; + ring_req->u.gtpi.id = starget->id; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + fc_starget_port_id(starget) = ring_res->u.gtpi.port_id; + } else { + fc_starget_port_id(starget) = (u32)0xffffffff; + } + +} + +static void fc_get_starget_node_name(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_STARGET_NODE_NAME; + ring_req->u.gtnn.channel = starget->channel; + ring_req->u.gtnn.id = starget->id; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + fc_starget_node_name(starget) = ring_res->u.gtnn.node_name; + } else { + fc_starget_node_name(starget) = (u32)0xffffffff; + } + +} + +static void fc_get_starget_port_name(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_STARGET_PORT_NAME; + ring_req->u.gtpn.channel = starget->channel; + ring_req->u.gtpn.id = starget->id; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + fc_starget_port_name(starget) = ring_res->u.gtpn.port_name; + } else { + fc_starget_port_name(starget) = (u32)0xffffffff; + } + +} + +static void fc_get_rport_loss_tmo(struct fc_rport *rport) +{ + struct Scsi_Host *shost = rport_to_shost(rport); + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_RPORT_LOSS_TMO; + ring_req->u.gplt.node_name = rport->node_name; + ring_req->u.gplt.port_name = rport->port_name; + ring_req->u.gplt.port_id = rport->port_id; + ring_req->u.gplt.roles = rport->roles; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + rport->dev_loss_tmo = ring_res->u.gplt.timeout; + } else { + rport->dev_loss_tmo = (u32)0xffffffff; + } + +} + +static void fc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) +{ + struct Scsi_Host *shost = rport_to_shost(rport); + struct comfront_info *cominfo = (struct comfront_info *) + (shost->hostdata); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_SET_RPORT_LOSS_TMO; + ring_req->u.splt.node_name = rport->node_name; + ring_req->u.splt.port_name = rport->port_name; + ring_req->u.splt.port_id = rport->port_id; + ring_req->u.splt.roles = rport->roles; + ring_req->u.splt.timeout = timeout; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + rport->dev_loss_tmo = ring_res->u.splt.timeout; + } else { + rport->dev_loss_tmo = (u32)0xffffffff; + } + +} + +struct fc_function_template fc_transport_functions = { + .get_rport_dev_loss_tmo = fc_get_rport_loss_tmo, + .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, + .get_starget_node_name = fc_get_starget_node_name, + .get_starget_port_name = fc_get_starget_port_name, + .get_starget_port_id = fc_get_starget_port_id, + .get_host_port_id = fc_get_host_port_id, + .get_host_port_type = fc_get_host_port_type, + .get_host_port_state = fc_get_host_port_state, + .get_host_active_fc4s = fc_get_host_active_fc4s, + /* lpfc does not support */ + .get_host_speed = fc_get_host_speed, + .get_host_fabric_name = fc_get_host_fabric_name, + .get_fc_host_stats = fc_get_stats, + .reset_fc_host_stats = fc_reset_stats, + .issue_fc_host_lip = fc_issue_lip, + + /* remote port fixed attributes */ + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + .show_rport_dev_loss_tmo = 1, + + /* target dynamic attributes */ + .show_starget_node_name = 1, + .show_starget_port_name = 1, + .show_starget_port_id = 1, + + /* host fixed attributes */ + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_permanent_port_name = 1, /* lpfc does not support */ + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_symbolic_name = 1, + .show_host_supported_speeds = 1, + .show_host_maxframe_size = 1, + .show_host_serial_number = 1, /* lpfc does not support */ + + /* host dynamic attribute */ + .show_host_port_id = 1, + .show_host_port_type = 1, + .show_host_port_state = 1, + .show_host_active_fc4s = 1, + .show_host_speed = 1, + .show_host_fabric_name = 1, +}; + + +/* ---------------------------------------------------------------------- */ + +static int __setup_grant_table(struct fcfront_info *info) +{ + void *addr; + int gntref; + + if ((addr = kmalloc(PAGE_SIZE, GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + gntref = gnttab_grant_foreign_access(info->cominfo->dev->otherend_id, + virt_to_mfn(addr), 0); + if (gntref < 0) { + printk(KERN_ERR + "fcfront: %s: gnttab_grant_foreign_access() error\n", + __FUNCTION__); + return gntref; + } + + info->addr = addr; + info->gntref = gntref; + + return 0; +} + +static void __unsetup_grant_table(struct fcfront_info *info) +{ + /* currently, nothing to do */ +} + +static int __setup_xenstore(struct fcfront_info *info) +{ + struct xenbus_device *dev = info->cominfo->dev; + struct xenbus_transaction xbt; + int ret = 0; + +again: + if ((ret = xenbus_transaction_start(&xbt)) != 0) { + printk(KERN_ERR + "fcfront: %s: xenbus_transaction_start() error\n", + __FUNCTION__); + goto out0; + } + + if ((ret = xenbus_printf(xbt, dev->nodename, + "fc_gntref", "%d", info->gntref)) != 0) { + printk(KERN_ERR "fcfront: %s: xenbus_printf() error\n", + __FUNCTION__); + goto out1; + } + + if ((ret = xenbus_transaction_end(xbt, 0)) != 0) { + if (ret == -EAGAIN) { + goto again; + } else { + printk(KERN_ERR + "fcfront: %s: xenbus_transaction_end() error\n", + __FUNCTION__); + goto out0; + } + } + + return 0; + +out1: + (void)xenbus_transaction_end(xbt, 0); + +out0: + return ret; +} + +static void __unsetup_xenstore(struct fcfront_info *info) +{ + /* currently, nothing to do */ +} + +int fcfront_connection_setup(struct comfront_info *cominfo) +{ + struct fcfront_info *info; + int ret; + +#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 fcfront_info), + GFP_KERNEL); + if (cominfo->fcinfo == NULL) { + ret = -ENOMEM; + goto out0; + } + memset(cominfo->fcinfo, 0, sizeof(struct fcfront_info)); + info = (struct fcfront_info *)(cominfo->fcinfo); + info->cominfo = cominfo; + } + + /* prepare for grant table */ + if ((ret = __setup_grant_table(info)) != 0) { + printk(KERN_ERR "fcfront: %s: __setup_grant_table() error\n", + __FUNCTION__); + goto out1; + } + + /* exchange various parameters through xenstore */ + if ((ret = __setup_xenstore(info)) != 0) { + printk(KERN_ERR "fcfront: %s: " + "__setup_xenstore() error\n", __FUNCTION__); + goto out2; + } + + return 0; + +out2: + __unsetup_grant_table(info); + +out1: + kfree(cominfo->fcinfo); + +out0: + return ret; +} + +void fcfront_connection_unsetup(struct comfront_info *cominfo) +{ + struct fcfront_info *info = (struct fcfront_info *)(cominfo->fcinfo); + + __unsetup_xenstore(info); + __unsetup_grant_table(info); + + kfree(cominfo->fcinfo); +} + + +/* ---------------------------------------------------------------------- */ + +static int __get_host_attrib(struct comfront_info *cominfo) +{ + struct fcfront_info *fcinfo = (struct fcfront_info *)(cominfo->fcinfo); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + struct fc_host_attrs *host_attrs = + (struct fc_host_attrs *)(cominfo->host->shost_data); + struct giha *giha = (struct giha *)(fcinfo->addr); + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_INITIAL_SHOST_ATTRIB; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt != 0) { + return 0; + } + + memcpy(host_attrs->active_fc4s, giha->active_fc4s, + sizeof(host_attrs->active_fc4s)); + host_attrs->fabric_name = giha->fabric_name; + host_attrs->maxframe_size = giha->maxframe_size; + host_attrs->node_name = giha->node_name; + host_attrs->permanent_port_name = giha->permanent_port_name; + host_attrs->port_id = giha->port_id; + host_attrs->port_name = giha->port_name; + host_attrs->port_state = giha->port_state; + host_attrs->port_type = giha->port_type; + memcpy(host_attrs->serial_number, giha->serial_number, + sizeof(host_attrs->serial_number)); + host_attrs->speed = giha->speed; + /* "fcinfo->hs" is appropriate position ? */ + memcpy(&(fcinfo->hs), &(giha->stats), sizeof(fcinfo->hs)); + host_attrs->supported_classes = giha->supported_classes; + memcpy(host_attrs->supported_fc4s, giha->supported_fc4s, + sizeof(host_attrs->supported_fc4s)); + host_attrs->supported_speeds = giha->supported_speeds; + memcpy(host_attrs->symbolic_name, giha->symbolic_name, + sizeof(host_attrs->symbolic_name)); + host_attrs->tgtid_bind_type = giha->tgtid_bind_type; + + return 0; +} + +static int __get_target_attrib(struct comfront_info *cominfo) +{ + struct fcfront_info *fcinfo = (struct fcfront_info *)(cominfo->fcinfo); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + struct scsi_target *starget; + struct fc_starget_attrs *starget_attrs; + struct gita *gita = (struct gita *)(fcinfo->addr); + unsigned int i; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_INITIAL_STARGET_ATTRIB; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt != 0) { + return 0; + } + + for (i = 0; i < gita->num; i++) { + list_for_each_entry(starget, &(cominfo->host->__targets), + siblings) { + starget_attrs = (struct fc_starget_attrs *) + (&(starget->starget_data)); + if ((starget->channel == gita->e[i].channel) && + (starget->id == gita->e[i].id)) { + starget_attrs->node_name = + gita->e[i].node_name; + starget_attrs->port_id = + gita->e[i].port_id; + starget_attrs->port_name = + gita->e[i].port_name; + break; + } else { + /* printk(KERN_ERR + "fcfront: %s: SCSI target not added\n", + __FUNCTION__);*/ + } + } + } + + return 0; +} + +static int __get_port_attrib(struct comfront_info *cominfo) +{ + struct fcfront_info *fcinfo = (struct fcfront_info *)(cominfo->fcinfo); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + int channel; + struct fc_rport_identifiers ids; + struct fc_rport *rport; + struct gipa *gipa = (struct gipa *)(fcinfo->addr); + unsigned int i; + int ret = 0; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_INITIAL_RPORT_ATTRIB; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt != 0) { + return ret; + } + + for (i = 0; i < gipa->num; i++) { + channel = gipa->e[i].channel; + ids.node_name = gipa->e[i].node_name; + ids.port_id = gipa->e[i].port_id; + ids.port_name = gipa->e[i].port_name; + ids.roles = gipa->e[i].roles; + + rport = fc_remote_port_add(cominfo->host, channel, &ids); + if (rport == NULL) { + ret = -ENOMEM; + break; + } + + rport->dev_loss_tmo = gipa->e[i].dev_loss_tmo; + rport->maxframe_size = gipa->e[i].maxframe_size; + rport->port_state = gipa->e[i].port_state; + rport->scsi_target_id = gipa->e[i].scsi_target_id; + rport->supported_classes = gipa->e[i].supported_classes; + } + + return ret; +} + +int fcfront_get_initial_attribute(struct comfront_info *cominfo) +{ + int ret = 0; + + /* get SCSI host attributes */ + (void)__get_host_attrib(cominfo); + + /* get SCSI target attributes */ + (void)__get_target_attrib(cominfo); + + /* get remote port attibutes */ + ret = __get_port_attrib(cominfo); + + return ret; +} + +#define CORRECT_FT(s, f, v, d) \ + if (((s)->f) == (v)) { \ + (d)->f = (v); \ + } + +int fcfront_get_function_template(struct comfront_info *cominfo) +{ + struct fcfront_info *fcinfo = (struct fcfront_info *)(cominfo->fcinfo); + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + struct gftp *gftp = (struct gftp *)(fcinfo->addr); + struct fc_function_template_shared *ft = &(gftp->ft); + struct fc_function_template *pvfcft = &fc_transport_functions; + + ring_req = comfront_pre_request(cominfo); + + ring_req->cmnd = CMND_GET_FUNCTION_TEMPLATE; + + ring_res = comfront_do_request_and_wait_response(cominfo, ring_req); + + if (ring_res->rslt == 0) { + + CORRECT_FT(ft, get_rport_dev_loss_tmo, 0, pvfcft); + CORRECT_FT(ft, set_rport_dev_loss_tmo, 0, pvfcft); + CORRECT_FT(ft, get_starget_node_name, 0, pvfcft); + CORRECT_FT(ft, get_starget_port_name, 0, pvfcft); + CORRECT_FT(ft, get_starget_port_id, 0, pvfcft); + CORRECT_FT(ft, get_host_port_id, 0, pvfcft); + CORRECT_FT(ft, get_host_port_type, 0, pvfcft); + CORRECT_FT(ft, get_host_port_state, 0, pvfcft); + CORRECT_FT(ft, get_host_active_fc4s, 0, pvfcft); + CORRECT_FT(ft, get_host_speed, 0, pvfcft); + CORRECT_FT(ft, get_host_fabric_name, 0, pvfcft); + CORRECT_FT(ft, get_fc_host_stats, 0, pvfcft); + CORRECT_FT(ft, reset_fc_host_stats, 0, pvfcft); + CORRECT_FT(ft, issue_fc_host_lip, 0, pvfcft); + + pvfcft->dd_fcrport_size = ft->dd_fcrport_size; + + CORRECT_FT(ft, show_rport_maxframe_size, 0, pvfcft); + CORRECT_FT(ft, show_rport_supported_classes, 0, pvfcft); + CORRECT_FT(ft, show_rport_dev_loss_tmo, 0, pvfcft); + CORRECT_FT(ft, show_starget_node_name, 0, pvfcft); + CORRECT_FT(ft, show_starget_port_name, 0, pvfcft); + CORRECT_FT(ft, show_starget_port_id, 0, pvfcft); + CORRECT_FT(ft, show_host_node_name, 0, pvfcft); + CORRECT_FT(ft, show_host_port_name, 0, pvfcft); + CORRECT_FT(ft, show_host_permanent_port_name, 0, pvfcft); + CORRECT_FT(ft, show_host_supported_classes, 0, pvfcft); + CORRECT_FT(ft, show_host_supported_fc4s, 0, pvfcft); + CORRECT_FT(ft, show_host_symbolic_name, 0, pvfcft); + CORRECT_FT(ft, show_host_supported_speeds, 0, pvfcft); + CORRECT_FT(ft, show_host_maxframe_size, 0, pvfcft); + CORRECT_FT(ft, show_host_serial_number, 0, pvfcft); + CORRECT_FT(ft, show_host_port_id, 0, pvfcft); + CORRECT_FT(ft, show_host_port_type, 0, pvfcft); + CORRECT_FT(ft, show_host_port_state, 0, pvfcft); + CORRECT_FT(ft, show_host_active_fc4s, 0, pvfcft); + CORRECT_FT(ft, show_host_speed, 0, pvfcft); + CORRECT_FT(ft, show_host_fabric_name, 0, pvfcft); + } + + return 0; +} + +MODULE_DESCRIPTION("Xen Para-Virtual SCSI Fibre Channel Frontend Driver"); +MODULE_LICENSE("GPL"); diff -r 709db80c03c3 -r 24e0cb9fe9df drivers/xen/scsifront/fcfront.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/fcfront.h Tue Oct 30 16:41:36 2007 +0900 @@ -0,0 +1,179 @@ +/* + * 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; +}; + +#endif /*__XEN_DRIVERS_FCFRONT_H__*/ diff -r 709db80c03c3 -r 24e0cb9fe9df drivers/xen/scsifront/scsifront.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/scsifront.c Tue Oct 30 16:41:36 2007 +0900 @@ -0,0 +1,739 @@ +/* + * 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. + */ + + +#include +#include "comfront.h" + + +#define DEFAULT_CMD_PER_LUN 10 +#define DEFAULT_TASK_COMM_LEN 16 + +extern int scsi_trace_init(void); +extern void scsi_trace(unsigned long, unsigned char *, unsigned int); + +static int req_per_lun = DEFAULT_CMD_PER_LUN; +module_param_named(cmds_lun, req_per_lun, int, 0); +MODULE_PARM_DESC(reqs, "Number of lun requests to allocate"); + +#ifdef CONFIG_XEN_FC +extern struct fc_function_template fc_transport_functions; +static struct scsi_transport_template * fc_transport_template = NULL; +#endif +extern int read_ftb_ring_resp_cons(struct comfront_info *info); + +static void scsifront_free(struct comfront_info *info) +{ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) + if (info->host->shost_state != SHOST_DEL) { +#else + if (!test_bit(SHOST_DEL, &info->host->shost_state)) { +#endif + +#ifdef CONFIG_XEN_FC + fc_remove_host(info->host); +#endif + scsi_remove_host(info->host); + scsi_host_put(info->host); + } + + flush_scheduled_work(); + + if (info->ftb_ring_ref != GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->ftb_ring_ref, 0, + (unsigned long)info->ftb_ring.sring); + info->ftb_ring_ref = GRANT_INVALID_REF; + info->ftb_ring.sring = NULL; + } + + if (info->btf_ring_ref != GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->btf_ring_ref, 0, + (unsigned long)info->btf_ring.sring); + info->btf_ring_ref = GRANT_INVALID_REF; + info->btf_ring.sring = NULL; + } + + if (info->irq) + unbind_from_irqhandler(info->irq, info); + info->irq = 0; +} + + +static int map_data_for_request(struct comfront_info *info, + struct scsi_cmnd *sc, struct vscsiif_ftb_request *ftb_req, uint32_t id) +{ + grant_ref_t gref_head; + struct page *page; + int err, i, ref, ref_cnt = 0; + int write = (sc->sc_data_direction == DMA_TO_DEVICE); + int nr_pages, off, len, bytes; + unsigned long buffer_pfn; + + if (sc->sc_data_direction == DMA_NONE) + return 0; + + err = gnttab_alloc_grant_references(SG_TABLESIZE, &gref_head); + if (err) { + printk(KERN_ERR "scsifront: gnttab_alloc_grant_references() error\n"); + return -ENOMEM; + } + + if (sc->use_sg) { + /* quoted scsi_lib.c/scsi_req_map_sg . */ + struct scatterlist *sg = (struct scatterlist *)sc->request_buffer; + unsigned int data_len = 0; + nr_pages = (sc->request_bufflen + sg[0].offset + PAGE_SIZE - 1) >> PAGE_SHIFT; + + if (nr_pages > SG_TABLESIZE) { + ref_cnt = (-ENOMEM); + goto big_to_sg; + } + + for (i = 0; i < sc->use_sg; i++) { + page = sg[i].page; + off = sg[i].offset; + len = sg[i].length; + data_len += len; + + buffer_pfn = page_to_phys(page) >> PAGE_SHIFT; + + while (len > 0) { + bytes = min_t(unsigned int, len, PAGE_SIZE - off); + + ref = gnttab_claim_grant_reference(&gref_head); + BUG_ON(ref == -ENOSPC); /*FIXME*/ + + gnttab_grant_foreign_access_ref(ref, info->dev->otherend_id, + buffer_pfn, write); + + info->shadow[id].u.scsi.gref[ref_cnt] = ref; + ftb_req->u.scsi.seg[ref_cnt].gref = ref; + ftb_req->u.scsi.seg[ref_cnt].offset = off; + ftb_req->u.scsi.seg[ref_cnt].length = bytes; + + buffer_pfn++; + len -= bytes; + off = 0; + ref_cnt++; + } + } + } else if (sc->request_bufflen) { + unsigned long end = ((unsigned long)sc->request_buffer + + sc->request_bufflen + PAGE_SIZE - 1) >> PAGE_SHIFT; + unsigned long start = (unsigned long)sc->request_buffer >> PAGE_SHIFT; + + page = virt_to_page(sc->request_buffer); + nr_pages = end - start; + len = sc->request_bufflen; + + if (nr_pages > SG_TABLESIZE) { + ref_cnt = (-ENOMEM); + goto big_to_sg; + } + + buffer_pfn = page_to_phys(page) >> PAGE_SHIFT; + + off = offset_in_page((unsigned long)sc->request_buffer); + for (i = 0; i < nr_pages; i++) { + bytes = PAGE_SIZE - off; + + if (bytes > len) + bytes = len; + + ref = gnttab_claim_grant_reference(&gref_head); + BUG_ON(ref == -ENOSPC); /*FIXME*/ + + gnttab_grant_foreign_access_ref(ref, info->dev->otherend_id, + buffer_pfn, write); + + info->shadow[id].u.scsi.gref[i] = ref; + ftb_req->u.scsi.seg[i].gref = ref; + ftb_req->u.scsi.seg[i].offset = off; + ftb_req->u.scsi.seg[i].length = bytes; + +/* page = pfn_to_page(++buffer_mfn);*/ + buffer_pfn++; + len -= bytes; + off = 0; + ref_cnt++; + } + } + +big_to_sg: + + gnttab_free_grant_references(gref_head); + + return ref_cnt; +} + +static int scsifront_queuecommand(struct scsi_cmnd *sc, + void (*done)(struct scsi_cmnd *)) +{ + struct Scsi_Host *host = sc->device->host; + struct comfront_info *info = (struct comfront_info *) host->hostdata; + struct vscsiif_ftb_request *ftb_req; + int ref_cnt; + + if (info->dev->state != XenbusStateConnected) { + printk(KERN_ERR "scsifront: XenbusState not connected %u!\n", + info->dev->state); + sc->result = DID_NO_CONNECT << 16; + done(sc); + return 0; + } + + if (RING_FULL(&info->ftb_ring)) { + /*printk(KERN_INFO "scsifront: FTB Ring Full busy\n");*/ + return SCSI_MLQUEUE_HOST_BUSY; + } + + if (read_ftb_ring_resp_cons(info)) { + /*printk("scsifront: BTF Ring Full busy\n");*/ + return SCSI_MLQUEUE_HOST_BUSY; + } + + sc->scsi_done = done; + sc->result = 0; + + ftb_req = comfront_pre_request(info); + ftb_req->cmnd = CMND_SCSI; + + ftb_req->u.scsi.id = sc->device->id; + ftb_req->u.scsi.lun = sc->device->lun; + ftb_req->u.scsi.channel = sc->device->channel; + ftb_req->u.scsi.cmd_len = sc->cmd_len; + + BUG_ON(sc->cmd_len > VSCSI_MAX_COMMAND_SIZE); + BUG_ON(sc == NULL); + + if ( sc->cmd_len ) + memcpy(ftb_req->u.scsi.cmnd, sc->cmnd, sc->cmd_len); + else + memset(ftb_req->u.scsi.cmnd, 0, VSCSI_MAX_COMMAND_SIZE); + + ftb_req->u.scsi.sc_data_direction = sc->sc_data_direction; + ftb_req->u.scsi.request_bufflen = sc->request_bufflen; + ftb_req->u.scsi.timeout_per_command = sc->timeout_per_command; + + info->shadow[ftb_req->rqid].u.scsi.req_scsi_cmnd = (unsigned long)sc; + info->shadow[ftb_req->rqid].u.scsi.sc_data_direction + = sc->sc_data_direction; + info->shadow[ftb_req->rqid].u.scsi.request_bufflen + = sc->request_bufflen; + info->shadow[ftb_req->rqid].cmnd = ftb_req->cmnd; + + ref_cnt = map_data_for_request(info, sc, ftb_req, ftb_req->rqid); + if (unlikely(ref_cnt < 0)) { + ADD_ID_TO_FREELIST(info, ftb_req->rqid); + notify_remote_via_irq(info->irq); + return SCSI_MLQUEUE_HOST_BUSY; + } + + ftb_req->u.scsi.use_sg = ref_cnt; + info->shadow[ftb_req->rqid].u.scsi.use_sg = ref_cnt; + + scsi_trace(1, (unsigned char *)ftb_req, + sizeof(struct vscsiif_ftb_request)); + + comfront_do_request(info); + + return 0; +} + + +static int scsifront_eh_abort_handler(struct scsi_cmnd *sc) +{ + /* not implemented */ + printk(KERN_ERR "scsifront: abort! 0 return.\n"); + return (FAILED); +} + + +static int scsifront_eh_host_reset_handler(struct scsi_cmnd *sc) +{ + struct Scsi_Host *host = sc->device->host; + struct comfront_info *info = (struct comfront_info *) host->hostdata; + struct vscsiif_ftb_request *ring_req; + struct vscsiif_btf_request *ring_res; + int err = FAILED; + + printk(KERN_INFO "scsifront: reset.\n"); + + scsi_block_requests(host); + + ring_req = comfront_pre_request(info); + + ring_req->cmnd = CMND_SCSI_RESET; + ring_req->u.scsi.id = sc->device->id; + ring_req->u.scsi.lun = sc->device->lun; + ring_req->u.scsi.channel = sc->device->channel; + + spin_unlock_irq(info->host->host_lock); + + ring_res = comfront_do_request_and_wait_response(info, ring_req); + + err = (ring_res->rslt ? SUCCESS : FAILED); + + scsi_unblock_requests(host); + + return (err); +} + + +static void scsifront_gnttab_done(struct comfront_shadow *s, uint32_t id) +{ + int i; + + if (s->u.scsi.sc_data_direction == DMA_NONE) + return; + + if (s->u.scsi.use_sg) { + for (i = 0; i < s->u.scsi.use_sg; i++) { + if (unlikely(gnttab_query_foreign_access( + s->u.scsi.gref[i]) != 0)) { + printk(KERN_ALERT "scsifront: " + "grant still in use by backend.\n"); + BUG(); + } + gnttab_end_foreign_access(s->u.scsi.gref[i], 0, 0UL); + } + } else if (s->u.scsi.request_bufflen) { + gnttab_end_foreign_access(s->u.scsi.gref[0], 0, 0UL); + } else { + return; + } +} + + +int scsifront_cmd_done(struct comfront_info *info, + struct vscsiif_btf_request *btf_req) +{ + struct scsi_cmnd *sc; + uint32_t id; + + id = btf_req->rqid; + sc = (struct scsi_cmnd *)info->shadow[id].u.scsi.req_scsi_cmnd; + + if (sc == NULL) + return 1; + + scsifront_gnttab_done(&info->shadow[id], id); + ADD_ID_TO_FREELIST(info, id); + + sc->result = btf_req->rslt; + sc->resid = 0; + + BUG_ON(btf_req->u.scsi.sense_len > SCSI_SENSE_BUFFERSIZE); + + if( btf_req->rslt != 0 ) { + if (btf_req->u.scsi.sense_len) + memcpy(sc->sense_buffer, btf_req->u.scsi.sense_buffer, + btf_req->u.scsi.sense_len); + } + + sc->scsi_done(sc); + + return 0; +} + + +static int scsifront_alloc_ring(struct comfront_info *info) +{ + struct xenbus_device *dev = info->dev; + struct vscsiif_ftb_sring *ftb; + struct vscsiif_btf_sring *btf; + int err = -ENOMEM; + + info->ftb_ring_ref = GRANT_INVALID_REF; + info->btf_ring_ref = GRANT_INVALID_REF; + + /***** Frontend to Backend ring start *****/ + ftb = (struct vscsiif_ftb_sring *) __get_free_page(GFP_KERNEL); + if (!ftb) { + xenbus_dev_fatal(dev, err, "fail to allocate shared ring (Front to Back)"); + return err; + } + SHARED_RING_INIT(ftb); + FRONT_RING_INIT(&info->ftb_ring, ftb, PAGE_SIZE); + + err = xenbus_grant_ring(dev, virt_to_mfn(ftb)); + if (err < 0) { + free_page((unsigned long) ftb); + info->ftb_ring.sring = NULL; + xenbus_dev_fatal(dev, err, "fail to grant shared ring (Front to Back)"); + goto free_sring; + } + info->ftb_ring_ref = err; + + /***** Backend to Frontend ring start *****/ + btf = (struct vscsiif_btf_sring *) __get_free_page(GFP_KERNEL); + if (!btf) { + xenbus_dev_fatal(dev, err, "fail to allocate shared ring (Back to Front)"); + return err; + } + SHARED_RING_INIT(btf); + BACK_RING_INIT(&info->btf_ring, btf, PAGE_SIZE); + + err = xenbus_grant_ring(dev, virt_to_mfn(btf)); + if (err < 0) { + free_page((unsigned long) btf); + info->btf_ring.sring = NULL; + xenbus_dev_fatal(dev, err, "fail to grant shared ring (Back to Front)"); + goto free_sring; + } + info->btf_ring_ref = err; + + err = bind_listening_port_to_irqhandler( + dev->otherend_id, comfront_intr, + SA_SAMPLE_RANDOM, "scsifront", info); + + if (err <= 0) { + xenbus_dev_fatal(dev, err, "bind_listening_port_to_irqhandler"); + goto fail; + } + info->irq = err; + + return 0; +fail: + /* free resource */ +free_sring: + scsifront_free(info); + + return err; +} + +static int scsifront_init_ring(struct comfront_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 %u\n", info->ftb_ring_ref, info->ftb_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, "ftb-ring-ref", "%u", + info->ftb_ring_ref); + if (err) { + xenbus_dev_fatal(dev, err, "%s", "writing ftb-ring-ref"); + goto fail; + } + err = xenbus_printf(xbt, dev->nodename, "btf-ring-ref", "%u", + info->btf_ring_ref); + if (err) { + xenbus_dev_fatal(dev, err, "%s", "writing btf-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_printf(xbt, dev->nodename, "vhostno", "%d", + info->host->host_no); + + 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"); + } else + xenbus_switch_state(dev, XenbusStateInitialised); + + return 0; +fail: + xenbus_transaction_end(xbt, 1); + /* free resource */ + scsifront_free(info); + + return err; +} + +static struct scsi_host_template scsifront_sht = { + .module = THIS_MODULE, + .name = "Xen SCSI frontend driver", + .queuecommand = scsifront_queuecommand, + .eh_abort_handler = scsifront_eh_abort_handler, + .eh_host_reset_handler = scsifront_eh_host_reset_handler, + .cmd_per_lun = DEFAULT_CMD_PER_LUN, + .can_queue = 16, + .this_id = -1, + .sg_tablesize = SG_TABLESIZE, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "scsifront", +}; + +static int scsifront_connect(struct comfront_info *info) +{ + struct xenbus_device *dev = info->dev; + struct Scsi_Host *host = info->host; + + int err = -ENOMEM; + + DPRINTK("%u\n", dev->state); + if (dev->state == XenbusStateConnected) + return 0; + + xenbus_switch_state(dev, XenbusStateConnected); + +#ifdef CONFIG_XEN_FC + (void)fcfront_get_function_template(info); + + fc_transport_template = fc_attach_transport(&fc_transport_functions); + host->transportt = fc_transport_template; +#endif + /* FIXME */ + host->max_id = 64; + host->max_channel = 0; + host->max_lun = 128; + host->max_sectors = (SG_TABLESIZE * PAGE_SIZE / 512); + host->cmd_per_lun = req_per_lun; + + err = scsi_add_host(host, &dev->dev); + if (err) { + printk(KERN_ERR "scsifront: fail to add scsi host %d\n", err); + return err; + } + + scsi_scan_host(host); + +#ifdef CONFIG_XEN_FC + (void)fcfront_get_initial_attribute(info); +#endif + + return 0; +} + +static int scsifront_disconnect(struct comfront_info *info) +{ + struct xenbus_device *dev = info->dev; + + DPRINTK("%s: %s remove\n",__FUNCTION__ ,dev->nodename); + +#ifdef CONFIG_XEN_FC + fc_remove_host(info->host); +#endif + scsi_remove_host(info->host); + scsi_host_put(info->host); + + xenbus_frontend_closed(dev); + + return 0; +} + +static int scsifront_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + struct Scsi_Host *host; + struct comfront_info *info; + 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 comfront_info *) host->hostdata; + dev->dev.driver_data = info; + info->dev = dev; + info->host = host; + + for (i = 0; i < DEFAULT_CAN_QUEUE; i++) { + info->shadow[i].rqid = i + 1; + init_waitqueue_head(&(info->shadow[i].wq)); + info->shadow[i].cond = 0; + } + info->shadow[DEFAULT_CAN_QUEUE - 1].rqid = 0x0fffffff; + +#ifdef CONFIG_XEN_FC + if ((err = fcfront_connection_setup(info)) != 0) { + printk(KERN_ERR + "scsifront: %s: fcfront_connection_setup() error\n", + __FUNCTION__); + return err; + } +#endif + + 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, "scsiif.%d", info->host->host_no); + + info->kthread = kthread_run(comfront_schedule, info, name); + if (IS_ERR(info->kthread)) { + err = PTR_ERR(info->kthread); + info->kthread = NULL; + } + + return 0; +} + + +static int scsifront_remove(struct xenbus_device *dev) +{ + struct comfront_info *info = dev->dev.driver_data; + + DPRINTK("%s: %s removed\n",__FUNCTION__ ,dev->nodename); + + if (info->kthread) { + kthread_stop(info->kthread); + info->kthread = NULL; + } + +#ifdef CONFIG_XEN_FC + fcfront_connection_unsetup(info); +#endif + scsifront_free(info); + + /*kfree(info);*/ + return 0; +} + +static void scsifront_backend_changed(struct xenbus_device *dev, + XenbusState backend_state) +{ + struct comfront_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 XenbusStateInitialised: + case XenbusStateClosed: + break; + + case XenbusStateConnected: + scsifront_connect(info); + break; + + case XenbusStateClosing: + scsifront_disconnect(info); + break; + } +} + +static struct xenbus_device_id scsifront_ids[] = { + { "scsihost" }, + { "" } +}; + + +static struct xenbus_driver scsifront_driver = { + .name = "scsihost", + .owner = THIS_MODULE, + .ids = scsifront_ids, + .probe = scsifront_probe, + .remove = scsifront_remove, +/* .resume = scsifront_resume, */ + .otherend_changed = scsifront_backend_changed, +}; + +static int __init scsifront_init(void) +{ + int err; + +#if 0 /* for DEBUG */ + printk(KERN_ERR "%s: ftb_req=%d\n", __FUNCTION__, + sizeof(struct vscsiif_ftb_request)); + printk(KERN_ERR "%s: ftb_res=%d\n", __FUNCTION__, + sizeof(struct vscsiif_ftb_response)); + printk(KERN_ERR "%s: btf_req=%d\n", __FUNCTION__, + sizeof(struct vscsiif_btf_request)); + printk(KERN_ERR "%s: btf_res=%d\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 /* for DEBUG */ + + if (!is_running_on_xen()) + return -ENODEV; + +#ifdef CONFIG_XEN_FC + if (is_initial_xendomain()) { + return 0; + } +#endif + + err = xenbus_register_frontend(&scsifront_driver); + + (void)scsi_trace_init(); + + return err; +} + +static void scsifront_exit(void) +{ + xenbus_unregister_driver(&scsifront_driver); + +#ifdef CONFIG_XEN_FC + fc_release_transport(fc_transport_template); +#endif +} + +module_init(scsifront_init); +module_exit(scsifront_exit); + +MODULE_DESCRIPTION("Xen SCSI frontend driver"); +MODULE_LICENSE("GPL"); diff -r 709db80c03c3 -r 24e0cb9fe9df drivers/xen/scsifront/tracefront.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/scsifront/tracefront.c Tue Oct 30 16:41:36 2007 +0900 @@ -0,0 +1,107 @@ +#include +#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 long itc; /* itc */ + 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].itc = ia64_get_itc(); + scsi_trace_buf[index].size = size; +// memset(scsi_trace_buf[index].data, 0, MAX_DATA_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 +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; +}