scsi_target.c revision 162704
139215Sgibbs/* 2107178Snjl * SCSI Disk Emulator 339215Sgibbs * 4107178Snjl * Copyright (c) 2002 Nate Lawson. 539215Sgibbs * All rights reserved. 639215Sgibbs * 739215Sgibbs * Redistribution and use in source and binary forms, with or without 839215Sgibbs * modification, are permitted provided that the following conditions 939215Sgibbs * are met: 1039215Sgibbs * 1. Redistributions of source code must retain the above copyright 1139215Sgibbs * notice, this list of conditions, and the following disclaimer, 1239215Sgibbs * without modification, immediately at the beginning of the file. 1339215Sgibbs * 2. The name of the author may not be used to endorse or promote products 1439215Sgibbs * derived from this software without specific prior written permission. 1539215Sgibbs * 1639215Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1739215Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1839215Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1939215Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 2039215Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2139215Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2239215Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2339215Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2439215Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2539215Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2639215Sgibbs * SUCH DAMAGE. 2739215Sgibbs * 2850476Speter * $FreeBSD: head/share/examples/scsi_target/scsi_target.c 162704 2006-09-27 15:38:13Z mjacob $ 2939215Sgibbs */ 3039215Sgibbs 3139215Sgibbs#include <sys/types.h> 32121184Ssimokawa#include <ctype.h> 3344498Sgibbs#include <errno.h> 34107178Snjl#include <err.h> 3539215Sgibbs#include <fcntl.h> 3644498Sgibbs#include <signal.h> 3739215Sgibbs#include <stddef.h> 3839215Sgibbs#include <stdio.h> 3939215Sgibbs#include <stdlib.h> 40107178Snjl#include <string.h> 4139215Sgibbs#include <sysexits.h> 4239215Sgibbs#include <unistd.h> 43107178Snjl#include <aio.h> 44109161Snjl#include <assert.h> 45107178Snjl#include <sys/stat.h> 46107178Snjl#include <sys/queue.h> 47107178Snjl#include <sys/event.h> 48107178Snjl#include <sys/param.h> 49120428Ssimokawa#include <sys/disk.h> 50107178Snjl#include <cam/cam_queue.h> 5139215Sgibbs#include <cam/scsi/scsi_all.h> 52107178Snjl#include <cam/scsi/scsi_targetio.h> 5339215Sgibbs#include <cam/scsi/scsi_message.h> 54107178Snjl#include "scsi_target.h" 5539215Sgibbs 56107178Snjl/* Maximum amount to transfer per CTIO */ 57107178Snjl#define MAX_XFER MAXPHYS 58107178Snjl/* Maximum number of allocated CTIOs */ 59162704Smjacob#define MAX_CTIOS 64 60107178Snjl/* Maximum sector size for emulated volume */ 61107178Snjl#define MAX_SECTOR 32768 62107178Snjl 63107178Snjl/* Global variables */ 64107178Snjlint debug; 65162704Smjacobint notaio = 0; 66121184Ssimokawaoff_t volume_size; 67121184Ssimokawau_int sector_size; 68107178Snjlsize_t buf_size; 69107178Snjl 70107178Snjl/* Local variables */ 71107178Snjlstatic int targ_fd; 72107178Snjlstatic int kq_fd; 73107178Snjlstatic int file_fd; 74107178Snjlstatic int num_ctios; 75107178Snjlstatic struct ccb_queue pending_queue; 76107178Snjlstatic struct ccb_queue work_queue; 77107178Snjlstatic struct ioc_enable_lun ioc_enlun = { 7844498Sgibbs CAM_BUS_WILDCARD, 7944498Sgibbs CAM_TARGET_WILDCARD, 8044498Sgibbs CAM_LUN_WILDCARD 8144498Sgibbs}; 8239215Sgibbs 83107178Snjl/* Local functions */ 84107178Snjlstatic void cleanup(void); 85107178Snjlstatic int init_ccbs(void); 86107178Snjlstatic void request_loop(void); 87107178Snjlstatic void handle_read(void); 88107178Snjl/* static int work_atio(struct ccb_accept_tio *); */ 89107178Snjlstatic void queue_io(struct ccb_scsiio *); 90162704Smjacobstatic int run_queue(struct ccb_accept_tio *); 91107178Snjlstatic int work_inot(struct ccb_immed_notify *); 92107178Snjlstatic struct ccb_scsiio * 93107178Snjl get_ctio(void); 94107178Snjl/* static void free_ccb(union ccb *); */ 95107178Snjlstatic cam_status get_sim_flags(u_int16_t *); 96107178Snjlstatic void rel_simq(void); 97107178Snjlstatic void abort_all_pending(void); 98107178Snjlstatic void usage(void); 9939215Sgibbs 10039215Sgibbsint 10139215Sgibbsmain(int argc, char *argv[]) 10239215Sgibbs{ 103107178Snjl int ch, unit; 104107178Snjl char *file_name, targname[16]; 105107178Snjl u_int16_t req_flags, sim_flags; 106107178Snjl off_t user_size; 10739215Sgibbs 108107178Snjl /* Initialize */ 109107178Snjl debug = 0; 110107178Snjl req_flags = sim_flags = 0; 111107178Snjl user_size = 0; 112107178Snjl targ_fd = file_fd = kq_fd = -1; 113107178Snjl num_ctios = 0; 114107178Snjl sector_size = SECTOR_SIZE; 115107178Snjl buf_size = DFLTPHYS; 116107178Snjl 117107178Snjl /* Prepare resource pools */ 118107178Snjl TAILQ_INIT(&pending_queue); 119107178Snjl TAILQ_INIT(&work_queue); 120107178Snjl 121162704Smjacob while ((ch = getopt(argc, argv, "AdSTYb:c:s:W:")) != -1) { 12239215Sgibbs switch(ch) { 123107178Snjl case 'A': 124107178Snjl req_flags |= SID_Addr16; 12539215Sgibbs break; 126107178Snjl case 'd': 127107178Snjl debug = 1; 12839215Sgibbs break; 129107178Snjl case 'S': 130107178Snjl req_flags |= SID_Sync; 13144498Sgibbs break; 132107178Snjl case 'T': 133107178Snjl req_flags |= SID_CmdQue; 13444498Sgibbs break; 135107178Snjl case 'b': 136107178Snjl buf_size = atoi(optarg); 137107178Snjl if (buf_size < 256 || buf_size > MAX_XFER) 138107178Snjl errx(1, "Unreasonable buf size: %s", optarg); 13944498Sgibbs break; 140107178Snjl case 'c': 141107178Snjl sector_size = atoi(optarg); 142107178Snjl if (sector_size < 512 || sector_size > MAX_SECTOR) 143107178Snjl errx(1, "Unreasonable sector size: %s", optarg); 14463185Smjacob break; 145107178Snjl case 's': 146121184Ssimokawa { 147121184Ssimokawa int last, shift = 0; 148121184Ssimokawa 149121184Ssimokawa last = strlen(optarg) - 1; 150121184Ssimokawa if (last > 0) { 151121184Ssimokawa switch (tolower(optarg[last])) { 152121184Ssimokawa case 'e': 153121184Ssimokawa shift += 10; 154121184Ssimokawa /* FALLTHROUGH */ 155121184Ssimokawa case 'p': 156121184Ssimokawa shift += 10; 157121184Ssimokawa /* FALLTHROUGH */ 158121184Ssimokawa case 't': 159121184Ssimokawa shift += 10; 160121184Ssimokawa /* FALLTHROUGH */ 161121184Ssimokawa case 'g': 162121184Ssimokawa shift += 10; 163121184Ssimokawa /* FALLTHROUGH */ 164121184Ssimokawa case 'm': 165121184Ssimokawa shift += 10; 166121184Ssimokawa /* FALLTHROUGH */ 167121184Ssimokawa case 'k': 168121184Ssimokawa shift += 10; 169121184Ssimokawa optarg[last] = 0; 170121184Ssimokawa break; 171121184Ssimokawa } 172121184Ssimokawa } 173107178Snjl user_size = strtoll(optarg, (char **)NULL, /*base*/10); 174121184Ssimokawa user_size <<= shift; 175107178Snjl if (user_size < 0) 176107178Snjl errx(1, "Unreasonable volume size: %s", optarg); 177107178Snjl break; 178121184Ssimokawa } 179107178Snjl case 'W': 180107178Snjl req_flags &= ~(SID_WBus16 | SID_WBus32); 181107178Snjl switch (atoi(optarg)) { 182107178Snjl case 8: 183107178Snjl /* Leave req_flags zeroed */ 184107178Snjl break; 185107178Snjl case 16: 186107178Snjl req_flags |= SID_WBus16; 187107178Snjl break; 188107178Snjl case 32: 189107178Snjl req_flags |= SID_WBus32; 190107178Snjl break; 191107178Snjl default: 192107178Snjl warnx("Width %s not supported", optarg); 193107178Snjl usage(); 194107178Snjl /* NOTREACHED */ 195107178Snjl } 196107178Snjl break; 197162704Smjacob case 'Y': 198162704Smjacob notaio = 1; 199162704Smjacob break; 20039215Sgibbs default: 20139215Sgibbs usage(); 20239215Sgibbs /* NOTREACHED */ 20339215Sgibbs } 20439215Sgibbs } 20539215Sgibbs argc -= optind; 20639215Sgibbs argv += optind; 207107178Snjl 208107178Snjl if (argc != 2) 20939215Sgibbs usage(); 21039215Sgibbs 211107178Snjl sscanf(argv[0], "%u:%u:%u", &ioc_enlun.path_id, &ioc_enlun.target_id, 212107178Snjl &ioc_enlun.lun_id); 213107178Snjl file_name = argv[1]; 214107178Snjl 215107178Snjl if (ioc_enlun.path_id == CAM_BUS_WILDCARD || 216107178Snjl ioc_enlun.target_id == CAM_TARGET_WILDCARD || 217107178Snjl ioc_enlun.lun_id == CAM_LUN_WILDCARD) { 218107178Snjl warnx("Incomplete target path specified"); 21944498Sgibbs usage(); 22044498Sgibbs /* NOTREACHED */ 22144498Sgibbs } 222107178Snjl /* We don't support any vendor-specific commands */ 223107178Snjl ioc_enlun.grp6_len = 0; 224107178Snjl ioc_enlun.grp7_len = 0; 22544498Sgibbs 226107178Snjl /* Open backing store for IO */ 227107178Snjl file_fd = open(file_name, O_RDWR); 228107178Snjl if (file_fd < 0) 229107178Snjl err(1, "open backing store file"); 23044498Sgibbs 231107178Snjl /* Check backing store size or use the size user gave us */ 232107178Snjl if (user_size == 0) { 233107178Snjl struct stat st; 234107178Snjl 235107178Snjl if (fstat(file_fd, &st) < 0) 236107178Snjl err(1, "fstat file"); 237120428Ssimokawa#if __FreeBSD_version >= 500000 238120428Ssimokawa if ((st.st_mode & S_IFCHR) != 0) { 239120428Ssimokawa /* raw device */ 240120428Ssimokawa off_t mediasize; 241120428Ssimokawa if (ioctl(file_fd, DIOCGMEDIASIZE, &mediasize) < 0) 242120428Ssimokawa err(1, "DIOCGMEDIASIZE"); 243120428Ssimokawa 244120428Ssimokawa /* XXX get sector size by ioctl()?? */ 245120428Ssimokawa volume_size = mediasize / sector_size; 246120428Ssimokawa } else 247120428Ssimokawa#endif 248120428Ssimokawa volume_size = st.st_size / sector_size; 249107178Snjl } else { 250107178Snjl volume_size = user_size / sector_size; 25144498Sgibbs } 252121184Ssimokawa if (debug) 253162704Smjacob warnx("volume_size: %d bytes x " OFF_FMT " sectors", 254121184Ssimokawa sector_size, volume_size); 255121184Ssimokawa 256107178Snjl if (volume_size <= 0) 257107178Snjl errx(1, "volume must be larger than %d", sector_size); 25844498Sgibbs 259162704Smjacob if (notaio == 0) { 260109161Snjl struct aiocb aio, *aiop; 261109161Snjl 262162704Smjacob /* See if we have we have working AIO support */ 263109161Snjl memset(&aio, 0, sizeof(aio)); 264109161Snjl aio.aio_buf = malloc(sector_size); 265109161Snjl if (aio.aio_buf == NULL) 266109161Snjl err(1, "malloc"); 267109161Snjl aio.aio_fildes = file_fd; 268109161Snjl aio.aio_offset = 0; 269109161Snjl aio.aio_nbytes = sector_size; 270109161Snjl signal(SIGSYS, SIG_IGN); 271109161Snjl if (aio_read(&aio) != 0) { 272162704Smjacob printf("AIO support is not available- switchin to" 273162704Smjacob " single-threaded mode.\n"); 274162704Smjacob notaio = 1; 275162704Smjacob } else { 276162704Smjacob if (aio_waitcomplete(&aiop, NULL) != sector_size) 277162704Smjacob err(1, "aio_waitcomplete"); 278162704Smjacob assert(aiop == &aio); 279162704Smjacob signal(SIGSYS, SIG_DFL); 280109161Snjl } 281109161Snjl free((void *)aio.aio_buf); 282162704Smjacob if (debug && notaio == 0) 283109161Snjl warnx("aio support tested ok"); 284109161Snjl } 285109161Snjl 286107178Snjl /* Go through all the control devices and find one that isn't busy. */ 287107178Snjl unit = 0; 288107178Snjl do { 289107178Snjl snprintf(targname, sizeof(targname), "/dev/targ%d", unit++); 290107178Snjl targ_fd = open(targname, O_RDWR); 291107178Snjl } while (targ_fd < 0 && errno == EBUSY); 29244498Sgibbs 293107178Snjl if (targ_fd < 0) 294107178Snjl err(1, "Tried to open %d devices, none available", unit); 29563185Smjacob 296107178Snjl /* The first three are handled by kevent() later */ 297107178Snjl signal(SIGHUP, SIG_IGN); 298107178Snjl signal(SIGINT, SIG_IGN); 299107178Snjl signal(SIGTERM, SIG_IGN); 300107178Snjl signal(SIGPROF, SIG_IGN); 301107178Snjl signal(SIGALRM, SIG_IGN); 302107178Snjl signal(SIGSTOP, SIG_IGN); 303107178Snjl signal(SIGTSTP, SIG_IGN); 30439215Sgibbs 305107178Snjl /* Register a cleanup handler to run when exiting */ 306107178Snjl atexit(cleanup); 307107178Snjl 308107178Snjl /* Enable listening on the specified LUN */ 309107178Snjl if (ioctl(targ_fd, TARGIOCENABLE, &ioc_enlun) != 0) 310107178Snjl err(1, "TARGIOCENABLE"); 311107178Snjl 312107178Snjl /* Enable debugging if requested */ 313107178Snjl if (debug) { 314107178Snjl if (ioctl(targ_fd, TARGIOCDEBUG, &debug) != 0) 315162704Smjacob warnx("TARGIOCDEBUG"); 31639215Sgibbs } 31739215Sgibbs 318107178Snjl /* Set up inquiry data according to what SIM supports */ 319107178Snjl if (get_sim_flags(&sim_flags) != CAM_REQ_CMP) 320107178Snjl errx(1, "get_sim_flags"); 321107178Snjl if (tcmd_init(req_flags, sim_flags) != 0) 322107178Snjl errx(1, "Initializing tcmd subsystem failed"); 32344498Sgibbs 324107178Snjl /* Queue ATIOs and INOTs on descriptor */ 325107178Snjl if (init_ccbs() != 0) 326107178Snjl errx(1, "init_ccbs failed"); 32749935Sgibbs 328107178Snjl if (debug) 329107178Snjl warnx("main loop beginning"); 330107178Snjl request_loop(); 33139215Sgibbs 332107178Snjl exit(0); 33349935Sgibbs} 33449935Sgibbs 33549935Sgibbsstatic void 33649935Sgibbscleanup() 33749935Sgibbs{ 338107178Snjl struct ccb_hdr *ccb_h; 339107178Snjl 34063290Smjacob if (debug) { 341107178Snjl warnx("cleanup called"); 34263290Smjacob debug = 0; 343107178Snjl ioctl(targ_fd, TARGIOCDEBUG, &debug); 34463290Smjacob } 345107178Snjl ioctl(targ_fd, TARGIOCDISABLE, NULL); 346107178Snjl close(targ_fd); 347107178Snjl 348107178Snjl while ((ccb_h = TAILQ_FIRST(&pending_queue)) != NULL) { 349107178Snjl TAILQ_REMOVE(&pending_queue, ccb_h, periph_links.tqe); 350107178Snjl free_ccb((union ccb *)ccb_h); 35144498Sgibbs } 352107178Snjl while ((ccb_h = TAILQ_FIRST(&work_queue)) != NULL) { 353107178Snjl TAILQ_REMOVE(&work_queue, ccb_h, periph_links.tqe); 354107178Snjl free_ccb((union ccb *)ccb_h); 355107178Snjl } 356107178Snjl 357107178Snjl if (kq_fd != -1) 358107178Snjl close(kq_fd); 35939215Sgibbs} 36039215Sgibbs 361107178Snjl/* Allocate ATIOs/INOTs and queue on HBA */ 362107178Snjlstatic int 363107178Snjlinit_ccbs() 364107178Snjl{ 365107178Snjl int i; 366107178Snjl 367107178Snjl for (i = 0; i < MAX_INITIATORS; i++) { 368107178Snjl struct ccb_accept_tio *atio; 369107178Snjl struct atio_descr *a_descr; 370107178Snjl struct ccb_immed_notify *inot; 371107178Snjl 372107178Snjl atio = (struct ccb_accept_tio *)malloc(sizeof(*atio)); 373107178Snjl if (atio == NULL) { 374107178Snjl warn("malloc ATIO"); 375107178Snjl return (-1); 376107178Snjl } 377107178Snjl a_descr = (struct atio_descr *)malloc(sizeof(*a_descr)); 378107178Snjl if (a_descr == NULL) { 379107178Snjl free(atio); 380107178Snjl warn("malloc atio_descr"); 381107178Snjl return (-1); 382107178Snjl } 383107178Snjl atio->ccb_h.func_code = XPT_ACCEPT_TARGET_IO; 384107178Snjl atio->ccb_h.targ_descr = a_descr; 385107178Snjl send_ccb((union ccb *)atio, /*priority*/1); 386107178Snjl 387107178Snjl inot = (struct ccb_immed_notify *)malloc(sizeof(*inot)); 388107178Snjl if (inot == NULL) { 389107178Snjl warn("malloc INOT"); 390107178Snjl return (-1); 391107178Snjl } 392107178Snjl inot->ccb_h.func_code = XPT_IMMED_NOTIFY; 393107178Snjl send_ccb((union ccb *)inot, /*priority*/1); 394107178Snjl } 395107178Snjl 396107178Snjl return (0); 397107178Snjl} 398107178Snjl 39939215Sgibbsstatic void 400107178Snjlrequest_loop() 40139215Sgibbs{ 402107178Snjl struct kevent events[MAX_EVENTS]; 403107178Snjl struct timespec ts, *tptr; 404107178Snjl int quit; 40539215Sgibbs 406107178Snjl /* Register kqueue for event notification */ 407107178Snjl if ((kq_fd = kqueue()) < 0) 408107178Snjl err(1, "init kqueue"); 40939215Sgibbs 410107178Snjl /* Set up some default events */ 411107178Snjl EV_SET(&events[0], SIGHUP, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); 412107178Snjl EV_SET(&events[1], SIGINT, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); 413107178Snjl EV_SET(&events[2], SIGTERM, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); 414107178Snjl EV_SET(&events[3], targ_fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); 415107178Snjl if (kevent(kq_fd, events, 4, NULL, 0, NULL) < 0) 416107178Snjl err(1, "kevent signal registration"); 417107178Snjl 418107178Snjl ts.tv_sec = 0; 419107178Snjl ts.tv_nsec = 0; 420107178Snjl tptr = NULL; 421107178Snjl quit = 0; 422107178Snjl 423107178Snjl /* Loop until user signal */ 42444498Sgibbs while (quit == 0) { 425162704Smjacob int retval, i, oo; 426107178Snjl struct ccb_hdr *ccb_h; 42739215Sgibbs 428107178Snjl /* Check for the next signal, read ready, or AIO completion */ 429107178Snjl retval = kevent(kq_fd, NULL, 0, events, MAX_EVENTS, tptr); 430107178Snjl if (retval < 0) { 431107178Snjl if (errno == EINTR) { 432107178Snjl if (debug) 433107178Snjl warnx("EINTR, looping"); 43444498Sgibbs continue; 435107178Snjl } 436107178Snjl else { 437107178Snjl err(1, "kevent failed"); 438107178Snjl } 439107178Snjl } else if (retval > MAX_EVENTS) { 440107178Snjl errx(1, "kevent returned more events than allocated?"); 44139215Sgibbs } 44239215Sgibbs 443107178Snjl /* Process all received events. */ 444162704Smjacob for (oo = i = 0; i < retval; i++) { 445107178Snjl if ((events[i].flags & EV_ERROR) != 0) 446107178Snjl errx(1, "kevent registration failed"); 44739215Sgibbs 448107178Snjl switch (events[i].filter) { 449107178Snjl case EVFILT_READ: 450107178Snjl if (debug) 451107178Snjl warnx("read ready"); 452107178Snjl handle_read(); 453107178Snjl break; 454107178Snjl case EVFILT_AIO: 455107178Snjl { 456107178Snjl struct ccb_scsiio *ctio; 457107178Snjl struct ctio_descr *c_descr; 458107178Snjl if (debug) 459107178Snjl warnx("aio ready"); 46039215Sgibbs 461107178Snjl ctio = (struct ccb_scsiio *)events[i].udata; 462107178Snjl c_descr = (struct ctio_descr *) 463107178Snjl ctio->ccb_h.targ_descr; 464107178Snjl c_descr->event = AIO_DONE; 465107178Snjl /* Queue on the appropriate ATIO */ 466107178Snjl queue_io(ctio); 467107178Snjl /* Process any queued completions. */ 468162704Smjacob oo += run_queue(c_descr->atio); 469107178Snjl break; 470107178Snjl } 471107178Snjl case EVFILT_SIGNAL: 472107178Snjl if (debug) 473107178Snjl warnx("signal ready, setting quit"); 474107178Snjl quit = 1; 475107178Snjl break; 476107178Snjl default: 477162704Smjacob warnx("unknown event %d", events[i].filter); 478107178Snjl break; 479107178Snjl } 480107178Snjl 481107178Snjl if (debug) 482162704Smjacob warnx("event %d done", events[i].filter); 48339215Sgibbs } 48439215Sgibbs 485162704Smjacob if (oo) { 486162704Smjacob tptr = &ts; 487162704Smjacob continue; 488162704Smjacob } 489162704Smjacob 490107178Snjl /* Grab the first CCB and perform one work unit. */ 491107178Snjl if ((ccb_h = TAILQ_FIRST(&work_queue)) != NULL) { 492107178Snjl union ccb *ccb; 49339215Sgibbs 494107178Snjl ccb = (union ccb *)ccb_h; 495107178Snjl switch (ccb_h->func_code) { 496107178Snjl case XPT_ACCEPT_TARGET_IO: 497107178Snjl /* Start one more transfer. */ 498107178Snjl retval = work_atio(&ccb->atio); 499107178Snjl break; 500107178Snjl case XPT_IMMED_NOTIFY: 501107178Snjl retval = work_inot(&ccb->cin); 502107178Snjl break; 503107178Snjl default: 504107178Snjl warnx("Unhandled ccb type %#x on workq", 505107178Snjl ccb_h->func_code); 506107178Snjl abort(); 507107178Snjl /* NOTREACHED */ 50839215Sgibbs } 50939215Sgibbs 510107178Snjl /* Assume work function handled the exception */ 511107178Snjl if ((ccb_h->status & CAM_DEV_QFRZN) != 0) { 512109345Snjl if (debug) { 513109345Snjl warnx("Queue frozen receiving CCB, " 514109345Snjl "releasing"); 515109345Snjl } 516107178Snjl rel_simq(); 51739215Sgibbs } 51839215Sgibbs 519107178Snjl /* No more work needed for this command. */ 520107178Snjl if (retval == 0) { 521107178Snjl TAILQ_REMOVE(&work_queue, ccb_h, 522107178Snjl periph_links.tqe); 52339215Sgibbs } 524107178Snjl } 52539215Sgibbs 526107178Snjl /* 527107178Snjl * Poll for new events (i.e. completions) while we 528107178Snjl * are processing CCBs on the work_queue. Once it's 529107178Snjl * empty, use an infinite wait. 530107178Snjl */ 531107178Snjl if (!TAILQ_EMPTY(&work_queue)) 532107178Snjl tptr = &ts; 533107178Snjl else 534107178Snjl tptr = NULL; 53539215Sgibbs } 53639215Sgibbs} 53739215Sgibbs 538107178Snjl/* CCBs are ready from the kernel */ 53939215Sgibbsstatic void 540107178Snjlhandle_read() 54139215Sgibbs{ 542107178Snjl union ccb *ccb_array[MAX_INITIATORS], *ccb; 543162704Smjacob int ccb_count, i, oo; 54439215Sgibbs 545107178Snjl ccb_count = read(targ_fd, ccb_array, sizeof(ccb_array)); 546107178Snjl if (ccb_count <= 0) { 547107178Snjl warn("read ccb ptrs"); 548107178Snjl return; 54939215Sgibbs } 550107178Snjl ccb_count /= sizeof(union ccb *); 551107178Snjl if (ccb_count < 1) { 552107178Snjl warnx("truncated read ccb ptr?"); 553107178Snjl return; 554107178Snjl } 55539215Sgibbs 556107178Snjl for (i = 0; i < ccb_count; i++) { 557107178Snjl ccb = ccb_array[i]; 558107178Snjl TAILQ_REMOVE(&pending_queue, &ccb->ccb_h, periph_links.tqe); 559107178Snjl 560107178Snjl switch (ccb->ccb_h.func_code) { 561107178Snjl case XPT_ACCEPT_TARGET_IO: 562107178Snjl { 563107178Snjl struct ccb_accept_tio *atio; 564107178Snjl struct atio_descr *a_descr; 565107178Snjl 566107178Snjl /* Initialize ATIO descr for this transaction */ 567107178Snjl atio = &ccb->atio; 568107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 569107178Snjl bzero(a_descr, sizeof(*a_descr)); 570107178Snjl TAILQ_INIT(&a_descr->cmplt_io); 571107178Snjl a_descr->flags = atio->ccb_h.flags & 572107178Snjl (CAM_DIS_DISCONNECT | CAM_TAG_ACTION_VALID); 573107178Snjl /* XXX add a_descr->priority */ 574107178Snjl if ((atio->ccb_h.flags & CAM_CDB_POINTER) == 0) 575107178Snjl a_descr->cdb = atio->cdb_io.cdb_bytes; 576107178Snjl else 577107178Snjl a_descr->cdb = atio->cdb_io.cdb_ptr; 578107178Snjl 579107178Snjl /* ATIOs are processed in FIFO order */ 580107178Snjl TAILQ_INSERT_TAIL(&work_queue, &ccb->ccb_h, 581107178Snjl periph_links.tqe); 582107178Snjl break; 583107178Snjl } 584107178Snjl case XPT_CONT_TARGET_IO: 585107178Snjl { 586107178Snjl struct ccb_scsiio *ctio; 587107178Snjl struct ctio_descr *c_descr; 588107178Snjl 589107178Snjl ctio = &ccb->ctio; 590107178Snjl c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 591107178Snjl c_descr->event = CTIO_DONE; 592107178Snjl /* Queue on the appropriate ATIO */ 593107178Snjl queue_io(ctio); 594107178Snjl /* Process any queued completions. */ 595162704Smjacob oo += run_queue(c_descr->atio); 596107178Snjl break; 597107178Snjl } 598107178Snjl case XPT_IMMED_NOTIFY: 599107178Snjl /* INOTs are handled with priority */ 600107178Snjl TAILQ_INSERT_HEAD(&work_queue, &ccb->ccb_h, 601107178Snjl periph_links.tqe); 602107178Snjl break; 603107178Snjl default: 604107178Snjl warnx("Unhandled ccb type %#x in handle_read", 605107178Snjl ccb->ccb_h.func_code); 606107178Snjl break; 607107178Snjl } 60839215Sgibbs } 609107178Snjl} 61039215Sgibbs 611107178Snjl/* Process an ATIO CCB from the kernel */ 612107178Snjlint 613107178Snjlwork_atio(struct ccb_accept_tio *atio) 614107178Snjl{ 615107178Snjl struct ccb_scsiio *ctio; 616107178Snjl struct atio_descr *a_descr; 617107178Snjl struct ctio_descr *c_descr; 618107178Snjl cam_status status; 619107178Snjl int ret; 620107178Snjl 621107178Snjl if (debug) 622107178Snjl warnx("Working on ATIO %p", atio); 623107178Snjl 624107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 625107178Snjl 626107178Snjl /* Get a CTIO and initialize it according to our known parameters */ 627107178Snjl ctio = get_ctio(); 628162704Smjacob if (ctio == NULL) { 629107178Snjl return (1); 630162704Smjacob } 631107178Snjl ret = 0; 632107178Snjl ctio->ccb_h.flags = a_descr->flags; 633107178Snjl ctio->tag_id = atio->tag_id; 634107178Snjl ctio->init_id = atio->init_id; 635107178Snjl /* XXX priority needs to be added to a_descr */ 636107178Snjl c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 637107178Snjl c_descr->atio = atio; 638107178Snjl if ((a_descr->flags & CAM_DIR_IN) != 0) 639107178Snjl c_descr->offset = a_descr->base_off + a_descr->targ_req; 640107178Snjl else if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT) 641107178Snjl c_descr->offset = a_descr->base_off + a_descr->init_req; 642120428Ssimokawa else 643120428Ssimokawa c_descr->offset = a_descr->base_off; 644107178Snjl 645107178Snjl /* 646107178Snjl * Return a check condition if there was an error while 647107178Snjl * receiving this ATIO. 648107178Snjl */ 649107178Snjl if (atio->sense_len != 0) { 65039215Sgibbs struct scsi_sense_data *sense; 65139215Sgibbs 652107178Snjl if (debug) { 653107178Snjl warnx("ATIO with %u bytes sense received", 654107178Snjl atio->sense_len); 65539215Sgibbs } 656107178Snjl sense = &atio->sense_data; 657107178Snjl tcmd_sense(ctio->init_id, ctio, sense->flags, 658107178Snjl sense->add_sense_code, sense->add_sense_code_qual); 659107178Snjl send_ccb((union ccb *)ctio, /*priority*/1); 660107178Snjl return (0); 661107178Snjl } 66239215Sgibbs 663107178Snjl status = atio->ccb_h.status & CAM_STATUS_MASK; 664107178Snjl switch (status) { 665107178Snjl case CAM_CDB_RECVD: 666107178Snjl ret = tcmd_handle(atio, ctio, ATIO_WORK); 667107178Snjl break; 668107178Snjl case CAM_REQ_ABORTED: 669162704Smjacob warn("ATIO %p aborted", a_descr); 670107178Snjl /* Requeue on HBA */ 671107178Snjl TAILQ_REMOVE(&work_queue, &atio->ccb_h, periph_links.tqe); 672107178Snjl send_ccb((union ccb *)atio, /*priority*/1); 673107178Snjl ret = 1; 674107178Snjl break; 675107178Snjl default: 676107178Snjl warnx("ATIO completed with unhandled status %#x", status); 677107178Snjl abort(); 678107178Snjl /* NOTREACHED */ 679107178Snjl break; 680107178Snjl } 68139215Sgibbs 682107178Snjl return (ret); 683107178Snjl} 68439215Sgibbs 685107178Snjlstatic void 686107178Snjlqueue_io(struct ccb_scsiio *ctio) 687107178Snjl{ 688107178Snjl struct ccb_hdr *ccb_h; 689107178Snjl struct io_queue *ioq; 690162704Smjacob struct ctio_descr *c_descr; 691107178Snjl 692107178Snjl c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 693162704Smjacob if (c_descr->atio == NULL) { 694107178Snjl errx(1, "CTIO %p has NULL ATIO", ctio); 695107178Snjl } 696162704Smjacob ioq = &((struct atio_descr *)c_descr->atio->ccb_h.targ_descr)->cmplt_io; 697107178Snjl 698162704Smjacob if (TAILQ_EMPTY(ioq)) { 699162704Smjacob TAILQ_INSERT_HEAD(ioq, &ctio->ccb_h, periph_links.tqe); 700162704Smjacob return; 701162704Smjacob } 702162704Smjacob 703162704Smjacob TAILQ_FOREACH_REVERSE(ccb_h, ioq, io_queue, periph_links.tqe) { 704162704Smjacob struct ctio_descr *curr_descr = 705162704Smjacob (struct ctio_descr *)ccb_h->targ_descr; 706162704Smjacob if (curr_descr->offset <= c_descr->offset) { 707162704Smjacob break; 70839215Sgibbs } 709162704Smjacob } 710162704Smjacob 711162704Smjacob if (ccb_h) { 712162704Smjacob TAILQ_INSERT_AFTER(ioq, ccb_h, &ctio->ccb_h, periph_links.tqe); 713107178Snjl } else { 714107178Snjl TAILQ_INSERT_HEAD(ioq, &ctio->ccb_h, periph_links.tqe); 715107178Snjl } 716107178Snjl} 71739215Sgibbs 718107178Snjl/* 719107178Snjl * Go through all completed AIO/CTIOs for a given ATIO and advance data 720107178Snjl * counts, start continuation IO, etc. 721107178Snjl */ 722162704Smjacobstatic int 723107178Snjlrun_queue(struct ccb_accept_tio *atio) 724107178Snjl{ 725107178Snjl struct atio_descr *a_descr; 726107178Snjl struct ccb_hdr *ccb_h; 727107178Snjl int sent_status, event; 728107178Snjl 729107178Snjl if (atio == NULL) 730162704Smjacob return (0); 731107178Snjl 732107178Snjl a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 733107178Snjl 734107178Snjl while ((ccb_h = TAILQ_FIRST(&a_descr->cmplt_io)) != NULL) { 735107178Snjl struct ccb_scsiio *ctio; 736107178Snjl struct ctio_descr *c_descr; 737107178Snjl 738107178Snjl ctio = (struct ccb_scsiio *)ccb_h; 739107178Snjl c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 740107178Snjl 741120428Ssimokawa if (ctio->ccb_h.status == CAM_REQ_ABORTED) { 742120428Ssimokawa TAILQ_REMOVE(&a_descr->cmplt_io, ccb_h, 743120428Ssimokawa periph_links.tqe); 744120428Ssimokawa free_ccb((union ccb *)ctio); 745120428Ssimokawa send_ccb((union ccb *)atio, /*priority*/1); 746120428Ssimokawa continue; 747120428Ssimokawa } 748120428Ssimokawa 749107178Snjl /* If completed item is in range, call handler */ 750107178Snjl if ((c_descr->event == AIO_DONE && 751107178Snjl c_descr->offset == a_descr->base_off + a_descr->targ_ack) 752107178Snjl || (c_descr->event == CTIO_DONE && 753107178Snjl c_descr->offset == a_descr->base_off + a_descr->init_ack)) { 754107178Snjl sent_status = (ccb_h->flags & CAM_SEND_STATUS) != 0; 755107178Snjl event = c_descr->event; 756107178Snjl 757107178Snjl TAILQ_REMOVE(&a_descr->cmplt_io, ccb_h, 758107178Snjl periph_links.tqe); 759107178Snjl tcmd_handle(atio, ctio, c_descr->event); 760107178Snjl 761107178Snjl /* If entire transfer complete, send back ATIO */ 762107178Snjl if (sent_status != 0 && event == CTIO_DONE) 763107178Snjl send_ccb((union ccb *)atio, /*priority*/1); 764107178Snjl } else { 765107178Snjl /* Gap in offsets so wait until later callback */ 766162704Smjacob if (/* debug */ 1) 767162704Smjacob warnx("IO %p:%p out of order %s", ccb_h, 768162704Smjacob a_descr, c_descr->event == AIO_DONE? 769162704Smjacob "aio" : "ctio"); 770162704Smjacob return (1); 77163185Smjacob } 772107178Snjl } 773162704Smjacob return (0); 774107178Snjl} 77563185Smjacob 776107178Snjlstatic int 777107178Snjlwork_inot(struct ccb_immed_notify *inot) 778107178Snjl{ 779107178Snjl cam_status status; 780107178Snjl int sense; 78163185Smjacob 782107178Snjl if (debug) 783107178Snjl warnx("Working on INOT %p", inot); 784107178Snjl 785107178Snjl status = inot->ccb_h.status; 786107178Snjl sense = (status & CAM_AUTOSNS_VALID) != 0; 787107178Snjl status &= CAM_STATUS_MASK; 788107178Snjl 789107178Snjl switch (status) { 790107178Snjl case CAM_SCSI_BUS_RESET: 791107178Snjl tcmd_ua(CAM_TARGET_WILDCARD, UA_BUS_RESET); 792107178Snjl abort_all_pending(); 793107178Snjl break; 794107178Snjl case CAM_BDR_SENT: 795107178Snjl tcmd_ua(CAM_TARGET_WILDCARD, UA_BDR); 796107178Snjl abort_all_pending(); 797107178Snjl break; 798107178Snjl case CAM_MESSAGE_RECV: 799107178Snjl switch (inot->message_args[0]) { 800107178Snjl case MSG_TASK_COMPLETE: 801107178Snjl case MSG_INITIATOR_DET_ERR: 802107178Snjl case MSG_ABORT_TASK_SET: 803107178Snjl case MSG_MESSAGE_REJECT: 804107178Snjl case MSG_NOOP: 805107178Snjl case MSG_PARITY_ERROR: 806107178Snjl case MSG_TARGET_RESET: 807107178Snjl case MSG_ABORT_TASK: 808107178Snjl case MSG_CLEAR_TASK_SET: 809107178Snjl default: 810107178Snjl warnx("INOT message %#x", inot->message_args[0]); 811107178Snjl break; 81239215Sgibbs } 813107178Snjl break; 814107178Snjl case CAM_REQ_ABORTED: 815107178Snjl warnx("INOT %p aborted", inot); 816107178Snjl break; 817107178Snjl default: 818107178Snjl warnx("Unhandled INOT status %#x", status); 819107178Snjl break; 82039215Sgibbs } 82139215Sgibbs 822107178Snjl /* If there is sense data, use it */ 823107178Snjl if (sense != 0) { 824107178Snjl struct scsi_sense_data *sense; 825107178Snjl 826107178Snjl sense = &inot->sense_data; 827107178Snjl tcmd_sense(inot->initiator_id, NULL, sense->flags, 828107178Snjl sense->add_sense_code, sense->add_sense_code_qual); 829107178Snjl if (debug) 830107178Snjl warnx("INOT has sense: %#x", sense->flags); 831107178Snjl } 832107178Snjl 833107178Snjl /* Requeue on SIM */ 834107178Snjl TAILQ_REMOVE(&work_queue, &inot->ccb_h, periph_links.tqe); 835107178Snjl send_ccb((union ccb *)inot, /*priority*/1); 836107178Snjl 837107178Snjl return (1); 83839215Sgibbs} 83939215Sgibbs 840107178Snjlvoid 841107178Snjlsend_ccb(union ccb *ccb, int priority) 842107178Snjl{ 843107178Snjl if (debug) 844107178Snjl warnx("sending ccb (%#x)", ccb->ccb_h.func_code); 845107178Snjl ccb->ccb_h.pinfo.priority = priority; 846107178Snjl if (XPT_FC_IS_QUEUED(ccb)) { 847107178Snjl TAILQ_INSERT_TAIL(&pending_queue, &ccb->ccb_h, 848107178Snjl periph_links.tqe); 849107178Snjl } 850107178Snjl if (write(targ_fd, &ccb, sizeof(ccb)) != sizeof(ccb)) { 851107178Snjl warn("write ccb"); 852107178Snjl ccb->ccb_h.status = CAM_PROVIDE_FAIL; 853107178Snjl } 854107178Snjl} 855107178Snjl 856107178Snjl/* Return a CTIO/descr/buf combo from the freelist or malloc one */ 857107178Snjlstatic struct ccb_scsiio * 858107178Snjlget_ctio() 859107178Snjl{ 860107178Snjl struct ccb_scsiio *ctio; 861107178Snjl struct ctio_descr *c_descr; 862107178Snjl struct sigevent *se; 863107178Snjl 864162704Smjacob if (num_ctios == MAX_CTIOS) { 865162704Smjacob warnx("at CTIO max"); 866107178Snjl return (NULL); 867162704Smjacob } 868107178Snjl 869107178Snjl ctio = (struct ccb_scsiio *)malloc(sizeof(*ctio)); 870107178Snjl if (ctio == NULL) { 871107178Snjl warn("malloc CTIO"); 872107178Snjl return (NULL); 873107178Snjl } 874107178Snjl c_descr = (struct ctio_descr *)malloc(sizeof(*c_descr)); 875107178Snjl if (c_descr == NULL) { 876107178Snjl free(ctio); 877107178Snjl warn("malloc ctio_descr"); 878107178Snjl return (NULL); 879107178Snjl } 880107178Snjl c_descr->buf = malloc(buf_size); 881107178Snjl if (c_descr->buf == NULL) { 882107178Snjl free(c_descr); 883107178Snjl free(ctio); 884107178Snjl warn("malloc backing store"); 885107178Snjl return (NULL); 886107178Snjl } 887107178Snjl num_ctios++; 888107178Snjl 889107178Snjl /* Initialize CTIO, CTIO descr, and AIO */ 890107178Snjl ctio->ccb_h.func_code = XPT_CONT_TARGET_IO; 891107178Snjl ctio->ccb_h.retry_count = 2; 892109345Snjl ctio->ccb_h.timeout = CAM_TIME_INFINITY; 893107178Snjl ctio->data_ptr = c_descr->buf; 894107178Snjl ctio->ccb_h.targ_descr = c_descr; 895107178Snjl c_descr->aiocb.aio_buf = c_descr->buf; 896107178Snjl c_descr->aiocb.aio_fildes = file_fd; 897107178Snjl se = &c_descr->aiocb.aio_sigevent; 898107178Snjl se->sigev_notify = SIGEV_KEVENT; 899107178Snjl se->sigev_notify_kqueue = kq_fd; 900157009Smjacob se->sigev_value.sival_ptr = ctio; 901107178Snjl 902107178Snjl return (ctio); 903107178Snjl} 904107178Snjl 905107178Snjlvoid 906107178Snjlfree_ccb(union ccb *ccb) 907107178Snjl{ 908107178Snjl switch (ccb->ccb_h.func_code) { 909107178Snjl case XPT_CONT_TARGET_IO: 910107178Snjl { 911107178Snjl struct ctio_descr *c_descr; 912107178Snjl 913107178Snjl c_descr = (struct ctio_descr *)ccb->ccb_h.targ_descr; 914107178Snjl free(c_descr->buf); 915107178Snjl num_ctios--; 916107178Snjl /* FALLTHROUGH */ 917107178Snjl } 918107178Snjl case XPT_ACCEPT_TARGET_IO: 919107178Snjl free(ccb->ccb_h.targ_descr); 920107178Snjl /* FALLTHROUGH */ 921107178Snjl case XPT_IMMED_NOTIFY: 922107178Snjl default: 923107178Snjl free(ccb); 924107178Snjl break; 925107178Snjl } 926107178Snjl} 927107178Snjl 928107178Snjlstatic cam_status 929107178Snjlget_sim_flags(u_int16_t *flags) 930107178Snjl{ 931107178Snjl struct ccb_pathinq cpi; 932107178Snjl cam_status status; 933107178Snjl 934107178Snjl /* Find SIM capabilities */ 935107178Snjl bzero(&cpi, sizeof(cpi)); 936107178Snjl cpi.ccb_h.func_code = XPT_PATH_INQ; 937107178Snjl send_ccb((union ccb *)&cpi, /*priority*/1); 938107178Snjl status = cpi.ccb_h.status & CAM_STATUS_MASK; 939107178Snjl if (status != CAM_REQ_CMP) { 940107178Snjl fprintf(stderr, "CPI failed, status %#x\n", status); 941107178Snjl return (status); 942107178Snjl } 943107178Snjl 944107178Snjl /* Can only enable on controllers that support target mode */ 945107178Snjl if ((cpi.target_sprt & PIT_PROCESSOR) == 0) { 946107178Snjl fprintf(stderr, "HBA does not support target mode\n"); 947107178Snjl status = CAM_PATH_INVALID; 948107178Snjl return (status); 949107178Snjl } 950107178Snjl 951107178Snjl *flags = cpi.hba_inquiry; 952107178Snjl return (status); 953107178Snjl} 954107178Snjl 95539215Sgibbsstatic void 956107178Snjlrel_simq() 95744498Sgibbs{ 958107178Snjl struct ccb_relsim crs; 959107178Snjl 960107178Snjl bzero(&crs, sizeof(crs)); 961107178Snjl crs.ccb_h.func_code = XPT_REL_SIMQ; 962107178Snjl crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; 963107178Snjl crs.openings = 0; 964107178Snjl crs.release_timeout = 0; 965107178Snjl crs.qfrozen_cnt = 0; 966107178Snjl send_ccb((union ccb *)&crs, /*priority*/0); 96744498Sgibbs} 96844498Sgibbs 969107178Snjl/* Cancel all pending CCBs. */ 97044498Sgibbsstatic void 971107178Snjlabort_all_pending() 97239215Sgibbs{ 973107178Snjl struct ccb_abort cab; 974107178Snjl struct ccb_hdr *ccb_h; 97539215Sgibbs 976107178Snjl if (debug) 977107178Snjl warnx("abort_all_pending"); 97839215Sgibbs 979107178Snjl bzero(&cab, sizeof(cab)); 980107178Snjl cab.ccb_h.func_code = XPT_ABORT; 981107178Snjl TAILQ_FOREACH(ccb_h, &pending_queue, periph_links.tqe) { 982107178Snjl if (debug) 983107178Snjl warnx("Aborting pending CCB %p\n", ccb_h); 984107178Snjl cab.abort_ccb = (union ccb *)ccb_h; 985107178Snjl send_ccb((union ccb *)&cab, /*priority*/1); 986107178Snjl if (cab.ccb_h.status != CAM_REQ_CMP) { 987107178Snjl warnx("Unable to abort CCB, status %#x\n", 988107178Snjl cab.ccb_h.status); 989107178Snjl } 990107178Snjl } 99139215Sgibbs} 99239215Sgibbs 993107178Snjlstatic void 994107178Snjlusage() 995107178Snjl{ 996107178Snjl fprintf(stderr, 997162704Smjacob "Usage: scsi_target [-AdSTY] [-b bufsize] [-c sectorsize]\n" 998107178Snjl "\t\t[-r numbufs] [-s volsize] [-W 8,16,32]\n" 999107178Snjl "\t\tbus:target:lun filename\n"); 1000107178Snjl exit(1); 1001107178Snjl} 1002