scsi_target.c (73388) | scsi_target.c (107178) |
---|---|
1/* | 1/* |
2 * Sample program to attach to the "targ" processor target, target mode 3 * peripheral driver and push or receive data. | 2 * SCSI Disk Emulator |
4 * | 3 * |
5 * Copyright (c) 1998 Justin T. Gibbs. | 4 * Copyright (c) 2002 Nate Lawson. |
6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions, and the following disclaimer, 13 * without modification, immediately at the beginning of the file. --- 7 unchanged lines hidden (view full) --- 21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * | 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions, and the following disclaimer, 12 * without modification, immediately at the beginning of the file. --- 7 unchanged lines hidden (view full) --- 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * |
29 * $FreeBSD: head/share/examples/scsi_target/scsi_target.c 73388 2001-03-03 21:23:31Z mjacob $ | 28 * $FreeBSD: head/share/examples/scsi_target/scsi_target.c 107178 2002-11-22 22:55:51Z njl $ |
30 */ 31 32#include <sys/types.h> 33#include <errno.h> | 29 */ 30 31#include <sys/types.h> 32#include <errno.h> |
33#include <err.h> |
|
34#include <fcntl.h> | 34#include <fcntl.h> |
35#include <paths.h> 36#include <poll.h> | |
37#include <signal.h> 38#include <stddef.h> 39#include <stdio.h> 40#include <stdlib.h> | 35#include <signal.h> 36#include <stddef.h> 37#include <stdio.h> 38#include <stdlib.h> |
39#include <string.h> |
|
41#include <sysexits.h> 42#include <unistd.h> | 40#include <sysexits.h> 41#include <unistd.h> |
43 | 42#include <aio.h> 43#include <sys/stat.h> 44#include <sys/queue.h> 45#include <sys/event.h> 46#include <sys/param.h> 47#include <cam/cam_queue.h> |
44#include <cam/scsi/scsi_all.h> | 48#include <cam/scsi/scsi_all.h> |
45#include <cam/scsi/scsi_message.h> | |
46#include <cam/scsi/scsi_targetio.h> | 49#include <cam/scsi/scsi_targetio.h> |
50#include <cam/scsi/scsi_message.h> 51#include "scsi_target.h" |
|
47 | 52 |
48char *appname; 49int ifd; 50char *ifilename; 51int ofd; 52char *ofilename; 53size_t bufsize = 64 * 1024; 54void *buf; 55char targdevname[80]; 56int targctlfd; 57int targfd; 58int quit; 59int debug = 0; 60struct ioc_alloc_unit alloc_unit = { | 53/* Maximum amount to transfer per CTIO */ 54#define MAX_XFER MAXPHYS 55/* Maximum number of allocated CTIOs */ 56#define MAX_CTIOS 32 57/* Maximum sector size for emulated volume */ 58#define MAX_SECTOR 32768 59 60/* Global variables */ 61int debug; 62u_int32_t volume_size; 63size_t sector_size; 64size_t buf_size; 65 66/* Local variables */ 67static int targ_fd; 68static int kq_fd; 69static int file_fd; 70static int num_ctios; 71static struct ccb_queue pending_queue; 72static struct ccb_queue work_queue; 73static struct ioc_enable_lun ioc_enlun = { |
61 CAM_BUS_WILDCARD, 62 CAM_TARGET_WILDCARD, 63 CAM_LUN_WILDCARD 64}; 65 | 74 CAM_BUS_WILDCARD, 75 CAM_TARGET_WILDCARD, 76 CAM_LUN_WILDCARD 77}; 78 |
66static void pump_events(); 67static void cleanup(); 68static void handle_exception(); 69static void quit_handler(); 70static void usage(); | 79/* Local functions */ 80static void cleanup(void); 81static int init_ccbs(void); 82static void request_loop(void); 83static void handle_read(void); 84/* static int work_atio(struct ccb_accept_tio *); */ 85static void queue_io(struct ccb_scsiio *); 86static void run_queue(struct ccb_accept_tio *); 87static int work_inot(struct ccb_immed_notify *); 88static struct ccb_scsiio * 89 get_ctio(void); 90/* static void free_ccb(union ccb *); */ 91static cam_status get_sim_flags(u_int16_t *); 92static void rel_simq(void); 93static void abort_all_pending(void); 94static void usage(void); |
71 72int 73main(int argc, char *argv[]) 74{ | 95 96int 97main(int argc, char *argv[]) 98{ |
75 int ch; | 99 int ch, unit; 100 char *file_name, targname[16]; 101 u_int16_t req_flags, sim_flags; 102 off_t user_size; |
76 | 103 |
77 appname = *argv; 78 while ((ch = getopt(argc, argv, "i:o:p:t:l:d")) != -1) { | 104 /* Initialize */ 105 debug = 0; 106 req_flags = sim_flags = 0; 107 user_size = 0; 108 targ_fd = file_fd = kq_fd = -1; 109 num_ctios = 0; 110 sector_size = SECTOR_SIZE; 111 buf_size = DFLTPHYS; 112 113 /* Prepare resource pools */ 114 TAILQ_INIT(&pending_queue); 115 TAILQ_INIT(&work_queue); 116 117 while ((ch = getopt(argc, argv, "AdSTb:c:s:W:")) != -1) { |
79 switch(ch) { | 118 switch(ch) { |
80 case 'i': 81 if ((ifd = open(optarg, O_RDONLY)) == -1) { 82 perror(optarg); 83 exit(EX_NOINPUT); 84 } 85 ifilename = optarg; | 119 case 'A': 120 req_flags |= SID_Addr16; |
86 break; | 121 break; |
87 case 'o': 88 if ((ofd = open(optarg, 89 O_WRONLY|O_CREAT), 0600) == -1) { 90 perror(optarg); 91 exit(EX_CANTCREAT); 92 } 93 ofilename = optarg; | 122 case 'd': 123 debug = 1; |
94 break; | 124 break; |
95 case 'p': 96 alloc_unit.path_id = atoi(optarg); | 125 case 'S': 126 req_flags |= SID_Sync; |
97 break; | 127 break; |
98 case 't': 99 alloc_unit.target_id = atoi(optarg); | 128 case 'T': 129 req_flags |= SID_CmdQue; |
100 break; | 130 break; |
101 case 'l': 102 alloc_unit.lun_id = atoi(optarg); | 131 case 'b': 132 buf_size = atoi(optarg); 133 if (buf_size < 256 || buf_size > MAX_XFER) 134 errx(1, "Unreasonable buf size: %s", optarg); |
103 break; | 135 break; |
104 case 'd': 105 debug++; | 136 case 'c': 137 sector_size = atoi(optarg); 138 if (sector_size < 512 || sector_size > MAX_SECTOR) 139 errx(1, "Unreasonable sector size: %s", optarg); |
106 break; | 140 break; |
107 case '?': | 141 case 's': 142 user_size = strtoll(optarg, (char **)NULL, /*base*/10); 143 if (user_size < 0) 144 errx(1, "Unreasonable volume size: %s", optarg); 145 break; 146 case 'W': 147 req_flags &= ~(SID_WBus16 | SID_WBus32); 148 switch (atoi(optarg)) { 149 case 8: 150 /* Leave req_flags zeroed */ 151 break; 152 case 16: 153 req_flags |= SID_WBus16; 154 break; 155 case 32: 156 req_flags |= SID_WBus32; 157 break; 158 default: 159 warnx("Width %s not supported", optarg); 160 usage(); 161 /* NOTREACHED */ 162 } 163 break; |
108 default: 109 usage(); 110 /* NOTREACHED */ 111 } 112 } 113 argc -= optind; 114 argv += optind; | 164 default: 165 usage(); 166 /* NOTREACHED */ 167 } 168 } 169 argc -= optind; 170 argv += optind; |
115 116 if (alloc_unit.path_id == CAM_BUS_WILDCARD 117 || alloc_unit.target_id == CAM_TARGET_WILDCARD 118 || alloc_unit.lun_id == CAM_LUN_WILDCARD) { 119 fprintf(stderr, "%s: Incomplete device path specifiled\n", 120 appname); | 171 172 if (argc != 2) |
121 usage(); | 173 usage(); |
122 /* NOTREACHED */ 123 } | |
124 | 174 |
125 if (argc != 0) { 126 fprintf(stderr, "%s: Too many arguments\n", appname); | 175 sscanf(argv[0], "%u:%u:%u", &ioc_enlun.path_id, &ioc_enlun.target_id, 176 &ioc_enlun.lun_id); 177 file_name = argv[1]; 178 179 if (ioc_enlun.path_id == CAM_BUS_WILDCARD || 180 ioc_enlun.target_id == CAM_TARGET_WILDCARD || 181 ioc_enlun.lun_id == CAM_LUN_WILDCARD) { 182 warnx("Incomplete target path specified"); |
127 usage(); 128 /* NOTREACHED */ 129 } | 183 usage(); 184 /* NOTREACHED */ 185 } |
186 /* We don't support any vendor-specific commands */ 187 ioc_enlun.grp6_len = 0; 188 ioc_enlun.grp7_len = 0; |
|
130 | 189 |
131 /* Allocate a new instance */ 132 if ((targctlfd = open("/dev/targ.ctl", O_RDWR)) == -1) { 133 perror("/dev/targ.ctl"); 134 exit(EX_UNAVAILABLE); 135 } | 190 /* Open backing store for IO */ 191 file_fd = open(file_name, O_RDWR); 192 if (file_fd < 0) 193 err(1, "open backing store file"); |
136 | 194 |
137 if (ioctl(targctlfd, TARGCTLIOALLOCUNIT, &alloc_unit) == -1) { 138 perror("TARGCTLIOALLOCUNIT"); 139 exit(EX_SOFTWARE); | 195 /* Check backing store size or use the size user gave us */ 196 if (user_size == 0) { 197 struct stat st; 198 199 if (fstat(file_fd, &st) < 0) 200 err(1, "fstat file"); 201 volume_size = st.st_size / sector_size; 202 } else { 203 volume_size = user_size / sector_size; |
140 } | 204 } |
205 if (volume_size <= 0) 206 errx(1, "volume must be larger than %d", sector_size); |
|
141 | 207 |
142 snprintf(targdevname, sizeof(targdevname), "%starg%d", _PATH_DEV, 143 alloc_unit.unit); | 208 /* Go through all the control devices and find one that isn't busy. */ 209 unit = 0; 210 do { 211 snprintf(targname, sizeof(targname), "/dev/targ%d", unit++); 212 targ_fd = open(targname, O_RDWR); 213 } while (targ_fd < 0 && errno == EBUSY); |
144 | 214 |
145 if ((targfd = open(targdevname, O_RDWR)) == -1) { 146 perror(targdevname); 147 ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit); 148 exit(EX_NOINPUT); 149 } 150 151 if (ioctl(targfd, TARGIODEBUG, &debug) == -1) { 152 perror("TARGIODEBUG"); 153 (void) ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit); 154 exit(EX_SOFTWARE); 155 } | 215 if (targ_fd < 0) 216 err(1, "Tried to open %d devices, none available", unit); |
156 | 217 |
157 buf = malloc(bufsize); | 218 /* The first three are handled by kevent() later */ 219 signal(SIGHUP, SIG_IGN); 220 signal(SIGINT, SIG_IGN); 221 signal(SIGTERM, SIG_IGN); 222 signal(SIGPROF, SIG_IGN); 223 signal(SIGALRM, SIG_IGN); 224 signal(SIGSTOP, SIG_IGN); 225 signal(SIGTSTP, SIG_IGN); |
158 | 226 |
159 if (buf == NULL) { 160 fprintf(stderr, "%s: Could not malloc I/O buffer", appname); 161 if (debug) { 162 debug = 0; 163 (void) ioctl(targfd, TARGIODEBUG, &debug); 164 } 165 (void) ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit); 166 exit(EX_OSERR); | 227 /* Register a cleanup handler to run when exiting */ 228 atexit(cleanup); 229 230 /* Enable listening on the specified LUN */ 231 if (ioctl(targ_fd, TARGIOCENABLE, &ioc_enlun) != 0) 232 err(1, "TARGIOCENABLE"); 233 234 /* Enable debugging if requested */ 235 if (debug) { 236 if (ioctl(targ_fd, TARGIOCDEBUG, &debug) != 0) 237 err(1, "TARGIOCDEBUG"); |
167 } 168 | 238 } 239 |
169 signal(SIGHUP, quit_handler); 170 signal(SIGINT, quit_handler); 171 signal(SIGTERM, quit_handler); | 240 /* Set up inquiry data according to what SIM supports */ 241 if (get_sim_flags(&sim_flags) != CAM_REQ_CMP) 242 errx(1, "get_sim_flags"); 243 if (tcmd_init(req_flags, sim_flags) != 0) 244 errx(1, "Initializing tcmd subsystem failed"); |
172 | 245 |
173 atexit(cleanup); | 246 /* Queue ATIOs and INOTs on descriptor */ 247 if (init_ccbs() != 0) 248 errx(1, "init_ccbs failed"); |
174 | 249 |
175 pump_events(); | 250 if (debug) 251 warnx("main loop beginning"); 252 request_loop(); |
176 | 253 |
177 return (0); | 254 exit(0); |
178} 179 180static void 181cleanup() 182{ | 255} 256 257static void 258cleanup() 259{ |
260 struct ccb_hdr *ccb_h; 261 |
|
183 if (debug) { | 262 if (debug) { |
263 warnx("cleanup called"); |
|
184 debug = 0; | 264 debug = 0; |
185 (void) ioctl(targfd, TARGIODEBUG, &debug); | 265 ioctl(targ_fd, TARGIOCDEBUG, &debug); |
186 } | 266 } |
187 close(targfd); 188 if (ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit) == -1) { 189 perror("TARGCTLIOFREEUNIT"); | 267 ioctl(targ_fd, TARGIOCDISABLE, NULL); 268 close(targ_fd); 269 270 while ((ccb_h = TAILQ_FIRST(&pending_queue)) != NULL) { 271 TAILQ_REMOVE(&pending_queue, ccb_h, periph_links.tqe); 272 free_ccb((union ccb *)ccb_h); |
190 } | 273 } |
191 close(targctlfd); | 274 while ((ccb_h = TAILQ_FIRST(&work_queue)) != NULL) { 275 TAILQ_REMOVE(&work_queue, ccb_h, periph_links.tqe); 276 free_ccb((union ccb *)ccb_h); 277 } 278 279 if (kq_fd != -1) 280 close(kq_fd); |
192} 193 | 281} 282 |
194static void 195pump_events() | 283/* Allocate ATIOs/INOTs and queue on HBA */ 284static int 285init_ccbs() |
196{ | 286{ |
197 struct pollfd targpoll; | 287 int i; |
198 | 288 |
199 targpoll.fd = targfd; 200 targpoll.events = POLLRDNORM|POLLWRNORM; | 289 for (i = 0; i < MAX_INITIATORS; i++) { 290 struct ccb_accept_tio *atio; 291 struct atio_descr *a_descr; 292 struct ccb_immed_notify *inot; |
201 | 293 |
202 while (quit == 0) { 203 int retval; 204 205 retval = poll(&targpoll, 1, INFTIM); 206 207 if (retval == -1) { 208 if (errno == EINTR) 209 continue; 210 perror("Poll Failed"); 211 exit(EX_SOFTWARE); | 294 atio = (struct ccb_accept_tio *)malloc(sizeof(*atio)); 295 if (atio == NULL) { 296 warn("malloc ATIO"); 297 return (-1); |
212 } | 298 } |
213 214 if (retval == 0) { 215 perror("Poll returned 0 although timeout infinite???"); 216 exit(EX_SOFTWARE); | 299 a_descr = (struct atio_descr *)malloc(sizeof(*a_descr)); 300 if (a_descr == NULL) { 301 free(atio); 302 warn("malloc atio_descr"); 303 return (-1); |
217 } | 304 } |
305 atio->ccb_h.func_code = XPT_ACCEPT_TARGET_IO; 306 atio->ccb_h.targ_descr = a_descr; 307 send_ccb((union ccb *)atio, /*priority*/1); |
|
218 | 308 |
219 if (retval > 1) { 220 perror("Poll returned more fds ready than allocated"); 221 exit(EX_SOFTWARE); | 309 inot = (struct ccb_immed_notify *)malloc(sizeof(*inot)); 310 if (inot == NULL) { 311 warn("malloc INOT"); 312 return (-1); |
222 } | 313 } |
314 inot->ccb_h.func_code = XPT_IMMED_NOTIFY; 315 send_ccb((union ccb *)inot, /*priority*/1); 316 } |
|
223 | 317 |
224 /* Process events */ 225 if ((targpoll.revents & POLLERR) != 0) { 226 handle_exception(); 227 } | 318 return (0); 319} |
228 | 320 |
229 if ((targpoll.revents & POLLRDNORM) != 0) { 230 retval = read(targfd, buf, bufsize); | 321static void 322request_loop() 323{ 324 struct kevent events[MAX_EVENTS]; 325 struct timespec ts, *tptr; 326 int quit; |
231 | 327 |
232 if (retval == -1) { 233 perror("Read from targ failed"); 234 /* Go look for exceptions */ | 328 /* Register kqueue for event notification */ 329 if ((kq_fd = kqueue()) < 0) 330 err(1, "init kqueue"); 331 332 /* Set up some default events */ 333 EV_SET(&events[0], SIGHUP, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); 334 EV_SET(&events[1], SIGINT, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); 335 EV_SET(&events[2], SIGTERM, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); 336 EV_SET(&events[3], targ_fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); 337 if (kevent(kq_fd, events, 4, NULL, 0, NULL) < 0) 338 err(1, "kevent signal registration"); 339 340 ts.tv_sec = 0; 341 ts.tv_nsec = 0; 342 tptr = NULL; 343 quit = 0; 344 345 /* Loop until user signal */ 346 while (quit == 0) { 347 int retval, i; 348 struct ccb_hdr *ccb_h; 349 350 /* Check for the next signal, read ready, or AIO completion */ 351 retval = kevent(kq_fd, NULL, 0, events, MAX_EVENTS, tptr); 352 if (retval < 0) { 353 if (errno == EINTR) { 354 if (debug) 355 warnx("EINTR, looping"); |
235 continue; | 356 continue; |
236 } else { 237 retval = write(ofd, buf, retval); 238 if (retval == -1) { 239 perror("Write to file failed"); 240 } | 357 } 358 else { 359 err(1, "kevent failed"); |
241 } | 360 } |
361 } else if (retval > MAX_EVENTS) { 362 errx(1, "kevent returned more events than allocated?"); |
|
242 } 243 | 363 } 364 |
244 if ((targpoll.revents & POLLWRNORM) != 0) { 245 int amount_read; | 365 /* Process all received events. */ 366 for (i = 0; i < retval; i++) { 367 if ((events[i].flags & EV_ERROR) != 0) 368 errx(1, "kevent registration failed"); |
246 | 369 |
247 retval = read(ifd, buf, bufsize); 248 if (retval == -1) { 249 perror("Read from file failed"); 250 exit(EX_SOFTWARE); | 370 switch (events[i].filter) { 371 case EVFILT_READ: 372 if (debug) 373 warnx("read ready"); 374 handle_read(); 375 break; 376 case EVFILT_AIO: 377 { 378 struct ccb_scsiio *ctio; 379 struct ctio_descr *c_descr; 380 if (debug) 381 warnx("aio ready"); 382 383 ctio = (struct ccb_scsiio *)events[i].udata; 384 c_descr = (struct ctio_descr *) 385 ctio->ccb_h.targ_descr; 386 c_descr->event = AIO_DONE; 387 /* Queue on the appropriate ATIO */ 388 queue_io(ctio); 389 /* Process any queued completions. */ 390 run_queue(c_descr->atio); 391 break; |
251 } | 392 } |
393 case EVFILT_SIGNAL: 394 if (debug) 395 warnx("signal ready, setting quit"); 396 quit = 1; 397 break; 398 default: 399 warnx("unknown event %#x", events[i].filter); 400 break; 401 } |
|
252 | 402 |
253 amount_read = retval; 254 retval = write(targfd, buf, retval); 255 if (retval == -1) { 256 perror("Write to targ failed"); 257 retval = 0; | 403 if (debug) 404 warnx("event done"); 405 } 406 407 /* Grab the first CCB and perform one work unit. */ 408 if ((ccb_h = TAILQ_FIRST(&work_queue)) != NULL) { 409 union ccb *ccb; 410 411 ccb = (union ccb *)ccb_h; 412 switch (ccb_h->func_code) { 413 case XPT_ACCEPT_TARGET_IO: 414 /* Start one more transfer. */ 415 retval = work_atio(&ccb->atio); 416 break; 417 case XPT_IMMED_NOTIFY: 418 retval = work_inot(&ccb->cin); 419 break; 420 default: 421 warnx("Unhandled ccb type %#x on workq", 422 ccb_h->func_code); 423 abort(); 424 /* NOTREACHED */ |
258 } 259 | 425 } 426 |
260 /* Backup in our input stream on short writes */ 261 if (retval != amount_read) 262 lseek(ifd, retval - amount_read, SEEK_CUR); | 427 /* Assume work function handled the exception */ 428 if ((ccb_h->status & CAM_DEV_QFRZN) != 0) { 429 warnx("Queue frozen receiving CCB, releasing"); 430 rel_simq(); 431 } 432 433 /* No more work needed for this command. */ 434 if (retval == 0) { 435 TAILQ_REMOVE(&work_queue, ccb_h, 436 periph_links.tqe); 437 } |
263 } | 438 } |
439 440 /* 441 * Poll for new events (i.e. completions) while we 442 * are processing CCBs on the work_queue. Once it's 443 * empty, use an infinite wait. 444 */ 445 if (!TAILQ_EMPTY(&work_queue)) 446 tptr = &ts; 447 else 448 tptr = NULL; |
|
264 } 265} 266 | 449 } 450} 451 |
452/* CCBs are ready from the kernel */ |
|
267static void | 453static void |
268handle_exception() | 454handle_read() |
269{ | 455{ |
270 targ_exception exceptions; | 456 union ccb *ccb_array[MAX_INITIATORS], *ccb; 457 int ccb_count, i; |
271 | 458 |
272 if (ioctl(targfd, TARGIOCFETCHEXCEPTION, &exceptions) == -1) { 273 perror("TARGIOCFETCHEXCEPTION"); 274 exit(EX_SOFTWARE); | 459 ccb_count = read(targ_fd, ccb_array, sizeof(ccb_array)); 460 if (ccb_count <= 0) { 461 warn("read ccb ptrs"); 462 return; |
275 } | 463 } |
464 ccb_count /= sizeof(union ccb *); 465 if (ccb_count < 1) { 466 warnx("truncated read ccb ptr?"); 467 return; 468 } |
|
276 | 469 |
277 printf("Saw exceptions %x\n", exceptions); 278 if ((exceptions & TARG_EXCEPT_DEVICE_INVALID) != 0) { 279 /* Device went away. Nothing more to do. */ 280 printf("Device went away\n"); 281 exit(0); | 470 for (i = 0; i < ccb_count; i++) { 471 ccb = ccb_array[i]; 472 TAILQ_REMOVE(&pending_queue, &ccb->ccb_h, periph_links.tqe); 473 474 switch (ccb->ccb_h.func_code) { 475 case XPT_ACCEPT_TARGET_IO: 476 { 477 struct ccb_accept_tio *atio; 478 struct atio_descr *a_descr; 479 480 /* Initialize ATIO descr for this transaction */ 481 atio = &ccb->atio; 482 a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 483 bzero(a_descr, sizeof(*a_descr)); 484 TAILQ_INIT(&a_descr->cmplt_io); 485 a_descr->flags = atio->ccb_h.flags & 486 (CAM_DIS_DISCONNECT | CAM_TAG_ACTION_VALID); 487 /* XXX add a_descr->priority */ 488 if ((atio->ccb_h.flags & CAM_CDB_POINTER) == 0) 489 a_descr->cdb = atio->cdb_io.cdb_bytes; 490 else 491 a_descr->cdb = atio->cdb_io.cdb_ptr; 492 493 /* ATIOs are processed in FIFO order */ 494 TAILQ_INSERT_TAIL(&work_queue, &ccb->ccb_h, 495 periph_links.tqe); 496 break; 497 } 498 case XPT_CONT_TARGET_IO: 499 { 500 struct ccb_scsiio *ctio; 501 struct ctio_descr *c_descr; 502 503 ctio = &ccb->ctio; 504 c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 505 c_descr->event = CTIO_DONE; 506 /* Queue on the appropriate ATIO */ 507 queue_io(ctio); 508 /* Process any queued completions. */ 509 run_queue(c_descr->atio); 510 break; 511 } 512 case XPT_IMMED_NOTIFY: 513 /* INOTs are handled with priority */ 514 TAILQ_INSERT_HEAD(&work_queue, &ccb->ccb_h, 515 periph_links.tqe); 516 break; 517 default: 518 warnx("Unhandled ccb type %#x in handle_read", 519 ccb->ccb_h.func_code); 520 break; 521 } |
282 } | 522 } |
523} |
|
283 | 524 |
284 if ((exceptions & TARG_EXCEPT_UNKNOWN_ATIO) != 0) { 285 struct ccb_accept_tio atio; 286 struct ioc_initiator_state ioc_istate; | 525/* Process an ATIO CCB from the kernel */ 526int 527work_atio(struct ccb_accept_tio *atio) 528{ 529 struct ccb_scsiio *ctio; 530 struct atio_descr *a_descr; 531 struct ctio_descr *c_descr; 532 cam_status status; 533 int ret; 534 535 if (debug) 536 warnx("Working on ATIO %p", atio); 537 538 a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 539 540 /* Get a CTIO and initialize it according to our known parameters */ 541 ctio = get_ctio(); 542 if (ctio == NULL) 543 return (1); 544 ret = 0; 545 ctio->ccb_h.flags = a_descr->flags; 546 ctio->tag_id = atio->tag_id; 547 ctio->init_id = atio->init_id; 548 /* XXX priority needs to be added to a_descr */ 549 c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 550 c_descr->atio = atio; 551 if ((a_descr->flags & CAM_DIR_IN) != 0) 552 c_descr->offset = a_descr->base_off + a_descr->targ_req; 553 else if ((a_descr->flags & CAM_DIR_MASK) == CAM_DIR_OUT) 554 c_descr->offset = a_descr->base_off + a_descr->init_req; 555 556 /* 557 * Return a check condition if there was an error while 558 * receiving this ATIO. 559 */ 560 if (atio->sense_len != 0) { |
287 struct scsi_sense_data *sense; | 561 struct scsi_sense_data *sense; |
288 union ccb ccb; | |
289 | 562 |
290 if (ioctl(targfd, TARGIOCFETCHATIO, &atio) == -1) { 291 perror("TARGIOCFETCHATIO"); 292 exit(EX_SOFTWARE); | 563 if (debug) { 564 warnx("ATIO with %u bytes sense received", 565 atio->sense_len); |
293 } | 566 } |
567 sense = &atio->sense_data; 568 tcmd_sense(ctio->init_id, ctio, sense->flags, 569 sense->add_sense_code, sense->add_sense_code_qual); 570 send_ccb((union ccb *)ctio, /*priority*/1); 571 return (0); 572 } |
|
294 | 573 |
295 printf("Ignoring unhandled command 0x%x for Id %d\n", 296 atio.cdb_io.cdb_bytes[0], atio.init_id); | 574 status = atio->ccb_h.status & CAM_STATUS_MASK; 575 switch (status) { 576 case CAM_CDB_RECVD: 577 ret = tcmd_handle(atio, ctio, ATIO_WORK); 578 break; 579 case CAM_REQ_ABORTED: 580 /* Requeue on HBA */ 581 TAILQ_REMOVE(&work_queue, &atio->ccb_h, periph_links.tqe); 582 send_ccb((union ccb *)atio, /*priority*/1); 583 ret = 1; 584 break; 585 default: 586 warnx("ATIO completed with unhandled status %#x", status); 587 abort(); 588 /* NOTREACHED */ 589 break; 590 } |
297 | 591 |
298 ioc_istate.initiator_id = atio.init_id; 299 if (ioctl(targfd, TARGIOCGETISTATE, &ioc_istate) == -1) { 300 perror("TARGIOCGETISTATE"); 301 exit(EX_SOFTWARE); 302 } | 592 return (ret); 593} |
303 | 594 |
304 /* Send back Illegal Command code status */ 305 ioc_istate.istate.pending_ca |= CA_CMD_SENSE; 306 sense = &ioc_istate.istate.sense_data; 307 bzero(sense, sizeof(*sense)); 308 sense->error_code = SSD_CURRENT_ERROR; 309 sense->flags = SSD_KEY_ILLEGAL_REQUEST; 310 sense->add_sense_code = 0x20; 311 sense->add_sense_code_qual = 0x00; 312 sense->extra_len = offsetof(struct scsi_sense_data, fru) 313 - offsetof(struct scsi_sense_data, extra_len); | 595static void 596queue_io(struct ccb_scsiio *ctio) 597{ 598 struct ccb_hdr *ccb_h; 599 struct io_queue *ioq; 600 struct ctio_descr *c_descr, *curr_descr; 601 602 c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 603 /* If the completion is for a specific ATIO, queue in order */ 604 if (c_descr->atio != NULL) { 605 struct atio_descr *a_descr; |
314 | 606 |
315 if (ioctl(targfd, TARGIOCSETISTATE, &ioc_istate) == -1) { 316 perror("TARGIOCSETISTATE"); 317 exit(EX_SOFTWARE); | 607 a_descr = (struct atio_descr *)c_descr->atio->ccb_h.targ_descr; 608 ioq = &a_descr->cmplt_io; 609 } else { 610 errx(1, "CTIO %p has NULL ATIO", ctio); 611 } 612 613 /* Insert in order, sorted by offset */ 614 if (!TAILQ_EMPTY(ioq)) { 615 TAILQ_FOREACH_REVERSE(ccb_h, ioq, io_queue, periph_links.tqe) { 616 curr_descr = (struct ctio_descr *)ccb_h->targ_descr; 617 if (curr_descr->offset <= c_descr->offset) { 618 TAILQ_INSERT_AFTER(ioq, ccb_h, &ctio->ccb_h, 619 periph_links.tqe); 620 break; 621 } 622 if (TAILQ_PREV(ccb_h, io_queue, periph_links.tqe) 623 == NULL) { 624 TAILQ_INSERT_BEFORE(ccb_h, &ctio->ccb_h, 625 periph_links.tqe); 626 break; 627 } |
318 } | 628 } |
629 } else { 630 TAILQ_INSERT_HEAD(ioq, &ctio->ccb_h, periph_links.tqe); 631 } 632} |
|
319 | 633 |
320 /* Clear the exception so the kernel will take our response */ 321 if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) { 322 perror("TARGIOCCLEAREXCEPTION"); 323 exit(EX_SOFTWARE); | 634/* 635 * Go through all completed AIO/CTIOs for a given ATIO and advance data 636 * counts, start continuation IO, etc. 637 */ 638static void 639run_queue(struct ccb_accept_tio *atio) 640{ 641 struct atio_descr *a_descr; 642 struct ccb_hdr *ccb_h; 643 int sent_status, event; 644 645 if (atio == NULL) 646 return; 647 648 a_descr = (struct atio_descr *)atio->ccb_h.targ_descr; 649 650 while ((ccb_h = TAILQ_FIRST(&a_descr->cmplt_io)) != NULL) { 651 struct ccb_scsiio *ctio; 652 struct ctio_descr *c_descr; 653 654 ctio = (struct ccb_scsiio *)ccb_h; 655 c_descr = (struct ctio_descr *)ctio->ccb_h.targ_descr; 656 657 /* If completed item is in range, call handler */ 658 if ((c_descr->event == AIO_DONE && 659 c_descr->offset == a_descr->base_off + a_descr->targ_ack) 660 || (c_descr->event == CTIO_DONE && 661 c_descr->offset == a_descr->base_off + a_descr->init_ack)) { 662 sent_status = (ccb_h->flags & CAM_SEND_STATUS) != 0; 663 event = c_descr->event; 664 665 TAILQ_REMOVE(&a_descr->cmplt_io, ccb_h, 666 periph_links.tqe); 667 tcmd_handle(atio, ctio, c_descr->event); 668 669 /* If entire transfer complete, send back ATIO */ 670 if (sent_status != 0 && event == CTIO_DONE) 671 send_ccb((union ccb *)atio, /*priority*/1); 672 } else { 673 /* Gap in offsets so wait until later callback */ 674 if (debug) 675 warnx("IO %p out of order", ccb_h); 676 break; |
324 } | 677 } |
678 } 679} |
|
325 | 680 |
326 bzero(&ccb, sizeof(ccb)); 327 cam_fill_ctio(&ccb.csio, 328 /*retries*/2, 329 /*cbfcnp*/NULL, 330 CAM_DIR_NONE | CAM_SEND_STATUS, 331 (atio.ccb_h.flags & CAM_TAG_ACTION_VALID)? 332 MSG_SIMPLE_Q_TAG : 0, 333 atio.tag_id, 334 atio.init_id, 335 SCSI_STATUS_CHECK_COND, 336 /*data_ptr*/NULL, 337 /*dxfer_len*/0, 338 /*timeout*/5 * 1000); 339 /* 340 * Make sure that periph_priv pointers are clean. 341 */ 342 bzero(&ccb.ccb_h.periph_priv, sizeof ccb.ccb_h.periph_priv); | 681static int 682work_inot(struct ccb_immed_notify *inot) 683{ 684 cam_status status; 685 int sense; |
343 | 686 |
344 if (ioctl(targfd, TARGIOCCOMMAND, &ccb) == -1) { 345 perror("TARGIOCCOMMAND"); 346 exit(EX_SOFTWARE); | 687 if (debug) 688 warnx("Working on INOT %p", inot); 689 690 status = inot->ccb_h.status; 691 sense = (status & CAM_AUTOSNS_VALID) != 0; 692 status &= CAM_STATUS_MASK; 693 694 switch (status) { 695 case CAM_SCSI_BUS_RESET: 696 tcmd_ua(CAM_TARGET_WILDCARD, UA_BUS_RESET); 697 abort_all_pending(); 698 break; 699 case CAM_BDR_SENT: 700 tcmd_ua(CAM_TARGET_WILDCARD, UA_BDR); 701 abort_all_pending(); 702 break; 703 case CAM_MESSAGE_RECV: 704 switch (inot->message_args[0]) { 705 case MSG_TASK_COMPLETE: 706 case MSG_INITIATOR_DET_ERR: 707 case MSG_ABORT_TASK_SET: 708 case MSG_MESSAGE_REJECT: 709 case MSG_NOOP: 710 case MSG_PARITY_ERROR: 711 case MSG_TARGET_RESET: 712 case MSG_ABORT_TASK: 713 case MSG_CLEAR_TASK_SET: 714 default: 715 warnx("INOT message %#x", inot->message_args[0]); 716 break; |
347 } | 717 } |
348 349 } else { 350 if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) { 351 perror("TARGIOCCLEAREXCEPTION"); 352 exit(EX_SOFTWARE); 353 } | 718 break; 719 case CAM_REQ_ABORTED: 720 warnx("INOT %p aborted", inot); 721 break; 722 default: 723 warnx("Unhandled INOT status %#x", status); 724 break; |
354 } 355 | 725 } 726 |
727 /* If there is sense data, use it */ 728 if (sense != 0) { 729 struct scsi_sense_data *sense; 730 731 sense = &inot->sense_data; 732 tcmd_sense(inot->initiator_id, NULL, sense->flags, 733 sense->add_sense_code, sense->add_sense_code_qual); 734 if (debug) 735 warnx("INOT has sense: %#x", sense->flags); 736 } 737 738 /* Requeue on SIM */ 739 TAILQ_REMOVE(&work_queue, &inot->ccb_h, periph_links.tqe); 740 send_ccb((union ccb *)inot, /*priority*/1); 741 742 return (1); |
|
356} 357 | 743} 744 |
745void 746send_ccb(union ccb *ccb, int priority) 747{ 748 if (debug) 749 warnx("sending ccb (%#x)", ccb->ccb_h.func_code); 750 ccb->ccb_h.pinfo.priority = priority; 751 if (XPT_FC_IS_QUEUED(ccb)) { 752 TAILQ_INSERT_TAIL(&pending_queue, &ccb->ccb_h, 753 periph_links.tqe); 754 } 755 if (write(targ_fd, &ccb, sizeof(ccb)) != sizeof(ccb)) { 756 warn("write ccb"); 757 ccb->ccb_h.status = CAM_PROVIDE_FAIL; 758 } 759} 760 761/* Return a CTIO/descr/buf combo from the freelist or malloc one */ 762static struct ccb_scsiio * 763get_ctio() 764{ 765 struct ccb_scsiio *ctio; 766 struct ctio_descr *c_descr; 767 struct sigevent *se; 768 769 if (num_ctios == MAX_CTIOS) 770 return (NULL); 771 772 ctio = (struct ccb_scsiio *)malloc(sizeof(*ctio)); 773 if (ctio == NULL) { 774 warn("malloc CTIO"); 775 return (NULL); 776 } 777 c_descr = (struct ctio_descr *)malloc(sizeof(*c_descr)); 778 if (c_descr == NULL) { 779 free(ctio); 780 warn("malloc ctio_descr"); 781 return (NULL); 782 } 783 c_descr->buf = malloc(buf_size); 784 if (c_descr->buf == NULL) { 785 free(c_descr); 786 free(ctio); 787 warn("malloc backing store"); 788 return (NULL); 789 } 790 num_ctios++; 791 792 /* Initialize CTIO, CTIO descr, and AIO */ 793 ctio->ccb_h.func_code = XPT_CONT_TARGET_IO; 794 ctio->ccb_h.retry_count = 2; 795 ctio->ccb_h.timeout = 5; 796 ctio->data_ptr = c_descr->buf; 797 ctio->ccb_h.targ_descr = c_descr; 798 c_descr->aiocb.aio_buf = c_descr->buf; 799 c_descr->aiocb.aio_fildes = file_fd; 800 se = &c_descr->aiocb.aio_sigevent; 801 se->sigev_notify = SIGEV_KEVENT; 802 se->sigev_notify_kqueue = kq_fd; 803 se->sigev_value.sigval_ptr = ctio; 804 805 return (ctio); 806} 807 808void 809free_ccb(union ccb *ccb) 810{ 811 switch (ccb->ccb_h.func_code) { 812 case XPT_CONT_TARGET_IO: 813 { 814 struct ctio_descr *c_descr; 815 816 c_descr = (struct ctio_descr *)ccb->ccb_h.targ_descr; 817 free(c_descr->buf); 818 num_ctios--; 819 /* FALLTHROUGH */ 820 } 821 case XPT_ACCEPT_TARGET_IO: 822 free(ccb->ccb_h.targ_descr); 823 /* FALLTHROUGH */ 824 case XPT_IMMED_NOTIFY: 825 default: 826 free(ccb); 827 break; 828 } 829} 830 831static cam_status 832get_sim_flags(u_int16_t *flags) 833{ 834 struct ccb_pathinq cpi; 835 cam_status status; 836 837 /* Find SIM capabilities */ 838 bzero(&cpi, sizeof(cpi)); 839 cpi.ccb_h.func_code = XPT_PATH_INQ; 840 send_ccb((union ccb *)&cpi, /*priority*/1); 841 status = cpi.ccb_h.status & CAM_STATUS_MASK; 842 if (status != CAM_REQ_CMP) { 843 fprintf(stderr, "CPI failed, status %#x\n", status); 844 return (status); 845 } 846 847 /* Can only enable on controllers that support target mode */ 848 if ((cpi.target_sprt & PIT_PROCESSOR) == 0) { 849 fprintf(stderr, "HBA does not support target mode\n"); 850 status = CAM_PATH_INVALID; 851 return (status); 852 } 853 854 *flags = cpi.hba_inquiry; 855 return (status); 856} 857 |
|
358static void | 858static void |
359quit_handler(int signum) | 859rel_simq() |
360{ | 860{ |
361 quit = 1; | 861 struct ccb_relsim crs; 862 863 bzero(&crs, sizeof(crs)); 864 crs.ccb_h.func_code = XPT_REL_SIMQ; 865 crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; 866 crs.openings = 0; 867 crs.release_timeout = 0; 868 crs.qfrozen_cnt = 0; 869 send_ccb((union ccb *)&crs, /*priority*/0); |
362} 363 | 870} 871 |
872/* Cancel all pending CCBs. */ |
|
364static void | 873static void |
365usage() | 874abort_all_pending() |
366{ | 875{ |
876 struct ccb_abort cab; 877 struct ccb_hdr *ccb_h; |
|
367 | 878 |
368 (void)fprintf(stderr, 369"usage: %-16s [ -d ] [-o output_file] [-i input_file] -p path -t target -l lun\n", 370 appname); | 879 if (debug) 880 warnx("abort_all_pending"); |
371 | 881 |
372 exit(EX_USAGE); | 882 bzero(&cab, sizeof(cab)); 883 cab.ccb_h.func_code = XPT_ABORT; 884 TAILQ_FOREACH(ccb_h, &pending_queue, periph_links.tqe) { 885 if (debug) 886 warnx("Aborting pending CCB %p\n", ccb_h); 887 cab.abort_ccb = (union ccb *)ccb_h; 888 send_ccb((union ccb *)&cab, /*priority*/1); 889 if (cab.ccb_h.status != CAM_REQ_CMP) { 890 warnx("Unable to abort CCB, status %#x\n", 891 cab.ccb_h.status); 892 } 893 } |
373} 374 | 894} 895 |
896static void 897usage() 898{ 899 fprintf(stderr, 900 "Usage: scsi_target [-AdST] [-b bufsize] [-c sectorsize]\n" 901 "\t\t[-r numbufs] [-s volsize] [-W 8,16,32]\n" 902 "\t\tbus:target:lun filename\n"); 903 exit(1); 904} |
|