scsi_target.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,732 行 · 第 1/3 页
C
1,732 行
/* * Implementation of a simple Target Mode SCSI Proccessor Target driver for CAM. * * Copyright (c) 1998, 1999 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: scsi_target.c,v 1.7.2.3 1999/05/04 19:25:53 gibbs Exp $ */#include <stddef.h> /* For offsetof */#include <sys/param.h>#include <sys/queue.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/types.h>#include <sys/buf.h>#include <sys/conf.h>#include <sys/devicestat.h>#include <sys/malloc.h>#include <sys/poll.h>#include <sys/select.h> /* For struct selinfo. */#include <sys/uio.h>#include <cam/cam.h>#include <cam/cam_ccb.h>#include <cam/cam_extend.h>#include <cam/cam_periph.h>#include <cam/cam_queue.h>#include <cam/cam_xpt_periph.h>#include <cam/cam_debug.h>#include <cam/scsi/scsi_all.h>#include <cam/scsi/scsi_pt.h>#include <cam/scsi/scsi_targetio.h>#include <cam/scsi/scsi_message.h>typedef enum { TARG_STATE_NORMAL, TARG_STATE_EXCEPTION, TARG_STATE_TEARDOWN} targ_state;typedef enum { TARG_FLAG_NONE = 0x00, TARG_FLAG_SEND_EOF = 0x01, TARG_FLAG_RECEIVE_EOF = 0x02, TARG_FLAG_LUN_ENABLED = 0x04} targ_flags;typedef enum { TARG_CCB_WORKQ, TARG_CCB_WAITING} targ_ccb_types;#define MAX_ACCEPT 16#define MAX_IMMEDIATE 16#define MAX_BUF_SIZE 256 /* Max inquiry/sense/mode page transfer */#define MAX_INITIATORS 16 /* XXX More for Fibre-Channel */#define MIN(a, b) ((a > b) ? b : a)#define TARG_CONTROL_UNIT 0xffff00ff#define TARG_IS_CONTROL_DEV(unit) ((unit) == TARG_CONTROL_UNIT)/* Offsets into our private CCB area for storing accept information */#define ccb_type ppriv_field0#define ccb_descr ppriv_ptr1/* We stick a pointer to the originating accept TIO in each continue I/O CCB */#define ccb_atio ppriv_ptr1TAILQ_HEAD(ccb_queue, ccb_hdr);struct targ_softc { struct ccb_queue pending_queue; struct ccb_queue work_queue; struct ccb_queue snd_ccb_queue; struct ccb_queue rcv_ccb_queue; struct ccb_queue unknown_atio_queue; struct buf_queue_head snd_buf_queue; struct buf_queue_head rcv_buf_queue; struct devstat device_stats; struct selinfo snd_select; struct selinfo rcv_select; targ_state state; targ_flags flags; targ_exception exceptions; u_int init_level; u_int inq_data_len; struct scsi_inquiry_data *inq_data; struct ccb_accept_tio *accept_tio_list; struct ccb_hdr_slist immed_notify_slist; struct initiator_state istate[MAX_INITIATORS];};struct targ_cmd_desc { struct ccb_accept_tio* atio_link; u_int data_resid; /* How much left to transfer */ u_int data_increment;/* Amount to send before next disconnect */ void* data; /* The data. Can be from backing_store or not */ void* backing_store;/* Backing store allocated for this descriptor*/ struct buf *bp; /* Buffer for this transfer */ u_int max_size; /* Size of backing_store */ u_int32_t timeout; u_int8_t status; /* Status to return to initiator */};static d_open_t targopen;static d_close_t targclose;static d_read_t targread;static d_write_t targwrite;static d_ioctl_t targioctl;static d_poll_t targpoll;static d_strategy_t targstrategy;#define TARG_CDEV_MAJOR 65static struct cdevsw targ_cdevsw = { /*d_open*/ targopen, /*d_close*/ targclose, /*d_read*/ targread, /*d_write*/ targwrite, /*d_ioctl*/ targioctl, /*d_stop*/ nostop, /*d_reset*/ noreset, /*d_devtotty*/ nodevtotty, /*d_poll*/ targpoll, /*d_mmap*/ nommap, /*d_strategy*/ targstrategy, /*d_name*/ "targ", /*d_spare*/ NULL, /*d_maj*/ -1, /*d_dump*/ nodump, /*d_psize*/ nopsize, /*d_flags*/ 0, /*d_maxio*/ 0, /*b_maj*/ -1};static int targsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb);static periph_init_t targinit;static void targasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg);static int targallocinstance(struct ioc_alloc_unit *alloc_unit);static int targfreeinstance(struct ioc_alloc_unit *alloc_unit);static cam_status targenlun(struct cam_periph *periph);static cam_status targdislun(struct cam_periph *periph);static periph_ctor_t targctor;static periph_dtor_t targdtor;static void targrunqueue(struct cam_periph *periph, struct targ_softc *softc);static periph_start_t targstart;static void targdone(struct cam_periph *periph, union ccb *done_ccb);static void targfireexception(struct cam_periph *periph, struct targ_softc *softc);static int targerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags);static struct targ_cmd_desc* allocdescr(void);static void freedescr(struct targ_cmd_desc *buf);static void fill_sense(struct scsi_sense_data *sense, u_int error_code, u_int sense_key, u_int asc, u_int ascq); static struct periph_driver targdriver ={ targinit, "targ", TAILQ_HEAD_INITIALIZER(targdriver.units), /* generation */ 0};DATA_SET(periphdriver_set, targdriver);static struct extend_array *targperiphs;static voidtarginit(void){ dev_t dev; /* * Create our extend array for storing the devices we attach to. */ targperiphs = cam_extend_new(); if (targperiphs == NULL) { printf("targ: Failed to alloc extend array!\n"); return; } /* If we were successfull, register our devsw */ dev = makedev(TARG_CDEV_MAJOR, 0); cdevsw_add(&dev,&targ_cdevsw, NULL);}static voidtargasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg){ struct cam_periph *periph; periph = (struct cam_periph *)callback_arg; switch (code) { case AC_PATH_DEREGISTERED: { /* XXX Implement */ break; } case AC_BUS_RESET: { /* Flush transaction queue */ } default: break; }}/* Attempt to enable our lun */static cam_statustargenlun(struct cam_periph *periph){ union ccb immed_ccb; struct targ_softc *softc; cam_status status; int i; softc = (struct targ_softc *)periph->softc; if ((softc->flags & TARG_FLAG_LUN_ENABLED) != 0) return (CAM_REQ_CMP); xpt_setup_ccb(&immed_ccb.ccb_h, periph->path, /*priority*/1); immed_ccb.ccb_h.func_code = XPT_EN_LUN; /* Don't need support for any vendor specific commands */ immed_ccb.cel.grp6_len = 0; immed_ccb.cel.grp7_len = 0; immed_ccb.cel.enable = 1; xpt_action(&immed_ccb); status = immed_ccb.ccb_h.status; if (status != CAM_REQ_CMP) { xpt_print_path(periph->path); printf("targenlun - Enable Lun Rejected for status 0x%x\n", status); return (status); } softc->flags |= TARG_FLAG_LUN_ENABLED; /* * Build up a buffer of accept target I/O * operations for incoming selections. */ for (i = 0; i < MAX_ACCEPT; i++) { struct ccb_accept_tio *atio; atio = (struct ccb_accept_tio*)malloc(sizeof(*atio), M_DEVBUF, M_NOWAIT); if (atio == NULL) { status = CAM_RESRC_UNAVAIL; break; } atio->ccb_h.ccb_descr = allocdescr(); if (atio->ccb_h.ccb_descr == NULL) { free(atio, M_DEVBUF); status = CAM_RESRC_UNAVAIL; break; } xpt_setup_ccb(&atio->ccb_h, periph->path, /*priority*/1); atio->ccb_h.func_code = XPT_ACCEPT_TARGET_IO; atio->ccb_h.cbfcnp = targdone; xpt_action((union ccb *)atio); status = atio->ccb_h.status; if (status != CAM_REQ_INPROG) { xpt_print_path(periph->path); printf("Queue of atio failed\n"); freedescr(atio->ccb_h.ccb_descr); free(atio, M_DEVBUF); break; } ((struct targ_cmd_desc*)atio->ccb_h.ccb_descr)->atio_link = softc->accept_tio_list; softc->accept_tio_list = atio; } if (i == 0) { xpt_print_path(periph->path); printf("targenlun - Could not allocate accept tio CCBs: " "status = 0x%x\n", status); targdislun(periph); return (CAM_REQ_CMP_ERR); } /* * Build up a buffer of immediate notify CCBs * so the SIM can tell us of asynchronous target mode events. */ for (i = 0; i < MAX_ACCEPT; i++) { struct ccb_immed_notify *inot; inot = (struct ccb_immed_notify*)malloc(sizeof(*inot), M_DEVBUF, M_NOWAIT); if (inot == NULL) { status = CAM_RESRC_UNAVAIL; break; } xpt_setup_ccb(&inot->ccb_h, periph->path, /*priority*/1); inot->ccb_h.func_code = XPT_IMMED_NOTIFY; inot->ccb_h.cbfcnp = targdone; xpt_action((union ccb *)inot); status = inot->ccb_h.status; if (status != CAM_REQ_INPROG) { printf("Queue of inot failed\n"); free(inot, M_DEVBUF); break; } SLIST_INSERT_HEAD(&softc->immed_notify_slist, &inot->ccb_h, periph_links.sle); } if (i == 0) { xpt_print_path(periph->path); printf("targenlun - Could not allocate immediate notify CCBs: " "status = 0x%x\n", status); targdislun(periph); return (CAM_REQ_CMP_ERR); } return (CAM_REQ_CMP);}static cam_statustargdislun(struct cam_periph *periph){ union ccb ccb; struct targ_softc *softc; struct ccb_accept_tio* atio; struct ccb_hdr *ccb_h; softc = (struct targ_softc *)periph->softc; if ((softc->flags & TARG_FLAG_LUN_ENABLED) == 0) return CAM_REQ_CMP; /* XXX Block for Continue I/O completion */ /* Kill off all ACCECPT and IMMEDIATE CCBs */ while ((atio = softc->accept_tio_list) != NULL) { softc->accept_tio_list = ((struct targ_cmd_desc*)atio->ccb_h.ccb_descr)->atio_link; xpt_setup_ccb(&ccb.cab.ccb_h, periph->path, /*priority*/1); ccb.cab.ccb_h.func_code = XPT_ABORT; ccb.cab.abort_ccb = (union ccb *)atio; xpt_action(&ccb); } while ((ccb_h = SLIST_FIRST(&softc->immed_notify_slist)) != NULL) { SLIST_REMOVE_HEAD(&softc->immed_notify_slist, periph_links.sle); xpt_setup_ccb(&ccb.cab.ccb_h, periph->path, /*priority*/1); ccb.cab.ccb_h.func_code = XPT_ABORT; ccb.cab.abort_ccb = (union ccb *)ccb_h; xpt_action(&ccb); } /* * Dissable this lun. */ xpt_setup_ccb(&ccb.cel.ccb_h, periph->path, /*priority*/1); ccb.cel.ccb_h.func_code = XPT_EN_LUN; ccb.cel.enable = 0; xpt_action(&ccb); if (ccb.cel.ccb_h.status != CAM_REQ_CMP) printf("targdislun - Disabling lun on controller failed " "with status 0x%x\n", ccb.cel.ccb_h.status); else softc->flags &= ~TARG_FLAG_LUN_ENABLED; return (ccb.cel.ccb_h.status);}static cam_statustargctor(struct cam_periph *periph, void *arg){ struct ccb_pathinq *cpi; struct targ_softc *softc; int i; cpi = (struct ccb_pathinq *)arg; /* Allocate our per-instance private storage */ softc = (struct targ_softc *)malloc(sizeof(*softc), M_DEVBUF, M_NOWAIT); if (softc == NULL) { printf("targctor: unable to malloc softc\n"); return (CAM_REQ_CMP_ERR); } bzero(softc, sizeof(softc)); TAILQ_INIT(&softc->pending_queue); TAILQ_INIT(&softc->work_queue); TAILQ_INIT(&softc->snd_ccb_queue); TAILQ_INIT(&softc->rcv_ccb_queue); TAILQ_INIT(&softc->unknown_atio_queue); bufq_init(&softc->snd_buf_queue); bufq_init(&softc->rcv_buf_queue); softc->accept_tio_list = NULL; SLIST_INIT(&softc->immed_notify_slist); softc->state = TARG_STATE_NORMAL; periph->softc = softc; softc->init_level++; cam_extend_set(targperiphs, periph->unit_number, periph); /* * We start out life with a UA to indicate power-on/reset. */ for (i = 0; i < MAX_INITIATORS; i++) softc->istate[i].pending_ua = UA_POWER_ON; /* * Allocate an initial inquiry data buffer. We might allow the * user to override this later via an ioctl. */ softc->inq_data_len = sizeof(*softc->inq_data); softc->inq_data = malloc(softc->inq_data_len, M_DEVBUF, M_NOWAIT); if (softc->inq_data == NULL) { printf("targctor - Unable to malloc inquiry data\n"); targdtor(periph); return (CAM_RESRC_UNAVAIL); } bzero(softc->inq_data, softc->inq_data_len); softc->inq_data->device = T_PROCESSOR | (SID_QUAL_LU_CONNECTED << 5); softc->inq_data->version = 2; softc->inq_data->response_format = 2; /* SCSI2 Inquiry Format */ softc->inq_data->flags = cpi->hba_inquiry & (PI_SDTR_ABLE|PI_WIDE_16|PI_WIDE_32); softc->inq_data->additional_length = softc->inq_data_len - 4; strncpy(softc->inq_data->vendor, "FreeBSD ", SID_VENDOR_SIZE); strncpy(softc->inq_data->product, "TM-PT ", SID_PRODUCT_SIZE); strncpy(softc->inq_data->revision, "0.0 ", SID_REVISION_SIZE); softc->init_level++; return (CAM_REQ_CMP);}static voidtargdtor(struct cam_periph *periph){ struct targ_softc *softc; softc = (struct targ_softc *)periph->softc; softc->state = TARG_STATE_TEARDOWN; targdislun(periph); cam_extend_release(targperiphs, periph->unit_number); switch (softc->init_level) { default: /* FALLTHROUGH */ case 2: free(softc->inq_data, M_DEVBUF); /* FALLTHROUGH */ case 1: free(softc, M_DEVBUF); break; case 0: panic("targdtor - impossible init level");; }}static inttargopen(dev_t dev, int flags, int fmt, struct proc *p){ struct cam_periph *periph; struct targ_softc *softc; u_int unit; cam_status status; int error; int s; unit = minor(dev); /* An open of the control device always succeeds */ if (TARG_IS_CONTROL_DEV(unit)) return 0; s = splsoftcam(); periph = cam_extend_get(targperiphs, unit); if (periph == NULL) { return (ENXIO); splx(s); } if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) { splx(s); return (error); } softc = (struct targ_softc *)periph->softc; if ((softc->flags & TARG_FLAG_LUN_ENABLED) == 0) { if (cam_periph_acquire(periph) != CAM_REQ_CMP) { splx(s); cam_periph_unlock(periph); return(ENXIO); } } splx(s); status = targenlun(periph); switch (status) { case CAM_REQ_CMP: error = 0; break; case CAM_RESRC_UNAVAIL: error = ENOMEM; break; case CAM_LUN_ALRDY_ENA: error = EADDRINUSE; break; default: error = ENXIO; break; } cam_periph_unlock(periph); return (error);}static inttargclose(dev_t dev, int flag, int fmt, struct proc *p){ struct cam_periph *periph; struct targ_softc *softc; u_int unit; int s; int error; unit = minor(dev); /* A close of the control device always succeeds */ if (TARG_IS_CONTROL_DEV(unit)) return 0; s = splsoftcam(); periph = cam_extend_get(targperiphs, unit); if (periph == NULL) { splx(s); return (ENXIO); } if ((error = cam_periph_lock(periph, PRIBIO)) != 0) return (error); softc = (struct targ_softc *)periph->softc; splx(s); targdislun(periph);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?