Path: blob/main/share/examples/scsi_target/scsi_target.c
39476 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* SCSI Disk Emulator4*5* Copyright (c) 2002 Nate Lawson.6* All rights reserved.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions, and the following disclaimer,13* without modification, immediately at the beginning of the file.14* 2. The name of the author may not be used to endorse or promote products15* derived from this software without specific prior written permission.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR21* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930#include <sys/types.h>31#include <ctype.h>32#include <errno.h>33#include <err.h>34#include <fcntl.h>35#include <signal.h>36#include <stddef.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <sysexits.h>41#include <unistd.h>42#include <aio.h>43#include <assert.h>44#include <sys/stat.h>45#include <sys/queue.h>46#include <sys/event.h>47#include <sys/param.h>48#include <sys/disk.h>49#include <cam/cam_queue.h>50#include <cam/scsi/scsi_all.h>51#include <cam/scsi/scsi_targetio.h>52#include <cam/scsi/scsi_message.h>53#include "scsi_target.h"5455/* Maximum amount to transfer per CTIO */56#define MAX_XFER MAXPHYS57/* Maximum number of allocated CTIOs */58#define MAX_CTIOS 6459/* Maximum sector size for emulated volume */60#define MAX_SECTOR 327686162/* Global variables */63int debug;64int notaio = 0;65off_t volume_size;66u_int sector_size;67size_t buf_size;6869/* Local variables */70static int targ_fd;71static int kq_fd;72static int file_fd;73static int num_ctios;74static struct ccb_queue pending_queue;75static struct ccb_queue work_queue;76static struct ioc_enable_lun ioc_enlun = {77CAM_BUS_WILDCARD,78CAM_TARGET_WILDCARD,79CAM_LUN_WILDCARD,800,81082};8384/* Local functions */85static void cleanup(void);86static int init_ccbs(void);87static void request_loop(void);88static void handle_read(void);89/* static int work_atio(struct ccb_accept_tio *); */90static void queue_io(struct ccb_scsiio *);91static int run_queue(struct ccb_accept_tio *);92static int work_inot(struct ccb_immediate_notify *);93static struct ccb_scsiio *94get_ctio(void);95/* static void free_ccb(union ccb *); */96static cam_status get_sim_flags(u_int16_t *);97static void rel_simq(void);98static void abort_all_pending(void);99static void usage(void);100101int102main(int argc, char *argv[])103{104int ch;105char *file_name;106u_int16_t req_flags, sim_flags;107off_t user_size;108109/* Initialize */110debug = 0;111req_flags = sim_flags = 0;112user_size = 0;113targ_fd = file_fd = kq_fd = -1;114num_ctios = 0;115sector_size = SECTOR_SIZE;116buf_size = DFLTPHYS;117118/* Prepare resource pools */119TAILQ_INIT(&pending_queue);120TAILQ_INIT(&work_queue);121122while ((ch = getopt(argc, argv, "AdSTYb:c:s:W:")) != -1) {123switch(ch) {124case 'A':125req_flags |= SID_Addr16;126break;127case 'd':128debug = 1;129break;130case 'S':131req_flags |= SID_Sync;132break;133case 'T':134req_flags |= SID_CmdQue;135break;136case 'b':137buf_size = atoi(optarg);138if (buf_size < 256 || buf_size > MAX_XFER)139errx(1, "Unreasonable buf size: %s", optarg);140break;141case 'c':142sector_size = atoi(optarg);143if (sector_size < 512 || sector_size > MAX_SECTOR)144errx(1, "Unreasonable sector size: %s", optarg);145break;146case 's':147{148int last, shift = 0;149150last = strlen(optarg) - 1;151if (last > 0) {152switch (tolower(optarg[last])) {153case 'e':154shift += 10;155/* FALLTHROUGH */156case 'p':157shift += 10;158/* FALLTHROUGH */159case 't':160shift += 10;161/* FALLTHROUGH */162case 'g':163shift += 10;164/* FALLTHROUGH */165case 'm':166shift += 10;167/* FALLTHROUGH */168case 'k':169shift += 10;170optarg[last] = 0;171break;172}173}174user_size = strtoll(optarg, (char **)NULL, /*base*/10);175user_size <<= shift;176if (user_size < 0)177errx(1, "Unreasonable volume size: %s", optarg);178break;179}180case 'W':181req_flags &= ~(SID_WBus16 | SID_WBus32);182switch (atoi(optarg)) {183case 8:184/* Leave req_flags zeroed */185break;186case 16:187req_flags |= SID_WBus16;188break;189case 32:190req_flags |= SID_WBus32;191break;192default:193warnx("Width %s not supported", optarg);194usage();195/* NOTREACHED */196}197break;198case 'Y':199notaio = 1;200break;201default:202usage();203/* NOTREACHED */204}205}206argc -= optind;207argv += optind;208209if (argc != 2)210usage();211212sscanf(argv[0], "%u:%u:%ju", &ioc_enlun.path_id, &ioc_enlun.target_id,213&ioc_enlun.lun_id);214file_name = argv[1];215216if (ioc_enlun.path_id == CAM_BUS_WILDCARD ||217ioc_enlun.target_id == CAM_TARGET_WILDCARD ||218ioc_enlun.lun_id == CAM_LUN_WILDCARD) {219warnx("Incomplete target path specified");220usage();221/* NOTREACHED */222}223/* We don't support any vendor-specific commands */224ioc_enlun.grp6_len = 0;225ioc_enlun.grp7_len = 0;226227/* Open backing store for IO */228file_fd = open(file_name, O_RDWR);229if (file_fd < 0)230errx(EX_NOINPUT, "open backing store file");231232/* Check backing store size or use the size user gave us */233if (user_size == 0) {234struct stat st;235236if (fstat(file_fd, &st) < 0)237err(1, "fstat file");238#if __FreeBSD_version >= 500000239if ((st.st_mode & S_IFCHR) != 0) {240/* raw device */241off_t mediasize;242if (ioctl(file_fd, DIOCGMEDIASIZE, &mediasize) < 0)243err(1, "DIOCGMEDIASIZE");244245/* XXX get sector size by ioctl()?? */246volume_size = mediasize / sector_size;247} else248#endif249volume_size = st.st_size / sector_size;250} else {251volume_size = user_size / sector_size;252}253if (debug)254warnx("volume_size: %d bytes x " OFF_FMT " sectors",255sector_size, volume_size);256257if (volume_size <= 0)258errx(1, "volume must be larger than %d", sector_size);259260if (notaio == 0) {261struct aiocb aio, *aiop;262void *aio_buf;263264/* See if we have we have working AIO support */265memset(&aio, 0, sizeof(aio));266aio_buf = malloc(sector_size);267aio.aio_buf = aio_buf;268if (aio.aio_buf == NULL)269err(1, "malloc");270aio.aio_fildes = file_fd;271aio.aio_offset = 0;272aio.aio_nbytes = sector_size;273signal(SIGSYS, SIG_IGN);274if (aio_read(&aio) != 0) {275printf("AIO support is not available- switchin to"276" single-threaded mode.\n");277notaio = 1;278} else {279if (aio_waitcomplete(&aiop, NULL) != sector_size)280err(1, "aio_waitcomplete");281assert(aiop == &aio);282signal(SIGSYS, SIG_DFL);283}284free(aio_buf);285if (debug && notaio == 0)286warnx("aio support tested ok");287}288289targ_fd = open("/dev/targ", O_RDWR);290if (targ_fd < 0)291err(1, "/dev/targ");292else293warnx("opened /dev/targ");294295/* The first three are handled by kevent() later */296signal(SIGHUP, SIG_IGN);297signal(SIGINT, SIG_IGN);298signal(SIGTERM, SIG_IGN);299signal(SIGPROF, SIG_IGN);300signal(SIGALRM, SIG_IGN);301signal(SIGSTOP, SIG_IGN);302signal(SIGTSTP, SIG_IGN);303304/* Register a cleanup handler to run when exiting */305atexit(cleanup);306307/* Enable listening on the specified LUN */308if (ioctl(targ_fd, TARGIOCENABLE, &ioc_enlun) != 0)309err(1, "TARGIOCENABLE");310311/* Enable debugging if requested */312if (debug) {313if (ioctl(targ_fd, TARGIOCDEBUG, &debug) != 0)314warnx("TARGIOCDEBUG");315}316317/* Set up inquiry data according to what SIM supports */318if (get_sim_flags(&sim_flags) != CAM_REQ_CMP)319errx(1, "get_sim_flags");320321if (tcmd_init(req_flags, sim_flags) != 0)322errx(1, "Initializing tcmd subsystem failed");323324/* Queue ATIOs and INOTs on descriptor */325if (init_ccbs() != 0)326errx(1, "init_ccbs failed");327328if (debug)329warnx("main loop beginning");330331request_loop();332333exit(0);334}335336static void337cleanup(void)338{339struct ccb_hdr *ccb_h;340341if (debug) {342warnx("cleanup called");343debug = 0;344ioctl(targ_fd, TARGIOCDEBUG, &debug);345}346ioctl(targ_fd, TARGIOCDISABLE, NULL);347close(targ_fd);348349while ((ccb_h = TAILQ_FIRST(&pending_queue)) != NULL) {350TAILQ_REMOVE(&pending_queue, ccb_h, periph_links.tqe);351free_ccb((union ccb *)ccb_h);352}353while ((ccb_h = TAILQ_FIRST(&work_queue)) != NULL) {354TAILQ_REMOVE(&work_queue, ccb_h, periph_links.tqe);355free_ccb((union ccb *)ccb_h);356}357358if (kq_fd != -1)359close(kq_fd);360}361362/* Allocate ATIOs/INOTs and queue on HBA */363static int364init_ccbs(void)365{366int i;367368for (i = 0; i < MAX_INITIATORS; i++) {369struct ccb_accept_tio *atio;370struct atio_descr *a_descr;371struct ccb_immediate_notify *inot;372373atio = (struct ccb_accept_tio *)malloc(sizeof(*atio));374if (atio == NULL) {375warn("malloc ATIO");376return (-1);377}378a_descr = (struct atio_descr *)malloc(sizeof(*a_descr));379if (a_descr == NULL) {380free(atio);381warn("malloc atio_descr");382return (-1);383}384atio->ccb_h.func_code = XPT_ACCEPT_TARGET_IO;385atio->ccb_h.targ_descr = a_descr;386send_ccb((union ccb *)atio, /*priority*/1);387388inot = (struct ccb_immediate_notify *)malloc(sizeof(*inot));389if (inot == NULL) {390warn("malloc INOT");391return (-1);392}393inot->ccb_h.func_code = XPT_IMMEDIATE_NOTIFY;394send_ccb((union ccb *)inot, /*priority*/1);395}396397return (0);398}399400static void401request_loop(void)402{403struct kevent events[MAX_EVENTS];404struct timespec ts, *tptr;405int quit;406407/* Register kqueue for event notification */408if ((kq_fd = kqueue()) < 0)409err(1, "init kqueue");410411/* Set up some default events */412EV_SET(&events[0], SIGHUP, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0);413EV_SET(&events[1], SIGINT, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0);414EV_SET(&events[2], SIGTERM, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0);415EV_SET(&events[3], targ_fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0);416if (kevent(kq_fd, events, 4, NULL, 0, NULL) < 0)417err(1, "kevent signal registration");418419ts.tv_sec = 0;420ts.tv_nsec = 0;421tptr = NULL;422quit = 0;423424/* Loop until user signal */425while (quit == 0) {426int retval, i, oo;427struct ccb_hdr *ccb_h;428429/* Check for the next signal, read ready, or AIO completion */430retval = kevent(kq_fd, NULL, 0, events, MAX_EVENTS, tptr);431if (retval < 0) {432if (errno == EINTR) {433if (debug)434warnx("EINTR, looping");435continue;436}437else {438err(1, "kevent failed");439}440} else if (retval > MAX_EVENTS) {441errx(1, "kevent returned more events than allocated?");442}443444/* Process all received events. */445for (oo = i = 0; i < retval; i++) {446if ((events[i].flags & EV_ERROR) != 0)447errx(1, "kevent registration failed");448449switch (events[i].filter) {450case EVFILT_READ:451if (debug)452warnx("read ready");453handle_read();454break;455case EVFILT_AIO:456{457struct ccb_scsiio *ctio;458struct ctio_descr *c_descr;459if (debug)460warnx("aio ready");461462ctio = (struct ccb_scsiio *)events[i].udata;463c_descr = (struct ctio_descr *)464ctio->ccb_h.targ_descr;465c_descr->event = AIO_DONE;466/* Queue on the appropriate ATIO */467queue_io(ctio);468/* Process any queued completions. */469oo += run_queue(c_descr->atio);470break;471}472case EVFILT_SIGNAL:473if (debug)474warnx("signal ready, setting quit");475quit = 1;476break;477default:478warnx("unknown event %d", events[i].filter);479break;480}481482if (debug)483warnx("event %d done", events[i].filter);484}485486if (oo) {487tptr = &ts;488continue;489}490491/* Grab the first CCB and perform one work unit. */492if ((ccb_h = TAILQ_FIRST(&work_queue)) != NULL) {493union ccb *ccb;494495ccb = (union ccb *)ccb_h;496switch (ccb_h->func_code) {497case XPT_ACCEPT_TARGET_IO:498/* Start one more transfer. */499retval = work_atio(&ccb->atio);500break;501case XPT_IMMEDIATE_NOTIFY:502retval = work_inot(&ccb->cin1);503break;504default:505warnx("Unhandled ccb type %#x on workq",506ccb_h->func_code);507abort();508/* NOTREACHED */509}510511/* Assume work function handled the exception */512if ((ccb_h->status & CAM_DEV_QFRZN) != 0) {513if (debug) {514warnx("Queue frozen receiving CCB, "515"releasing");516}517rel_simq();518}519520/* No more work needed for this command. */521if (retval == 0) {522TAILQ_REMOVE(&work_queue, ccb_h,523periph_links.tqe);524}525}526527/*528* Poll for new events (i.e. completions) while we529* are processing CCBs on the work_queue. Once it's530* empty, use an infinite wait.531*/532if (!TAILQ_EMPTY(&work_queue))533tptr = &ts;534else535tptr = NULL;536}537}538539/* CCBs are ready from the kernel */540static void541handle_read(void)542{543union ccb *ccb_array[MAX_INITIATORS], *ccb;544int ccb_count, i;545546ccb_count = read(targ_fd, ccb_array, sizeof(ccb_array));547if (ccb_count <= 0) {548warn("read ccb ptrs");549return;550}551ccb_count /= sizeof(union ccb *);552if (ccb_count < 1) {553warnx("truncated read ccb ptr?");554return;555}556557for (i = 0; i < ccb_count; i++) {558ccb = ccb_array[i];559TAILQ_REMOVE(&pending_queue, &ccb->ccb_h, periph_links.tqe);560561switch (ccb->ccb_h.func_code) {562case XPT_ACCEPT_TARGET_IO:563{564struct ccb_accept_tio *atio;565struct atio_descr *a_descr;566567/* Initialize ATIO descr for this transaction */568atio = &ccb->atio;569a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;570bzero(a_descr, sizeof(*a_descr));571TAILQ_INIT(&a_descr->cmplt_io);572a_descr->flags = atio->ccb_h.flags &573(CAM_DIS_DISCONNECT | CAM_TAG_ACTION_VALID);574/* XXX add a_descr->priority */575if ((atio->ccb_h.flags & CAM_CDB_POINTER) == 0)576a_descr->cdb = atio->cdb_io.cdb_bytes;577else578a_descr->cdb = atio->cdb_io.cdb_ptr;579580/* ATIOs are processed in FIFO order */581TAILQ_INSERT_TAIL(&work_queue, &ccb->ccb_h,582periph_links.tqe);583break;584}585case XPT_CONT_TARGET_IO:586{587struct ccb_scsiio *ctio;588struct ctio_descr *c_descr;589590ctio = &ccb->ctio;591c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;592c_descr->event = CTIO_DONE;593/* Queue on the appropriate ATIO */594queue_io(ctio);595/* Process any queued completions. */596run_queue(c_descr->atio);597break;598}599case XPT_IMMEDIATE_NOTIFY:600/* INOTs are handled with priority */601TAILQ_INSERT_HEAD(&work_queue, &ccb->ccb_h,602periph_links.tqe);603break;604default:605warnx("Unhandled ccb type %#x in handle_read",606ccb->ccb_h.func_code);607break;608}609}610}611612/* Process an ATIO CCB from the kernel */613int614work_atio(struct ccb_accept_tio *atio)615{616struct ccb_scsiio *ctio;617struct atio_descr *a_descr;618struct ctio_descr *c_descr;619cam_status status;620int ret;621622if (debug)623warnx("Working on ATIO %p", atio);624625a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;626627/* Get a CTIO and initialize it according to our known parameters */628ctio = get_ctio();629if (ctio == NULL) {630return (1);631}632ret = 0;633ctio->ccb_h.flags = a_descr->flags;634ctio->tag_id = atio->tag_id;635ctio->init_id = atio->init_id;636/* XXX priority needs to be added to a_descr */637c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;638c_descr->atio = atio;639if ((a_descr->flags & CAM_DIR_IN) != 0)640c_descr->offset = a_descr->base_off + a_descr->targ_req;641else if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT)642c_descr->offset = a_descr->base_off + a_descr->init_req;643else644c_descr->offset = a_descr->base_off;645646/*647* Return a check condition if there was an error while648* receiving this ATIO.649*/650if (atio->sense_len != 0) {651struct scsi_sense_data_fixed *sense;652653if (debug) {654warnx("ATIO with %u bytes sense received",655atio->sense_len);656}657sense = (struct scsi_sense_data_fixed *)&atio->sense_data;658tcmd_sense(ctio->init_id, ctio, sense->flags,659sense->add_sense_code, sense->add_sense_code_qual);660send_ccb((union ccb *)ctio, /*priority*/1);661return (0);662}663664status = atio->ccb_h.status & CAM_STATUS_MASK;665switch (status) {666case CAM_CDB_RECVD:667ret = tcmd_handle(atio, ctio, ATIO_WORK);668break;669case CAM_REQ_ABORTED:670warn("ATIO %p aborted", a_descr);671/* Requeue on HBA */672TAILQ_REMOVE(&work_queue, &atio->ccb_h, periph_links.tqe);673send_ccb((union ccb *)atio, /*priority*/1);674ret = 1;675break;676default:677warnx("ATIO completed with unhandled status %#x", status);678abort();679/* NOTREACHED */680break;681}682683return (ret);684}685686static void687queue_io(struct ccb_scsiio *ctio)688{689struct ccb_hdr *ccb_h;690struct io_queue *ioq;691struct ctio_descr *c_descr;692693c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;694if (c_descr->atio == NULL) {695errx(1, "CTIO %p has NULL ATIO", ctio);696}697ioq = &((struct atio_descr *)c_descr->atio->ccb_h.targ_descr)->cmplt_io;698699if (TAILQ_EMPTY(ioq)) {700TAILQ_INSERT_HEAD(ioq, &ctio->ccb_h, periph_links.tqe);701return;702}703704TAILQ_FOREACH_REVERSE(ccb_h, ioq, io_queue, periph_links.tqe) {705struct ctio_descr *curr_descr =706(struct ctio_descr *)ccb_h->targ_descr;707if (curr_descr->offset <= c_descr->offset) {708break;709}710}711712if (ccb_h) {713TAILQ_INSERT_AFTER(ioq, ccb_h, &ctio->ccb_h, periph_links.tqe);714} else {715TAILQ_INSERT_HEAD(ioq, &ctio->ccb_h, periph_links.tqe);716}717}718719/*720* Go through all completed AIO/CTIOs for a given ATIO and advance data721* counts, start continuation IO, etc.722*/723static int724run_queue(struct ccb_accept_tio *atio)725{726struct atio_descr *a_descr;727struct ccb_hdr *ccb_h;728int sent_status, event;729730if (atio == NULL)731return (0);732733a_descr = (struct atio_descr *)atio->ccb_h.targ_descr;734735while ((ccb_h = TAILQ_FIRST(&a_descr->cmplt_io)) != NULL) {736struct ccb_scsiio *ctio;737struct ctio_descr *c_descr;738739ctio = (struct ccb_scsiio *)ccb_h;740c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr;741742if (ctio->ccb_h.status == CAM_REQ_ABORTED) {743TAILQ_REMOVE(&a_descr->cmplt_io, ccb_h,744periph_links.tqe);745free_ccb((union ccb *)ctio);746send_ccb((union ccb *)atio, /*priority*/1);747continue;748}749750/* If completed item is in range, call handler */751if ((c_descr->event == AIO_DONE &&752c_descr->offset == a_descr->base_off + a_descr->targ_ack)753|| (c_descr->event == CTIO_DONE &&754c_descr->offset == a_descr->base_off + a_descr->init_ack)) {755sent_status = (ccb_h->flags & CAM_SEND_STATUS) != 0;756event = c_descr->event;757758TAILQ_REMOVE(&a_descr->cmplt_io, ccb_h,759periph_links.tqe);760tcmd_handle(atio, ctio, c_descr->event);761762/* If entire transfer complete, send back ATIO */763if (sent_status != 0 && event == CTIO_DONE)764send_ccb((union ccb *)atio, /*priority*/1);765} else {766/* Gap in offsets so wait until later callback */767if (/* debug */ 1)768warnx("IO %p:%p out of order %s", ccb_h,769a_descr, c_descr->event == AIO_DONE?770"aio" : "ctio");771return (1);772}773}774return (0);775}776777static int778work_inot(struct ccb_immediate_notify *inot)779{780cam_status status;781782if (debug)783warnx("Working on INOT %p", inot);784785status = inot->ccb_h.status;786status &= CAM_STATUS_MASK;787788switch (status) {789case CAM_SCSI_BUS_RESET:790tcmd_ua(CAM_TARGET_WILDCARD, UA_BUS_RESET);791abort_all_pending();792break;793case CAM_BDR_SENT:794tcmd_ua(CAM_TARGET_WILDCARD, UA_BDR);795abort_all_pending();796break;797case CAM_MESSAGE_RECV:798switch (inot->arg) {799case MSG_TASK_COMPLETE:800case MSG_INITIATOR_DET_ERR:801case MSG_ABORT_TASK_SET:802case MSG_MESSAGE_REJECT:803case MSG_NOOP:804case MSG_PARITY_ERROR:805case MSG_TARGET_RESET:806case MSG_ABORT_TASK:807case MSG_CLEAR_TASK_SET:808default:809warnx("INOT message %#x", inot->arg);810break;811}812break;813case CAM_REQ_ABORTED:814warnx("INOT %p aborted", inot);815break;816default:817warnx("Unhandled INOT status %#x", status);818break;819}820821/* Requeue on SIM */822TAILQ_REMOVE(&work_queue, &inot->ccb_h, periph_links.tqe);823send_ccb((union ccb *)inot, /*priority*/1);824825return (1);826}827828void829send_ccb(union ccb *ccb, int priority)830{831if (debug)832warnx("sending ccb (%#x)", ccb->ccb_h.func_code);833ccb->ccb_h.pinfo.priority = priority;834if (XPT_FC_IS_QUEUED(ccb)) {835TAILQ_INSERT_TAIL(&pending_queue, &ccb->ccb_h,836periph_links.tqe);837}838if (write(targ_fd, &ccb, sizeof(ccb)) != sizeof(ccb)) {839warn("write ccb");840ccb->ccb_h.status = CAM_PROVIDE_FAIL;841}842}843844/* Return a CTIO/descr/buf combo from the freelist or malloc one */845static struct ccb_scsiio *846get_ctio(void)847{848struct ccb_scsiio *ctio;849struct ctio_descr *c_descr;850struct sigevent *se;851852if (num_ctios == MAX_CTIOS) {853warnx("at CTIO max");854return (NULL);855}856857ctio = (struct ccb_scsiio *)malloc(sizeof(*ctio));858if (ctio == NULL) {859warn("malloc CTIO");860return (NULL);861}862c_descr = (struct ctio_descr *)malloc(sizeof(*c_descr));863if (c_descr == NULL) {864free(ctio);865warn("malloc ctio_descr");866return (NULL);867}868c_descr->buf = malloc(buf_size);869if (c_descr->buf == NULL) {870free(c_descr);871free(ctio);872warn("malloc backing store");873return (NULL);874}875num_ctios++;876877/* Initialize CTIO, CTIO descr, and AIO */878ctio->ccb_h.func_code = XPT_CONT_TARGET_IO;879ctio->ccb_h.retry_count = 2;880ctio->ccb_h.timeout = CAM_TIME_INFINITY;881ctio->data_ptr = c_descr->buf;882ctio->ccb_h.targ_descr = c_descr;883c_descr->aiocb.aio_buf = c_descr->buf;884c_descr->aiocb.aio_fildes = file_fd;885se = &c_descr->aiocb.aio_sigevent;886se->sigev_notify = SIGEV_KEVENT;887se->sigev_notify_kqueue = kq_fd;888se->sigev_value.sival_ptr = ctio;889890return (ctio);891}892893void894free_ccb(union ccb *ccb)895{896switch (ccb->ccb_h.func_code) {897case XPT_CONT_TARGET_IO:898{899struct ctio_descr *c_descr;900901c_descr = (struct ctio_descr *)ccb->ccb_h.targ_descr;902free(c_descr->buf);903num_ctios--;904/* FALLTHROUGH */905}906case XPT_ACCEPT_TARGET_IO:907free(ccb->ccb_h.targ_descr);908/* FALLTHROUGH */909case XPT_IMMEDIATE_NOTIFY:910default:911free(ccb);912break;913}914}915916static cam_status917get_sim_flags(u_int16_t *flags)918{919struct ccb_pathinq cpi;920cam_status status;921922/* Find SIM capabilities */923bzero(&cpi, sizeof(cpi));924cpi.ccb_h.func_code = XPT_PATH_INQ;925send_ccb((union ccb *)&cpi, /*priority*/1);926status = cpi.ccb_h.status & CAM_STATUS_MASK;927if (status != CAM_REQ_CMP) {928fprintf(stderr, "CPI failed, status %#x\n", status);929return (status);930}931932/* Can only enable on controllers that support target mode */933if ((cpi.target_sprt & PIT_PROCESSOR) == 0) {934fprintf(stderr, "HBA does not support target mode\n");935status = CAM_PATH_INVALID;936return (status);937}938939*flags = cpi.hba_inquiry;940return (status);941}942943static void944rel_simq(void)945{946struct ccb_relsim crs;947948bzero(&crs, sizeof(crs));949crs.ccb_h.func_code = XPT_REL_SIMQ;950crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY;951crs.openings = 0;952crs.release_timeout = 0;953crs.qfrozen_cnt = 0;954send_ccb((union ccb *)&crs, /*priority*/0);955}956957/* Cancel all pending CCBs. */958static void959abort_all_pending(void)960{961struct ccb_abort cab;962struct ccb_hdr *ccb_h;963964if (debug)965warnx("abort_all_pending");966967bzero(&cab, sizeof(cab));968cab.ccb_h.func_code = XPT_ABORT;969TAILQ_FOREACH(ccb_h, &pending_queue, periph_links.tqe) {970if (debug)971warnx("Aborting pending CCB %p\n", ccb_h);972cab.abort_ccb = (union ccb *)ccb_h;973send_ccb((union ccb *)&cab, /*priority*/1);974if (cab.ccb_h.status != CAM_REQ_CMP) {975warnx("Unable to abort CCB, status %#x\n",976cab.ccb_h.status);977}978}979}980981static void982usage(void)983{984fprintf(stderr,985"Usage: scsi_target [-AdSTY] [-b bufsize] [-c sectorsize]\n"986"\t\t[-r numbufs] [-s volsize] [-W 8,16,32]\n"987"\t\tbus:target:lun filename\n");988exit(1);989}990991992