xref: /dragonfly/sys/bus/cam/scsi/scsi_target.c (revision 857fcb57d0e5e7f20e3d0ef58163c718377e363e)
1 /*
2  * Generic SCSI Target Kernel Mode Driver
3  *
4  * Copyright (c) 2002 Nate Lawson.
5  * Copyright (c) 1998, 1999, 2001, 2002 Justin T. Gibbs.
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.
14  * 2. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
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  *
29  * $FreeBSD: src/sys/cam/scsi/scsi_target.c,v 1.22.2.7 2003/02/18 22:07:10 njl Exp $
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/uio.h>
35 #include <sys/kernel.h>
36 #include <sys/caps.h>
37 #include <sys/conf.h>
38 #include <sys/device.h>
39 #include <sys/malloc.h>
40 #include <sys/vnode.h>
41 #include <sys/devicestat.h>
42 #include <sys/thread2.h>
43 #include <sys/devfs.h>
44 
45 #include "../cam.h"
46 #include "../cam_ccb.h"
47 #include "../cam_periph.h"
48 #include "../cam_xpt_periph.h"
49 #include "../cam_sim.h"
50 #include "scsi_targetio.h"
51 
52 /* Transaction information attached to each CCB sent by the user */
53 struct targ_cmd_descr {
54           struct cam_periph_map_info  mapinfo;
55           TAILQ_ENTRY(targ_cmd_descr) tqe;
56           union ccb *user_ccb;
57           int          priority;
58           int          func_code;
59 };
60 
61 /* Offset into the private CCB area for storing our descriptor */
62 #define targ_descr  periph_priv.entries[1].ptr
63 
64 TAILQ_HEAD(descr_queue, targ_cmd_descr);
65 
66 typedef enum {
67           TARG_STATE_RESV               = 0x00, /* Invalid state */
68           TARG_STATE_OPENED   = 0x01, /* Device opened, softc initialized */
69           TARG_STATE_LUN_ENABLED        = 0x02  /* Device enabled for a path */
70 } targ_state;
71 
72 /* Per-instance device software context */
73 struct targ_softc {
74           /* CCBs (CTIOs, ATIOs, INOTs) pending on the controller */
75           struct ccb_queue     pending_ccb_queue;
76 
77           /* Command descriptors awaiting CTIO resources from the XPT */
78           struct descr_queue   work_queue;
79 
80           /* Command descriptors that have been aborted back to the user. */
81           struct descr_queue   abort_queue;
82 
83           /*
84            * Queue of CCBs that have been copied out to userland, but our
85            * userland daemon has not yet seen.
86            */
87           struct ccb_queue     user_ccb_queue;
88 
89           struct cam_periph   *periph;
90           struct cam_path               *path;
91           targ_state                     state;
92           struct kqinfo                  read_kq;
93           struct devstat                 device_stats;
94 };
95 
96 static d_open_t               targopen;
97 static d_close_t    targclose;
98 static d_read_t               targread;
99 static d_write_t    targwrite;
100 static d_ioctl_t    targioctl;
101 static d_kqfilter_t targkqfilter;
102 static d_clone_t    targclone;
103 DEVFS_DEFINE_CLONE_BITMAP(targ);
104 
105 static void                   targfiltdetach(struct knote *kn);
106 static int                    targreadfilt(struct knote *kn, long hint);
107 static int                    targwritefilt(struct knote *kn, long hint);
108 static struct filterops targread_filtops =
109           { FILTEROP_ISFD, NULL, targfiltdetach, targreadfilt };
110 static struct filterops targwrite_filtops =
111           { FILTEROP_ISFD, NULL, targfiltdetach, targwritefilt };
112 
113 static struct dev_ops targ_ops = {
114           { "targ", 0, 0 },
115           .d_open = targopen,
116           .d_close = targclose,
117           .d_read = targread,
118           .d_write = targwrite,
119           .d_ioctl = targioctl,
120           .d_kqfilter = targkqfilter
121 };
122 
123 static cam_status   targendislun(struct cam_path *path, int enable,
124                                              int grp6_len, int grp7_len);
125 static cam_status   targenable(struct targ_softc *softc,
126                                            struct cam_path *path,
127                                            int grp6_len, int grp7_len);
128 static cam_status   targdisable(struct targ_softc *softc);
129 static periph_ctor_t    targctor;
130 static periph_dtor_t    targdtor;
131 static periph_start_t   targstart;
132 static int                    targusermerge(struct targ_softc *softc,
133                                               struct targ_cmd_descr *descr,
134                                               union ccb *ccb);
135 static int                    targsendccb(struct targ_softc *softc, union ccb *ccb,
136                                             struct targ_cmd_descr *descr);
137 static void                   targdone(struct cam_periph *periph,
138                                          union  ccb *done_ccb);
139 static int                    targreturnccb(struct targ_softc *softc,
140                                               union  ccb *ccb);
141 static union ccb *  targgetccb(struct targ_softc *softc, xpt_opcode type,
142                                            int priority);
143 static void                   targfreeccb(struct targ_softc *softc, union ccb *ccb);
144 static struct targ_cmd_descr *
145                               targgetdescr(struct targ_softc *softc);
146 static periph_init_t          targinit;
147 static void                   targasync(void *callback_arg, u_int32_t code,
148                                           struct cam_path *path, void *arg);
149 static void                   abort_all_pending(struct targ_softc *softc);
150 static void                   notify_user(struct targ_softc *softc);
151 static int                    targcamstatus(cam_status status);
152 static size_t                 targccblen(xpt_opcode func_code);
153 
154 static struct periph_driver targdriver =
155 {
156           targinit, "targ",
157           TAILQ_HEAD_INITIALIZER(targdriver.units), /* generation */ 0
158 };
159 PERIPHDRIVER_DECLARE(targ, targdriver);
160 
161 static MALLOC_DEFINE(M_TARG, "TARG", "TARG data");
162 
163 /*
164  * Create softc and initialize it. Only one proc can open each targ device.
165  * There is no locking here because a periph doesn't get created until an
166  * ioctl is issued to do so, and that can't happen until this method returns.
167  */
168 static int
targopen(struct dev_open_args * ap)169 targopen(struct dev_open_args *ap)
170 {
171           cdev_t dev = ap->a_head.a_dev;
172           struct targ_softc *softc;
173 
174           /*
175            * Disallow CAM access if RESTRICTEDROOT
176            */
177           if (caps_priv_check_self(SYSCAP_RESTRICTEDROOT))
178                     return (EPERM);
179 
180           if (dev->si_drv1 != 0)
181                     return (EBUSY);
182 
183           /* Mark device busy before any potentially blocking operations */
184           dev->si_drv1 = (void *)~0;
185           reference_dev(dev);           /* save ref for later destroy_dev() */
186 
187           /* Create the targ device, allocate its softc, initialize it */
188 #if 0
189           make_dev(&targ_ops, minor(dev), UID_ROOT, GID_WHEEL, 0600,
190                                "targ%d", lminor(dev));
191 #endif
192           softc = kmalloc(sizeof(*softc), M_TARG, M_INTWAIT | M_ZERO);
193           dev->si_drv1 = softc;
194           softc->state = TARG_STATE_OPENED;
195           softc->periph = NULL;
196           softc->path = NULL;
197 
198           TAILQ_INIT(&softc->pending_ccb_queue);
199           TAILQ_INIT(&softc->work_queue);
200           TAILQ_INIT(&softc->abort_queue);
201           TAILQ_INIT(&softc->user_ccb_queue);
202 
203           return (0);
204 }
205 
206 /* Disable LUN if enabled and teardown softc */
207 static int
targclose(struct dev_close_args * ap)208 targclose(struct dev_close_args *ap)
209 {
210           cdev_t dev = ap->a_head.a_dev;
211           struct targ_softc     *softc;
212           struct cam_periph     *periph;
213           int    error;
214 
215           softc = (struct targ_softc *)dev->si_drv1;
216           if ((softc->periph == NULL) ||
217               (softc->state & TARG_STATE_LUN_ENABLED) == 0) {
218                     devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(targ), dev->si_uminor);
219                     destroy_dev(dev);
220                     kfree(softc, M_TARG);
221                     return (0);
222           }
223 
224           /*
225            * Acquire a hold on the periph so that it doesn't go away before
226            * we are ready at the end of the function.
227            */
228           periph = softc->periph;
229           cam_periph_acquire(periph);
230           cam_periph_lock(periph);
231           error = targdisable(softc);
232           if (error == CAM_REQ_CMP) {
233                     dev->si_drv1 = 0;
234                     if (softc->periph != NULL) {
235                               cam_periph_invalidate(softc->periph);
236                               softc->periph = NULL;
237                     }
238                     destroy_dev(dev);   /* eats the open ref */
239                     devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(targ), dev->si_uminor);
240                     kfree(softc, M_TARG);
241           } else {
242                     release_dev(dev);
243           }
244           cam_periph_unlock(periph);
245           cam_periph_release(periph);
246 
247           return (error);
248 }
249 
250 /* Enable/disable LUNs, set debugging level */
251 static int
targioctl(struct dev_ioctl_args * ap)252 targioctl(struct dev_ioctl_args *ap)
253 {
254           struct targ_softc *softc;
255           cam_status             status;
256 
257           softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1;
258 
259           switch (ap->a_cmd) {
260           case TARGIOCENABLE:
261           {
262                     struct ioc_enable_lun         *new_lun;
263                     struct cam_path               *path;
264                     struct cam_sim                *sim;
265 
266                     new_lun = (struct ioc_enable_lun *)ap->a_data;
267                     status = xpt_create_path_unlocked(&path, /*periph*/NULL,
268                                                               new_lun->path_id,
269                                                               new_lun->target_id,
270                                                               new_lun->lun_id);
271                     if (status != CAM_REQ_CMP) {
272                               kprintf("Couldn't create path, status %#x\n", status);
273                               break;
274                     }
275                     sim = xpt_path_sim(path);
276                     CAM_SIM_LOCK(sim);
277                     status = targenable(softc, path, new_lun->grp6_len,
278                                             new_lun->grp7_len);
279                     xpt_free_path(path);
280                     CAM_SIM_UNLOCK(sim);
281                     break;
282           }
283           case TARGIOCDISABLE:
284                     if (softc->periph == NULL) {
285                               status = CAM_DEV_NOT_THERE;
286                               break;
287                     }
288                     cam_periph_lock(softc->periph);
289                     status = targdisable(softc);
290                     cam_periph_unlock(softc->periph);
291                     break;
292           case TARGIOCDEBUG:
293           {
294 #ifdef    CAMDEBUG
295                     struct ccb_debug *cdbg;
296 
297                     /* If no periph available, disallow debugging changes */
298                     if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) {
299                               status = CAM_DEV_NOT_THERE;
300                               break;
301                     }
302                     cdbg = &xpt_alloc_ccb()->cdbg;
303                     if (*((int *)ap->a_data) != 0)
304                               cdbg->flags = CAM_DEBUG_PERIPH;
305                     else
306                               cdbg->flags = CAM_DEBUG_NONE;
307                     cam_periph_lock(softc->periph);
308                     xpt_setup_ccb(&cdbg->ccb_h, softc->path, /*priority*/0);
309                     cdbg->ccb_h.func_code = XPT_DEBUG;
310                     cdbg->ccb_h.cbfcnp = targdone;
311 
312                     xpt_action((union ccb *)cdbg);
313                     cam_periph_unlock(softc->periph);
314                     status = cdbg->ccb_h.status & CAM_STATUS_MASK;
315                     xpt_free_ccb(&cdbg->ccb_h);
316 #else
317                     status = CAM_FUNC_NOTAVAIL;
318 #endif
319                     break;
320           }
321           default:
322                     status = CAM_PROVIDE_FAIL;
323                     break;
324           }
325 
326           return (targcamstatus(status));
327 }
328 
329 static int
targkqfilter(struct dev_kqfilter_args * ap)330 targkqfilter(struct dev_kqfilter_args *ap)
331 {
332           struct    knote *kn = ap->a_kn;
333           struct  targ_softc *softc;
334 
335           softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1;
336 
337           ap->a_result = 0;
338 
339           switch (kn->kn_filter) {
340           case EVFILT_READ:
341                     kn->kn_hook = (caddr_t)softc;
342                     kn->kn_fop = &targread_filtops;
343                     break;
344           case EVFILT_WRITE:
345                     kn->kn_hook = (caddr_t)softc;
346                     kn->kn_fop = &targwrite_filtops;
347           default:
348                     ap->a_result = EOPNOTSUPP;
349                     return (0);
350           }
351 
352           knote_insert(&softc->read_kq.ki_note, kn);
353           return (0);
354 }
355 
356 static void
targfiltdetach(struct knote * kn)357 targfiltdetach(struct knote *kn)
358 {
359           struct  targ_softc *softc;
360 
361           softc = (struct targ_softc *)kn->kn_hook;
362           knote_remove(&softc->read_kq.ki_note, kn);
363 }
364 
365 /* Notify the user's kqueue when the user queue or abort queue gets a CCB */
366 static int
targreadfilt(struct knote * kn,long hint)367 targreadfilt(struct knote *kn, long hint)
368 {
369           struct targ_softc *softc;
370           int       retval;
371 
372           softc = (struct targ_softc *)kn->kn_hook;
373           cam_periph_lock(softc->periph);
374           retval = !TAILQ_EMPTY(&softc->user_ccb_queue) ||
375                      !TAILQ_EMPTY(&softc->abort_queue);
376           cam_periph_unlock(softc->periph);
377           return (retval);
378 }
379 
380 /* write() is always ok */
381 static int
targwritefilt(struct knote * kn,long hint)382 targwritefilt(struct knote *kn, long hint)
383 {
384           return (1);
385 }
386 
387 /* Send the HBA the enable/disable message */
388 static cam_status
targendislun(struct cam_path * path,int enable,int grp6_len,int grp7_len)389 targendislun(struct cam_path *path, int enable, int grp6_len, int grp7_len)
390 {
391           struct ccb_en_lun *en_ccb;
392           cam_status            status;
393 
394           /* Tell the lun to begin answering selects */
395           en_ccb = &xpt_alloc_ccb()->cel;
396           xpt_setup_ccb(&en_ccb->ccb_h, path, /*priority*/1);
397           en_ccb->ccb_h.func_code = XPT_EN_LUN;
398           /* Don't need support for any vendor specific commands */
399           en_ccb->grp6_len = grp6_len;
400           en_ccb->grp7_len = grp7_len;
401           en_ccb->enable = enable ? 1 : 0;
402           xpt_action((union ccb *)en_ccb);
403           status = en_ccb->ccb_h.status & CAM_STATUS_MASK;
404           if (status != CAM_REQ_CMP) {
405                     xpt_print(path, "%sable lun CCB rejected, status %#x\n",
406                         enable ? "en" : "dis", status);
407           }
408           xpt_free_ccb(&en_ccb->ccb_h);
409 
410           return (status);
411 }
412 
413 /* Enable target mode on a LUN, given its path */
414 static cam_status
targenable(struct targ_softc * softc,struct cam_path * path,int grp6_len,int grp7_len)415 targenable(struct targ_softc *softc, struct cam_path *path, int grp6_len,
416              int grp7_len)
417 {
418           struct cam_periph *periph;
419           struct ccb_pathinq *cpi;
420           cam_status             status;
421 
422           if ((softc->state & TARG_STATE_LUN_ENABLED) != 0)
423                     return (CAM_LUN_ALRDY_ENA);
424 
425           /* Make sure SIM supports target mode */
426           cpi = &xpt_alloc_ccb()->cpi;
427           xpt_setup_ccb(&cpi->ccb_h, path, /*priority*/1);
428           cpi->ccb_h.func_code = XPT_PATH_INQ;
429           xpt_action((union ccb *)cpi);
430           status = cpi->ccb_h.status & CAM_STATUS_MASK;
431           if (status != CAM_REQ_CMP) {
432                     kprintf("pathinq failed, status %#x\n", status);
433                     goto enable_fail;
434           }
435           if ((cpi->target_sprt & PIT_PROCESSOR) == 0) {
436                     kprintf("controller does not support target mode\n");
437                     status = CAM_FUNC_NOTAVAIL;
438                     goto enable_fail;
439           }
440 
441           /* Destroy any periph on our path if it is disabled */
442           periph = cam_periph_find(path, "targ");
443           if (periph != NULL) {
444                     struct targ_softc *del_softc;
445 
446                     del_softc = (struct targ_softc *)periph->softc;
447                     if ((del_softc->state & TARG_STATE_LUN_ENABLED) == 0) {
448                               cam_periph_invalidate(del_softc->periph);
449                               del_softc->periph = NULL;
450                     } else {
451                               kprintf("Requested path still in use by targ%d\n",
452                                      periph->unit_number);
453                               status = CAM_LUN_ALRDY_ENA;
454                               goto enable_fail;
455                     }
456           }
457 
458           /* Create a periph instance attached to this path */
459           status = cam_periph_alloc(targctor, NULL, targdtor, targstart,
460                               "targ", CAM_PERIPH_BIO, path, targasync, 0, softc);
461           if (status != CAM_REQ_CMP) {
462                     kprintf("cam_periph_alloc failed, status %#x\n", status);
463                     goto enable_fail;
464           }
465 
466           /* Ensure that the periph now exists. */
467           if (cam_periph_find(path, "targ") == NULL) {
468                     panic("targenable: succeeded but no periph?");
469                     /* NOTREACHED */
470           }
471 
472           /* Send the enable lun message */
473           status = targendislun(path, /*enable*/1, grp6_len, grp7_len);
474           if (status != CAM_REQ_CMP) {
475                     kprintf("enable lun failed, status %#x\n", status);
476                     goto enable_fail;
477           }
478           softc->state |= TARG_STATE_LUN_ENABLED;
479 
480 enable_fail:
481           xpt_free_ccb(&cpi->ccb_h);
482 
483           return (status);
484 }
485 
486 /* Disable this softc's target instance if enabled */
487 static cam_status
targdisable(struct targ_softc * softc)488 targdisable(struct targ_softc *softc)
489 {
490           cam_status status;
491 
492           if ((softc->state & TARG_STATE_LUN_ENABLED) == 0)
493                     return (CAM_REQ_CMP);
494 
495           CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targdisable\n"));
496 
497           /* Abort any ccbs pending on the controller */
498           crit_enter();
499           abort_all_pending(softc);
500           crit_exit();
501 
502           /* Disable this lun */
503           status = targendislun(softc->path, /*enable*/0,
504                                     /*grp6_len*/0, /*grp7_len*/0);
505           if (status == CAM_REQ_CMP)
506                     softc->state &= ~TARG_STATE_LUN_ENABLED;
507           else
508                     kprintf("Disable lun failed, status %#x\n", status);
509 
510           return (status);
511 }
512 
513 /* Initialize a periph (called from cam_periph_alloc) */
514 static cam_status
targctor(struct cam_periph * periph,void * arg)515 targctor(struct cam_periph *periph, void *arg)
516 {
517           struct targ_softc *softc;
518 
519           /* Store pointer to softc for periph-driven routines */
520           softc = (struct targ_softc *)arg;
521           periph->softc = softc;
522           softc->periph = periph;
523           softc->path = periph->path;
524           return (CAM_REQ_CMP);
525 }
526 
527 static void
targdtor(struct cam_periph * periph)528 targdtor(struct cam_periph *periph)
529 {
530           struct targ_softc     *softc;
531           struct ccb_hdr            *ccb_h;
532           struct targ_cmd_descr *descr;
533 
534           softc = (struct targ_softc *)periph->softc;
535 
536           /*
537            * targdisable() aborts CCBs back to the user and leaves them
538            * on user_ccb_queue and abort_queue in case the user is still
539            * interested in them.  We free them now.
540            */
541           while ((ccb_h = TAILQ_FIRST(&softc->user_ccb_queue)) != NULL) {
542                     TAILQ_REMOVE(&softc->user_ccb_queue, ccb_h, periph_links.tqe);
543                     targfreeccb(softc, (union ccb *)ccb_h);
544           }
545           while ((descr = TAILQ_FIRST(&softc->abort_queue)) != NULL) {
546                     TAILQ_REMOVE(&softc->abort_queue, descr, tqe);
547                     kfree(descr, M_TARG);
548           }
549 
550           softc->periph = NULL;
551           softc->path = NULL;
552           periph->softc = NULL;
553 }
554 
555 /* Receive CCBs from user mode proc and send them to the HBA */
556 static int
targwrite(struct dev_write_args * ap)557 targwrite(struct dev_write_args *ap)
558 {
559           struct uio *uio = ap->a_uio;
560           union ccb *user_ccb;
561           struct targ_softc *softc;
562           struct targ_cmd_descr *descr;
563           int write_len, error;
564           int func_code, priority;
565 
566           softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1;
567           write_len = error = 0;
568           CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
569                       ("write - uio_resid %zu\n", uio->uio_resid));
570           while (uio->uio_resid >= sizeof(user_ccb) && error == 0) {
571                     union ccb *ccb;
572 
573                     error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio);
574                     if (error != 0) {
575                               CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
576                                           ("write - uiomove failed (%d)\n", error));
577                               break;
578                     }
579                     priority = fuword32(&user_ccb->ccb_h.pinfo.priority);
580                     if (priority == -1) {
581                               error = EINVAL;
582                               break;
583                     }
584                     func_code = fuword32(&user_ccb->ccb_h.func_code);
585                     switch (func_code) {
586                     case XPT_ACCEPT_TARGET_IO:
587                     case XPT_IMMED_NOTIFY:
588                               cam_periph_lock(softc->periph);
589                               ccb = targgetccb(softc, func_code, priority);
590                               descr = (struct targ_cmd_descr *)ccb->ccb_h.targ_descr;
591                               descr->user_ccb = user_ccb;
592                               descr->func_code = func_code;
593                               CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
594                                           ("Sent ATIO/INOT (%p)\n", user_ccb));
595                               xpt_action(ccb);
596                               TAILQ_INSERT_TAIL(&softc->pending_ccb_queue,
597                                                     &ccb->ccb_h,
598                                                     periph_links.tqe);
599                               cam_periph_unlock(softc->periph);
600                               break;
601                     default:
602                               cam_periph_lock(softc->periph);
603                               if ((func_code & XPT_FC_QUEUED) != 0) {
604                                         CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
605                                                     ("Sending queued ccb %#x (%p)\n",
606                                                     func_code, user_ccb));
607                                         descr = targgetdescr(softc);
608                                         descr->user_ccb = user_ccb;
609                                         descr->priority = priority;
610                                         descr->func_code = func_code;
611                                         crit_enter();
612                                         TAILQ_INSERT_TAIL(&softc->work_queue,
613                                                               descr, tqe);
614                                         crit_exit();
615                                         xpt_schedule(softc->periph, priority);
616                               } else {
617                                         CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
618                                                     ("Sending inline ccb %#x (%p)\n",
619                                                     func_code, user_ccb));
620                                         ccb = targgetccb(softc, func_code, priority);
621                                         descr = (struct targ_cmd_descr *)
622                                                    ccb->ccb_h.targ_descr;
623                                         descr->user_ccb = user_ccb;
624                                         descr->priority = priority;
625                                         descr->func_code = func_code;
626                                         if (targusermerge(softc, descr, ccb) != EFAULT)
627                                                   targsendccb(softc, ccb, descr);
628                                         targreturnccb(softc, ccb);
629                               }
630                               cam_periph_unlock(softc->periph);
631                               break;
632                     }
633                     write_len += sizeof(user_ccb);
634           }
635 
636           /*
637            * If we've successfully taken in some amount of
638            * data, return success for that data first.  If
639            * an error is persistent, it will be reported
640            * on the next write.
641            */
642           if (error != 0 && write_len == 0)
643                     return (error);
644           if (write_len == 0 && uio->uio_resid != 0)
645                     return (ENOSPC);
646           return (0);
647 }
648 
649 /* Process requests (descrs) via the periph-supplied CCBs */
650 static void
targstart(struct cam_periph * periph,union ccb * start_ccb)651 targstart(struct cam_periph *periph, union ccb *start_ccb)
652 {
653           struct targ_softc *softc;
654           struct targ_cmd_descr *descr, *next_descr;
655           int error;
656 
657           softc = (struct targ_softc *)periph->softc;
658           CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targstart %p\n", start_ccb));
659 
660           crit_enter();
661           descr = TAILQ_FIRST(&softc->work_queue);
662           if (descr == NULL) {
663                     crit_exit();
664                     xpt_release_ccb(start_ccb);
665           } else {
666                     TAILQ_REMOVE(&softc->work_queue, descr, tqe);
667                     next_descr = TAILQ_FIRST(&softc->work_queue);
668                     crit_exit();
669 
670                     /* Initiate a transaction using the descr and supplied CCB */
671                     error = targusermerge(softc, descr, start_ccb);
672                     if (error == 0)
673                               error = targsendccb(softc, start_ccb, descr);
674                     if (error != 0) {
675                               xpt_print(periph->path,
676                                   "targsendccb failed, err %d\n", error);
677                               xpt_release_ccb(start_ccb);
678                               suword32(&descr->user_ccb->ccb_h.status,
679                                        CAM_REQ_CMP_ERR);
680                               crit_enter();
681                               TAILQ_INSERT_TAIL(&softc->abort_queue, descr, tqe);
682                               crit_exit();
683                               notify_user(softc);
684                     }
685 
686                     /* If we have more work to do, stay scheduled */
687                     if (next_descr != NULL)
688                               xpt_schedule(periph, next_descr->priority);
689           }
690 }
691 
692 static int
targusermerge(struct targ_softc * softc,struct targ_cmd_descr * descr,union ccb * ccb)693 targusermerge(struct targ_softc *softc, struct targ_cmd_descr *descr,
694                 union ccb *ccb)
695 {
696           struct ccb_hdr *u_ccbh, *k_ccbh;
697           size_t ccb_len;
698           int error;
699 
700           u_ccbh = &descr->user_ccb->ccb_h;
701           k_ccbh = &ccb->ccb_h;
702 
703           /*
704            * There are some fields in the CCB header that need to be
705            * preserved, the rest we get from the user ccb. (See xpt_merge_ccb)
706            */
707           xpt_setup_ccb(k_ccbh, softc->path, descr->priority);
708           k_ccbh->retry_count = fuword32(&u_ccbh->retry_count);
709           k_ccbh->func_code = descr->func_code;
710           k_ccbh->flags = fuword32(&u_ccbh->flags);
711           k_ccbh->timeout = fuword32(&u_ccbh->timeout);
712           ccb_len = targccblen(k_ccbh->func_code) - sizeof(struct ccb_hdr);
713           error = copyin(u_ccbh + 1, k_ccbh + 1, ccb_len);
714           if (error != 0) {
715                     k_ccbh->status = CAM_REQ_CMP_ERR;
716                     return (error);
717           }
718 
719           /* Translate usermode abort_ccb pointer to its kernel counterpart */
720           if (k_ccbh->func_code == XPT_ABORT) {
721                     struct ccb_abort *cab;
722                     struct ccb_hdr *ccb_h;
723 
724                     cab = (struct ccb_abort *)ccb;
725                     crit_enter();
726                     TAILQ_FOREACH(ccb_h, &softc->pending_ccb_queue,
727                         periph_links.tqe) {
728                               struct targ_cmd_descr *ab_descr;
729 
730                               ab_descr = (struct targ_cmd_descr *)ccb_h->targ_descr;
731                               if (ab_descr->user_ccb == cab->abort_ccb) {
732                                         CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
733                                                     ("Changing abort for %p to %p\n",
734                                                     cab->abort_ccb, ccb_h));
735                                         cab->abort_ccb = (union ccb *)ccb_h;
736                                         break;
737                               }
738                     }
739                     crit_exit();
740                     /* CCB not found, set appropriate status */
741                     if (ccb_h == NULL) {
742                               k_ccbh->status = CAM_PATH_INVALID;
743                               error = ESRCH;
744                     }
745           }
746 
747           return (error);
748 }
749 
750 /* Build and send a kernel CCB formed from descr->user_ccb */
751 static int
targsendccb(struct targ_softc * softc,union ccb * ccb,struct targ_cmd_descr * descr)752 targsendccb(struct targ_softc *softc, union ccb *ccb,
753               struct targ_cmd_descr *descr)
754 {
755           struct cam_periph_map_info *mapinfo;
756           struct ccb_hdr *ccb_h;
757           int error;
758 
759           ccb_h = &ccb->ccb_h;
760           mapinfo = &descr->mapinfo;
761           mapinfo->num_bufs_used = 0;
762 
763           /*
764            * There's no way for the user to have a completion
765            * function, so we put our own completion function in here.
766            * We also stash in a reference to our descriptor so targreturnccb()
767            * can find our mapping info.
768            */
769           ccb_h->cbfcnp = targdone;
770           ccb_h->targ_descr = descr;
771 
772           /*
773            * We only attempt to map the user memory into kernel space
774            * if they haven't passed in a physical memory pointer,
775            * and if there is actually an I/O operation to perform.
776            * Right now cam_periph_mapmem() only supports SCSI and device
777            * match CCBs.  For the SCSI CCBs, we only pass the CCB in if
778            * there's actually data to map.  cam_periph_mapmem() will do the
779            * right thing, even if there isn't data to map, but since CCBs
780            * without data are a reasonably common occurance (e.g. test unit
781            * ready), it will save a few cycles if we check for it here.
782            */
783           if (((ccb_h->flags & CAM_DATA_PHYS) == 0)
784            && (((ccb_h->func_code == XPT_CONT_TARGET_IO)
785               && ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE))
786             || (ccb_h->func_code == XPT_DEV_MATCH))) {
787 
788                     error = cam_periph_mapmem(ccb, mapinfo);
789 
790                     /*
791                      * cam_periph_mapmem returned an error, we can't continue.
792                      * Return the error to the user.
793                      */
794                     if (error) {
795                               ccb_h->status = CAM_REQ_CMP_ERR;
796                               mapinfo->num_bufs_used = 0;
797                               return (error);
798                     }
799           }
800 
801           /*
802            * Once queued on the pending CCB list, this CCB will be protected
803            * by our error recovery handler.
804            */
805           CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("sendccb %p\n", ccb));
806           if (XPT_FC_IS_QUEUED(ccb)) {
807                     crit_enter();
808                     TAILQ_INSERT_TAIL(&softc->pending_ccb_queue, ccb_h,
809                                           periph_links.tqe);
810                     crit_exit();
811           }
812           xpt_action(ccb);
813 
814           return (0);
815 }
816 
817 /* Completion routine for CCBs (called in a critical section) */
818 static void
targdone(struct cam_periph * periph,union ccb * done_ccb)819 targdone(struct cam_periph *periph, union ccb *done_ccb)
820 {
821           struct targ_softc *softc;
822 
823           CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("targdone %p\n", done_ccb));
824           softc = (struct targ_softc *)periph->softc;
825           TAILQ_REMOVE(&softc->pending_ccb_queue, &done_ccb->ccb_h,
826                          periph_links.tqe);
827 
828           /* If we're no longer enabled, throw away CCB */
829           if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) {
830                     targfreeccb(softc, done_ccb);
831                     return;
832           }
833           /* abort_all_pending() waits for pending queue to be empty */
834           if (TAILQ_EMPTY(&softc->pending_ccb_queue))
835                     wakeup(&softc->pending_ccb_queue);
836 
837           switch (done_ccb->ccb_h.func_code) {
838           /* All FC_*_QUEUED CCBs go back to userland */
839           case XPT_IMMED_NOTIFY:
840           case XPT_ACCEPT_TARGET_IO:
841           case XPT_CONT_TARGET_IO:
842                     TAILQ_INSERT_TAIL(&softc->user_ccb_queue, &done_ccb->ccb_h,
843                                           periph_links.tqe);
844                     notify_user(softc);
845                     break;
846           default:
847                     panic("targdone: impossible xpt opcode %#x",
848                           done_ccb->ccb_h.func_code);
849                     /* NOTREACHED */
850           }
851 }
852 
853 /* Return CCBs to the user from the user queue and abort queue */
854 static int
targread(struct dev_read_args * ap)855 targread(struct dev_read_args *ap)
856 {
857           struct uio *uio = ap->a_uio;
858           struct descr_queue  *abort_queue;
859           struct targ_cmd_descr         *user_descr;
860           struct targ_softc   *softc;
861           struct ccb_queue  *user_queue;
862           struct ccb_hdr        *ccb_h;
863           union  ccb            *user_ccb;
864           int                    read_len, error;
865 
866           error = 0;
867           read_len = 0;
868           softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1;
869           user_queue = &softc->user_ccb_queue;
870           abort_queue = &softc->abort_queue;
871           CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targread\n"));
872 
873           /* If no data is available, wait or return immediately */
874           cam_periph_lock(softc->periph);
875           ccb_h = TAILQ_FIRST(user_queue);
876           user_descr = TAILQ_FIRST(abort_queue);
877           while (ccb_h == NULL && user_descr == NULL) {
878                     if ((ap->a_ioflag & IO_NDELAY) == 0) {
879                               error = sim_lock_sleep(user_queue, PCATCH, "targrd", 0,
880                                                          softc->periph->sim->lock);
881                               ccb_h = TAILQ_FIRST(user_queue);
882                               user_descr = TAILQ_FIRST(abort_queue);
883                               if (error != 0) {
884                                         if (error == ERESTART) {
885                                                   continue;
886                                         } else {
887                                                   goto read_fail;
888                                         }
889                               }
890                     } else {
891                               cam_periph_unlock(softc->periph);
892                               return (EAGAIN);
893                     }
894           }
895 
896           /* Data is available so fill the user's buffer */
897           while (ccb_h != NULL) {
898                     struct targ_cmd_descr *descr;
899 
900                     if (uio->uio_resid < sizeof(user_ccb))
901                               break;
902                     TAILQ_REMOVE(user_queue, ccb_h, periph_links.tqe);
903                     descr = (struct targ_cmd_descr *)ccb_h->targ_descr;
904                     user_ccb = descr->user_ccb;
905                     CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
906                                 ("targread ccb %p (%p)\n", ccb_h, user_ccb));
907                     error = targreturnccb(softc, (union ccb *)ccb_h);
908                     if (error != 0)
909                               goto read_fail;
910                     cam_periph_unlock(softc->periph);
911                     error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio);
912                     cam_periph_lock(softc->periph);
913                     if (error != 0)
914                               goto read_fail;
915                     read_len += sizeof(user_ccb);
916 
917                     ccb_h = TAILQ_FIRST(user_queue);
918           }
919 
920           /* Flush out any aborted descriptors */
921           while (user_descr != NULL) {
922                     if (uio->uio_resid < sizeof(user_ccb))
923                               break;
924                     TAILQ_REMOVE(abort_queue, user_descr, tqe);
925                     user_ccb = user_descr->user_ccb;
926                     CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
927                                 ("targread aborted descr %p (%p)\n",
928                                 user_descr, user_ccb));
929                     suword32(&user_ccb->ccb_h.status, CAM_REQ_ABORTED);
930                     cam_periph_unlock(softc->periph);
931                     error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio);
932                     cam_periph_lock(softc->periph);
933                     if (error != 0)
934                               goto read_fail;
935                     read_len += sizeof(user_ccb);
936 
937                     user_descr = TAILQ_FIRST(abort_queue);
938           }
939 
940           /*
941            * If we've successfully read some amount of data, don't report an
942            * error.  If the error is persistent, it will be reported on the
943            * next read().
944            */
945           if (read_len == 0 && uio->uio_resid != 0)
946                     error = ENOSPC;
947 
948 read_fail:
949           cam_periph_unlock(softc->periph);
950           return (error);
951 }
952 
953 /* Copy completed ccb back to the user */
954 static int
targreturnccb(struct targ_softc * softc,union ccb * ccb)955 targreturnccb(struct targ_softc *softc, union ccb *ccb)
956 {
957           struct targ_cmd_descr *descr;
958           struct ccb_hdr *u_ccbh;
959           size_t ccb_len;
960           int error;
961 
962           CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targreturnccb %p\n", ccb));
963           descr = (struct targ_cmd_descr *)ccb->ccb_h.targ_descr;
964           u_ccbh = &descr->user_ccb->ccb_h;
965 
966           /* Copy out the central portion of the ccb_hdr */
967           copyout(&ccb->ccb_h.retry_count, &u_ccbh->retry_count,
968                     offsetof(struct ccb_hdr, periph_priv) -
969                     offsetof(struct ccb_hdr, retry_count));
970 
971           /* Copy out the rest of the ccb (after the ccb_hdr) */
972           ccb_len = targccblen(ccb->ccb_h.func_code) - sizeof(struct ccb_hdr);
973           if (descr->mapinfo.num_bufs_used != 0)
974                     cam_periph_unmapmem(ccb, &descr->mapinfo);
975           error = copyout(&ccb->ccb_h + 1, u_ccbh + 1, ccb_len);
976           if (error != 0) {
977                     xpt_print(softc->path,
978                         "targreturnccb - CCB copyout failed (%d)\n", error);
979           }
980           /* Free CCB or send back to devq. */
981           targfreeccb(softc, ccb);
982 
983           return (error);
984 }
985 
986 static union ccb *
targgetccb(struct targ_softc * softc,xpt_opcode type,int priority)987 targgetccb(struct targ_softc *softc, xpt_opcode type, int priority)
988 {
989           union ccb *ccb;
990 
991 #if 0
992           int ccb_len;
993           ccb_len = targccblen(type);
994           ccb = kmalloc(ccb_len, M_TARG, M_INTWAIT);
995 #endif
996           ccb = xpt_alloc_ccb();
997           CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("getccb %p\n", ccb));
998 
999           xpt_setup_ccb(&ccb->ccb_h, softc->path, priority);
1000           ccb->ccb_h.func_code = type;
1001           ccb->ccb_h.cbfcnp = targdone;
1002           ccb->ccb_h.targ_descr = targgetdescr(softc);
1003 
1004           return (ccb);
1005 }
1006 
1007 static void
targfreeccb(struct targ_softc * softc,union ccb * ccb)1008 targfreeccb(struct targ_softc *softc, union ccb *ccb)
1009 {
1010           CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, ("targfreeccb descr %p and\n",
1011                               ccb->ccb_h.targ_descr));
1012           kfree(ccb->ccb_h.targ_descr, M_TARG);
1013           ccb->ccb_h.targ_descr = NULL; /* safety */
1014 
1015           switch (ccb->ccb_h.func_code) {
1016           case XPT_ACCEPT_TARGET_IO:
1017           case XPT_IMMED_NOTIFY:
1018                     CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, ("freeing ccb %p\n", ccb));
1019                     xpt_free_ccb(&ccb->ccb_h);
1020                     break;
1021           default:
1022                     /* Send back CCB if we got it from the periph */
1023                     if (XPT_FC_IS_QUEUED(ccb)) {
1024                               CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH,
1025                                                   ("returning queued ccb %p\n", ccb));
1026                               xpt_release_ccb(ccb);
1027                     } else {
1028                               CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH,
1029                                                   ("freeing ccb %p\n", ccb));
1030                               xpt_free_ccb(&ccb->ccb_h);
1031                     }
1032                     break;
1033           }
1034 }
1035 
1036 static struct targ_cmd_descr *
targgetdescr(struct targ_softc * softc)1037 targgetdescr(struct targ_softc *softc)
1038 {
1039           struct targ_cmd_descr *descr;
1040 
1041           descr = kmalloc(sizeof(*descr), M_TARG, M_INTWAIT);
1042           descr->mapinfo.num_bufs_used = 0;
1043           return (descr);
1044 }
1045 
1046 static int
targclone(struct dev_clone_args * ap)1047 targclone(struct dev_clone_args *ap)
1048 {
1049           int unit;
1050 
1051           unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(targ), 0);
1052           ap->a_dev = make_only_dev(&targ_ops, unit, UID_ROOT, GID_WHEEL,
1053                                           0600, "targ%d", unit);
1054           return 0;
1055 }
1056 
1057 static void
targinit(void)1058 targinit(void)
1059 {
1060           make_autoclone_dev(&targ_ops, &DEVFS_CLONE_BITMAP(targ),
1061                     targclone, UID_ROOT, GID_WHEEL, 0600, "targ");
1062           /* XXX: need uninit or so? */
1063 }
1064 
1065 static void
targasync(void * callback_arg,u_int32_t code,struct cam_path * path,void * arg)1066 targasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
1067 {
1068           /* All events are handled in usermode by INOTs */
1069           panic("targasync() called, should be an INOT instead");
1070 }
1071 
1072 /* Cancel all pending requests and CCBs awaiting work. */
1073 static void
abort_all_pending(struct targ_softc * softc)1074 abort_all_pending(struct targ_softc *softc)
1075 {
1076           struct targ_cmd_descr   *descr;
1077           struct ccb_abort     *cab;
1078           struct ccb_hdr                *ccb_h;
1079           struct cam_sim                *sim;
1080 
1081           CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("abort_all_pending\n"));
1082 
1083           /* First abort the descriptors awaiting resources */
1084           while ((descr = TAILQ_FIRST(&softc->work_queue)) != NULL) {
1085                     CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
1086                                 ("Aborting descr from workq %p\n", descr));
1087                     TAILQ_REMOVE(&softc->work_queue, descr, tqe);
1088                     TAILQ_INSERT_TAIL(&softc->abort_queue, descr, tqe);
1089           }
1090 
1091           /*
1092            * Then abort all pending CCBs.
1093            * targdone() will return the aborted CCB via user_ccb_queue
1094            */
1095           cab = &xpt_alloc_ccb()->cab;
1096           xpt_setup_ccb(&cab->ccb_h, softc->path, /*priority*/0);
1097           cab->ccb_h.func_code = XPT_ABORT;
1098           cab->ccb_h.status = CAM_REQ_CMP_ERR;
1099           TAILQ_FOREACH(ccb_h, &softc->pending_ccb_queue, periph_links.tqe) {
1100                     CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH,
1101                                 ("Aborting pending CCB %p\n", ccb_h));
1102                     cab->abort_ccb = (union ccb *)ccb_h;
1103                     xpt_action((union ccb *)cab);
1104                     if (cab->ccb_h.status != CAM_REQ_CMP) {
1105                               xpt_print(cab->ccb_h.path,
1106                                   "Unable to abort CCB, status %#x\n",
1107                                   cab->ccb_h.status);
1108                     }
1109           }
1110 
1111           /* If we aborted at least one pending CCB ok, wait for it. */
1112           if (cab->ccb_h.status == CAM_REQ_CMP) {
1113                     sim = xpt_path_sim(softc->path);
1114                     sim_lock_sleep(&softc->pending_ccb_queue, PCATCH, "tgabrt", 0,
1115                                      sim->lock);
1116           }
1117 
1118           /* If we aborted anything from the work queue, wakeup user. */
1119           if (!TAILQ_EMPTY(&softc->user_ccb_queue) ||
1120               !TAILQ_EMPTY(&softc->abort_queue)) {
1121                     notify_user(softc);
1122           }
1123 
1124           xpt_free_ccb(&cab->ccb_h);
1125 }
1126 
1127 /* Notify the user that data is ready */
1128 static void
notify_user(struct targ_softc * softc)1129 notify_user(struct targ_softc *softc)
1130 {
1131           /*
1132            * Notify users sleeping via poll(), kqueue(), and
1133            * blocking read().
1134            */
1135           KNOTE(&softc->read_kq.ki_note, 0);
1136           wakeup(&softc->user_ccb_queue);
1137 }
1138 
1139 /* Convert CAM status to errno values */
1140 static int
targcamstatus(cam_status status)1141 targcamstatus(cam_status status)
1142 {
1143           switch (status & CAM_STATUS_MASK) {
1144           case CAM_REQ_CMP:   /* CCB request completed without error */
1145                     return (0);
1146           case CAM_REQ_INPROG:          /* CCB request is in progress */
1147                     return (EINPROGRESS);
1148           case CAM_REQ_CMP_ERR:         /* CCB request completed with an error */
1149                     return (EIO);
1150           case CAM_PROVIDE_FAIL:        /* Unable to provide requested capability */
1151                     return (ENOTTY);
1152           case CAM_FUNC_NOTAVAIL:       /* The requested function is not available */
1153                     return (ENOTSUP);
1154           case CAM_LUN_ALRDY_ENA:       /* LUN is already enabled for target mode */
1155                     return (EADDRINUSE);
1156           case CAM_PATH_INVALID:        /* Supplied Path ID is invalid */
1157           case CAM_DEV_NOT_THERE:       /* SCSI Device Not Installed/there */
1158                     return (ENOENT);
1159           case CAM_REQ_ABORTED:         /* CCB request aborted by the host */
1160                     return (ECANCELED);
1161           case CAM_CMD_TIMEOUT:         /* Command timeout */
1162                     return (ETIMEDOUT);
1163           case CAM_REQUEUE_REQ:         /* Requeue to preserve transaction ordering */
1164                     return (EAGAIN);
1165           case CAM_REQ_INVALID:         /* CCB request was invalid */
1166                     return (EINVAL);
1167           case CAM_RESRC_UNAVAIL:       /* Resource Unavailable */
1168                     return (ENOMEM);
1169           case CAM_BUSY:                /* CAM subsytem is busy */
1170           case CAM_UA_ABORT:  /* Unable to abort CCB request */
1171                     return (EBUSY);
1172           default:
1173                     return (ENXIO);
1174           }
1175 }
1176 
1177 static size_t
targccblen(xpt_opcode func_code)1178 targccblen(xpt_opcode func_code)
1179 {
1180           int len;
1181 
1182           /* Codes we expect to see as a target */
1183           switch (func_code) {
1184           case XPT_CONT_TARGET_IO:
1185           case XPT_SCSI_IO:
1186                     len = sizeof(struct ccb_scsiio);
1187                     break;
1188           case XPT_ACCEPT_TARGET_IO:
1189                     len = sizeof(struct ccb_accept_tio);
1190                     break;
1191           case XPT_IMMED_NOTIFY:
1192                     len = sizeof(struct ccb_immed_notify);
1193                     break;
1194           case XPT_REL_SIMQ:
1195                     len = sizeof(struct ccb_relsim);
1196                     break;
1197           case XPT_PATH_INQ:
1198                     len = sizeof(struct ccb_pathinq);
1199                     break;
1200           case XPT_DEBUG:
1201                     len = sizeof(struct ccb_debug);
1202                     break;
1203           case XPT_ABORT:
1204                     len = sizeof(struct ccb_abort);
1205                     break;
1206           case XPT_EN_LUN:
1207                     len = sizeof(struct ccb_en_lun);
1208                     break;
1209           default:
1210                     len = sizeof(union ccb);
1211                     break;
1212           }
1213 
1214           return (len);
1215 }
1216