diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/cam/scsi/scsi_target.c | 2915 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_targetio.h | 110 | ||||
-rw-r--r-- | sys/modules/cam/Makefile | 2 |
3 files changed, 923 insertions, 2104 deletions
diff --git a/sys/cam/scsi/scsi_target.c b/sys/cam/scsi/scsi_target.c index 2c8f32e..b775c43 100644 --- a/sys/cam/scsi/scsi_target.c +++ b/sys/cam/scsi/scsi_target.c @@ -1,7 +1,8 @@ /* - * Implementation of a simple Target Mode SCSI Proccessor Target driver for CAM. + * Generic SCSI Target Kernel Mode Driver * - * Copyright (c) 1998, 1999, 2001 Justin T. Gibbs. + * Copyright (c) 2002 Nate Lawson. + * Copyright (c) 1998, 1999, 2001, 2002 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,17 +30,17 @@ */ #include <sys/param.h> -#include <sys/queue.h> #include <sys/systm.h> #include <sys/kernel.h> -#include <sys/types.h> -#include <sys/bio.h> #include <sys/conf.h> -#include <sys/devicestat.h> +#include <sys/event.h> #include <sys/malloc.h> #include <sys/poll.h> #include <sys/selinfo.h> #include <sys/uio.h> +#include <sys/vnode.h> +#include <sys/queue.h> +#include <sys/devicestat.h> #include <cam/cam.h> #include <cam/cam_ccb.h> @@ -47,131 +48,66 @@ #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_NONE = 0x00, - TARG_CCB_WAITING = 0x01, - TARG_CCB_HELDQ = 0x02, - TARG_CCB_ABORT_TO_HELDQ = 0x04 -} targ_ccb_flags; - -#define MAX_ACCEPT 16 -#define MAX_IMMEDIATE 16 -#define MAX_BUF_SIZE 256 /* Max inquiry/sense/mode page transfer */ -#define MAX_INITIATORS 256 /* includes widest fibre channel for now */ - -#define MIN(a, b) ((a > b) ? b : a) -#define TARG_CONTROL_UNIT 0xffff00ff -#define TARG_IS_CONTROL_DEV(d) (minor((d)) == TARG_CONTROL_UNIT) - -#define TARG_TAG_WILDCARD ((u_int)~0) +/* Transaction information attached to each CCB sent by the user */ +struct targ_cmd_descr { + struct cam_periph_map_info mapinfo; + TAILQ_ENTRY(targ_cmd_descr) tqe; + union ccb *user_ccb; + int priority; + int func_code; +}; -/* Offsets into our private CCB area for storing accept information */ -#define ccb_flags ppriv_field0 -#define ccb_descr ppriv_ptr1 +/* Offset into the private CCB area for storing our descriptor */ +#define targ_descr periph_priv.entries[1].ptr -/* We stick a pointer to the originating accept TIO in each continue I/O CCB */ -#define ccb_atio ppriv_ptr1 +TAILQ_HEAD(descr_queue, targ_cmd_descr); -/* - * When we're constructing a unit, we point to passed in user inquiry data here. - */ -#define ccb_inq ppriv_ptr1 +typedef enum { + TARG_STATE_RESV = 0x00, /* Invalid state */ + TARG_STATE_OPENED = 0x01, /* Device opened, softc initialized */ + TARG_STATE_LUN_ENABLED = 0x02 /* Device enabled for a path */ +} targ_state; +/* Per-instance device software context */ struct targ_softc { - /* CTIOs pending on the controller */ - struct ccb_queue pending_queue; - - /* ATIOs awaiting CTIO resources from the XPT */ - struct ccb_queue work_queue; - - /* - * ATIOs for SEND operations waiting for 'write' - * buffer resources from our userland daemon. - */ - struct ccb_queue snd_ccb_queue; + /* CCBs (CTIOs, ATIOs, INOTs) pending on the controller */ + struct ccb_queue pending_ccb_queue; - /* - * ATIOs for RCV operations waiting for 'read' - * buffer resources from our userland daemon. - */ - struct ccb_queue rcv_ccb_queue; + /* Command descriptors awaiting CTIO resources from the XPT */ + struct descr_queue work_queue; - /* - * ATIOs for commands unknown to the kernel driver. - * These are queued for the userland daemon to - * consume. - */ - struct ccb_queue unknown_atio_queue; + /* Command descriptors that have been aborted back to the user. */ + struct descr_queue abort_queue; /* - * Userland buffers for SEND commands waiting for - * SEND ATIOs to be queued by an initiator. + * Queue of CCBs that have been copied out to userland, but our + * userland daemon has not yet seen. */ - struct bio_queue_head snd_bio_queue; - - /* - * Userland buffers for RCV commands waiting for - * RCV ATIOs to be queued by an initiator. - */ - struct bio_queue_head rcv_bio_queue; - struct devstat device_stats; - dev_t targ_dev; - 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 bio *bp; /* Buffer for this transfer */ - u_int max_size; /* Size of backing_store */ - u_int32_t timeout; - u_int32_t - user_atio : 1, /* user ATIO (will define last CTIO) */ - status : 8; /* Status to return to initiator */ + struct ccb_queue user_ccb_queue; + + struct cam_periph *periph; + struct cam_path *path; + targ_state state; + struct selinfo read_select; + struct devstat device_stats; + struct mtx mtx; }; -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 65 +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_kqfilter_t targkqfilter; +static void targreadfiltdetach(struct knote *kn); +static int targreadfilt(struct knote *kn, long hint); +static struct filterops targread_filtops = + { 1, NULL, targreadfiltdetach, targreadfilt }; + +#define TARG_CDEV_MAJOR 65 static struct cdevsw targ_cdevsw = { /* open */ targopen, /* close */ targclose, @@ -180,805 +116,644 @@ static struct cdevsw targ_cdevsw = { /* ioctl */ targioctl, /* poll */ targpoll, /* mmap */ nommap, - /* strategy */ targstrategy, + /* strategy */ nostrategy, /* name */ "targ", /* maj */ TARG_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, - /* flags */ 0, + /* flags */ D_KQFILTER, + /* kqfilter */ targkqfilter }; -static int targsendccb(struct cam_periph *periph, union ccb *ccb, - union ccb *inccb); +static cam_status targendislun(struct cam_path *path, int enable, + int grp6_len, int grp7_len); +static cam_status targenable(struct targ_softc *softc, + struct cam_path *path, + int grp6_len, int grp7_len); +static cam_status targdisable(struct targ_softc *softc); +static periph_ctor_t targctor; +static periph_dtor_t targdtor; +static periph_start_t targstart; +static int targusermerge(struct targ_softc *softc, + struct targ_cmd_descr *descr, + union ccb *ccb); +static int targsendccb(struct targ_softc *softc, union ccb *ccb, + struct targ_cmd_descr *descr); +static void targdone(struct cam_periph *periph, + union ccb *done_ccb); +static int targreturnccb(struct targ_softc *softc, + union ccb *ccb); +static union ccb * targgetccb(struct targ_softc *softc, xpt_opcode type, + int priority); +static void targfreeccb(struct targ_softc *softc, union ccb *ccb); +static struct targ_cmd_descr * + targgetdescr(struct targ_softc *softc); static periph_init_t targinit; +static void targclone(void *arg, char *name, int namelen, + dev_t *dev); static void targasync(void *callback_arg, u_int32_t code, - struct cam_path *path, void *arg); -static int targallocinstance(void *, u_long); -static int targfreeinstance(struct ioc_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 void targinoterror(struct cam_periph *periph, - struct targ_softc *softc, - struct ccb_immed_notify *inot); -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 targ_softc *softc, - u_int initiator_id, u_int error_code, - u_int sense_key, u_int asc, u_int ascq); -static void copy_sense(struct targ_softc *softc, - struct initiator_state *istate, - u_int8_t *sense_buffer, size_t sense_len); -static void set_unit_attention_cond(struct cam_periph *periph, - u_int initiator_id, ua_types ua); -static void set_ca_condition(struct cam_periph *periph, - u_int initiator_id, ca_types ca); -static void abort_pending_transactions(struct cam_periph *periph, - u_int initiator_id, u_int tag_id, - int errno, int to_held_queue); - + struct cam_path *path, void *arg); +static void abort_all_pending(struct targ_softc *softc); +static void notify_user(struct targ_softc *softc); +static int targcamstatus(cam_status status); +static size_t targccblen(xpt_opcode func_code); + static struct periph_driver targdriver = { targinit, "targ", TAILQ_HEAD_INITIALIZER(targdriver.units), /* generation */ 0 }; - PERIPHDRIVER_DECLARE(targ, targdriver); -static dev_t targ_ctl_dev; +static struct mtx targ_mtx; +#define TARG_LOCK(softc) mtx_lock(&(softc)->mtx) +#define TARG_UNLOCK(softc) mtx_unlock(&(softc)->mtx) -static void -targinit(void) -{ - targ_ctl_dev = make_dev(&targ_cdevsw, TARG_CONTROL_UNIT, UID_ROOT, - GID_OPERATOR, 0600, "%s.ctl", "targ"); - if (targ_ctl_dev == (dev_t) 0) { - printf("targ: failed to create control dev\n"); - } -} +static MALLOC_DEFINE(M_TARG, "TARG", "TARG data"); -static void -targasync(void *callback_arg, u_int32_t code, - struct cam_path *path, void *arg) +/* Create softc and initialize it. Only one proc can open each targ device. */ +static int +targopen(dev_t dev, int flags, int fmt, struct thread *td) { - struct cam_periph *periph; struct targ_softc *softc; - periph = (struct cam_periph *)callback_arg; - softc = (struct targ_softc *)periph->softc; - switch (code) { - case AC_PATH_DEREGISTERED: - { - /* XXX Implement */ - break; - } - default: - break; + mtx_lock(&targ_mtx); + if (dev->si_drv1 != 0) { + mtx_unlock(&targ_mtx); + return (EBUSY); } + + /* Mark device busy before any potentially blocking operations */ + dev->si_drv1 = (void *)~0; + mtx_unlock(&targ_mtx); + + /* Create the targ device, allocate its softc, initialize it */ + if ((dev->si_flags & SI_NAMED) == 0) { + make_dev(&targ_cdevsw, minor(dev), UID_ROOT, GID_WHEEL, 0600, + "targ%d", dev2unit(dev)); + } + MALLOC(softc, struct targ_softc *, sizeof(*softc), M_TARG, + M_WAITOK | M_ZERO); + dev->si_drv1 = softc; + softc->state = TARG_STATE_OPENED; + softc->periph = NULL; + softc->path = NULL; + mtx_init(&softc->mtx, devtoname(dev), "targ cdev", MTX_DEF); + + TAILQ_INIT(&softc->pending_ccb_queue); + TAILQ_INIT(&softc->work_queue); + TAILQ_INIT(&softc->abort_queue); + TAILQ_INIT(&softc->user_ccb_queue); + + return (0); } -/* Attempt to enable our lun */ -static cam_status -targenlun(struct cam_periph *periph) +/* Disable LUN if enabled and teardown softc */ +static int +targclose(dev_t dev, int flag, int fmt, struct thread *td) { - 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 with 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; + struct targ_softc *softc; + int error; - atio = (struct ccb_accept_tio*)malloc(sizeof(*atio), M_DEVBUF, - M_NOWAIT); - if (atio == NULL) { - status = CAM_RESRC_UNAVAIL; - break; + softc = (struct targ_softc *)dev->si_drv1; + TARG_LOCK(softc); + error = targdisable(softc); + if (error == 0) { + dev->si_drv1 = 0; + mtx_lock(&targ_mtx); + if (softc->periph != NULL) { + cam_periph_invalidate(softc->periph); + softc->periph = NULL; } + mtx_unlock(&targ_mtx); + TARG_UNLOCK(softc); + mtx_destroy(&softc->mtx); + destroy_dev(dev); + FREE(softc, M_TARG); + } else { + TARG_UNLOCK(softc); + } + return (error); +} - atio->ccb_h.ccb_descr = allocdescr(); +/* Enable/disable LUNs, set debugging level */ +static int +targioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td) +{ + struct targ_softc *softc; + cam_status status; - if (atio->ccb_h.ccb_descr == NULL) { - free(atio, M_DEVBUF); - status = CAM_RESRC_UNAVAIL; - break; - } + softc = (struct targ_softc *)dev->si_drv1; - xpt_setup_ccb(&atio->ccb_h, periph->path, /*priority*/1); - atio->ccb_h.func_code = XPT_ACCEPT_TARGET_IO; - atio->ccb_h.cbfcnp = targdone; - atio->ccb_h.ccb_flags = TARG_CCB_NONE; - 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); + switch (cmd) { + case TARGIOCENABLE: + { + struct ioc_enable_lun *new_lun; + struct cam_path *path; + + new_lun = (struct ioc_enable_lun *)addr; + status = xpt_create_path(&path, /*periph*/NULL, + new_lun->path_id, + new_lun->target_id, + new_lun->lun_id); + if (status != CAM_REQ_CMP) { + printf("Couldn't create path, status %#x\n", status); 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); + TARG_LOCK(softc); + status = targenable(softc, path, new_lun->grp6_len, + new_lun->grp7_len); + TARG_UNLOCK(softc); + xpt_free_path(path); + break; } + case TARGIOCDISABLE: + TARG_LOCK(softc); + status = targdisable(softc); + TARG_UNLOCK(softc); + break; + case TARGIOCDEBUG: + { +#ifdef CAMDEBUG + struct ccb_debug cdbg; - /* - * 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; + bzero(&cdbg, sizeof cdbg); + if (*((int *)addr) != 0) + cdbg.flags = CAM_DEBUG_PERIPH; + else + cdbg.flags = CAM_DEBUG_NONE; + xpt_setup_ccb(&cdbg.ccb_h, softc->path, /*priority*/0); + cdbg.ccb_h.func_code = XPT_DEBUG; + cdbg.ccb_h.cbfcnp = targdone; + + /* If no periph available, disallow debugging changes */ + TARG_LOCK(softc); + if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) { + status = CAM_DEV_NOT_THERE; + TARG_UNLOCK(softc); break; } - - xpt_setup_ccb(&inot->ccb_h, periph->path, /*priority*/1); - inot->ccb_h.func_code = XPT_IMMED_NOTIFY; - inot->ccb_h.cbfcnp = targdone; - SLIST_INSERT_HEAD(&softc->immed_notify_slist, &inot->ccb_h, - periph_links.sle); - xpt_action((union ccb *)inot); + xpt_action((union ccb *)&cdbg); + TARG_UNLOCK(softc); + status = cdbg.ccb_h.status & CAM_STATUS_MASK; +#else + status = CAM_FUNC_NOTAVAIL; +#endif + break; } - - 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); + default: + status = CAM_PROVIDE_FAIL; + break; } - return (CAM_REQ_CMP); + return (targcamstatus(status)); } -static cam_status -targdislun(struct cam_periph *periph) +/* Writes are always ready, reads wait for user_ccb_queue or abort_queue */ +static int +targpoll(dev_t dev, int poll_events, struct thread *td) { - union ccb ccb; struct targ_softc *softc; - struct ccb_accept_tio* atio; - struct ccb_hdr *ccb_h; + int revents; - softc = (struct targ_softc *)periph->softc; - if ((softc->flags & TARG_FLAG_LUN_ENABLED) == 0) - return CAM_REQ_CMP; + softc = (struct targ_softc *)dev->si_drv1; - /* 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); + /* Poll for write() is always ok. */ + revents = poll_events & (POLLOUT | POLLWRNORM); + if ((poll_events & (POLLIN | POLLRDNORM)) != 0) { + /* Poll for read() depends on user and abort queues. */ + TARG_LOCK(softc); + if (!TAILQ_EMPTY(&softc->user_ccb_queue) || + !TAILQ_EMPTY(&softc->abort_queue)) { + revents |= poll_events & (POLLIN | POLLRDNORM); + } + /* Only sleep if the user didn't poll for write. */ + if (revents == 0) + selrecord(td, &softc->read_select); + TARG_UNLOCK(softc); } - /* - * 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); + return (revents); } -static cam_status -targctor(struct cam_periph *periph, void *arg) +static int +targkqfilter(dev_t dev, struct knote *kn) { - 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); - bioq_init(&softc->snd_bio_queue); - bioq_init(&softc->rcv_bio_queue); - softc->accept_tio_list = NULL; - SLIST_INIT(&softc->immed_notify_slist); - softc->state = TARG_STATE_NORMAL; - periph->softc = softc; - softc->init_level++; - - /* - * 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 inquiry data buffer. - * We let the user to override this if desired. - */ - 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); - } - if (cpi->ccb_h.ccb_inq) { - bcopy(cpi->ccb_h.ccb_inq, softc->inq_data, softc->inq_data_len); - } else { - 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->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); - } - - /* - * Preserve the SIM's capabilities here. Don't let user applications - * do something dumb. - */ - if (softc->inq_data->version >= 2) { - softc->inq_data->flags &= - ~(PI_SDTR_ABLE|PI_WIDE_16|PI_WIDE_32|PI_TAG_ABLE); - softc->inq_data->flags |= (cpi->hba_inquiry & - (PI_SDTR_ABLE|PI_WIDE_16|PI_WIDE_32|PI_TAG_ABLE)); - } - softc->targ_dev = make_dev(&targ_cdevsw, periph->unit_number, UID_ROOT, - GID_OPERATOR, 0600, "%s%d", - periph->periph_name, periph->unit_number); - softc->targ_dev->si_drv1 = periph; - - softc->init_level++; - return (CAM_REQ_CMP); + struct targ_softc *softc; + + softc = (struct targ_softc *)dev->si_drv1; + kn->kn_hook = (caddr_t)softc; + kn->kn_fop = &targread_filtops; + TARG_LOCK(softc); + SLIST_INSERT_HEAD(&softc->read_select.si_note, kn, kn_selnext); + TARG_UNLOCK(softc); + return (0); } static void -targdtor(struct cam_periph *periph) +targreadfiltdetach(struct knote *kn) { - struct targ_softc *softc; - - softc = (struct targ_softc *)periph->softc; + struct targ_softc *softc; - softc->state = TARG_STATE_TEARDOWN; - - targdislun(periph); - - switch (softc->init_level) { - default: - /* FALLTHROUGH */ - case 2: - free(softc->inq_data, M_DEVBUF); - destroy_dev(softc->targ_dev); - /* FALLTHROUGH */ - case 1: - free(softc, M_DEVBUF); - break; - case 0: - panic("targdtor - impossible init level");; - } + softc = (struct targ_softc *)kn->kn_hook; + TARG_LOCK(softc); + SLIST_REMOVE(&softc->read_select.si_note, kn, knote, kn_selnext); + TARG_UNLOCK(softc); } +/* Notify the user's kqueue when the user queue or abort queue gets a CCB */ static int -targopen(dev_t dev, int flags, int fmt, struct thread *td) +targreadfilt(struct knote *kn, long hint) { - struct cam_periph *periph; - struct targ_softc *softc; - cam_status status; - int error; - int s; - - /* An open of the control device always succeeds */ - if (TARG_IS_CONTROL_DEV(dev)) - return 0; - - s = splsoftcam(); - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) { - splx(s); - return (ENXIO); - } - 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); - if (error) { - cam_periph_release(periph); - } - return (error); + struct targ_softc *softc; + int retval; + + softc = (struct targ_softc *)kn->kn_hook; + TARG_LOCK(softc); + retval = !TAILQ_EMPTY(&softc->user_ccb_queue) || + !TAILQ_EMPTY(&softc->abort_queue); + TARG_UNLOCK(softc); + return (retval); } -static int -targclose(dev_t dev, int flag, int fmt, struct thread *td) +/* Send the HBA the enable/disable message */ +static cam_status +targendislun(struct cam_path *path, int enable, int grp6_len, int grp7_len) { - struct cam_periph *periph; - struct targ_softc *softc; - int s; - int error; - - /* A close of the control device always succeeds */ - if (TARG_IS_CONTROL_DEV(dev)) - return 0; - - s = splsoftcam(); - periph = (struct cam_periph *)dev->si_drv1; - 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); - - cam_periph_unlock(periph); - cam_periph_release(periph); + struct ccb_en_lun en_ccb; + cam_status status; - return (0); + /* Tell the lun to begin answering selects */ + xpt_setup_ccb(&en_ccb.ccb_h, path, /*priority*/1); + en_ccb.ccb_h.func_code = XPT_EN_LUN; + /* Don't need support for any vendor specific commands */ + en_ccb.grp6_len = grp6_len; + en_ccb.grp7_len = grp7_len; + en_ccb.enable = enable ? 1 : 0; + xpt_action((union ccb *)&en_ccb); + status = en_ccb.ccb_h.status & CAM_STATUS_MASK; + if (status != CAM_REQ_CMP) { + xpt_print_path(path); + printf("%sable lun CCB rejected, status %#x\n", + enable ? "en" : "dis", status); + } + return (status); } -static int -targallocinstance(void *arg, u_long cmd) +/* Enable target mode on a LUN, given its path */ +static cam_status +targenable(struct targ_softc *softc, struct cam_path *path, int grp6_len, + int grp7_len) { - struct ioc_alloc_unit *alloc_unit = arg; - struct scsi_inquiry_data local; - struct ccb_pathinq cpi; - struct cam_path *path; struct cam_periph *periph; - cam_status status; - int free_path_on_return; - int error; - - free_path_on_return = 0; - status = xpt_create_path(&path, /*periph*/NULL, - alloc_unit->path_id, - alloc_unit->target_id, - alloc_unit->lun_id); - if (status != CAM_REQ_CMP) { - printf("Couldn't Allocate Path %x\n", status); - goto fail; - } + struct ccb_pathinq cpi; + cam_status status; - free_path_on_return++; + if ((softc->state & TARG_STATE_LUN_ENABLED) != 0) + return (CAM_LUN_ALRDY_ENA); + /* Make sure SIM supports target mode */ xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); - status = cpi.ccb_h.status; - + status = cpi.ccb_h.status & CAM_STATUS_MASK; if (status != CAM_REQ_CMP) { - printf("Couldn't CPI %x\n", status); - goto fail; + printf("pathinq failed, status %#x\n", status); + goto enable_fail; } - - /* Can only alloc units on controllers that support target mode */ if ((cpi.target_sprt & PIT_PROCESSOR) == 0) { - printf("Controller does not support target mode - status %x\n", - status); - status = CAM_PATH_INVALID; - goto fail; + printf("controller does not support target mode\n"); + status = CAM_FUNC_NOTAVAIL; + goto enable_fail; } - /* Ensure that we don't already have an instance for this unit. */ - if ((periph = cam_periph_find(path, "targ")) != NULL) { - status = CAM_LUN_ALRDY_ENA; - goto fail; - } + /* Destroy any periph on our path if it is disabled */ + mtx_lock(&targ_mtx); + periph = cam_periph_find(path, "targ"); + if (periph != NULL) { + struct targ_softc *del_softc; - if (cmd == TARGCTLIOALLOCUNIT) { - status = copyin(alloc_unit->inquiry_data, &local, sizeof local); - if (status) - goto fail; - cpi.ccb_h.ccb_inq = &local; - } else { - cpi.ccb_h.ccb_inq = NULL; + del_softc = (struct targ_softc *)periph->softc; + if ((del_softc->state & TARG_STATE_LUN_ENABLED) == 0) { + cam_periph_invalidate(del_softc->periph); + del_softc->periph = NULL; + } else { + printf("Requested path still in use by targ%d\n", + periph->unit_number); + mtx_unlock(&targ_mtx); + status = CAM_LUN_ALRDY_ENA; + goto enable_fail; + } } - - /* - * Allocate a peripheral instance for - * this target instance. - */ + /* Create a periph instance attached to this path */ status = cam_periph_alloc(targctor, NULL, targdtor, targstart, - "targ", CAM_PERIPH_BIO, path, targasync, - 0, &cpi); - -fail: - switch (status) { - case CAM_REQ_CMP: - { - struct cam_periph *periph; + "targ", CAM_PERIPH_BIO, path, targasync, 0, softc); + mtx_unlock(&targ_mtx); + if (status != CAM_REQ_CMP) { + printf("cam_periph_alloc failed, status %#x\n", status); + goto enable_fail; + } - if ((periph = cam_periph_find(path, "targ")) == NULL) - panic("targallocinstance: Succeeded but no periph?"); - error = 0; - alloc_unit->unit = periph->unit_number; - break; + /* Ensure that the periph now exists. */ + if (cam_periph_find(path, "targ") == NULL) { + panic("targenable: succeeded but no periph?"); + /* NOTREACHED */ } - case CAM_RESRC_UNAVAIL: - error = ENOMEM; - break; - case CAM_LUN_ALRDY_ENA: - error = EADDRINUSE; - break; - default: - printf("targallocinstance: Unexpected CAM status %x\n", status); - /* FALLTHROUGH */ - case CAM_PATH_INVALID: - error = ENXIO; - break; - case CAM_PROVIDE_FAIL: - error = ENODEV; - break; + + /* Send the enable lun message */ + status = targendislun(path, /*enable*/1, grp6_len, grp7_len); + if (status != CAM_REQ_CMP) { + printf("enable lun failed, status %#x\n", status); + goto enable_fail; } + softc->state |= TARG_STATE_LUN_ENABLED; - if (free_path_on_return != 0) - xpt_free_path(path); +enable_fail: + return (status); +} - return (error); +/* Disable this softc's target instance if enabled */ +static cam_status +targdisable(struct targ_softc *softc) +{ + cam_status status; + + if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) + return (CAM_REQ_CMP); + + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targdisable\n")); + + /* Abort any ccbs pending on the controller */ + abort_all_pending(softc); + + /* Disable this lun */ + status = targendislun(softc->path, /*enable*/0, + /*grp6_len*/0, /*grp7_len*/0); + if (status == CAM_REQ_CMP) + softc->state &= ~TARG_STATE_LUN_ENABLED; + else + printf("Disable lun failed, status %#x\n", status); + + return (status); } -static int -targfreeinstance(struct ioc_alloc_unit *alloc_unit) +/* Initialize a periph (called from cam_periph_alloc) */ +static cam_status +targctor(struct cam_periph *periph, void *arg) { - struct cam_path *path; - struct cam_periph *periph; struct targ_softc *softc; - cam_status status; - int free_path_on_return; - int error; - - periph = NULL; - free_path_on_return = 0; - status = xpt_create_path(&path, /*periph*/NULL, - alloc_unit->path_id, - alloc_unit->target_id, - alloc_unit->lun_id); - free_path_on_return++; - - if (status != CAM_REQ_CMP) - goto fail; - - /* Find our instance. */ - if ((periph = cam_periph_find(path, "targ")) == NULL) { - xpt_print_path(path); - printf("Invalid path specified for freeing target instance\n"); - status = CAM_PATH_INVALID; - goto fail; - } - softc = (struct targ_softc *)periph->softc; - - if ((softc->flags & TARG_FLAG_LUN_ENABLED) != 0) { - status = CAM_BUSY; - goto fail; - } + /* Store pointer to softc for periph-driven routines */ + softc = (struct targ_softc *)arg; + periph->softc = softc; + softc->periph = periph; + softc->path = periph->path; + return (CAM_REQ_CMP); +} -fail: - if (free_path_on_return != 0) - xpt_free_path(path); +static void +targdtor(struct cam_periph *periph) +{ + struct targ_softc *softc; + struct ccb_hdr *ccb_h; + struct targ_cmd_descr *descr; - switch (status) { - case CAM_REQ_CMP: - if (periph != NULL) - cam_periph_invalidate(periph); - error = 0; - break; - case CAM_RESRC_UNAVAIL: - error = ENOMEM; - break; - case CAM_LUN_ALRDY_ENA: - error = EADDRINUSE; - break; - default: - printf("targfreeinstance: Unexpected CAM status %x\n", status); - /* FALLTHROUGH */ - case CAM_PATH_INVALID: - error = ENODEV; - break; + softc = (struct targ_softc *)periph->softc; + + /* + * targdisable() aborts CCBs back to the user and leaves them + * on user_ccb_queue and abort_queue in case the user is still + * interested in them. We free them now. + */ + while ((ccb_h = TAILQ_FIRST(&softc->user_ccb_queue)) != NULL) { + TAILQ_REMOVE(&softc->user_ccb_queue, ccb_h, periph_links.tqe); + targfreeccb(softc, (union ccb *)ccb_h); } - return (error); + while ((descr = TAILQ_FIRST(&softc->abort_queue)) != NULL) { + TAILQ_REMOVE(&softc->abort_queue, descr, tqe); + FREE(descr, M_TARG); + } + + softc->periph = NULL; + softc->path = NULL; + periph->softc = NULL; } +/* Receive CCBs from user mode proc and send them to the HBA */ static int -targioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td) +targwrite(dev_t dev, struct uio *uio, int ioflag) { - struct cam_periph *periph; + union ccb *user_ccb; struct targ_softc *softc; - int error; + struct targ_cmd_descr *descr; + int write_len, error; + int func_code, priority; + + softc = (struct targ_softc *)dev->si_drv1; + write_len = error = 0; + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("write - uio_resid %d\n", uio->uio_resid)); + while (uio->uio_resid >= sizeof(user_ccb) && error == 0) { + union ccb *ccb; + int error; - error = 0; - if (TARG_IS_CONTROL_DEV(dev)) { - switch (cmd) { - case OTARGCTLIOALLOCUNIT: - case TARGCTLIOALLOCUNIT: - error = targallocinstance(addr, cmd); + error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio); + if (error != 0) { + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("write - uiomove failed (%d)\n", error)); break; - case OTARGCTLIOFREEUNIT: - case TARGCTLIOFREEUNIT: - /* - * Old_ioc_alloc_unit and ioc_alloc_unit are the - * same with respect to what we need from the structure - * for this function. - */ - error = targfreeinstance((struct ioc_alloc_unit*)addr); + } + priority = fuword(&user_ccb->ccb_h.pinfo.priority); + if (priority == -1) { + error = EINVAL; + break; + } + func_code = fuword(&user_ccb->ccb_h.func_code); + switch (func_code) { + case XPT_ACCEPT_TARGET_IO: + case XPT_IMMED_NOTIFY: + ccb = targgetccb(softc, func_code, priority); + descr = (struct targ_cmd_descr *)ccb->ccb_h.targ_descr; + descr->user_ccb = user_ccb; + descr->func_code = func_code; + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("Sent ATIO/INOT (%p)\n", user_ccb)); + xpt_action(ccb); + TARG_LOCK(softc); + TAILQ_INSERT_TAIL(&softc->pending_ccb_queue, + &ccb->ccb_h, + periph_links.tqe); + TARG_UNLOCK(softc); break; default: - error = EINVAL; + if ((func_code & XPT_FC_QUEUED) != 0) { + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("Sending queued ccb %#x (%p)\n", + func_code, user_ccb)); + descr = targgetdescr(softc); + descr->user_ccb = user_ccb; + descr->priority = priority; + descr->func_code = func_code; + TARG_LOCK(softc); + TAILQ_INSERT_TAIL(&softc->work_queue, + descr, tqe); + TARG_UNLOCK(softc); + xpt_schedule(softc->periph, priority); + } else { + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("Sending inline ccb %#x (%p)\n", + func_code, user_ccb)); + ccb = targgetccb(softc, func_code, priority); + descr = (struct targ_cmd_descr *) + ccb->ccb_h.targ_descr; + descr->user_ccb = user_ccb; + descr->priority = priority; + descr->func_code = func_code; + if (targusermerge(softc, descr, ccb) != EFAULT) + targsendccb(softc, ccb, descr); + targreturnccb(softc, ccb); + } break; } - return (error); + write_len += sizeof(user_ccb); } + + /* + * If we've successfully taken in some amount of + * data, return success for that data first. If + * an error is persistent, it will be reported + * on the next write. + */ + if (error != 0 && write_len == 0) + return (error); + if (write_len == 0 && uio->uio_resid != 0) + return (ENOSPC); + return (0); +} + +/* Process requests (descrs) via the periph-supplied CCBs */ +static void +targstart(struct cam_periph *periph, union ccb *start_ccb) +{ + struct targ_softc *softc; + struct targ_cmd_descr *descr, *next_descr; + int error; - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) - return (ENXIO); softc = (struct targ_softc *)periph->softc; - switch (cmd) { - case TARGIOCFETCHEXCEPTION: - *((targ_exception *)addr) = softc->exceptions; - break; - case TARGIOCCLEAREXCEPTION: - { - targ_exception clear_mask; - - clear_mask = *((targ_exception *)addr); - if ((clear_mask & TARG_EXCEPT_UNKNOWN_ATIO) != 0) { - struct ccb_hdr *ccbh; - - ccbh = TAILQ_FIRST(&softc->unknown_atio_queue); - if (ccbh != NULL) { - TAILQ_REMOVE(&softc->unknown_atio_queue, - ccbh, periph_links.tqe); - /* Requeue the ATIO back to the controller */ - ccbh->ccb_flags = TARG_CCB_NONE; - xpt_action((union ccb *)ccbh); - ccbh = TAILQ_FIRST(&softc->unknown_atio_queue); - } - if (ccbh != NULL) - clear_mask &= ~TARG_EXCEPT_UNKNOWN_ATIO; - } - softc->exceptions &= ~clear_mask; - if (softc->exceptions == TARG_EXCEPT_NONE - && softc->state == TARG_STATE_EXCEPTION) { - softc->state = TARG_STATE_NORMAL; - targrunqueue(periph, softc); - } - break; - } - case TARGIOCFETCHATIO: - { - struct ccb_hdr *ccbh; + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targstart %p\n", start_ccb)); - ccbh = TAILQ_FIRST(&softc->unknown_atio_queue); - if (ccbh != NULL) { - bcopy(ccbh, addr, sizeof(struct ccb_accept_tio)); - } else { - error = ENOENT; + TARG_LOCK(softc); + descr = TAILQ_FIRST(&softc->work_queue); + if (descr == NULL) { + TARG_UNLOCK(softc); + xpt_release_ccb(start_ccb); + } else { + TAILQ_REMOVE(&softc->work_queue, descr, tqe); + next_descr = TAILQ_FIRST(&softc->work_queue); + TARG_UNLOCK(softc); + + /* Initiate a transaction using the descr and supplied CCB */ + error = targusermerge(softc, descr, start_ccb); + if (error == 0) + error = targsendccb(softc, start_ccb, descr); + if (error != 0) { + xpt_print_path(periph->path); + printf("targsendccb failed, err %d\n", error); + xpt_release_ccb(start_ccb); + suword(&descr->user_ccb->ccb_h.status, + CAM_REQ_CMP_ERR); + TARG_LOCK(softc); + TAILQ_INSERT_TAIL(&softc->abort_queue, descr, tqe); + TARG_UNLOCK(softc); + notify_user(softc); } - break; - } - case TARGIOCCOMMAND: - { - union ccb *inccb; - union ccb *ccb; - /* - * XXX JGibbs - * This code is lifted directly from the pass-thru driver. - * Perhaps this should be moved to a library???? - */ - inccb = (union ccb *)addr; - ccb = cam_periph_getccb(periph, inccb->ccb_h.pinfo.priority); + /* If we have more work to do, stay scheduled */ + if (next_descr != NULL) + xpt_schedule(periph, next_descr->priority); + } +} - error = targsendccb(periph, ccb, inccb); +static int +targusermerge(struct targ_softc *softc, struct targ_cmd_descr *descr, + union ccb *ccb) +{ + struct ccb_hdr *u_ccbh, *k_ccbh; + size_t ccb_len; + int error; - xpt_release_ccb(ccb); + u_ccbh = &descr->user_ccb->ccb_h; + k_ccbh = &ccb->ccb_h; - break; + /* + * There are some fields in the CCB header that need to be + * preserved, the rest we get from the user ccb. (See xpt_merge_ccb) + */ + xpt_setup_ccb(k_ccbh, softc->path, descr->priority); + k_ccbh->retry_count = fuword(&u_ccbh->retry_count); + k_ccbh->func_code = descr->func_code; + k_ccbh->flags = fuword(&u_ccbh->flags); + k_ccbh->timeout = fuword(&u_ccbh->timeout); + ccb_len = targccblen(k_ccbh->func_code) - sizeof(struct ccb_hdr); + error = copyin(u_ccbh + 1, k_ccbh + 1, ccb_len); + if (error != 0) { + k_ccbh->status = CAM_REQ_CMP_ERR; + return (error); } - case TARGIOCGETISTATE: - case TARGIOCSETISTATE: - { - struct ioc_initiator_state *ioc_istate; - ioc_istate = (struct ioc_initiator_state *)addr; - if (ioc_istate->initiator_id > MAX_INITIATORS) { - error = EINVAL; - break; - } - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("GET/SETISTATE for %d\n", ioc_istate->initiator_id)); - if (cmd == TARGIOCGETISTATE) { - bcopy(&softc->istate[ioc_istate->initiator_id], - &ioc_istate->istate, sizeof(ioc_istate->istate)); - } else { - bcopy(&ioc_istate->istate, - &softc->istate[ioc_istate->initiator_id], - sizeof(ioc_istate->istate)); - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("pending_ca now %x\n", - softc->istate[ioc_istate->initiator_id].pending_ca)); - } - break; - } - case TARGIODEBUG: - { -#ifdef CAMDEBUG - union ccb ccb; - bzero (&ccb, sizeof ccb); - if (xpt_create_path(&ccb.ccb_h.path, periph, - xpt_path_path_id(periph->path), - xpt_path_target_id(periph->path), - xpt_path_lun_id(periph->path)) != CAM_REQ_CMP) { - error = EINVAL; - break; - } - if (*((int *)addr)) { - ccb.cdbg.flags = CAM_DEBUG_PERIPH; - } else { - ccb.cdbg.flags = CAM_DEBUG_NONE; + /* Translate usermode abort_ccb pointer to its kernel counterpart */ + if (k_ccbh->func_code == XPT_ABORT) { + struct ccb_abort *cab; + struct ccb_hdr *ccb_h; + + cab = (struct ccb_abort *)ccb; + TARG_LOCK(softc); + TAILQ_FOREACH(ccb_h, &softc->pending_ccb_queue, + periph_links.tqe) { + struct targ_cmd_descr *ab_descr; + + ab_descr = (struct targ_cmd_descr *)ccb_h->targ_descr; + if (ab_descr->user_ccb == cab->abort_ccb) { + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("Changing abort for %p to %p\n", + cab->abort_ccb, ccb_h)); + cab->abort_ccb = (union ccb *)ccb_h; + break; + } } - xpt_setup_ccb(&ccb.ccb_h, ccb.ccb_h.path, 0); - ccb.ccb_h.func_code = XPT_DEBUG; - ccb.ccb_h.path_id = xpt_path_path_id(ccb.ccb_h.path); - ccb.ccb_h.target_id = xpt_path_target_id(ccb.ccb_h.path); - ccb.ccb_h.target_lun = xpt_path_lun_id(ccb.ccb_h.path); - ccb.ccb_h.cbfcnp = targdone; - xpt_action(&ccb); - if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { - error = EIO; - } else { - error = 0; + TARG_UNLOCK(softc); + /* CCB not found, set appropriate status */ + if (ccb_h == NULL) { + k_ccbh->status = CAM_PATH_INVALID; + error = ESRCH; } - xpt_free_path(ccb.ccb_h.path); -#else - error = 0; -#endif - break; - } - default: - error = ENOTTY; - break; } + return (error); } -/* - * XXX JGibbs lifted from pass-thru driver. - * Generally, "ccb" should be the CCB supplied by the kernel. "inccb" - * should be the CCB that is copied in from the user. - */ +/* Build and send a kernel CCB formed from descr->user_ccb */ static int -targsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb) +targsendccb(struct targ_softc *softc, union ccb *ccb, + struct targ_cmd_descr *descr) { - struct targ_softc *softc; - struct cam_periph_map_info mapinfo; - int error, need_unmap; - int s; - - softc = (struct targ_softc *)periph->softc; - - need_unmap = 0; + struct cam_periph_map_info *mapinfo; + struct ccb_hdr *ccb_h; + int error; - /* - * There are some fields in the CCB header that need to be - * preserved, the rest we get from the user. - */ - xpt_merge_ccb(ccb, inccb); + ccb_h = &ccb->ccb_h; + mapinfo = &descr->mapinfo; + mapinfo->num_bufs_used = 0; /* * There's no way for the user to have a completion * function, so we put our own completion function in here. + * We also stash in a reference to our descriptor so targreturnccb() + * can find our mapping info. */ - ccb->ccb_h.cbfcnp = targdone; + ccb_h->cbfcnp = targdone; + ccb_h->targ_descr = descr; /* * We only attempt to map the user memory into kernel space @@ -991,1327 +766,435 @@ targsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb) * without data are a reasonably common occurance (e.g. test unit * ready), it will save a few cycles if we check for it here. */ - if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0) - && (((ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) - && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)) - || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) { - - bzero(&mapinfo, sizeof(mapinfo)); + if (((ccb_h->flags & CAM_DATA_PHYS) == 0) + && (((ccb_h->func_code == XPT_CONT_TARGET_IO) + && ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE)) + || (ccb_h->func_code == XPT_DEV_MATCH))) { - error = cam_periph_mapmem(ccb, &mapinfo); + error = cam_periph_mapmem(ccb, mapinfo); /* * cam_periph_mapmem returned an error, we can't continue. * Return the error to the user. */ - if (error) - return(error); - - /* - * We successfully mapped the memory in, so we need to - * unmap it when the transaction is done. - */ - need_unmap = 1; + if (error) { + ccb_h->status = CAM_REQ_CMP_ERR; + mapinfo->num_bufs_used = 0; + return (error); + } } /* * Once queued on the pending CCB list, this CCB will be protected - * by the error recovery handling used for 'buffer I/O' ccbs. Since - * we are in a process context here, however, the software interrupt - * for this driver may deliver an event invalidating this CCB just - * before we queue it. Close this race condition by blocking - * software interrupt delivery, checking for any pertinent queued - * events, and only then queuing this CCB. + * by our error recovery handler. */ - s = splsoftcam(); - if (softc->exceptions == 0) { - if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) - TAILQ_INSERT_TAIL(&softc->pending_queue, &ccb->ccb_h, - periph_links.tqe); - - /* - * If the user wants us to perform any error recovery, - * then honor that request. Otherwise, it's up to the - * user to perform any error recovery. - */ - error = cam_periph_runccb(ccb, /* error handler */NULL, - CAM_RETRY_SELTO, SF_RETRY_UA, - &softc->device_stats); - - if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) - TAILQ_REMOVE(&softc->pending_queue, &ccb->ccb_h, - periph_links.tqe); - } else { - ccb->ccb_h.status = CAM_UNACKED_EVENT; - error = 0; + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("sendccb %p\n", ccb)); + if (XPT_FC_IS_QUEUED(ccb)) { + TARG_LOCK(softc); + TAILQ_INSERT_TAIL(&softc->pending_ccb_queue, ccb_h, + periph_links.tqe); + TARG_UNLOCK(softc); } - splx(s); + xpt_action(ccb); - if (need_unmap != 0) - cam_periph_unmapmem(ccb, &mapinfo); - - ccb->ccb_h.cbfcnp = NULL; - ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv; - bcopy(ccb, inccb, sizeof(union ccb)); - - return(error); + return (0); } - -static int -targpoll(dev_t dev, int poll_events, struct thread *td) +/* Completion routine for CCBs (called at splsoftcam) */ +static void +targdone(struct cam_periph *periph, union ccb *done_ccb) { - struct cam_periph *periph; struct targ_softc *softc; - int revents; - int s; - - /* ioctl is the only supported operation of the control device */ - if (TARG_IS_CONTROL_DEV(dev)) - return EINVAL; + cam_status status; - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) - return (ENXIO); + CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("targdone %p\n", done_ccb)); softc = (struct targ_softc *)periph->softc; - - revents = 0; - s = splcam(); - if ((poll_events & (POLLOUT | POLLWRNORM)) != 0) { - if (TAILQ_FIRST(&softc->rcv_ccb_queue) != NULL - && bioq_first(&softc->rcv_bio_queue) == NULL) - revents |= poll_events & (POLLOUT | POLLWRNORM); - } - if ((poll_events & (POLLIN | POLLRDNORM)) != 0) { - if (TAILQ_FIRST(&softc->snd_ccb_queue) != NULL - && bioq_first(&softc->snd_bio_queue) == NULL) - revents |= poll_events & (POLLIN | POLLRDNORM); + TARG_LOCK(softc); + TAILQ_REMOVE(&softc->pending_ccb_queue, &done_ccb->ccb_h, + periph_links.tqe); + status = done_ccb->ccb_h.status & CAM_STATUS_MASK; + + /* If we're no longer enabled, throw away CCB */ + if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) { + targfreeccb(softc, done_ccb); + return; } + /* abort_all_pending() waits for pending queue to be empty */ + if (TAILQ_EMPTY(&softc->pending_ccb_queue)) + wakeup(&softc->pending_ccb_queue); - if (softc->state != TARG_STATE_NORMAL) - revents |= POLLERR; - - if (revents == 0) { - if (poll_events & (POLLOUT | POLLWRNORM)) - selrecord(td, &softc->rcv_select); - if (poll_events & (POLLIN | POLLRDNORM)) - selrecord(td, &softc->snd_select); + switch (done_ccb->ccb_h.func_code) { + /* All FC_*_QUEUED CCBs go back to userland */ + case XPT_IMMED_NOTIFY: + case XPT_ACCEPT_TARGET_IO: + case XPT_CONT_TARGET_IO: + TAILQ_INSERT_TAIL(&softc->user_ccb_queue, &done_ccb->ccb_h, + periph_links.tqe); + notify_user(softc); + break; + default: + panic("targdone: impossible xpt opcode %#x", + done_ccb->ccb_h.func_code); + /* NOTREACHED */ } - splx(s); - return (revents); + TARG_UNLOCK(softc); } +/* Return CCBs to the user from the user queue and abort queue */ static int targread(dev_t dev, struct uio *uio, int ioflag) { - /* ioctl is the only supported operation of the control device */ - if (TARG_IS_CONTROL_DEV(dev)) - return EINVAL; - - if (uio->uio_iovcnt == 0 - || uio->uio_iov->iov_len == 0) { - /* EOF */ - struct cam_periph *periph; - struct targ_softc *softc; - int s; - - s = splcam(); - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) - return (ENXIO); - softc = (struct targ_softc *)periph->softc; - softc->flags |= TARG_FLAG_SEND_EOF; - splx(s); - targrunqueue(periph, softc); - return (0); - } - return(physread(dev, uio, ioflag)); -} + struct descr_queue *abort_queue; + struct targ_cmd_descr *user_descr; + struct targ_softc *softc; + struct ccb_queue *user_queue; + struct ccb_hdr *ccb_h; + union ccb *user_ccb; + int read_len, error; -static int -targwrite(dev_t dev, struct uio *uio, int ioflag) -{ - /* ioctl is the only supported operation of the control device */ - if (TARG_IS_CONTROL_DEV(dev)) - return EINVAL; - - if (uio->uio_iovcnt == 0 - || uio->uio_iov->iov_len == 0) { - /* EOF */ - struct cam_periph *periph; - struct targ_softc *softc; - int s; - - s = splcam(); - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) - return (ENXIO); - softc = (struct targ_softc *)periph->softc; - softc->flags |= TARG_FLAG_RECEIVE_EOF; - splx(s); - targrunqueue(periph, softc); - return (0); - } - return(physwrite(dev, uio, ioflag)); -} - -/* - * Actually translate the requested transfer into one the physical driver - * can understand. The transfer is described by a buf and will include - * only one physical transfer. - */ -static void -targstrategy(struct bio *bp) -{ - struct cam_periph *periph; - struct targ_softc *softc; - int s; - - bp->bio_resid = bp->bio_bcount; - - /* ioctl is the only supported operation of the control device */ - if (TARG_IS_CONTROL_DEV(bp->bio_dev)) { - biofinish(bp, NULL, EINVAL); - return; + error = 0; + read_len = 0; + softc = (struct targ_softc *)dev->si_drv1; + user_queue = &softc->user_ccb_queue; + abort_queue = &softc->abort_queue; + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targread\n")); + + /* If no data is available, wait or return immediately */ + TARG_LOCK(softc); + ccb_h = TAILQ_FIRST(user_queue); + user_descr = TAILQ_FIRST(abort_queue); + while (ccb_h == NULL && user_descr == NULL) { + if ((ioflag & IO_NDELAY) == 0) { + error = msleep(user_queue, &softc->mtx, + PRIBIO | PCATCH, "targrd", 0); + ccb_h = TAILQ_FIRST(user_queue); + user_descr = TAILQ_FIRST(abort_queue); + if (error != 0) { + if (error == ERESTART) { + continue; + } else { + TARG_UNLOCK(softc); + goto read_fail; + } + } + } else { + TARG_UNLOCK(softc); + return (EAGAIN); + } } - periph = (struct cam_periph *)bp->bio_dev->si_drv1; - if (periph == NULL) { - biofinish(bp, NULL, ENXIO); - return; - } - softc = (struct targ_softc *)periph->softc; + /* Data is available so fill the user's buffer */ + while (ccb_h != NULL) { + struct targ_cmd_descr *descr; - /* - * Mask interrupts so that the device cannot be invalidated until - * after we are in the queue. Otherwise, we might not properly - * clean up one of the buffers. - */ - s = splbio(); - - /* - * If there is an exception pending, error out - */ - if (softc->state != TARG_STATE_NORMAL) { - splx(s); - if (softc->state == TARG_STATE_EXCEPTION - && (softc->exceptions & TARG_EXCEPT_DEVICE_INVALID) == 0) - s = EBUSY; - else - s = ENXIO; - biofinish(bp, NULL, s); - return; - } - - /* - * Place it in the queue of buffers available for either - * SEND or RECEIVE commands. - * - */ - bp->bio_resid = bp->bio_bcount; - if (bp->bio_cmd == BIO_READ) { - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Queued a SEND buffer\n")); - bioq_insert_tail(&softc->snd_bio_queue, bp); - } else { - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Queued a RECEIVE buffer\n")); - bioq_insert_tail(&softc->rcv_bio_queue, bp); - } + if (uio->uio_resid < sizeof(user_ccb)) + break; + TAILQ_REMOVE(user_queue, ccb_h, periph_links.tqe); + TARG_UNLOCK(softc); + descr = (struct targ_cmd_descr *)ccb_h->targ_descr; + user_ccb = descr->user_ccb; + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("targread ccb %p (%p)\n", ccb_h, user_ccb)); + error = targreturnccb(softc, (union ccb *)ccb_h); + if (error != 0) + goto read_fail; + error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio); + if (error != 0) + goto read_fail; + read_len += sizeof(user_ccb); + + TARG_LOCK(softc); + ccb_h = TAILQ_FIRST(user_queue); + } + + /* Flush out any aborted descriptors */ + while (user_descr != NULL) { + if (uio->uio_resid < sizeof(user_ccb)) + break; + TAILQ_REMOVE(abort_queue, user_descr, tqe); + TARG_UNLOCK(softc); + user_ccb = user_descr->user_ccb; + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("targread aborted descr %p (%p)\n", + user_descr, user_ccb)); + suword(&user_ccb->ccb_h.status, CAM_REQ_ABORTED); + error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio); + if (error != 0) + goto read_fail; + read_len += sizeof(user_ccb); + + TARG_LOCK(softc); + user_descr = TAILQ_FIRST(abort_queue); + } + TARG_UNLOCK(softc); - splx(s); - /* - * Attempt to use the new buffer to service any pending - * target commands. + * If we've successfully read some amount of data, don't report an + * error. If the error is persistent, it will be reported on the + * next read(). */ - targrunqueue(periph, softc); + if (read_len == 0 && uio->uio_resid != 0) + error = ENOSPC; - return; +read_fail: + return (error); } -static void -targrunqueue(struct cam_periph *periph, struct targ_softc *softc) +/* Copy completed ccb back to the user */ +static int +targreturnccb(struct targ_softc *softc, union ccb *ccb) { - struct ccb_queue *pending_queue; - struct ccb_accept_tio *atio; - struct bio_queue_head *bioq; - struct bio *bp; - struct targ_cmd_desc *desc; - struct ccb_hdr *ccbh; - int s; - - s = splbio(); - pending_queue = NULL; - bioq = NULL; - ccbh = NULL; - /* Only run one request at a time to maintain data ordering. */ - if (softc->state != TARG_STATE_NORMAL - || TAILQ_FIRST(&softc->work_queue) != NULL - || TAILQ_FIRST(&softc->pending_queue) != NULL) { - splx(s); - return; - } + struct targ_cmd_descr *descr; + struct ccb_hdr *u_ccbh; + size_t ccb_len; + int error; - if (((bp = bioq_first(&softc->snd_bio_queue)) != NULL - || (softc->flags & TARG_FLAG_SEND_EOF) != 0) - && (ccbh = TAILQ_FIRST(&softc->snd_ccb_queue)) != NULL) { + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targreturnccb %p\n", ccb)); + descr = (struct targ_cmd_descr *)ccb->ccb_h.targ_descr; + u_ccbh = &descr->user_ccb->ccb_h; - if (bp == NULL) - softc->flags &= ~TARG_FLAG_SEND_EOF; - else { - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("De-Queued a SEND buffer %ld\n", - bp->bio_bcount)); - } - bioq = &softc->snd_bio_queue; - pending_queue = &softc->snd_ccb_queue; - } else if (((bp = bioq_first(&softc->rcv_bio_queue)) != NULL - || (softc->flags & TARG_FLAG_RECEIVE_EOF) != 0) - && (ccbh = TAILQ_FIRST(&softc->rcv_ccb_queue)) != NULL) { - - if (bp == NULL) - softc->flags &= ~TARG_FLAG_RECEIVE_EOF; - else { - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("De-Queued a RECEIVE buffer %ld\n", - bp->bio_bcount)); - } - bioq = &softc->rcv_bio_queue; - pending_queue = &softc->rcv_ccb_queue; - } + /* Copy out the central portion of the ccb_hdr */ + copyout(&ccb->ccb_h.retry_count, &u_ccbh->retry_count, + offsetof(struct ccb_hdr, periph_priv) - + offsetof(struct ccb_hdr, retry_count)); - if (pending_queue != NULL) { - /* Process a request */ - atio = (struct ccb_accept_tio *)ccbh; - TAILQ_REMOVE(pending_queue, ccbh, periph_links.tqe); - desc = (struct targ_cmd_desc *)atio->ccb_h.ccb_descr; - desc->bp = bp; - if (bp == NULL) { - /* EOF */ - desc->data = NULL; - desc->data_increment = 0; - desc->data_resid = 0; - atio->ccb_h.flags &= ~CAM_DIR_MASK; - atio->ccb_h.flags |= CAM_DIR_NONE; - } else { - bioq_remove(bioq, bp); - desc->data = &bp->bio_data[bp->bio_bcount - bp->bio_resid]; - desc->data_increment = - MIN(desc->data_resid, bp->bio_resid); - } - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Buffer command: data %p: datacnt %d\n", - desc->data, desc->data_increment)); - TAILQ_INSERT_TAIL(&softc->work_queue, &atio->ccb_h, - periph_links.tqe); + /* Copy out the rest of the ccb (after the ccb_hdr) */ + ccb_len = targccblen(ccb->ccb_h.func_code) - sizeof(struct ccb_hdr); + if (descr->mapinfo.num_bufs_used != 0) + cam_periph_unmapmem(ccb, &descr->mapinfo); + error = copyout(&ccb->ccb_h + 1, u_ccbh + 1, ccb_len); + if (error != 0) { + xpt_print_path(softc->path); + printf("targreturnccb - CCB copyout failed (%d)\n", + error); } - atio = (struct ccb_accept_tio *)TAILQ_FIRST(&softc->work_queue); - if (atio != NULL) { - int priority; - - priority = (atio->ccb_h.flags & CAM_DIS_DISCONNECT) ? 0 : 1; - splx(s); - xpt_schedule(periph, priority); - } else - splx(s); + /* Free CCB or send back to devq. */ + targfreeccb(softc, ccb); + + return (error); } -static void -targstart(struct cam_periph *periph, union ccb *start_ccb) +static union ccb * +targgetccb(struct targ_softc *softc, xpt_opcode type, int priority) { - struct targ_softc *softc; - struct ccb_hdr *ccbh; - struct ccb_accept_tio *atio; - struct targ_cmd_desc *desc; - struct ccb_scsiio *csio; - targ_ccb_flags flags; - int s; + union ccb *ccb; + int ccb_len; - softc = (struct targ_softc *)periph->softc; - - s = splbio(); - ccbh = TAILQ_FIRST(&softc->work_queue); - if (periph->immediate_priority <= periph->pinfo.priority) { - start_ccb->ccb_h.ccb_flags = TARG_CCB_WAITING; - SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, - periph_links.sle); - periph->immediate_priority = CAM_PRIORITY_NONE; - splx(s); - wakeup(&periph->ccb_list); - } else if (ccbh == NULL) { - splx(s); - xpt_release_ccb(start_ccb); - } else { - TAILQ_REMOVE(&softc->work_queue, ccbh, periph_links.tqe); - splx(s); - atio = (struct ccb_accept_tio*)ccbh; - desc = (struct targ_cmd_desc *)atio->ccb_h.ccb_descr; - - /* Is this a tagged request? */ - flags = atio->ccb_h.flags & (CAM_DIS_DISCONNECT | - CAM_TAG_ACTION_VALID | CAM_DIR_MASK | CAM_SEND_STATUS); - - /* - * If we are done with the transaction, tell the - * controller to send status and perform a CMD_CMPLT. - */ - if (desc->user_atio == 0 && - desc->data_resid == desc->data_increment) { - flags |= CAM_SEND_STATUS; - } - - csio = &start_ccb->csio; - cam_fill_ctio(csio, - /*retries*/2, - targdone, - flags, - (flags & CAM_TAG_ACTION_VALID) ? - MSG_SIMPLE_Q_TAG : 0, - atio->tag_id, - atio->init_id, - desc->status, - /*data_ptr*/desc->data_increment == 0 - ? NULL : desc->data, - /*dxfer_len*/desc->data_increment, - /*timeout*/desc->timeout); - - if ((flags & CAM_SEND_STATUS) != 0 - && (desc->status == SCSI_STATUS_CHECK_COND - || desc->status == SCSI_STATUS_CMD_TERMINATED)) { - struct initiator_state *istate; - - istate = &softc->istate[atio->init_id]; - csio->sense_len = istate->sense_data.extra_len - + offsetof(struct scsi_sense_data, - extra_len); - bcopy(&istate->sense_data, &csio->sense_data, - csio->sense_len); - csio->ccb_h.flags |= CAM_SEND_SENSE; - } else { - csio->sense_len = 0; - } + ccb_len = targccblen(type); + MALLOC(ccb, union ccb *, ccb_len, M_TARG, M_WAITOK); + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("getccb %p\n", ccb)); - start_ccb->ccb_h.ccb_flags = TARG_CCB_NONE; - start_ccb->ccb_h.ccb_atio = atio; - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Sending a CTIO (flags 0x%x)\n", csio->ccb_h.flags)); - TAILQ_INSERT_TAIL(&softc->pending_queue, &csio->ccb_h, - periph_links.tqe); - xpt_action(start_ccb); - /* - * If the queue was frozen waiting for the response - * to this ATIO (for instance disconnection was disallowed), - * then release it now that our response has been queued. - */ - if ((atio->ccb_h.status & CAM_DEV_QFRZN) != 0) { - cam_release_devq(periph->path, - /*relsim_flags*/0, - /*reduction*/0, - /*timeout*/0, - /*getcount_only*/0); - atio->ccb_h.status &= ~CAM_DEV_QFRZN; - } - s = splbio(); - ccbh = TAILQ_FIRST(&softc->work_queue); - splx(s); - } - if (ccbh != NULL) - targrunqueue(periph, softc); + xpt_setup_ccb(&ccb->ccb_h, softc->path, priority); + ccb->ccb_h.func_code = type; + ccb->ccb_h.cbfcnp = targdone; + ccb->ccb_h.targ_descr = targgetdescr(softc); + return (ccb); } static void -targdone(struct cam_periph *periph, union ccb *done_ccb) +targfreeccb(struct targ_softc *softc, union ccb *ccb) { - struct targ_softc *softc; - - softc = (struct targ_softc *)periph->softc; - - if (done_ccb->ccb_h.ccb_flags == TARG_CCB_WAITING) { - /* Caller will release the CCB */ - wakeup(&done_ccb->ccb_h.cbfcnp); - return; - } - - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("targdone %x\n", done_ccb->ccb_h.func_code)); + CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, ("targfreeccb descr %p and\n", + ccb->ccb_h.targ_descr)); + FREE(ccb->ccb_h.targ_descr, M_TARG); - switch (done_ccb->ccb_h.func_code) { + switch (ccb->ccb_h.func_code) { case XPT_ACCEPT_TARGET_IO: - { - struct ccb_accept_tio *atio; - struct targ_cmd_desc *descr; - struct initiator_state *istate; - u_int8_t *cdb; - int priority; - - atio = &done_ccb->atio; - descr = (struct targ_cmd_desc*)atio->ccb_h.ccb_descr; - istate = &softc->istate[atio->init_id]; - cdb = atio->cdb_io.cdb_bytes; - if (softc->state == TARG_STATE_TEARDOWN - || atio->ccb_h.status == CAM_REQ_ABORTED) { - freedescr(descr); - free(done_ccb, M_DEVBUF); - return; - } - descr->data_resid = 0; - descr->data_increment = 0; - descr->user_atio = 0; - -#ifdef CAMDEBUG - { - int i; - char dcb[128]; - for (dcb[0] = 0, i = 0; i < atio->cdb_len; i++) { - snprintf(dcb, sizeof dcb, - "%s %02x", dcb, cdb[i] & 0xff); - } - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("flags %x cdb:%s\n", atio->ccb_h.flags, dcb)); - } -#endif - if (atio->sense_len != 0) { - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("ATIO with sense_len\n")); - - /* - * We had an error in the reception of - * this command. Immediately issue a CA. - */ - atio->ccb_h.flags &= ~CAM_DIR_MASK; - atio->ccb_h.flags |= CAM_DIR_NONE; - descr->timeout = 5 * 1000; - descr->status = SCSI_STATUS_CHECK_COND; - copy_sense(softc, istate, (u_int8_t *)&atio->sense_data, - atio->sense_len); - set_ca_condition(periph, atio->init_id, CA_CMD_SENSE); - } else if (istate->pending_ca == 0 - && istate->pending_ua != 0 - && cdb[0] != INQUIRY) { - - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("pending_ca %d pending_ua %d\n", - istate->pending_ca, istate->pending_ua)); - - /* Pending UA, tell initiator */ - /* Direction is always relative to the initator */ - atio->ccb_h.flags &= ~CAM_DIR_MASK; - atio->ccb_h.flags |= CAM_DIR_NONE; - descr->timeout = 5 * 1000; - descr->status = SCSI_STATUS_CHECK_COND; - fill_sense(softc, atio->init_id, - SSD_CURRENT_ERROR, SSD_KEY_UNIT_ATTENTION, - 0x29, - istate->pending_ua == UA_POWER_ON ? 1 : 2); - set_ca_condition(periph, atio->init_id, CA_UNIT_ATTN); - } else { - /* - * Save the current CA and UA status so - * they can be used by this command. - */ - ua_types pending_ua; - ca_types pending_ca; - - pending_ua = istate->pending_ua; - pending_ca = istate->pending_ca; - - /* - * As per the SCSI2 spec, any command that occurs - * after a CA is reported, clears the CA. We must - * also clear the UA condition, if any, that caused - * the CA to occur assuming the UA is not for a - * persistant condition. - */ - istate->pending_ca = CA_NONE; - if (pending_ca == CA_UNIT_ATTN) - istate->pending_ua = UA_NONE; - - /* - * Determine the type of incoming command and - * setup our buffer for a response. - */ - switch (cdb[0]) { - case INQUIRY: - { - struct scsi_inquiry *inq; - struct scsi_sense_data *sense; - - inq = (struct scsi_inquiry *)cdb; - sense = &istate->sense_data; - descr->status = SCSI_STATUS_OK; - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Saw an inquiry!\n")); - /* - * Validate the command. We don't - * support any VPD pages, so complain - * if EVPD is set. - */ - if ((inq->byte2 & SI_EVPD) != 0 - || inq->page_code != 0) { - atio->ccb_h.flags &= ~CAM_DIR_MASK; - atio->ccb_h.flags |= CAM_DIR_NONE; - descr->timeout = 5 * 1000; - descr->status = SCSI_STATUS_CHECK_COND; - fill_sense(softc, atio->init_id, - SSD_CURRENT_ERROR, - SSD_KEY_ILLEGAL_REQUEST, - /*asc*/0x24, /*ascq*/0x00); - sense->extra_len = - offsetof(struct scsi_sense_data, - extra_bytes) - - offsetof(struct scsi_sense_data, - extra_len); - set_ca_condition(periph, atio->init_id, - CA_CMD_SENSE); - } - - if ((inq->byte2 & SI_EVPD) != 0) { - sense->sense_key_spec[0] = - SSD_SCS_VALID|SSD_FIELDPTR_CMD - |SSD_BITPTR_VALID| /*bit value*/1; - sense->sense_key_spec[1] = 0; - sense->sense_key_spec[2] = - offsetof(struct scsi_inquiry, - byte2); - } else if (inq->page_code != 0) { - sense->sense_key_spec[0] = - SSD_SCS_VALID|SSD_FIELDPTR_CMD; - sense->sense_key_spec[1] = 0; - sense->sense_key_spec[2] = - offsetof(struct scsi_inquiry, - page_code); - } - if (descr->status == SCSI_STATUS_CHECK_COND) - break; - - /* - * Direction is always relative - * to the initator. - */ - atio->ccb_h.flags &= ~CAM_DIR_MASK; - atio->ccb_h.flags |= CAM_DIR_IN; - descr->data = softc->inq_data; - descr->data_resid = - MIN(softc->inq_data_len, - SCSI_CDB6_LEN(inq->length)); - descr->data_increment = descr->data_resid; - descr->timeout = 5 * 1000; - break; - } - case TEST_UNIT_READY: - atio->ccb_h.flags &= ~CAM_DIR_MASK; - atio->ccb_h.flags |= CAM_DIR_NONE; - descr->timeout = 5 * 1000; - descr->status = SCSI_STATUS_OK; - break; - case REQUEST_SENSE: - { - struct scsi_request_sense *rsense; - struct scsi_sense_data *sense; - - rsense = (struct scsi_request_sense *)cdb; - sense = &istate->sense_data; - if (pending_ca == 0) { - fill_sense(softc, atio->init_id, - SSD_CURRENT_ERROR, - SSD_KEY_NO_SENSE, 0x00, - 0x00); - CAM_DEBUG(periph->path, - CAM_DEBUG_PERIPH, - ("No pending CA!\n")); - } - /* - * Direction is always relative - * to the initator. - */ - atio->ccb_h.flags &= ~CAM_DIR_MASK; - atio->ccb_h.flags |= CAM_DIR_IN; - descr->data = sense; - descr->data_resid = - offsetof(struct scsi_sense_data, - extra_len) - + sense->extra_len; - descr->data_resid = - MIN(descr->data_resid, - SCSI_CDB6_LEN(rsense->length)); - descr->data_increment = descr->data_resid; - descr->timeout = 5 * 1000; - descr->status = SCSI_STATUS_OK; - break; - } - case RECEIVE: - case SEND: - if (SID_TYPE(softc->inq_data) == T_PROCESSOR) { - struct scsi_send_receive *sr; - - sr = (struct scsi_send_receive *)cdb; - - /* - * Direction is always relative - * to the initator. - */ - atio->ccb_h.flags &= ~CAM_DIR_MASK; - descr->data_resid = scsi_3btoul(sr->xfer_len); - descr->timeout = 5 * 1000; - descr->status = SCSI_STATUS_OK; - if (cdb[0] == SEND) { - atio->ccb_h.flags |= CAM_DIR_OUT; - CAM_DEBUG(periph->path, - CAM_DEBUG_PERIPH, - ("Saw a SEND!\n")); - atio->ccb_h.flags |= CAM_DIR_OUT; - TAILQ_INSERT_TAIL(&softc->snd_ccb_queue, - &atio->ccb_h, - periph_links.tqe); - selwakeup(&softc->snd_select); - } else { - atio->ccb_h.flags |= CAM_DIR_IN; - CAM_DEBUG(periph->path, - CAM_DEBUG_PERIPH, - ("Saw a RECEIVE!\n")); - TAILQ_INSERT_TAIL(&softc->rcv_ccb_queue, - &atio->ccb_h, - periph_links.tqe); - selwakeup(&softc->rcv_select); - } - /* - * Attempt to satisfy this request with - * a user buffer. - */ - targrunqueue(periph, softc); - return; - } - default: - /* - * Queue for consumption by our userland - * counterpart and transition to the exception - * state. - */ - descr->data_resid = 0; - descr->data_increment = 0; - descr->user_atio = 1; - TAILQ_INSERT_TAIL(&softc->unknown_atio_queue, - &atio->ccb_h, - periph_links.tqe); - softc->exceptions |= TARG_EXCEPT_UNKNOWN_ATIO; - targfireexception(periph, softc); - return; - } - } - - /* Queue us up to receive a Continue Target I/O ccb. */ - if ((atio->ccb_h.flags & CAM_DIS_DISCONNECT) != 0) { - TAILQ_INSERT_HEAD(&softc->work_queue, &atio->ccb_h, - periph_links.tqe); - priority = 0; - } else { - TAILQ_INSERT_TAIL(&softc->work_queue, &atio->ccb_h, - periph_links.tqe); - priority = 1; - } - xpt_schedule(periph, priority); - break; - } - case XPT_CONT_TARGET_IO: - { - struct ccb_scsiio *csio; - struct ccb_accept_tio *atio; - struct targ_cmd_desc *desc; - struct bio *bp; - int error, lastctio; - - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Received completed CTIO\n")); - csio = &done_ccb->csio; - atio = (struct ccb_accept_tio*)done_ccb->ccb_h.ccb_atio; - desc = (struct targ_cmd_desc *)atio->ccb_h.ccb_descr; - - TAILQ_REMOVE(&softc->pending_queue, &done_ccb->ccb_h, - periph_links.tqe); - - if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { - printf("CCB with error %x\n", done_ccb->ccb_h.status); - error = targerror(done_ccb, 0, 0); - if (error == ERESTART) - break; - /* - * Right now we don't need to do anything - * prior to unfreezing the queue. This may - * change if certain errors are reported while - * we are in a connected state. - */ - if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { - printf("Releasing Queue\n"); - cam_release_devq(done_ccb->ccb_h.path, - /*relsim_flags*/0, - /*reduction*/0, - /*timeout*/0, - /*getcount_only*/0); - done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN; - } - } else - error = 0; - - /* - * If we shipped back sense data when completing - * this command, clear the pending CA for it. - */ - if (done_ccb->ccb_h.status & CAM_SENT_SENSE) { - struct initiator_state *istate; - - istate = &softc->istate[csio->init_id]; - if (istate->pending_ca == CA_UNIT_ATTN) - istate->pending_ua = UA_NONE; - istate->pending_ca = CA_NONE; - softc->istate[csio->init_id].pending_ca = CA_NONE; - done_ccb->ccb_h.status &= ~CAM_SENT_SENSE; - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Sent Sense\n")); - done_ccb->ccb_h.flags &= ~CAM_SEND_SENSE; - } - - if (done_ccb->ccb_h.status & CAM_AUTOSNS_VALID) { - struct initiator_state *istate; - - istate = &softc->istate[csio->init_id]; - copy_sense(softc, istate, (u_int8_t *)&csio->sense_data, - csio->sense_len); - set_ca_condition(periph, csio->init_id, CA_CMD_SENSE); - done_ccb->ccb_h.status &= ~CAM_AUTOSNS_VALID; - } - /* - * Was this the last CTIO? - */ - lastctio = done_ccb->ccb_h.status & CAM_SEND_STATUS; - - desc->data_increment -= csio->resid; - desc->data_resid -= desc->data_increment; - if ((bp = desc->bp) != NULL) { - - bp->bio_resid -= desc->data_increment; - bp->bio_error = error; - - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Buffer I/O Completed - Resid %ld:%d\n", - bp->bio_resid, desc->data_resid)); - /* - * Send the buffer back to the client if - * either the command has completed or all - * buffer space has been consumed. - */ - if (desc->data_resid == 0 - || bp->bio_resid == 0 - || error != 0) { - if (bp->bio_resid != 0) - /* Short transfer */ - bp->bio_flags |= BIO_ERROR; - - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Completing a buffer\n")); - biodone(bp); - desc->bp = NULL; - } - } - - if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) - atio->ccb_h.status |= CAM_DEV_QFRZN; - xpt_release_ccb(done_ccb); - if (softc->state != TARG_STATE_TEARDOWN) { - if (lastctio) { - /* - * Send the original accept TIO back to the - * controller to handle more work. - */ - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Returning ATIO to target SIM\n")); - atio->ccb_h.ccb_flags = TARG_CCB_NONE; - xpt_action((union ccb *)atio); - break; - } - - if (SID_TYPE(softc->inq_data) == T_PROCESSOR) { - /* Queue us up for another buffer */ - if (atio->cdb_io.cdb_bytes[0] == SEND) { - if (desc->bp != NULL) - TAILQ_INSERT_HEAD(&softc->snd_bio_queue.queue, - bp, bio_queue); - TAILQ_INSERT_HEAD(&softc->snd_ccb_queue, - &atio->ccb_h, - periph_links.tqe); - } else { - if (desc->bp != NULL) - TAILQ_INSERT_HEAD(&softc->rcv_bio_queue.queue, - bp, bio_queue); - TAILQ_INSERT_HEAD(&softc->rcv_ccb_queue, - &atio->ccb_h, - periph_links.tqe); - } - desc->bp = NULL; - } - targrunqueue(periph, softc); - } else { - if (desc->bp != NULL) { - bp->bio_flags |= BIO_ERROR; - bp->bio_error = ENXIO; - biodone(bp); - } - freedescr(desc); - free(atio, M_DEVBUF); - } - break; - } case XPT_IMMED_NOTIFY: - { - int frozen; - - frozen = (done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0; - if (softc->state == TARG_STATE_TEARDOWN) { - SLIST_REMOVE(&softc->immed_notify_slist, - &done_ccb->ccb_h, ccb_hdr, - periph_links.sle); - free(done_ccb, M_DEVBUF); - } else if (done_ccb->ccb_h.status == CAM_REQ_ABORTED) { - free(done_ccb, M_DEVBUF); - } else { - printf("Saw event %x:%x\n", done_ccb->ccb_h.status, - done_ccb->cin.message_args[0]); - /* Process error condition. */ - targinoterror(periph, softc, &done_ccb->cin); - - /* Requeue for another immediate event */ - xpt_action(done_ccb); - } - if (frozen != 0) - cam_release_devq(periph->path, - /*relsim_flags*/0, - /*opening reduction*/0, - /*timeout*/0, - /*getcount_only*/0); - break; - } - case XPT_DEBUG: - wakeup(&done_ccb->ccb_h.cbfcnp); - break; - default: - panic("targdone: Impossible xpt opcode %x encountered.", - done_ccb->ccb_h.func_code); - /* NOTREACHED */ - break; - } -} - -/* - * Transition to the exception state and notify our symbiotic - * userland process of the change. - */ -static void -targfireexception(struct cam_periph *periph, struct targ_softc *softc) -{ - /* - * return all pending buffers with short read/write status so our - * process unblocks, and do a selwakeup on any process queued - * waiting for reads or writes. When the selwakeup is performed, - * the waking process will wakeup, call our poll routine again, - * and pick up the exception. - */ - struct bio *bp; - - if (softc->state != TARG_STATE_NORMAL) - /* Already either tearing down or in exception state */ - return; - - softc->state = TARG_STATE_EXCEPTION; - - while ((bp = bioq_first(&softc->snd_bio_queue)) != NULL) { - bioq_remove(&softc->snd_bio_queue, bp); - bp->bio_flags |= BIO_ERROR; - biodone(bp); - } - - while ((bp = bioq_first(&softc->rcv_bio_queue)) != NULL) { - bioq_remove(&softc->snd_bio_queue, bp); - bp->bio_flags |= BIO_ERROR; - biodone(bp); - } - - selwakeup(&softc->snd_select); - selwakeup(&softc->rcv_select); -} - -static void -targinoterror(struct cam_periph *periph, struct targ_softc *softc, - struct ccb_immed_notify *inot) -{ - cam_status status; - int sense; - - status = inot->ccb_h.status; - sense = (status & CAM_AUTOSNS_VALID) != 0; - status &= CAM_STATUS_MASK; - switch (status) { - case CAM_SCSI_BUS_RESET: - set_unit_attention_cond(periph, /*init_id*/CAM_TARGET_WILDCARD, - UA_BUS_RESET); - abort_pending_transactions(periph, - /*init_id*/CAM_TARGET_WILDCARD, - TARG_TAG_WILDCARD, EINTR, - /*to_held_queue*/FALSE); - softc->exceptions |= TARG_EXCEPT_BUS_RESET_SEEN; - targfireexception(periph, softc); - break; - case CAM_BDR_SENT: - set_unit_attention_cond(periph, /*init_id*/CAM_TARGET_WILDCARD, - UA_BDR); - abort_pending_transactions(periph, CAM_TARGET_WILDCARD, - TARG_TAG_WILDCARD, EINTR, - /*to_held_queue*/FALSE); - softc->exceptions |= TARG_EXCEPT_BDR_RECEIVED; - targfireexception(periph, softc); - break; - case CAM_MESSAGE_RECV: - switch (inot->message_args[0]) { - case MSG_INITIATOR_DET_ERR: - break; - case MSG_ABORT: - break; - case MSG_BUS_DEV_RESET: - break; - case MSG_ABORT_TAG: - break; - case MSG_CLEAR_QUEUE: - break; - case MSG_TERM_IO_PROC: - break; - default: - break; - } + CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, ("freeing ccb %p\n", ccb)); + FREE(ccb, M_TARG); break; default: - break; - } -} - -static int -targerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) -{ - struct cam_periph *periph; - struct targ_softc *softc; - struct ccb_scsiio *csio; - struct initiator_state *istate; - cam_status status; - int frozen; - int sense; - int error; - int on_held_queue; - - periph = xpt_path_periph(ccb->ccb_h.path); - softc = (struct targ_softc *)periph->softc; - status = ccb->ccb_h.status; - sense = (status & CAM_AUTOSNS_VALID) != 0; - frozen = (status & CAM_DEV_QFRZN) != 0; - status &= CAM_STATUS_MASK; - on_held_queue = FALSE; - csio = &ccb->csio; - istate = &softc->istate[csio->init_id]; - switch (status) { - case CAM_REQ_ABORTED: - if ((ccb->ccb_h.ccb_flags & TARG_CCB_ABORT_TO_HELDQ) != 0) { - - /* - * Place this CCB into the initiators - * 'held' queue until the pending CA is cleared. - * If there is no CA pending, reissue immediately. - */ - if (istate->pending_ca == 0) { - ccb->ccb_h.ccb_flags = TARG_CCB_NONE; - xpt_action(ccb); - } else { - ccb->ccb_h.ccb_flags = TARG_CCB_HELDQ; - TAILQ_INSERT_TAIL(&softc->pending_queue, - &ccb->ccb_h, - periph_links.tqe); - } - /* The command will be retried at a later time. */ - on_held_queue = TRUE; - error = ERESTART; - break; - } - /* FALLTHROUGH */ - case CAM_SCSI_BUS_RESET: - case CAM_BDR_SENT: - case CAM_REQ_TERMIO: - case CAM_CMD_TIMEOUT: - /* Assume we did not send any data */ - csio->resid = csio->dxfer_len; - error = EIO; - break; - case CAM_SEL_TIMEOUT: - if (ccb->ccb_h.retry_count > 0) { - ccb->ccb_h.retry_count--; - error = ERESTART; + /* Send back CCB if we got it from the periph */ + if (XPT_FC_IS_QUEUED(ccb)) { + CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, + ("returning queued ccb %p\n", ccb)); + xpt_release_ccb(ccb); } else { - /* "Select or reselect failure" */ - csio->resid = csio->dxfer_len; - fill_sense(softc, csio->init_id, SSD_CURRENT_ERROR, - SSD_KEY_HARDWARE_ERROR, 0x45, 0x00); - set_ca_condition(periph, csio->init_id, CA_CMD_SENSE); - error = EIO; + CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, + ("freeing ccb %p\n", ccb)); + FREE(ccb, M_TARG); } break; - case CAM_UNCOR_PARITY: - /* "SCSI parity error" */ - fill_sense(softc, csio->init_id, SSD_CURRENT_ERROR, - SSD_KEY_HARDWARE_ERROR, 0x47, 0x00); - set_ca_condition(periph, csio->init_id, CA_CMD_SENSE); - csio->resid = csio->dxfer_len; - error = EIO; - break; - case CAM_NO_HBA: - csio->resid = csio->dxfer_len; - error = ENXIO; - break; - case CAM_SEQUENCE_FAIL: - if (sense != 0) { - copy_sense(softc, istate, (u_int8_t *)&csio->sense_data, - csio->sense_len); - set_ca_condition(periph, csio->init_id, CA_CMD_SENSE); - } - csio->resid = csio->dxfer_len; - error = EIO; - break; - case CAM_IDE: - /* "Initiator detected error message received" */ - fill_sense(softc, csio->init_id, SSD_CURRENT_ERROR, - SSD_KEY_HARDWARE_ERROR, 0x48, 0x00); - set_ca_condition(periph, csio->init_id, CA_CMD_SENSE); - csio->resid = csio->dxfer_len; - error = EIO; - break; - case CAM_REQUEUE_REQ: - printf("Requeue Request!\n"); - error = ERESTART; - break; - default: - csio->resid = csio->dxfer_len; - error = EIO; - panic("targerror: Unexpected status %x encounterd", status); - /* NOTREACHED */ - } - - if (error == ERESTART || error == 0) { - /* Clear the QFRZN flag as we will release the queue */ - if (frozen != 0) - ccb->ccb_h.status &= ~CAM_DEV_QFRZN; - - if (error == ERESTART && !on_held_queue) - xpt_action(ccb); - - if (frozen != 0) - cam_release_devq(ccb->ccb_h.path, - /*relsim_flags*/0, - /*opening reduction*/0, - /*timeout*/0, - /*getcount_only*/0); } - return (error); } -static struct targ_cmd_desc* -allocdescr() +static struct targ_cmd_descr * +targgetdescr(struct targ_softc *softc) { - struct targ_cmd_desc* descr; - - /* Allocate the targ_descr structure */ - descr = (struct targ_cmd_desc *) - malloc(sizeof(*descr), M_DEVBUF, M_NOWAIT); - if (descr == NULL) - return (NULL); - - bzero(descr, sizeof(*descr)); + struct targ_cmd_descr *descr; - /* Allocate buffer backing store */ - descr->backing_store = malloc(MAX_BUF_SIZE, M_DEVBUF, M_NOWAIT); - if (descr->backing_store == NULL) { - free(descr, M_DEVBUF); - return (NULL); - } - descr->max_size = MAX_BUF_SIZE; + MALLOC(descr, struct targ_cmd_descr *, sizeof(*descr), M_TARG, + M_WAITOK); + descr->mapinfo.num_bufs_used = 0; return (descr); } static void -freedescr(struct targ_cmd_desc *descr) -{ - free(descr->backing_store, M_DEVBUF); - free(descr, M_DEVBUF); -} - -static void -fill_sense(struct targ_softc *softc, u_int initiator_id, u_int error_code, - u_int sense_key, u_int asc, u_int ascq) -{ - struct initiator_state *istate; - struct scsi_sense_data *sense; - - istate = &softc->istate[initiator_id]; - sense = &istate->sense_data; - bzero(sense, sizeof(*sense)); - sense->error_code = error_code; - sense->flags = sense_key; - sense->add_sense_code = asc; - sense->add_sense_code_qual = ascq; - - sense->extra_len = offsetof(struct scsi_sense_data, fru) - - offsetof(struct scsi_sense_data, extra_len); -} - -static void -copy_sense(struct targ_softc *softc, struct initiator_state *istate, - u_int8_t *sense_buffer, size_t sense_len) +targinit(void) { - struct scsi_sense_data *sense; - size_t copylen; - - sense = &istate->sense_data; - copylen = sizeof(*sense); - if (copylen > sense_len) - copylen = sense_len; - bcopy(sense_buffer, sense, copylen); + mtx_init(&targ_mtx, "targ global", NULL, MTX_DEF); + EVENTHANDLER_REGISTER(dev_clone, targclone, 0, 1000); + cdevsw_add(&targ_cdevsw); } static void -set_unit_attention_cond(struct cam_periph *periph, - u_int initiator_id, ua_types ua) +targclone(void *arg, char *name, int namelen, dev_t *dev) { - int start; - int end; - struct targ_softc *softc; + int u; - softc = (struct targ_softc *)periph->softc; - if (initiator_id == CAM_TARGET_WILDCARD) { - start = 0; - end = MAX_INITIATORS - 1; - } else - start = end = initiator_id; - - while (start <= end) { - softc->istate[start].pending_ua = ua; - start++; - } + if (*dev != NODEV) + return; + if (dev_stdclone(name, NULL, "targ", &u) != 1) + return; + *dev = make_dev(&targ_cdevsw, unit2minor(u), UID_ROOT, GID_WHEEL, + 0600, "targ%d", u); + (*dev)->si_flags |= SI_CHEAPCLONE; } static void -set_ca_condition(struct cam_periph *periph, u_int initiator_id, ca_types ca) +targasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { - struct targ_softc *softc; - - softc = (struct targ_softc *)periph->softc; - softc->istate[initiator_id].pending_ca = ca; - abort_pending_transactions(periph, initiator_id, TARG_TAG_WILDCARD, - /*errno*/0, /*to_held_queue*/TRUE); + /* All events are handled in usermode by INOTs */ + panic("targasync() called, should be an INOT instead"); } +/* Cancel all pending requests and CCBs awaiting work. */ static void -abort_pending_transactions(struct cam_periph *periph, u_int initiator_id, - u_int tag_id, int errno, int to_held_queue) +abort_all_pending(struct targ_softc *softc) { - struct ccb_abort cab; - struct ccb_queue *atio_queues[3]; - struct targ_softc *softc; - struct ccb_hdr *ccbh; - u_int i; - - softc = (struct targ_softc *)periph->softc; + struct targ_cmd_descr *descr; + struct ccb_abort cab; + struct ccb_hdr *ccb_h; - atio_queues[0] = &softc->work_queue; - atio_queues[1] = &softc->snd_ccb_queue; - atio_queues[2] = &softc->rcv_ccb_queue; + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("abort_all_pending\n")); - /* First address the ATIOs awaiting resources */ - for (i = 0; i < (sizeof(atio_queues) / sizeof(*atio_queues)); i++) { - struct ccb_queue *atio_queue; + /* First abort the descriptors awaiting resources */ + while ((descr = TAILQ_FIRST(&softc->work_queue)) != NULL) { + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("Aborting descr from workq %p\n", descr)); + TAILQ_REMOVE(&softc->work_queue, descr, tqe); + TAILQ_INSERT_TAIL(&softc->abort_queue, descr, tqe); + } - if (to_held_queue) { - /* - * The device queue is frozen anyway, so there - * is nothing for us to do. - */ - continue; - } - atio_queue = atio_queues[i]; - ccbh = TAILQ_FIRST(atio_queue); - while (ccbh != NULL) { - struct ccb_accept_tio *atio; - struct targ_cmd_desc *desc; - - atio = (struct ccb_accept_tio *)ccbh; - desc = (struct targ_cmd_desc *)atio->ccb_h.ccb_descr; - ccbh = TAILQ_NEXT(ccbh, periph_links.tqe); - - /* Only abort the CCBs that match */ - if ((atio->init_id != initiator_id - && initiator_id != CAM_TARGET_WILDCARD) - || (tag_id != TARG_TAG_WILDCARD - && ((atio->ccb_h.flags & CAM_TAG_ACTION_VALID) == 0 - || atio->tag_id != tag_id))) - continue; - - TAILQ_REMOVE(atio_queue, &atio->ccb_h, - periph_links.tqe); - - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Aborting ATIO\n")); - if (desc->bp != NULL) { - desc->bp->bio_flags |= BIO_ERROR; - if (softc->state != TARG_STATE_TEARDOWN) - desc->bp->bio_error = errno; - else - desc->bp->bio_error = ENXIO; - biodone(desc->bp); - desc->bp = NULL; - } - if (softc->state == TARG_STATE_TEARDOWN) { - freedescr(desc); - free(atio, M_DEVBUF); - } else { - /* Return the ATIO back to the controller */ - atio->ccb_h.ccb_flags = TARG_CCB_NONE; - xpt_action((union ccb *)atio); - } + /* + * Then abort all pending CCBs. + * targdone() will return the aborted CCB via user_ccb_queue + */ + xpt_setup_ccb(&cab.ccb_h, softc->path, /*priority*/0); + cab.ccb_h.func_code = XPT_ABORT; + cab.ccb_h.status = CAM_REQ_CMP_ERR; + TAILQ_FOREACH(ccb_h, &softc->pending_ccb_queue, periph_links.tqe) { + CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, + ("Aborting pending CCB %p\n", ccb_h)); + cab.abort_ccb = (union ccb *)ccb_h; + xpt_action((union ccb *)&cab); + if (cab.ccb_h.status != CAM_REQ_CMP) { + xpt_print_path(cab.ccb_h.path); + printf("Unable to abort CCB, status %#x\n", + cab.ccb_h.status); } } - ccbh = TAILQ_FIRST(&softc->pending_queue); - while (ccbh != NULL) { - struct ccb_scsiio *csio; + /* If we aborted at least one pending CCB ok, wait for it. */ + if (cab.ccb_h.status == CAM_REQ_CMP) { + msleep(&softc->pending_ccb_queue, &softc->mtx, + PRIBIO | PCATCH, "tgabrt", 0); + } - csio = (struct ccb_scsiio *)ccbh; - ccbh = TAILQ_NEXT(ccbh, periph_links.tqe); + /* If we aborted anything from the work queue, wakeup user. */ + if (!TAILQ_EMPTY(&softc->user_ccb_queue) + || !TAILQ_EMPTY(&softc->abort_queue)) + notify_user(softc); +} - /* Only abort the CCBs that match */ - if ((csio->init_id != initiator_id - && initiator_id != CAM_TARGET_WILDCARD) - || (tag_id != TARG_TAG_WILDCARD - && ((csio->ccb_h.flags & CAM_TAG_ACTION_VALID) == 0 - || csio->tag_id != tag_id))) - continue; +/* Notify the user that data is ready */ +static void +notify_user(struct targ_softc *softc) +{ + /* + * Notify users sleeping via poll(), kqueue(), and + * blocking read(). + */ + selwakeup(&softc->read_select); + KNOTE(&softc->read_select.si_note, 0); + wakeup(&softc->user_ccb_queue); +} - CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, - ("Aborting CTIO\n")); +/* Convert CAM status to errno values */ +static int +targcamstatus(cam_status status) +{ + switch (status & CAM_STATUS_MASK) { + case CAM_REQ_CMP: /* CCB request completed without error */ + return (0); + case CAM_REQ_INPROG: /* CCB request is in progress */ + return (EINPROGRESS); + case CAM_REQ_CMP_ERR: /* CCB request completed with an error */ + return (EIO); + case CAM_PROVIDE_FAIL: /* Unable to provide requested capability */ + return (ENOTTY); + case CAM_FUNC_NOTAVAIL: /* The requested function is not available */ + return (ENOTSUP); + case CAM_LUN_ALRDY_ENA: /* LUN is already enabled for target mode */ + return (EADDRINUSE); + case CAM_PATH_INVALID: /* Supplied Path ID is invalid */ + case CAM_DEV_NOT_THERE: /* SCSI Device Not Installed/there */ + return (ENOENT); + case CAM_REQ_ABORTED: /* CCB request aborted by the host */ + return (ECANCELED); + case CAM_CMD_TIMEOUT: /* Command timeout */ + return (ETIMEDOUT); + case CAM_REQUEUE_REQ: /* Requeue to preserve transaction ordering */ + return (EAGAIN); + case CAM_REQ_INVALID: /* CCB request was invalid */ + return (EINVAL); + case CAM_RESRC_UNAVAIL: /* Resource Unavailable */ + return (ENOMEM); + case CAM_BUSY: /* CAM subsytem is busy */ + case CAM_UA_ABORT: /* Unable to abort CCB request */ + return (EBUSY); + default: + return (ENXIO); + } +} - TAILQ_REMOVE(&softc->pending_queue, &csio->ccb_h, - periph_links.tqe); +static size_t +targccblen(xpt_opcode func_code) +{ + int len; - if (to_held_queue != 0) - csio->ccb_h.ccb_flags |= TARG_CCB_ABORT_TO_HELDQ; - xpt_setup_ccb(&cab.ccb_h, csio->ccb_h.path, /*priority*/1); - cab.abort_ccb = (union ccb *)csio; - xpt_action((union ccb *)&cab); - if (cab.ccb_h.status != CAM_REQ_CMP) { - xpt_print_path(cab.ccb_h.path); - printf("Unable to abort CCB. Status %x\n", - cab.ccb_h.status); - } + /* Codes we expect to see as a target */ + switch (func_code) { + case XPT_CONT_TARGET_IO: + case XPT_SCSI_IO: + len = sizeof(struct ccb_scsiio); + break; + case XPT_ACCEPT_TARGET_IO: + len = sizeof(struct ccb_accept_tio); + break; + case XPT_IMMED_NOTIFY: + len = sizeof(struct ccb_immed_notify); + break; + case XPT_REL_SIMQ: + len = sizeof(struct ccb_relsim); + break; + case XPT_PATH_INQ: + len = sizeof(struct ccb_pathinq); + break; + case XPT_DEBUG: + len = sizeof(struct ccb_debug); + break; + case XPT_ABORT: + len = sizeof(struct ccb_abort); + break; + case XPT_EN_LUN: + len = sizeof(struct ccb_en_lun); + break; + default: + len = sizeof(union ccb); + break; } + + return (len); } diff --git a/sys/cam/scsi/scsi_targetio.h b/sys/cam/scsi/scsi_targetio.h index a67f78e..b6f57db 100644 --- a/sys/cam/scsi/scsi_targetio.h +++ b/sys/cam/scsi/scsi_targetio.h @@ -1,6 +1,7 @@ /* - * Ioctl definitions for the Target Mode SCSI Proccessor Target driver for CAM. + * Ioctl definitions for the SCSI Target Driver * + * Copyright (c) 2002 Nate Lawson. * Copyright (c) 1998 Justin T. Gibbs. * All rights reserved. * @@ -38,104 +39,39 @@ #include <cam/cam.h> #include <cam/cam_ccb.h> -TAILQ_HEAD(ccb_queue, ccb_hdr); - -/* Determine and clear exception state in the driver */ -typedef enum { - TARG_EXCEPT_NONE = 0x00, - TARG_EXCEPT_DEVICE_INVALID = 0x01, - TARG_EXCEPT_BDR_RECEIVED = 0x02, - TARG_EXCEPT_BUS_RESET_SEEN = 0x04, - TARG_EXCEPT_ABORT_SEEN = 0x08, - TARG_EXCEPT_ABORT_TAG_SEEN = 0x10, - TARG_EXCEPT_UNKNOWN_ATIO = 0x80 -} targ_exception; - -#define TARGIOCFETCHEXCEPTION _IOR('C', 1, targ_exception) -#define TARGIOCCLEAREXCEPTION _IOW('C', 2, targ_exception) - -/* - * Retreive an Accept Target I/O CCB for a command that is not handled - * directly by the kernel target driver. - */ -#define TARGIOCFETCHATIO _IOR('C', 3, struct ccb_accept_tio) - /* - * Used for responding to incoming ATIO requests. XPT_CONTINUE_TARG_IO - * operations are the norm, but ccb types for manipulating the device - * queue, etc. can also be used if error handling is to be performed by the - * user land process. + * CCBs (ATIO, CTIO, INOT, REL_SIMQ) are sent to the kernel driver + * by writing one or more pointers. The user receives notification + * of CCB completion through poll/select/kqueue and then calls + * read(2) which outputs pointers to the completed CCBs. */ -#define TARGIOCCOMMAND _IOWR('C', 4, union ccb) - - -typedef enum { - UA_NONE = 0x00, - UA_POWER_ON = 0x01, - UA_BUS_RESET = 0x02, - UA_BDR = 0x04 -} ua_types; - -typedef enum { - CA_NONE = 0x00, - CA_UNIT_ATTN = 0x01, - CA_CMD_SENSE = 0x02 -} ca_types; - -struct initiator_state { - ua_types pending_ua; - ca_types pending_ca; - struct scsi_sense_data sense_data; - struct ccb_queue held_queue; -}; - -struct ioc_initiator_state { - u_int initiator_id; - struct initiator_state istate; -}; /* - * Get and Set Contingent Allegiance and Unit Attention state - * presented by the target driver. This is usually used to - * properly report and error condition in response to an incoming - * ATIO request handled by the userland process. - * - * The initiator_id must be properly initialized in the ioc_initiator_state - * structure before calling TARGIOCGETISTATE. + * Enable and disable a target mode instance. For enabling, the path_id, + * target_id, and lun_id fields must be set. The grp6/7_len fields + * specify the length of vendor-specific CDBs the target expects and + * should normally be set to 0. On successful completion + * of enable, the specified target instance will answer selection. + * Disable causes the target instance to abort any outstanding commands + * and stop accepting new ones. The aborted CCBs will be returned to + * the user via read(2) or discarded if the user closes the device. + * The user can then re-enable the device for a new path. */ -#define TARGIOCGETISTATE _IOWR('C', 6, struct ioc_initiator_state) -#define TARGIOCSETISTATE _IOW('C', 5, struct ioc_initiator_state) - -struct old_ioc_alloc_unit { +struct ioc_enable_lun { path_id_t path_id; target_id_t target_id; lun_id_t lun_id; - u_int unit; + int grp6_len; + int grp7_len; }; - -struct ioc_alloc_unit { - path_id_t path_id; - target_id_t target_id; - lun_id_t lun_id; - u_int unit; - struct scsi_inquiry_data *inquiry_data; -}; - -/* - * Allocate and Free a target mode instance. For allocation, the path_id, - * target_id, and lun_id fields must be set. On successful completion - * of the ioctl, the unit field will indicate the unit number of the - * newly created instance. For de-allocation, all fields must match - * an instance in the inactive (i.e. closed) state. - */ -#define OTARGCTLIOALLOCUNIT _IOWR('C', 7, struct old_ioc_alloc_unit) -#define OTARGCTLIOFREEUNIT _IOW('C', 8, struct old_ioc_alloc_unit) -#define TARGCTLIOALLOCUNIT _IOWR('C', 7, struct ioc_alloc_unit) -#define TARGCTLIOFREEUNIT _IOW('C', 8, struct ioc_alloc_unit) +#define TARGIOCENABLE _IOW('C', 5, struct ioc_enable_lun) +#define TARGIOCDISABLE _IO('C', 6) /* * Set/clear debugging for this target mode instance */ -#define TARGIODEBUG _IOW('C', 9, int) +#define TARGIOCDEBUG _IOW('C', 7, int) + +TAILQ_HEAD(ccb_queue, ccb_hdr); #endif /* _CAM_SCSI_SCSI_TARGETIO_H_ */ diff --git a/sys/modules/cam/Makefile b/sys/modules/cam/Makefile index 633668d..0f0d238 100644 --- a/sys/modules/cam/Makefile +++ b/sys/modules/cam/Makefile @@ -15,7 +15,7 @@ SRCS+= opt_hw_wdog.h SRCS+= opt_pt.h SRCS+= opt_sa.h SRCS+= opt_ses.h -SRCS+= device_if.h bus_if.h +SRCS+= device_if.h bus_if.h vnode_if.h SRCS+= cam.c cam_periph.c cam_queue.c SRCS+= cam_sim.c cam_xpt.c SRCS+= scsi_all.c scsi_cd.c scsi_ch.c |