Deleted Added
full compact
scsi_pt.c (101940) scsi_pt.c (111815)
1/*
2 * Implementation of SCSI Processor Target Peripheral driver for CAM.
3 *
4 * Copyright (c) 1998 Justin T. Gibbs.
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.
13 * 2. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
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 *
1/*
2 * Implementation of SCSI Processor Target Peripheral driver for CAM.
3 *
4 * Copyright (c) 1998 Justin T. Gibbs.
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.
13 * 2. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
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 *
28 * $FreeBSD: head/sys/cam/scsi/scsi_pt.c 101940 2002-08-15 20:54:03Z njl $
28 * $FreeBSD: head/sys/cam/scsi/scsi_pt.c 111815 2003-03-03 12:15:54Z phk $
29 */
30
31#include <sys/param.h>
32#include <sys/queue.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/types.h>
36#include <sys/bio.h>
37#include <sys/devicestat.h>
38#include <sys/malloc.h>
39#include <sys/conf.h>
40#include <sys/ptio.h>
41
42#include <cam/cam.h>
43#include <cam/cam_ccb.h>
44#include <cam/cam_periph.h>
45#include <cam/cam_xpt_periph.h>
46#include <cam/cam_debug.h>
47
48#include <cam/scsi/scsi_all.h>
49#include <cam/scsi/scsi_message.h>
50#include <cam/scsi/scsi_pt.h>
51
52#include "opt_pt.h"
53
54typedef enum {
55 PT_STATE_PROBE,
56 PT_STATE_NORMAL
57} pt_state;
58
59typedef enum {
60 PT_FLAG_NONE = 0x00,
61 PT_FLAG_OPEN = 0x01,
62 PT_FLAG_DEVICE_INVALID = 0x02,
63 PT_FLAG_RETRY_UA = 0x04
64} pt_flags;
65
66typedef enum {
67 PT_CCB_BUFFER_IO = 0x01,
68 PT_CCB_WAITING = 0x02,
69 PT_CCB_RETRY_UA = 0x04,
70 PT_CCB_BUFFER_IO_UA = PT_CCB_BUFFER_IO|PT_CCB_RETRY_UA
71} pt_ccb_state;
72
73/* Offsets into our private area for storing information */
74#define ccb_state ppriv_field0
75#define ccb_bp ppriv_ptr1
76
77struct pt_softc {
78 struct bio_queue_head bio_queue;
79 struct devstat device_stats;
80 LIST_HEAD(, ccb_hdr) pending_ccbs;
81 pt_state state;
82 pt_flags flags;
83 union ccb saved_ccb;
84 int io_timeout;
85 dev_t dev;
86};
87
88static d_open_t ptopen;
89static d_close_t ptclose;
90static d_strategy_t ptstrategy;
91static periph_init_t ptinit;
92static void ptasync(void *callback_arg, u_int32_t code,
93 struct cam_path *path, void *arg);
94static periph_ctor_t ptctor;
95static periph_oninv_t ptoninvalidate;
96static periph_dtor_t ptdtor;
97static periph_start_t ptstart;
98static void ptdone(struct cam_periph *periph,
99 union ccb *done_ccb);
100static d_ioctl_t ptioctl;
101static int pterror(union ccb *ccb, u_int32_t cam_flags,
102 u_int32_t sense_flags);
103
104void scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
105 void (*cbfcnp)(struct cam_periph *, union ccb *),
106 u_int tag_action, int readop, u_int byte2,
107 u_int32_t xfer_len, u_int8_t *data_ptr,
108 u_int8_t sense_len, u_int32_t timeout);
109
110static struct periph_driver ptdriver =
111{
112 ptinit, "pt",
113 TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
114};
115
116PERIPHDRIVER_DECLARE(pt, ptdriver);
117
118#define PT_CDEV_MAJOR 61
119
120static struct cdevsw pt_cdevsw = {
29 */
30
31#include <sys/param.h>
32#include <sys/queue.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/types.h>
36#include <sys/bio.h>
37#include <sys/devicestat.h>
38#include <sys/malloc.h>
39#include <sys/conf.h>
40#include <sys/ptio.h>
41
42#include <cam/cam.h>
43#include <cam/cam_ccb.h>
44#include <cam/cam_periph.h>
45#include <cam/cam_xpt_periph.h>
46#include <cam/cam_debug.h>
47
48#include <cam/scsi/scsi_all.h>
49#include <cam/scsi/scsi_message.h>
50#include <cam/scsi/scsi_pt.h>
51
52#include "opt_pt.h"
53
54typedef enum {
55 PT_STATE_PROBE,
56 PT_STATE_NORMAL
57} pt_state;
58
59typedef enum {
60 PT_FLAG_NONE = 0x00,
61 PT_FLAG_OPEN = 0x01,
62 PT_FLAG_DEVICE_INVALID = 0x02,
63 PT_FLAG_RETRY_UA = 0x04
64} pt_flags;
65
66typedef enum {
67 PT_CCB_BUFFER_IO = 0x01,
68 PT_CCB_WAITING = 0x02,
69 PT_CCB_RETRY_UA = 0x04,
70 PT_CCB_BUFFER_IO_UA = PT_CCB_BUFFER_IO|PT_CCB_RETRY_UA
71} pt_ccb_state;
72
73/* Offsets into our private area for storing information */
74#define ccb_state ppriv_field0
75#define ccb_bp ppriv_ptr1
76
77struct pt_softc {
78 struct bio_queue_head bio_queue;
79 struct devstat device_stats;
80 LIST_HEAD(, ccb_hdr) pending_ccbs;
81 pt_state state;
82 pt_flags flags;
83 union ccb saved_ccb;
84 int io_timeout;
85 dev_t dev;
86};
87
88static d_open_t ptopen;
89static d_close_t ptclose;
90static d_strategy_t ptstrategy;
91static periph_init_t ptinit;
92static void ptasync(void *callback_arg, u_int32_t code,
93 struct cam_path *path, void *arg);
94static periph_ctor_t ptctor;
95static periph_oninv_t ptoninvalidate;
96static periph_dtor_t ptdtor;
97static periph_start_t ptstart;
98static void ptdone(struct cam_periph *periph,
99 union ccb *done_ccb);
100static d_ioctl_t ptioctl;
101static int pterror(union ccb *ccb, u_int32_t cam_flags,
102 u_int32_t sense_flags);
103
104void scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
105 void (*cbfcnp)(struct cam_periph *, union ccb *),
106 u_int tag_action, int readop, u_int byte2,
107 u_int32_t xfer_len, u_int8_t *data_ptr,
108 u_int8_t sense_len, u_int32_t timeout);
109
110static struct periph_driver ptdriver =
111{
112 ptinit, "pt",
113 TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
114};
115
116PERIPHDRIVER_DECLARE(pt, ptdriver);
117
118#define PT_CDEV_MAJOR 61
119
120static struct cdevsw pt_cdevsw = {
121 /* open */ ptopen,
122 /* close */ ptclose,
123 /* read */ physread,
124 /* write */ physwrite,
125 /* ioctl */ ptioctl,
126 /* poll */ nopoll,
127 /* mmap */ nommap,
128 /* strategy */ ptstrategy,
129 /* name */ "pt",
130 /* maj */ PT_CDEV_MAJOR,
131 /* dump */ nodump,
132 /* psize */ nopsize,
133 /* flags */ 0,
121 .d_open = ptopen,
122 .d_close = ptclose,
123 .d_read = physread,
124 .d_write = physwrite,
125 .d_ioctl = ptioctl,
126 .d_strategy = ptstrategy,
127 .d_name = "pt",
128 .d_maj = PT_CDEV_MAJOR,
134};
135
136#ifndef SCSI_PT_DEFAULT_TIMEOUT
137#define SCSI_PT_DEFAULT_TIMEOUT 60
138#endif
139
140static int
141ptopen(dev_t dev, int flags, int fmt, struct thread *td)
142{
143 struct cam_periph *periph;
144 struct pt_softc *softc;
145 int unit;
146 int error;
147 int s;
148
149 unit = minor(dev);
150 periph = (struct cam_periph *)dev->si_drv1;
151 if (periph == NULL)
152 return (ENXIO);
153
154 softc = (struct pt_softc *)periph->softc;
155
156 s = splsoftcam();
157 if (softc->flags & PT_FLAG_DEVICE_INVALID) {
158 splx(s);
159 return(ENXIO);
160 }
161
162 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
163 ("ptopen: dev=%s (unit %d)\n", devtoname(dev), unit));
164
165 if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
166 splx(s);
167 return (error); /* error code from tsleep */
168 }
169
170 splx(s);
171
172 if ((softc->flags & PT_FLAG_OPEN) == 0) {
173 if (cam_periph_acquire(periph) != CAM_REQ_CMP)
174 error = ENXIO;
175 else
176 softc->flags |= PT_FLAG_OPEN;
177 } else
178 error = EBUSY;
179
180 cam_periph_unlock(periph);
181 return (error);
182}
183
184static int
185ptclose(dev_t dev, int flag, int fmt, struct thread *td)
186{
187 struct cam_periph *periph;
188 struct pt_softc *softc;
189 int error;
190
191 periph = (struct cam_periph *)dev->si_drv1;
192 if (periph == NULL)
193 return (ENXIO);
194
195 softc = (struct pt_softc *)periph->softc;
196
197 if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
198 return (error); /* error code from tsleep */
199
200 softc->flags &= ~PT_FLAG_OPEN;
201 cam_periph_unlock(periph);
202 cam_periph_release(periph);
203 return (0);
204}
205
206/*
207 * Actually translate the requested transfer into one the physical driver
208 * can understand. The transfer is described by a buf and will include
209 * only one physical transfer.
210 */
211static void
212ptstrategy(struct bio *bp)
213{
214 struct cam_periph *periph;
215 struct pt_softc *softc;
216 int s;
217
218 periph = (struct cam_periph *)bp->bio_dev->si_drv1;
219 bp->bio_resid = bp->bio_bcount;
220 if (periph == NULL) {
221 biofinish(bp, NULL, ENXIO);
222 return;
223 }
224 softc = (struct pt_softc *)periph->softc;
225
226 /*
227 * Mask interrupts so that the pack cannot be invalidated until
228 * after we are in the queue. Otherwise, we might not properly
229 * clean up one of the buffers.
230 */
231 s = splbio();
232
233 /*
234 * If the device has been made invalid, error out
235 */
236 if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
237 splx(s);
238 biofinish(bp, NULL, ENXIO);
239 return;
240 }
241
242 /*
243 * Place it in the queue of disk activities for this disk
244 */
245 bioq_insert_tail(&softc->bio_queue, bp);
246
247 splx(s);
248
249 /*
250 * Schedule ourselves for performing the work.
251 */
252 xpt_schedule(periph, /* XXX priority */1);
253
254 return;
255}
256
257static void
258ptinit(void)
259{
260 cam_status status;
261 struct cam_path *path;
262
263 /*
264 * Install a global async callback. This callback will
265 * receive async callbacks like "new device found".
266 */
267 status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
268 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
269
270 if (status == CAM_REQ_CMP) {
271 struct ccb_setasync csa;
272
273 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
274 csa.ccb_h.func_code = XPT_SASYNC_CB;
275 csa.event_enable = AC_FOUND_DEVICE;
276 csa.callback = ptasync;
277 csa.callback_arg = NULL;
278 xpt_action((union ccb *)&csa);
279 status = csa.ccb_h.status;
280 xpt_free_path(path);
281 }
282
283 if (status != CAM_REQ_CMP) {
284 printf("pt: Failed to attach master async callback "
285 "due to status 0x%x!\n", status);
286 }
287}
288
289static cam_status
290ptctor(struct cam_periph *periph, void *arg)
291{
292 struct pt_softc *softc;
293 struct ccb_setasync csa;
294 struct ccb_getdev *cgd;
295
296 cgd = (struct ccb_getdev *)arg;
297 if (periph == NULL) {
298 printf("ptregister: periph was NULL!!\n");
299 return(CAM_REQ_CMP_ERR);
300 }
301
302 if (cgd == NULL) {
303 printf("ptregister: no getdev CCB, can't register device\n");
304 return(CAM_REQ_CMP_ERR);
305 }
306
307 softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
308
309 if (softc == NULL) {
310 printf("daregister: Unable to probe new device. "
311 "Unable to allocate softc\n");
312 return(CAM_REQ_CMP_ERR);
313 }
314
315 bzero(softc, sizeof(*softc));
316 LIST_INIT(&softc->pending_ccbs);
317 softc->state = PT_STATE_NORMAL;
318 bioq_init(&softc->bio_queue);
319
320 softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
321
322 periph->softc = softc;
323
324 devstat_add_entry(&softc->device_stats, "pt",
325 periph->unit_number, 0,
326 DEVSTAT_NO_BLOCKSIZE,
327 SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
328 DEVSTAT_PRIORITY_OTHER);
329
330 softc->dev = make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT,
331 GID_OPERATOR, 0600, "%s%d", periph->periph_name,
332 periph->unit_number);
333 softc->dev->si_drv1 = periph;
334
335 /*
336 * Add async callbacks for bus reset and
337 * bus device reset calls. I don't bother
338 * checking if this fails as, in most cases,
339 * the system will function just fine without
340 * them and the only alternative would be to
341 * not attach the device on failure.
342 */
343 xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
344 csa.ccb_h.func_code = XPT_SASYNC_CB;
345 csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
346 csa.callback = ptasync;
347 csa.callback_arg = periph;
348 xpt_action((union ccb *)&csa);
349
350 /* Tell the user we've attached to the device */
351 xpt_announce_periph(periph, NULL);
352
353 return(CAM_REQ_CMP);
354}
355
356static void
357ptoninvalidate(struct cam_periph *periph)
358{
359 int s;
360 struct pt_softc *softc;
361 struct bio *q_bp;
362 struct ccb_setasync csa;
363
364 softc = (struct pt_softc *)periph->softc;
365
366 /*
367 * De-register any async callbacks.
368 */
369 xpt_setup_ccb(&csa.ccb_h, periph->path,
370 /* priority */ 5);
371 csa.ccb_h.func_code = XPT_SASYNC_CB;
372 csa.event_enable = 0;
373 csa.callback = ptasync;
374 csa.callback_arg = periph;
375 xpt_action((union ccb *)&csa);
376
377 softc->flags |= PT_FLAG_DEVICE_INVALID;
378
379 /*
380 * Although the oninvalidate() routines are always called at
381 * splsoftcam, we need to be at splbio() here to keep the buffer
382 * queue from being modified while we traverse it.
383 */
384 s = splbio();
385
386 /*
387 * Return all queued I/O with ENXIO.
388 * XXX Handle any transactions queued to the card
389 * with XPT_ABORT_CCB.
390 */
391 while ((q_bp = bioq_first(&softc->bio_queue)) != NULL){
392 bioq_remove(&softc->bio_queue, q_bp);
393 q_bp->bio_resid = q_bp->bio_bcount;
394 biofinish(q_bp, NULL, ENXIO);
395 }
396
397 splx(s);
398
399 xpt_print_path(periph->path);
400 printf("lost device\n");
401}
402
403static void
404ptdtor(struct cam_periph *periph)
405{
406 struct pt_softc *softc;
407
408 softc = (struct pt_softc *)periph->softc;
409
410 devstat_remove_entry(&softc->device_stats);
411
412 destroy_dev(softc->dev);
413
414 xpt_print_path(periph->path);
415 printf("removing device entry\n");
416 free(softc, M_DEVBUF);
417}
418
419static void
420ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
421{
422 struct cam_periph *periph;
423
424 periph = (struct cam_periph *)callback_arg;
425 switch (code) {
426 case AC_FOUND_DEVICE:
427 {
428 struct ccb_getdev *cgd;
429 cam_status status;
430
431 cgd = (struct ccb_getdev *)arg;
432 if (cgd == NULL)
433 break;
434
435 if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
436 break;
437
438 /*
439 * Allocate a peripheral instance for
440 * this device and start the probe
441 * process.
442 */
443 status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
444 ptstart, "pt", CAM_PERIPH_BIO,
445 cgd->ccb_h.path, ptasync,
446 AC_FOUND_DEVICE, cgd);
447
448 if (status != CAM_REQ_CMP
449 && status != CAM_REQ_INPROG)
450 printf("ptasync: Unable to attach to new device "
451 "due to status 0x%x\n", status);
452 break;
453 }
454 case AC_SENT_BDR:
455 case AC_BUS_RESET:
456 {
457 struct pt_softc *softc;
458 struct ccb_hdr *ccbh;
459 int s;
460
461 softc = (struct pt_softc *)periph->softc;
462 s = splsoftcam();
463 /*
464 * Don't fail on the expected unit attention
465 * that will occur.
466 */
467 softc->flags |= PT_FLAG_RETRY_UA;
468 LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le)
469 ccbh->ccb_state |= PT_CCB_RETRY_UA;
470 splx(s);
471 /* FALLTHROUGH */
472 }
473 default:
474 cam_periph_async(periph, code, path, arg);
475 break;
476 }
477}
478
479static void
480ptstart(struct cam_periph *periph, union ccb *start_ccb)
481{
482 struct pt_softc *softc;
483 struct bio *bp;
484 int s;
485
486 softc = (struct pt_softc *)periph->softc;
487
488 /*
489 * See if there is a buf with work for us to do..
490 */
491 s = splbio();
492 bp = bioq_first(&softc->bio_queue);
493 if (periph->immediate_priority <= periph->pinfo.priority) {
494 CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
495 ("queuing for immediate ccb\n"));
496 start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
497 SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
498 periph_links.sle);
499 periph->immediate_priority = CAM_PRIORITY_NONE;
500 splx(s);
501 wakeup(&periph->ccb_list);
502 } else if (bp == NULL) {
503 splx(s);
504 xpt_release_ccb(start_ccb);
505 } else {
506 int oldspl;
507
508 bioq_remove(&softc->bio_queue, bp);
509
510 devstat_start_transaction(&softc->device_stats);
511
512 scsi_send_receive(&start_ccb->csio,
513 /*retries*/4,
514 ptdone,
515 MSG_SIMPLE_Q_TAG,
516 bp->bio_cmd == BIO_READ,
517 /*byte2*/0,
518 bp->bio_bcount,
519 bp->bio_data,
520 /*sense_len*/SSD_FULL_SIZE,
521 /*timeout*/softc->io_timeout);
522
523 start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO_UA;
524
525 /*
526 * Block out any asyncronous callbacks
527 * while we touch the pending ccb list.
528 */
529 oldspl = splcam();
530 LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
531 periph_links.le);
532 splx(oldspl);
533
534 start_ccb->ccb_h.ccb_bp = bp;
535 bp = bioq_first(&softc->bio_queue);
536 splx(s);
537
538 xpt_action(start_ccb);
539
540 if (bp != NULL) {
541 /* Have more work to do, so ensure we stay scheduled */
542 xpt_schedule(periph, /* XXX priority */1);
543 }
544 }
545}
546
547static void
548ptdone(struct cam_periph *periph, union ccb *done_ccb)
549{
550 struct pt_softc *softc;
551 struct ccb_scsiio *csio;
552
553 softc = (struct pt_softc *)periph->softc;
554 csio = &done_ccb->csio;
555 switch (csio->ccb_h.ccb_state) {
556 case PT_CCB_BUFFER_IO:
557 case PT_CCB_BUFFER_IO_UA:
558 {
559 struct bio *bp;
560 int oldspl;
561
562 bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
563 if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
564 int error;
565 int s;
566 int sf;
567
568 if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
569 sf = SF_RETRY_UA;
570 else
571 sf = 0;
572
573 error = pterror(done_ccb, CAM_RETRY_SELTO, sf);
574 if (error == ERESTART) {
575 /*
576 * A retry was scheuled, so
577 * just return.
578 */
579 return;
580 }
581 if (error != 0) {
582 struct bio *q_bp;
583
584 s = splbio();
585
586 if (error == ENXIO) {
587 /*
588 * Catastrophic error. Mark our device
589 * as invalid.
590 */
591 xpt_print_path(periph->path);
592 printf("Invalidating device\n");
593 softc->flags |= PT_FLAG_DEVICE_INVALID;
594 }
595
596 /*
597 * return all queued I/O with EIO, so that
598 * the client can retry these I/Os in the
599 * proper order should it attempt to recover.
600 */
601 while ((q_bp = bioq_first(&softc->bio_queue))
602 != NULL) {
603 bioq_remove(&softc->bio_queue, q_bp);
604 q_bp->bio_resid = q_bp->bio_bcount;
605 biofinish(q_bp, NULL, EIO);
606 }
607 splx(s);
608 bp->bio_error = error;
609 bp->bio_resid = bp->bio_bcount;
610 bp->bio_flags |= BIO_ERROR;
611 } else {
612 bp->bio_resid = csio->resid;
613 bp->bio_error = 0;
614 if (bp->bio_resid != 0) {
615 /* Short transfer ??? */
616 bp->bio_flags |= BIO_ERROR;
617 }
618 }
619 if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
620 cam_release_devq(done_ccb->ccb_h.path,
621 /*relsim_flags*/0,
622 /*reduction*/0,
623 /*timeout*/0,
624 /*getcount_only*/0);
625 } else {
626 bp->bio_resid = csio->resid;
627 if (bp->bio_resid != 0)
628 bp->bio_flags |= BIO_ERROR;
629 }
630
631 /*
632 * Block out any asyncronous callbacks
633 * while we touch the pending ccb list.
634 */
635 oldspl = splcam();
636 LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
637 splx(oldspl);
638
639 biofinish(bp, &softc->device_stats, 0);
640 break;
641 }
642 case PT_CCB_WAITING:
643 /* Caller will release the CCB */
644 wakeup(&done_ccb->ccb_h.cbfcnp);
645 return;
646 }
647 xpt_release_ccb(done_ccb);
648}
649
650static int
651pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
652{
653 struct pt_softc *softc;
654 struct cam_periph *periph;
655
656 periph = xpt_path_periph(ccb->ccb_h.path);
657 softc = (struct pt_softc *)periph->softc;
658
659 return(cam_periph_error(ccb, cam_flags, sense_flags,
660 &softc->saved_ccb));
661}
662
663static int
664ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
665{
666 struct cam_periph *periph;
667 struct pt_softc *softc;
668 int error;
669
670 periph = (struct cam_periph *)dev->si_drv1;
671 if (periph == NULL)
672 return(ENXIO);
673
674 softc = (struct pt_softc *)periph->softc;
675
676 if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
677 return (error); /* error code from tsleep */
678 }
679
680 switch(cmd) {
681 case PTIOCGETTIMEOUT:
682 if (softc->io_timeout >= 1000)
683 *(int *)addr = softc->io_timeout / 1000;
684 else
685 *(int *)addr = 0;
686 break;
687 case PTIOCSETTIMEOUT:
688 {
689 int s;
690
691 if (*(int *)addr < 1) {
692 error = EINVAL;
693 break;
694 }
695
696 s = splsoftcam();
697 softc->io_timeout = *(int *)addr * 1000;
698 splx(s);
699
700 break;
701 }
702 default:
703 error = cam_periph_ioctl(periph, cmd, addr, pterror);
704 break;
705 }
706
707 cam_periph_unlock(periph);
708
709 return(error);
710}
711
712void
713scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
714 void (*cbfcnp)(struct cam_periph *, union ccb *),
715 u_int tag_action, int readop, u_int byte2,
716 u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
717 u_int32_t timeout)
718{
719 struct scsi_send_receive *scsi_cmd;
720
721 scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
722 scsi_cmd->opcode = readop ? RECEIVE : SEND;
723 scsi_cmd->byte2 = byte2;
724 scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
725 scsi_cmd->control = 0;
726
727 cam_fill_csio(csio,
728 retries,
729 cbfcnp,
730 /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
731 tag_action,
732 data_ptr,
733 xfer_len,
734 sense_len,
735 sizeof(*scsi_cmd),
736 timeout);
737}
129};
130
131#ifndef SCSI_PT_DEFAULT_TIMEOUT
132#define SCSI_PT_DEFAULT_TIMEOUT 60
133#endif
134
135static int
136ptopen(dev_t dev, int flags, int fmt, struct thread *td)
137{
138 struct cam_periph *periph;
139 struct pt_softc *softc;
140 int unit;
141 int error;
142 int s;
143
144 unit = minor(dev);
145 periph = (struct cam_periph *)dev->si_drv1;
146 if (periph == NULL)
147 return (ENXIO);
148
149 softc = (struct pt_softc *)periph->softc;
150
151 s = splsoftcam();
152 if (softc->flags & PT_FLAG_DEVICE_INVALID) {
153 splx(s);
154 return(ENXIO);
155 }
156
157 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
158 ("ptopen: dev=%s (unit %d)\n", devtoname(dev), unit));
159
160 if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
161 splx(s);
162 return (error); /* error code from tsleep */
163 }
164
165 splx(s);
166
167 if ((softc->flags & PT_FLAG_OPEN) == 0) {
168 if (cam_periph_acquire(periph) != CAM_REQ_CMP)
169 error = ENXIO;
170 else
171 softc->flags |= PT_FLAG_OPEN;
172 } else
173 error = EBUSY;
174
175 cam_periph_unlock(periph);
176 return (error);
177}
178
179static int
180ptclose(dev_t dev, int flag, int fmt, struct thread *td)
181{
182 struct cam_periph *periph;
183 struct pt_softc *softc;
184 int error;
185
186 periph = (struct cam_periph *)dev->si_drv1;
187 if (periph == NULL)
188 return (ENXIO);
189
190 softc = (struct pt_softc *)periph->softc;
191
192 if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
193 return (error); /* error code from tsleep */
194
195 softc->flags &= ~PT_FLAG_OPEN;
196 cam_periph_unlock(periph);
197 cam_periph_release(periph);
198 return (0);
199}
200
201/*
202 * Actually translate the requested transfer into one the physical driver
203 * can understand. The transfer is described by a buf and will include
204 * only one physical transfer.
205 */
206static void
207ptstrategy(struct bio *bp)
208{
209 struct cam_periph *periph;
210 struct pt_softc *softc;
211 int s;
212
213 periph = (struct cam_periph *)bp->bio_dev->si_drv1;
214 bp->bio_resid = bp->bio_bcount;
215 if (periph == NULL) {
216 biofinish(bp, NULL, ENXIO);
217 return;
218 }
219 softc = (struct pt_softc *)periph->softc;
220
221 /*
222 * Mask interrupts so that the pack cannot be invalidated until
223 * after we are in the queue. Otherwise, we might not properly
224 * clean up one of the buffers.
225 */
226 s = splbio();
227
228 /*
229 * If the device has been made invalid, error out
230 */
231 if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
232 splx(s);
233 biofinish(bp, NULL, ENXIO);
234 return;
235 }
236
237 /*
238 * Place it in the queue of disk activities for this disk
239 */
240 bioq_insert_tail(&softc->bio_queue, bp);
241
242 splx(s);
243
244 /*
245 * Schedule ourselves for performing the work.
246 */
247 xpt_schedule(periph, /* XXX priority */1);
248
249 return;
250}
251
252static void
253ptinit(void)
254{
255 cam_status status;
256 struct cam_path *path;
257
258 /*
259 * Install a global async callback. This callback will
260 * receive async callbacks like "new device found".
261 */
262 status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
263 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
264
265 if (status == CAM_REQ_CMP) {
266 struct ccb_setasync csa;
267
268 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
269 csa.ccb_h.func_code = XPT_SASYNC_CB;
270 csa.event_enable = AC_FOUND_DEVICE;
271 csa.callback = ptasync;
272 csa.callback_arg = NULL;
273 xpt_action((union ccb *)&csa);
274 status = csa.ccb_h.status;
275 xpt_free_path(path);
276 }
277
278 if (status != CAM_REQ_CMP) {
279 printf("pt: Failed to attach master async callback "
280 "due to status 0x%x!\n", status);
281 }
282}
283
284static cam_status
285ptctor(struct cam_periph *periph, void *arg)
286{
287 struct pt_softc *softc;
288 struct ccb_setasync csa;
289 struct ccb_getdev *cgd;
290
291 cgd = (struct ccb_getdev *)arg;
292 if (periph == NULL) {
293 printf("ptregister: periph was NULL!!\n");
294 return(CAM_REQ_CMP_ERR);
295 }
296
297 if (cgd == NULL) {
298 printf("ptregister: no getdev CCB, can't register device\n");
299 return(CAM_REQ_CMP_ERR);
300 }
301
302 softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
303
304 if (softc == NULL) {
305 printf("daregister: Unable to probe new device. "
306 "Unable to allocate softc\n");
307 return(CAM_REQ_CMP_ERR);
308 }
309
310 bzero(softc, sizeof(*softc));
311 LIST_INIT(&softc->pending_ccbs);
312 softc->state = PT_STATE_NORMAL;
313 bioq_init(&softc->bio_queue);
314
315 softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
316
317 periph->softc = softc;
318
319 devstat_add_entry(&softc->device_stats, "pt",
320 periph->unit_number, 0,
321 DEVSTAT_NO_BLOCKSIZE,
322 SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
323 DEVSTAT_PRIORITY_OTHER);
324
325 softc->dev = make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT,
326 GID_OPERATOR, 0600, "%s%d", periph->periph_name,
327 periph->unit_number);
328 softc->dev->si_drv1 = periph;
329
330 /*
331 * Add async callbacks for bus reset and
332 * bus device reset calls. I don't bother
333 * checking if this fails as, in most cases,
334 * the system will function just fine without
335 * them and the only alternative would be to
336 * not attach the device on failure.
337 */
338 xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
339 csa.ccb_h.func_code = XPT_SASYNC_CB;
340 csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
341 csa.callback = ptasync;
342 csa.callback_arg = periph;
343 xpt_action((union ccb *)&csa);
344
345 /* Tell the user we've attached to the device */
346 xpt_announce_periph(periph, NULL);
347
348 return(CAM_REQ_CMP);
349}
350
351static void
352ptoninvalidate(struct cam_periph *periph)
353{
354 int s;
355 struct pt_softc *softc;
356 struct bio *q_bp;
357 struct ccb_setasync csa;
358
359 softc = (struct pt_softc *)periph->softc;
360
361 /*
362 * De-register any async callbacks.
363 */
364 xpt_setup_ccb(&csa.ccb_h, periph->path,
365 /* priority */ 5);
366 csa.ccb_h.func_code = XPT_SASYNC_CB;
367 csa.event_enable = 0;
368 csa.callback = ptasync;
369 csa.callback_arg = periph;
370 xpt_action((union ccb *)&csa);
371
372 softc->flags |= PT_FLAG_DEVICE_INVALID;
373
374 /*
375 * Although the oninvalidate() routines are always called at
376 * splsoftcam, we need to be at splbio() here to keep the buffer
377 * queue from being modified while we traverse it.
378 */
379 s = splbio();
380
381 /*
382 * Return all queued I/O with ENXIO.
383 * XXX Handle any transactions queued to the card
384 * with XPT_ABORT_CCB.
385 */
386 while ((q_bp = bioq_first(&softc->bio_queue)) != NULL){
387 bioq_remove(&softc->bio_queue, q_bp);
388 q_bp->bio_resid = q_bp->bio_bcount;
389 biofinish(q_bp, NULL, ENXIO);
390 }
391
392 splx(s);
393
394 xpt_print_path(periph->path);
395 printf("lost device\n");
396}
397
398static void
399ptdtor(struct cam_periph *periph)
400{
401 struct pt_softc *softc;
402
403 softc = (struct pt_softc *)periph->softc;
404
405 devstat_remove_entry(&softc->device_stats);
406
407 destroy_dev(softc->dev);
408
409 xpt_print_path(periph->path);
410 printf("removing device entry\n");
411 free(softc, M_DEVBUF);
412}
413
414static void
415ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
416{
417 struct cam_periph *periph;
418
419 periph = (struct cam_periph *)callback_arg;
420 switch (code) {
421 case AC_FOUND_DEVICE:
422 {
423 struct ccb_getdev *cgd;
424 cam_status status;
425
426 cgd = (struct ccb_getdev *)arg;
427 if (cgd == NULL)
428 break;
429
430 if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
431 break;
432
433 /*
434 * Allocate a peripheral instance for
435 * this device and start the probe
436 * process.
437 */
438 status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
439 ptstart, "pt", CAM_PERIPH_BIO,
440 cgd->ccb_h.path, ptasync,
441 AC_FOUND_DEVICE, cgd);
442
443 if (status != CAM_REQ_CMP
444 && status != CAM_REQ_INPROG)
445 printf("ptasync: Unable to attach to new device "
446 "due to status 0x%x\n", status);
447 break;
448 }
449 case AC_SENT_BDR:
450 case AC_BUS_RESET:
451 {
452 struct pt_softc *softc;
453 struct ccb_hdr *ccbh;
454 int s;
455
456 softc = (struct pt_softc *)periph->softc;
457 s = splsoftcam();
458 /*
459 * Don't fail on the expected unit attention
460 * that will occur.
461 */
462 softc->flags |= PT_FLAG_RETRY_UA;
463 LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le)
464 ccbh->ccb_state |= PT_CCB_RETRY_UA;
465 splx(s);
466 /* FALLTHROUGH */
467 }
468 default:
469 cam_periph_async(periph, code, path, arg);
470 break;
471 }
472}
473
474static void
475ptstart(struct cam_periph *periph, union ccb *start_ccb)
476{
477 struct pt_softc *softc;
478 struct bio *bp;
479 int s;
480
481 softc = (struct pt_softc *)periph->softc;
482
483 /*
484 * See if there is a buf with work for us to do..
485 */
486 s = splbio();
487 bp = bioq_first(&softc->bio_queue);
488 if (periph->immediate_priority <= periph->pinfo.priority) {
489 CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
490 ("queuing for immediate ccb\n"));
491 start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
492 SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
493 periph_links.sle);
494 periph->immediate_priority = CAM_PRIORITY_NONE;
495 splx(s);
496 wakeup(&periph->ccb_list);
497 } else if (bp == NULL) {
498 splx(s);
499 xpt_release_ccb(start_ccb);
500 } else {
501 int oldspl;
502
503 bioq_remove(&softc->bio_queue, bp);
504
505 devstat_start_transaction(&softc->device_stats);
506
507 scsi_send_receive(&start_ccb->csio,
508 /*retries*/4,
509 ptdone,
510 MSG_SIMPLE_Q_TAG,
511 bp->bio_cmd == BIO_READ,
512 /*byte2*/0,
513 bp->bio_bcount,
514 bp->bio_data,
515 /*sense_len*/SSD_FULL_SIZE,
516 /*timeout*/softc->io_timeout);
517
518 start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO_UA;
519
520 /*
521 * Block out any asyncronous callbacks
522 * while we touch the pending ccb list.
523 */
524 oldspl = splcam();
525 LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
526 periph_links.le);
527 splx(oldspl);
528
529 start_ccb->ccb_h.ccb_bp = bp;
530 bp = bioq_first(&softc->bio_queue);
531 splx(s);
532
533 xpt_action(start_ccb);
534
535 if (bp != NULL) {
536 /* Have more work to do, so ensure we stay scheduled */
537 xpt_schedule(periph, /* XXX priority */1);
538 }
539 }
540}
541
542static void
543ptdone(struct cam_periph *periph, union ccb *done_ccb)
544{
545 struct pt_softc *softc;
546 struct ccb_scsiio *csio;
547
548 softc = (struct pt_softc *)periph->softc;
549 csio = &done_ccb->csio;
550 switch (csio->ccb_h.ccb_state) {
551 case PT_CCB_BUFFER_IO:
552 case PT_CCB_BUFFER_IO_UA:
553 {
554 struct bio *bp;
555 int oldspl;
556
557 bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
558 if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
559 int error;
560 int s;
561 int sf;
562
563 if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
564 sf = SF_RETRY_UA;
565 else
566 sf = 0;
567
568 error = pterror(done_ccb, CAM_RETRY_SELTO, sf);
569 if (error == ERESTART) {
570 /*
571 * A retry was scheuled, so
572 * just return.
573 */
574 return;
575 }
576 if (error != 0) {
577 struct bio *q_bp;
578
579 s = splbio();
580
581 if (error == ENXIO) {
582 /*
583 * Catastrophic error. Mark our device
584 * as invalid.
585 */
586 xpt_print_path(periph->path);
587 printf("Invalidating device\n");
588 softc->flags |= PT_FLAG_DEVICE_INVALID;
589 }
590
591 /*
592 * return all queued I/O with EIO, so that
593 * the client can retry these I/Os in the
594 * proper order should it attempt to recover.
595 */
596 while ((q_bp = bioq_first(&softc->bio_queue))
597 != NULL) {
598 bioq_remove(&softc->bio_queue, q_bp);
599 q_bp->bio_resid = q_bp->bio_bcount;
600 biofinish(q_bp, NULL, EIO);
601 }
602 splx(s);
603 bp->bio_error = error;
604 bp->bio_resid = bp->bio_bcount;
605 bp->bio_flags |= BIO_ERROR;
606 } else {
607 bp->bio_resid = csio->resid;
608 bp->bio_error = 0;
609 if (bp->bio_resid != 0) {
610 /* Short transfer ??? */
611 bp->bio_flags |= BIO_ERROR;
612 }
613 }
614 if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
615 cam_release_devq(done_ccb->ccb_h.path,
616 /*relsim_flags*/0,
617 /*reduction*/0,
618 /*timeout*/0,
619 /*getcount_only*/0);
620 } else {
621 bp->bio_resid = csio->resid;
622 if (bp->bio_resid != 0)
623 bp->bio_flags |= BIO_ERROR;
624 }
625
626 /*
627 * Block out any asyncronous callbacks
628 * while we touch the pending ccb list.
629 */
630 oldspl = splcam();
631 LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
632 splx(oldspl);
633
634 biofinish(bp, &softc->device_stats, 0);
635 break;
636 }
637 case PT_CCB_WAITING:
638 /* Caller will release the CCB */
639 wakeup(&done_ccb->ccb_h.cbfcnp);
640 return;
641 }
642 xpt_release_ccb(done_ccb);
643}
644
645static int
646pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
647{
648 struct pt_softc *softc;
649 struct cam_periph *periph;
650
651 periph = xpt_path_periph(ccb->ccb_h.path);
652 softc = (struct pt_softc *)periph->softc;
653
654 return(cam_periph_error(ccb, cam_flags, sense_flags,
655 &softc->saved_ccb));
656}
657
658static int
659ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
660{
661 struct cam_periph *periph;
662 struct pt_softc *softc;
663 int error;
664
665 periph = (struct cam_periph *)dev->si_drv1;
666 if (periph == NULL)
667 return(ENXIO);
668
669 softc = (struct pt_softc *)periph->softc;
670
671 if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
672 return (error); /* error code from tsleep */
673 }
674
675 switch(cmd) {
676 case PTIOCGETTIMEOUT:
677 if (softc->io_timeout >= 1000)
678 *(int *)addr = softc->io_timeout / 1000;
679 else
680 *(int *)addr = 0;
681 break;
682 case PTIOCSETTIMEOUT:
683 {
684 int s;
685
686 if (*(int *)addr < 1) {
687 error = EINVAL;
688 break;
689 }
690
691 s = splsoftcam();
692 softc->io_timeout = *(int *)addr * 1000;
693 splx(s);
694
695 break;
696 }
697 default:
698 error = cam_periph_ioctl(periph, cmd, addr, pterror);
699 break;
700 }
701
702 cam_periph_unlock(periph);
703
704 return(error);
705}
706
707void
708scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
709 void (*cbfcnp)(struct cam_periph *, union ccb *),
710 u_int tag_action, int readop, u_int byte2,
711 u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
712 u_int32_t timeout)
713{
714 struct scsi_send_receive *scsi_cmd;
715
716 scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
717 scsi_cmd->opcode = readop ? RECEIVE : SEND;
718 scsi_cmd->byte2 = byte2;
719 scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
720 scsi_cmd->control = 0;
721
722 cam_fill_csio(csio,
723 retries,
724 cbfcnp,
725 /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
726 tag_action,
727 data_ptr,
728 xfer_len,
729 sense_len,
730 sizeof(*scsi_cmd),
731 timeout);
732}