]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/cam/scsi/scsi_pt.c
zfs: merge openzfs/zfs@0ee9b0239
[FreeBSD/FreeBSD.git] / sys / cam / scsi / scsi_pt.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Implementation of SCSI Processor Target Peripheral driver for CAM.
5  *
6  * Copyright (c) 1998 Justin T. Gibbs.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions, and the following disclaimer,
14  *    without modification, immediately at the beginning of the file.
15  * 2. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 #include <sys/param.h>
33 #include <sys/queue.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/types.h>
37 #include <sys/bio.h>
38 #include <sys/devicestat.h>
39 #include <sys/malloc.h>
40 #include <sys/conf.h>
41 #include <sys/ptio.h>
42
43 #include <cam/cam.h>
44 #include <cam/cam_ccb.h>
45 #include <cam/cam_periph.h>
46 #include <cam/cam_xpt_periph.h>
47 #include <cam/cam_debug.h>
48
49 #include <cam/scsi/scsi_all.h>
50 #include <cam/scsi/scsi_message.h>
51 #include <cam/scsi/scsi_pt.h>
52
53 #include "opt_pt.h"
54
55 typedef enum {
56         PT_STATE_PROBE,
57         PT_STATE_NORMAL
58 } pt_state;
59
60 typedef enum {
61         PT_FLAG_NONE            = 0x00,
62         PT_FLAG_OPEN            = 0x01,
63         PT_FLAG_DEVICE_INVALID  = 0x02,
64         PT_FLAG_RETRY_UA        = 0x04
65 } pt_flags;
66
67 typedef enum {
68         PT_CCB_BUFFER_IO        = 0x01,
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
77 struct 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         int      io_timeout;
84         struct cdev *dev;
85 };
86
87 static  d_open_t        ptopen;
88 static  d_close_t       ptclose;
89 static  d_strategy_t    ptstrategy;
90 static  periph_init_t   ptinit;
91 static  void            ptasync(void *callback_arg, uint32_t code,
92                                 struct cam_path *path, void *arg);
93 static  periph_ctor_t   ptctor;
94 static  periph_oninv_t  ptoninvalidate;
95 static  periph_dtor_t   ptdtor;
96 static  periph_start_t  ptstart;
97 static  void            ptdone(struct cam_periph *periph,
98                                union ccb *done_ccb);
99 static  d_ioctl_t       ptioctl;
100 static  int             pterror(union ccb *ccb, uint32_t cam_flags,
101                                 uint32_t sense_flags);
102
103 void    scsi_send_receive(struct ccb_scsiio *csio, uint32_t retries,
104                           void (*cbfcnp)(struct cam_periph *, union ccb *),
105                           u_int tag_action, int readop, u_int byte2,
106                           uint32_t xfer_len, uint8_t *data_ptr,
107                           uint8_t sense_len, uint32_t timeout);
108
109 static struct periph_driver ptdriver =
110 {
111         ptinit, "pt",
112         TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
113 };
114
115 PERIPHDRIVER_DECLARE(pt, ptdriver);
116
117 static struct cdevsw pt_cdevsw = {
118         .d_version =    D_VERSION,
119         .d_flags =      0,
120         .d_open =       ptopen,
121         .d_close =      ptclose,
122         .d_read =       physread,
123         .d_write =      physwrite,
124         .d_ioctl =      ptioctl,
125         .d_strategy =   ptstrategy,
126         .d_name =       "pt",
127 };
128
129 #ifndef SCSI_PT_DEFAULT_TIMEOUT
130 #define SCSI_PT_DEFAULT_TIMEOUT         60
131 #endif
132
133 static int
134 ptopen(struct cdev *dev, int flags, int fmt, struct thread *td)
135 {
136         struct cam_periph *periph;
137         struct pt_softc *softc;
138         int error = 0;
139
140         periph = (struct cam_periph *)dev->si_drv1;
141         if (cam_periph_acquire(periph) != 0)
142                 return (ENXIO); 
143
144         softc = (struct pt_softc *)periph->softc;
145
146         cam_periph_lock(periph);
147         if (softc->flags & PT_FLAG_DEVICE_INVALID) {
148                 cam_periph_release_locked(periph);
149                 cam_periph_unlock(periph);
150                 return(ENXIO);
151         }
152
153         if ((softc->flags & PT_FLAG_OPEN) == 0)
154                 softc->flags |= PT_FLAG_OPEN;
155         else {
156                 error = EBUSY;
157                 cam_periph_release(periph);
158         }
159
160         CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
161             ("ptopen: dev=%s\n", devtoname(dev)));
162
163         cam_periph_unlock(periph);
164         return (error);
165 }
166
167 static int
168 ptclose(struct cdev *dev, int flag, int fmt, struct thread *td)
169 {
170         struct  cam_periph *periph;
171         struct  pt_softc *softc;
172
173         periph = (struct cam_periph *)dev->si_drv1;
174         softc = (struct pt_softc *)periph->softc;
175
176         cam_periph_lock(periph);
177
178         softc->flags &= ~PT_FLAG_OPEN;
179         cam_periph_release_locked(periph);
180         cam_periph_unlock(periph);
181         return (0);
182 }
183
184 /*
185  * Actually translate the requested transfer into one the physical driver
186  * can understand.  The transfer is described by a buf and will include
187  * only one physical transfer.
188  */
189 static void
190 ptstrategy(struct bio *bp)
191 {
192         struct cam_periph *periph;
193         struct pt_softc *softc;
194
195         periph = (struct cam_periph *)bp->bio_dev->si_drv1;
196         bp->bio_resid = bp->bio_bcount;
197         if (periph == NULL) {
198                 biofinish(bp, NULL, ENXIO);
199                 return;
200         }
201         cam_periph_lock(periph);
202         softc = (struct pt_softc *)periph->softc;
203
204         /*
205          * If the device has been made invalid, error out
206          */
207         if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
208                 cam_periph_unlock(periph);
209                 biofinish(bp, NULL, ENXIO);
210                 return;
211         }
212
213         /*
214          * Place it in the queue of disk activities for this disk
215          */
216         bioq_insert_tail(&softc->bio_queue, bp);
217
218         /*
219          * Schedule ourselves for performing the work.
220          */
221         xpt_schedule(periph, CAM_PRIORITY_NORMAL);
222         cam_periph_unlock(periph);
223
224         return;
225 }
226
227 static void
228 ptinit(void)
229 {
230         cam_status status;
231
232         /*
233          * Install a global async callback.  This callback will
234          * receive async callbacks like "new device found".
235          */
236         status = xpt_register_async(AC_FOUND_DEVICE, ptasync, NULL, NULL);
237
238         if (status != CAM_REQ_CMP) {
239                 printf("pt: Failed to attach master async callback "
240                        "due to status 0x%x!\n", status);
241         }
242 }
243
244 static cam_status
245 ptctor(struct cam_periph *periph, void *arg)
246 {
247         struct pt_softc *softc;
248         struct ccb_getdev *cgd;
249         struct ccb_pathinq cpi;
250         struct make_dev_args args;
251         int error;
252
253         cgd = (struct ccb_getdev *)arg;
254         if (cgd == NULL) {
255                 printf("ptregister: no getdev CCB, can't register device\n");
256                 return(CAM_REQ_CMP_ERR);
257         }
258
259         softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
260
261         if (softc == NULL) {
262                 printf("daregister: Unable to probe new device. "
263                        "Unable to allocate softc\n");                           
264                 return(CAM_REQ_CMP_ERR);
265         }
266
267         bzero(softc, sizeof(*softc));
268         LIST_INIT(&softc->pending_ccbs);
269         softc->state = PT_STATE_NORMAL;
270         bioq_init(&softc->bio_queue);
271
272         softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
273
274         periph->softc = softc;
275
276         xpt_path_inq(&cpi, periph->path);
277
278         cam_periph_unlock(periph);
279
280         make_dev_args_init(&args);
281         args.mda_devsw = &pt_cdevsw;
282         args.mda_unit = periph->unit_number;
283         args.mda_uid = UID_ROOT;
284         args.mda_gid = GID_OPERATOR;
285         args.mda_mode = 0600;
286         args.mda_si_drv1 = periph;
287         error = make_dev_s(&args, &softc->dev, "%s%d", periph->periph_name,
288             periph->unit_number);
289         if (error != 0) {
290                 cam_periph_lock(periph);
291                 return (CAM_REQ_CMP_ERR);
292         }
293
294         softc->device_stats = devstat_new_entry("pt",
295                           periph->unit_number, 0,
296                           DEVSTAT_NO_BLOCKSIZE,
297                           SID_TYPE(&cgd->inq_data) |
298                           XPORT_DEVSTAT_TYPE(cpi.transport),
299                           DEVSTAT_PRIORITY_OTHER);
300
301         cam_periph_lock(periph);
302
303         /*
304          * Add async callbacks for bus reset and
305          * bus device reset calls.  I don't bother
306          * checking if this fails as, in most cases,
307          * the system will function just fine without
308          * them and the only alternative would be to
309          * not attach the device on failure.
310          */
311         xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE,
312                            ptasync, periph, periph->path);
313
314         /* Tell the user we've attached to the device */
315         xpt_announce_periph(periph, NULL);
316
317         return(CAM_REQ_CMP);
318 }
319
320 static void
321 ptoninvalidate(struct cam_periph *periph)
322 {
323         struct pt_softc *softc;
324
325         softc = (struct pt_softc *)periph->softc;
326
327         /*
328          * De-register any async callbacks.
329          */
330         xpt_register_async(0, ptasync, periph, periph->path);
331
332         softc->flags |= PT_FLAG_DEVICE_INVALID;
333
334         /*
335          * Return all queued I/O with ENXIO.
336          * XXX Handle any transactions queued to the card
337          *     with XPT_ABORT_CCB.
338          */
339         bioq_flush(&softc->bio_queue, NULL, ENXIO);
340 }
341
342 static void
343 ptdtor(struct cam_periph *periph)
344 {
345         struct pt_softc *softc;
346
347         softc = (struct pt_softc *)periph->softc;
348
349         devstat_remove_entry(softc->device_stats);
350         cam_periph_unlock(periph);
351         destroy_dev(softc->dev);
352         cam_periph_lock(periph);
353         free(softc, M_DEVBUF);
354 }
355
356 static void
357 ptasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
358 {
359         struct cam_periph *periph;
360
361         periph = (struct cam_periph *)callback_arg;
362         switch (code) {
363         case AC_FOUND_DEVICE:
364         {
365                 struct ccb_getdev *cgd;
366                 cam_status status;
367
368                 cgd = (struct ccb_getdev *)arg;
369                 if (cgd == NULL)
370                         break;
371
372                 if (cgd->protocol != PROTO_SCSI)
373                         break;
374                 if (SID_QUAL(&cgd->inq_data) != SID_QUAL_LU_CONNECTED)
375                         break;
376                 if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
377                         break;
378
379                 /*
380                  * Allocate a peripheral instance for
381                  * this device and start the probe
382                  * process.
383                  */
384                 status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
385                                           ptstart, "pt", CAM_PERIPH_BIO,
386                                           path, ptasync,
387                                           AC_FOUND_DEVICE, cgd);
388
389                 if (status != CAM_REQ_CMP
390                  && status != CAM_REQ_INPROG)
391                         printf("ptasync: Unable to attach to new device "
392                                 "due to status 0x%x\n", status);
393                 break;
394         }
395         case AC_SENT_BDR:
396         case AC_BUS_RESET:
397         {
398                 struct pt_softc *softc;
399                 struct ccb_hdr *ccbh;
400
401                 softc = (struct pt_softc *)periph->softc;
402                 /*
403                  * Don't fail on the expected unit attention
404                  * that will occur.
405                  */
406                 softc->flags |= PT_FLAG_RETRY_UA;
407                 LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le)
408                         ccbh->ccb_state |= PT_CCB_RETRY_UA;
409         }
410         /* FALLTHROUGH */
411         default:
412                 cam_periph_async(periph, code, path, arg);
413                 break;
414         }
415 }
416
417 static void
418 ptstart(struct cam_periph *periph, union ccb *start_ccb)
419 {
420         struct pt_softc *softc;
421         struct bio *bp;
422
423         softc = (struct pt_softc *)periph->softc;
424
425         CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ptstart\n"));
426
427         /*
428          * See if there is a buf with work for us to do..
429          */
430         bp = bioq_first(&softc->bio_queue);
431         if (bp == NULL) {
432                 xpt_release_ccb(start_ccb);
433         } else {
434                 bioq_remove(&softc->bio_queue, bp);
435
436                 devstat_start_transaction_bio(softc->device_stats, bp);
437
438                 scsi_send_receive(&start_ccb->csio,
439                                   /*retries*/4,
440                                   ptdone,
441                                   MSG_SIMPLE_Q_TAG,
442                                   bp->bio_cmd == BIO_READ,
443                                   /*byte2*/0,
444                                   bp->bio_bcount,
445                                   bp->bio_data,
446                                   /*sense_len*/SSD_FULL_SIZE,
447                                   /*timeout*/softc->io_timeout);
448
449                 start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO_UA;
450
451                 /*
452                  * Block out any asynchronous callbacks
453                  * while we touch the pending ccb list.
454                  */
455                 LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
456                                  periph_links.le);
457
458                 start_ccb->ccb_h.ccb_bp = bp;
459                 bp = bioq_first(&softc->bio_queue);
460
461                 xpt_action(start_ccb);
462                 
463                 if (bp != NULL) {
464                         /* Have more work to do, so ensure we stay scheduled */
465                         xpt_schedule(periph, CAM_PRIORITY_NORMAL);
466                 }
467         }
468 }
469
470 static void
471 ptdone(struct cam_periph *periph, union ccb *done_ccb)
472 {
473         struct pt_softc *softc;
474         struct ccb_scsiio *csio;
475
476         softc = (struct pt_softc *)periph->softc;
477
478         CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("ptdone\n"));
479
480         csio = &done_ccb->csio;
481         switch (csio->ccb_h.ccb_state) {
482         case PT_CCB_BUFFER_IO:
483         case PT_CCB_BUFFER_IO_UA:
484         {
485                 struct bio *bp;
486
487                 bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
488                 if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
489                         int error;
490                         int sf;
491                         
492                         if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
493                                 sf = SF_RETRY_UA;
494                         else
495                                 sf = 0;
496
497                         error = pterror(done_ccb, CAM_RETRY_SELTO, sf);
498                         if (error == ERESTART) {
499                                 /*
500                                  * A retry was scheuled, so
501                                  * just return.
502                                  */
503                                 return;
504                         }
505                         if (error != 0) {
506                                 if (error == ENXIO) {
507                                         /*
508                                          * Catastrophic error.  Mark our device
509                                          * as invalid.
510                                          */
511                                         xpt_print(periph->path,
512                                             "Invalidating device\n");
513                                         softc->flags |= PT_FLAG_DEVICE_INVALID;
514                                 }
515
516                                 /*
517                                  * return all queued I/O with EIO, so that
518                                  * the client can retry these I/Os in the
519                                  * proper order should it attempt to recover.
520                                  */
521                                 bioq_flush(&softc->bio_queue, NULL, EIO);
522                                 bp->bio_error = error;
523                                 bp->bio_resid = bp->bio_bcount;
524                                 bp->bio_flags |= BIO_ERROR;
525                         } else {
526                                 bp->bio_resid = csio->resid;
527                                 bp->bio_error = 0;
528                                 if (bp->bio_resid != 0) {
529                                         /* Short transfer ??? */
530                                         bp->bio_flags |= BIO_ERROR;
531                                 }
532                         }
533                         if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
534                                 cam_release_devq(done_ccb->ccb_h.path,
535                                                  /*relsim_flags*/0,
536                                                  /*reduction*/0,
537                                                  /*timeout*/0,
538                                                  /*getcount_only*/0);
539                 } else {
540                         bp->bio_resid = csio->resid;
541                         if (bp->bio_resid != 0)
542                                 bp->bio_flags |= BIO_ERROR;
543                 }
544
545                 /*
546                  * Block out any asynchronous callbacks
547                  * while we touch the pending ccb list.
548                  */
549                 LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
550
551                 biofinish(bp, softc->device_stats, 0);
552                 break;
553         }
554         }
555         xpt_release_ccb(done_ccb);
556 }
557
558 static int
559 pterror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags)
560 {
561
562         return(cam_periph_error(ccb, cam_flags, sense_flags));
563 }
564
565 static int
566 ptioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
567 {
568         struct cam_periph *periph;
569         struct pt_softc *softc;
570         int error = 0;
571
572         periph = (struct cam_periph *)dev->si_drv1;
573         softc = (struct pt_softc *)periph->softc;
574
575         cam_periph_lock(periph);
576
577         switch(cmd) {
578         case PTIOCGETTIMEOUT:
579                 if (softc->io_timeout >= 1000)
580                         *(int *)addr = softc->io_timeout / 1000;
581                 else
582                         *(int *)addr = 0;
583                 break;
584         case PTIOCSETTIMEOUT:
585                 if (*(int *)addr < 1) {
586                         error = EINVAL;
587                         break;
588                 }
589
590                 softc->io_timeout = *(int *)addr * 1000;
591
592                 break;
593         default:
594                 error = cam_periph_ioctl(periph, cmd, addr, pterror);
595                 break;
596         }
597
598         cam_periph_unlock(periph);
599
600         return(error);
601 }
602
603 void
604 scsi_send_receive(struct ccb_scsiio *csio, uint32_t retries,
605                   void (*cbfcnp)(struct cam_periph *, union ccb *),
606                   u_int tag_action, int readop, u_int byte2,
607                   uint32_t xfer_len, uint8_t *data_ptr, uint8_t sense_len,
608                   uint32_t timeout)
609 {
610         struct scsi_send_receive *scsi_cmd;
611
612         scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
613         scsi_cmd->opcode = readop ? RECEIVE : SEND;
614         scsi_cmd->byte2 = byte2;
615         scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
616         scsi_cmd->control = 0;
617
618         cam_fill_csio(csio,
619                       retries,
620                       cbfcnp,
621                       /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
622                       tag_action,
623                       data_ptr,
624                       xfer_len,
625                       sense_len,
626                       sizeof(*scsi_cmd),
627                       timeout);
628 }