scsi_pass.c revision 67882
1/*
2 * Copyright (c) 1997, 1998 Justin T. Gibbs.
3 * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions, and the following disclaimer,
11 *    without modification, immediately at the beginning of the file.
12 * 2. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/cam/scsi/scsi_pass.c 67882 2000-10-29 13:57:19Z phk $
28 */
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/types.h>
34#include <sys/bio.h>
35#include <sys/malloc.h>
36#include <sys/fcntl.h>
37#include <sys/stat.h>
38#include <sys/conf.h>
39#include <sys/errno.h>
40#include <sys/devicestat.h>
41
42#include <cam/cam.h>
43#include <cam/cam_ccb.h>
44#include <cam/cam_extend.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_da.h>
52#include <cam/scsi/scsi_pass.h>
53
54typedef enum {
55	PASS_FLAG_OPEN			= 0x01,
56	PASS_FLAG_LOCKED		= 0x02,
57	PASS_FLAG_INVALID		= 0x04
58} pass_flags;
59
60typedef enum {
61	PASS_STATE_NORMAL
62} pass_state;
63
64typedef enum {
65	PASS_CCB_BUFFER_IO,
66	PASS_CCB_WAITING
67} pass_ccb_types;
68
69#define ccb_type	ppriv_field0
70#define ccb_bp		ppriv_ptr1
71
72struct pass_softc {
73	pass_state	state;
74	pass_flags	flags;
75	u_int8_t	pd_type;
76	struct		bio_queue_head bio_queue;
77	union ccb	saved_ccb;
78	struct devstat	device_stats;
79	dev_t		dev;
80};
81
82#ifndef MIN
83#define MIN(x,y) ((x<y) ? x : y)
84#endif
85
86#define PASS_CDEV_MAJOR 31
87
88static	d_open_t	passopen;
89static	d_close_t	passclose;
90static	d_ioctl_t	passioctl;
91static	d_strategy_t	passstrategy;
92
93static	periph_init_t	passinit;
94static	periph_ctor_t	passregister;
95static	periph_oninv_t	passoninvalidate;
96static	periph_dtor_t	passcleanup;
97static	periph_start_t	passstart;
98static	void		passasync(void *callback_arg, u_int32_t code,
99				  struct cam_path *path, void *arg);
100static	void		passdone(struct cam_periph *periph,
101				 union ccb *done_ccb);
102static	int		passerror(union ccb *ccb, u_int32_t cam_flags,
103				  u_int32_t sense_flags);
104static 	int		passsendccb(struct cam_periph *periph, union ccb *ccb,
105				    union ccb *inccb);
106
107static struct periph_driver passdriver =
108{
109	passinit, "pass",
110	TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0
111};
112
113DATA_SET(periphdriver_set, passdriver);
114
115static struct cdevsw pass_cdevsw = {
116	/* open */	passopen,
117	/* close */	passclose,
118	/* read */	physread,
119	/* write */	physwrite,
120	/* ioctl */	passioctl,
121	/* poll */	nopoll,
122	/* mmap */	nommap,
123	/* strategy */	passstrategy,
124	/* name */	"pass",
125	/* maj */	PASS_CDEV_MAJOR,
126	/* dump */	nodump,
127	/* psize */	nopsize,
128	/* flags */	0,
129	/* bmaj */	-1
130};
131
132static struct extend_array *passperiphs;
133
134static void
135passinit(void)
136{
137	cam_status status;
138	struct cam_path *path;
139
140	/*
141	 * Create our extend array for storing the devices we attach to.
142	 */
143	passperiphs = cam_extend_new();
144	if (passperiphs == NULL) {
145		printf("passm: Failed to alloc extend array!\n");
146		return;
147	}
148
149	/*
150	 * Install a global async callback.  This callback will
151	 * receive async callbacks like "new device found".
152	 */
153	status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
154				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
155
156	if (status == CAM_REQ_CMP) {
157		struct ccb_setasync csa;
158
159                xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
160                csa.ccb_h.func_code = XPT_SASYNC_CB;
161                csa.event_enable = AC_FOUND_DEVICE;
162                csa.callback = passasync;
163                csa.callback_arg = NULL;
164                xpt_action((union ccb *)&csa);
165		status = csa.ccb_h.status;
166                xpt_free_path(path);
167        }
168
169	if (status != CAM_REQ_CMP) {
170		printf("pass: Failed to attach master async callback "
171		       "due to status 0x%x!\n", status);
172	}
173
174}
175
176static void
177passoninvalidate(struct cam_periph *periph)
178{
179	int s;
180	struct pass_softc *softc;
181	struct bio *q_bp;
182	struct ccb_setasync csa;
183
184	softc = (struct pass_softc *)periph->softc;
185
186	/*
187	 * De-register any async callbacks.
188	 */
189	xpt_setup_ccb(&csa.ccb_h, periph->path,
190		      /* priority */ 5);
191	csa.ccb_h.func_code = XPT_SASYNC_CB;
192	csa.event_enable = 0;
193	csa.callback = passasync;
194	csa.callback_arg = periph;
195	xpt_action((union ccb *)&csa);
196
197	softc->flags |= PASS_FLAG_INVALID;
198
199	/*
200	 * Although the oninvalidate() routines are always called at
201	 * splsoftcam, we need to be at splbio() here to keep the buffer
202	 * queue from being modified while we traverse it.
203	 */
204	s = splbio();
205
206	/*
207	 * Return all queued I/O with ENXIO.
208	 * XXX Handle any transactions queued to the card
209	 *     with XPT_ABORT_CCB.
210	 */
211	while ((q_bp = bioq_first(&softc->bio_queue)) != NULL){
212		bioq_remove(&softc->bio_queue, q_bp);
213		q_bp->bio_resid = q_bp->bio_bcount;
214		q_bp->bio_error = ENXIO;
215		q_bp->bio_flags |= BIO_ERROR;
216		biodone(q_bp);
217	}
218	splx(s);
219
220	if (bootverbose) {
221		xpt_print_path(periph->path);
222		printf("lost device\n");
223	}
224
225}
226
227static void
228passcleanup(struct cam_periph *periph)
229{
230	struct pass_softc *softc;
231
232	softc = (struct pass_softc *)periph->softc;
233
234	devstat_remove_entry(&softc->device_stats);
235
236	destroy_dev(softc->dev);
237
238	cam_extend_release(passperiphs, periph->unit_number);
239
240	if (bootverbose) {
241		xpt_print_path(periph->path);
242		printf("removing device entry\n");
243	}
244	free(softc, M_DEVBUF);
245}
246
247static void
248passasync(void *callback_arg, u_int32_t code,
249	  struct cam_path *path, void *arg)
250{
251	struct cam_periph *periph;
252
253	periph = (struct cam_periph *)callback_arg;
254
255	switch (code) {
256	case AC_FOUND_DEVICE:
257	{
258		struct ccb_getdev *cgd;
259		cam_status status;
260
261		cgd = (struct ccb_getdev *)arg;
262
263		/*
264		 * Allocate a peripheral instance for
265		 * this device and start the probe
266		 * process.
267		 */
268		status = cam_periph_alloc(passregister, passoninvalidate,
269					  passcleanup, passstart, "pass",
270					  CAM_PERIPH_BIO, cgd->ccb_h.path,
271					  passasync, AC_FOUND_DEVICE, cgd);
272
273		if (status != CAM_REQ_CMP
274		 && status != CAM_REQ_INPROG)
275			printf("passasync: Unable to attach new device "
276				"due to status 0x%x\n", status);
277
278		break;
279	}
280	default:
281		cam_periph_async(periph, code, path, arg);
282		break;
283	}
284}
285
286static cam_status
287passregister(struct cam_periph *periph, void *arg)
288{
289	struct pass_softc *softc;
290	struct ccb_setasync csa;
291	struct ccb_getdev *cgd;
292
293	cgd = (struct ccb_getdev *)arg;
294	if (periph == NULL) {
295		printf("passregister: periph was NULL!!\n");
296		return(CAM_REQ_CMP_ERR);
297	}
298
299	if (cgd == NULL) {
300		printf("passregister: no getdev CCB, can't register device\n");
301		return(CAM_REQ_CMP_ERR);
302	}
303
304	softc = (struct pass_softc *)malloc(sizeof(*softc),
305					    M_DEVBUF, M_NOWAIT);
306
307	if (softc == NULL) {
308		printf("passregister: Unable to probe new device. "
309		       "Unable to allocate softc\n");
310		return(CAM_REQ_CMP_ERR);
311	}
312
313	bzero(softc, sizeof(*softc));
314	softc->state = PASS_STATE_NORMAL;
315	softc->pd_type = SID_TYPE(&cgd->inq_data);
316	bioq_init(&softc->bio_queue);
317
318	periph->softc = softc;
319
320	cam_extend_set(passperiphs, periph->unit_number, periph);
321	/*
322	 * We pass in 0 for a blocksize, since we don't
323	 * know what the blocksize of this device is, if
324	 * it even has a blocksize.
325	 */
326	devstat_add_entry(&softc->device_stats, "pass", periph->unit_number,
327			  0, DEVSTAT_NO_BLOCKSIZE | DEVSTAT_NO_ORDERED_TAGS,
328			  softc->pd_type |
329			  DEVSTAT_TYPE_IF_SCSI |
330			  DEVSTAT_TYPE_PASS,
331			  DEVSTAT_PRIORITY_PASS);
332
333	/* Register the device */
334	softc->dev = make_dev(&pass_cdevsw, periph->unit_number, UID_ROOT,
335			      GID_OPERATOR, 0600, "%s%d", periph->periph_name,
336			      periph->unit_number);
337
338	/*
339	 * Add an async callback so that we get
340	 * notified if this device goes away.
341	 */
342	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
343	csa.ccb_h.func_code = XPT_SASYNC_CB;
344	csa.event_enable = AC_LOST_DEVICE;
345	csa.callback = passasync;
346	csa.callback_arg = periph;
347	xpt_action((union ccb *)&csa);
348
349	if (bootverbose)
350		xpt_announce_periph(periph, NULL);
351
352	return(CAM_REQ_CMP);
353}
354
355static int
356passopen(dev_t dev, int flags, int fmt, struct proc *p)
357{
358	struct cam_periph *periph;
359	struct pass_softc *softc;
360	int unit, error;
361	int s;
362
363	error = 0; /* default to no error */
364
365	/* unit = dkunit(dev); */
366	/* XXX KDM fix this */
367	unit = minor(dev) & 0xff;
368
369	periph = cam_extend_get(passperiphs, unit);
370
371	if (periph == NULL)
372		return (ENXIO);
373
374	softc = (struct pass_softc *)periph->softc;
375
376	s = splsoftcam();
377	if (softc->flags & PASS_FLAG_INVALID) {
378		splx(s);
379		return(ENXIO);
380	}
381
382	/*
383	 * Don't allow access when we're running at a high securelvel.
384	 */
385	if (securelevel > 1) {
386		splx(s);
387		return(EPERM);
388	}
389
390	/*
391	 * Only allow read-write access.
392	 */
393	if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) {
394		splx(s);
395		return(EPERM);
396	}
397
398	/*
399	 * We don't allow nonblocking access.
400	 */
401	if ((flags & O_NONBLOCK) != 0) {
402		xpt_print_path(periph->path);
403		printf("can't do nonblocking accesss\n");
404		splx(s);
405		return(EINVAL);
406	}
407
408	if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) {
409		splx(s);
410		return (error);
411	}
412
413	splx(s);
414
415	if ((softc->flags & PASS_FLAG_OPEN) == 0) {
416		if (cam_periph_acquire(periph) != CAM_REQ_CMP)
417			return(ENXIO);
418		softc->flags |= PASS_FLAG_OPEN;
419	}
420
421	cam_periph_unlock(periph);
422
423	return (error);
424}
425
426static int
427passclose(dev_t dev, int flag, int fmt, struct proc *p)
428{
429	struct 	cam_periph *periph;
430	struct	pass_softc *softc;
431	int	unit, error;
432
433	/* unit = dkunit(dev); */
434	/* XXX KDM fix this */
435	unit = minor(dev) & 0xff;
436
437	periph = cam_extend_get(passperiphs, unit);
438	if (periph == NULL)
439		return (ENXIO);
440
441	softc = (struct pass_softc *)periph->softc;
442
443	if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
444		return (error);
445
446	softc->flags &= ~PASS_FLAG_OPEN;
447
448	cam_periph_unlock(periph);
449	cam_periph_release(periph);
450
451	return (0);
452}
453
454/*
455 * Actually translate the requested transfer into one the physical driver
456 * can understand.  The transfer is described by a buf and will include
457 * only one physical transfer.
458 */
459static void
460passstrategy(struct bio *bp)
461{
462	struct cam_periph *periph;
463	struct pass_softc *softc;
464	u_int  unit;
465	int    s;
466
467	/*
468	 * The read/write interface for the passthrough driver doesn't
469	 * really work right now.  So, we just pass back EINVAL to tell the
470	 * user to go away.
471	 */
472	bp->bio_error = EINVAL;
473	goto bad;
474
475	/* unit = dkunit(bp->bio_dev); */
476	/* XXX KDM fix this */
477	unit = minor(bp->bio_dev) & 0xff;
478
479	periph = cam_extend_get(passperiphs, unit);
480	if (periph == NULL) {
481		bp->bio_error = ENXIO;
482		goto bad;
483	}
484	softc = (struct pass_softc *)periph->softc;
485
486	/*
487	 * Odd number of bytes or negative offset
488	 */
489	/* valid request?  */
490	if (bp->bio_blkno < 0) {
491		bp->bio_error = EINVAL;
492		goto bad;
493        }
494
495	/*
496	 * Mask interrupts so that the pack cannot be invalidated until
497	 * after we are in the queue.  Otherwise, we might not properly
498	 * clean up one of the buffers.
499	 */
500	s = splbio();
501
502	bioq_insert_tail(&softc->bio_queue, bp);
503
504	splx(s);
505
506	/*
507	 * Schedule ourselves for performing the work.
508	 */
509	xpt_schedule(periph, /* XXX priority */1);
510
511	return;
512bad:
513	bp->bio_flags |= BIO_ERROR;
514
515	/*
516	 * Correctly set the buf to indicate a completed xfer
517	 */
518	bp->bio_resid = bp->bio_bcount;
519	biodone(bp);
520	return;
521}
522
523static void
524passstart(struct cam_periph *periph, union ccb *start_ccb)
525{
526	struct pass_softc *softc;
527	int s;
528
529	softc = (struct pass_softc *)periph->softc;
530
531	switch (softc->state) {
532	case PASS_STATE_NORMAL:
533	{
534		struct bio *bp;
535
536		s = splbio();
537		bp = bioq_first(&softc->bio_queue);
538		if (periph->immediate_priority <= periph->pinfo.priority) {
539			start_ccb->ccb_h.ccb_type = PASS_CCB_WAITING;
540			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
541					  periph_links.sle);
542			periph->immediate_priority = CAM_PRIORITY_NONE;
543			splx(s);
544			wakeup(&periph->ccb_list);
545		} else if (bp == NULL) {
546			splx(s);
547			xpt_release_ccb(start_ccb);
548		} else {
549
550			bioq_remove(&softc->bio_queue, bp);
551
552			devstat_start_transaction(&softc->device_stats);
553
554			/*
555			 * XXX JGibbs -
556			 * Interpret the contents of the bp as a CCB
557			 * and pass it to a routine shared by our ioctl
558			 * code and passtart.
559			 * For now, just biodone it with EIO so we don't
560			 * hang.
561			 */
562			bp->bio_error = EIO;
563			bp->bio_flags |= BIO_ERROR;
564			bp->bio_resid = bp->bio_bcount;
565			biodone(bp);
566			bp = bioq_first(&softc->bio_queue);
567			splx(s);
568
569			xpt_action(start_ccb);
570
571		}
572		if (bp != NULL) {
573			/* Have more work to do, so ensure we stay scheduled */
574			xpt_schedule(periph, /* XXX priority */1);
575		}
576		break;
577	}
578	}
579}
580static void
581passdone(struct cam_periph *periph, union ccb *done_ccb)
582{
583	struct pass_softc *softc;
584	struct ccb_scsiio *csio;
585
586	softc = (struct pass_softc *)periph->softc;
587	csio = &done_ccb->csio;
588	switch (csio->ccb_h.ccb_type) {
589	case PASS_CCB_BUFFER_IO:
590	{
591		struct bio		*bp;
592		cam_status		status;
593		u_int8_t		scsi_status;
594		devstat_trans_flags	ds_flags;
595
596		status = done_ccb->ccb_h.status;
597		scsi_status = done_ccb->csio.scsi_status;
598		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
599		/* XXX handle errors */
600		if (!(((status & CAM_STATUS_MASK) == CAM_REQ_CMP)
601		  && (scsi_status == SCSI_STATUS_OK))) {
602			int error;
603
604			if ((error = passerror(done_ccb, 0, 0)) == ERESTART) {
605				/*
606				 * A retry was scheuled, so
607				 * just return.
608				 */
609				return;
610			}
611
612			/*
613			 * XXX unfreeze the queue after we complete
614			 * the abort process
615			 */
616			bp->bio_error = error;
617			bp->bio_flags |= BIO_ERROR;
618		}
619
620		if ((done_ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
621			ds_flags = DEVSTAT_READ;
622		else if ((done_ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
623			ds_flags = DEVSTAT_WRITE;
624		else
625			ds_flags = DEVSTAT_NO_DATA;
626
627		devstat_end_transaction_bio(&softc->device_stats, bp);
628		biodone(bp);
629		break;
630	}
631	case PASS_CCB_WAITING:
632	{
633		/* Caller will release the CCB */
634		wakeup(&done_ccb->ccb_h.cbfcnp);
635		return;
636	}
637	}
638	xpt_release_ccb(done_ccb);
639}
640
641static int
642passioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
643{
644	struct 	cam_periph *periph;
645	struct	pass_softc *softc;
646	u_int8_t unit;
647	int      error;
648
649
650	/* unit = dkunit(dev); */
651	/* XXX KDM fix this */
652	unit = minor(dev) & 0xff;
653
654	periph = cam_extend_get(passperiphs, unit);
655
656	if (periph == NULL)
657		return(ENXIO);
658
659	softc = (struct pass_softc *)periph->softc;
660
661	error = 0;
662
663	switch (cmd) {
664
665	case CAMIOCOMMAND:
666	{
667		union ccb *inccb;
668		union ccb *ccb;
669		int ccb_malloced;
670
671		inccb = (union ccb *)addr;
672
673		/*
674		 * Some CCB types, like scan bus and scan lun can only go
675		 * through the transport layer device.
676		 */
677		if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) {
678			xpt_print_path(periph->path);
679			printf("CCB function code %#x is restricted to the "
680			       "XPT device\n", inccb->ccb_h.func_code);
681			error = ENODEV;
682			break;
683		}
684
685		/*
686		 * Non-immediate CCBs need a CCB from the per-device pool
687		 * of CCBs, which is scheduled by the transport layer.
688		 * Immediate CCBs and user-supplied CCBs should just be
689		 * malloced.
690		 */
691		if ((inccb->ccb_h.func_code & XPT_FC_QUEUED)
692		 && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) {
693			ccb = cam_periph_getccb(periph,
694						inccb->ccb_h.pinfo.priority);
695			ccb_malloced = 0;
696		} else {
697			ccb = xpt_alloc_ccb();
698
699			if (ccb != NULL)
700				xpt_setup_ccb(&ccb->ccb_h, periph->path,
701					      inccb->ccb_h.pinfo.priority);
702			ccb_malloced = 1;
703		}
704
705		if (ccb == NULL) {
706			xpt_print_path(periph->path);
707			printf("unable to allocate CCB\n");
708			error = ENOMEM;
709			break;
710		}
711
712		error = passsendccb(periph, ccb, inccb);
713
714		if (ccb_malloced)
715			xpt_free_ccb(ccb);
716		else
717			xpt_release_ccb(ccb);
718
719		break;
720	}
721	default:
722		error = cam_periph_ioctl(periph, cmd, addr, passerror);
723		break;
724	}
725
726	return(error);
727}
728
729/*
730 * Generally, "ccb" should be the CCB supplied by the kernel.  "inccb"
731 * should be the CCB that is copied in from the user.
732 */
733static int
734passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
735{
736	struct pass_softc *softc;
737	struct cam_periph_map_info mapinfo;
738	int error, need_unmap;
739
740	softc = (struct pass_softc *)periph->softc;
741
742	need_unmap = 0;
743
744	/*
745	 * There are some fields in the CCB header that need to be
746	 * preserved, the rest we get from the user.
747	 */
748	xpt_merge_ccb(ccb, inccb);
749
750	/*
751	 * There's no way for the user to have a completion
752	 * function, so we put our own completion function in here.
753	 */
754	ccb->ccb_h.cbfcnp = passdone;
755
756	/*
757	 * We only attempt to map the user memory into kernel space
758	 * if they haven't passed in a physical memory pointer,
759	 * and if there is actually an I/O operation to perform.
760	 * Right now cam_periph_mapmem() only supports SCSI and device
761	 * match CCBs.  For the SCSI CCBs, we only pass the CCB in if
762	 * there's actually data to map.  cam_periph_mapmem() will do the
763	 * right thing, even if there isn't data to map, but since CCBs
764	 * without data are a reasonably common occurance (e.g. test unit
765	 * ready), it will save a few cycles if we check for it here.
766	 */
767	if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0)
768	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO)
769	    && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
770	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) {
771
772		bzero(&mapinfo, sizeof(mapinfo));
773
774		error = cam_periph_mapmem(ccb, &mapinfo);
775
776		/*
777		 * cam_periph_mapmem returned an error, we can't continue.
778		 * Return the error to the user.
779		 */
780		if (error)
781			return(error);
782
783		/*
784		 * We successfully mapped the memory in, so we need to
785		 * unmap it when the transaction is done.
786		 */
787		need_unmap = 1;
788	}
789
790	/*
791	 * If the user wants us to perform any error recovery, then honor
792	 * that request.  Otherwise, it's up to the user to perform any
793	 * error recovery.
794	 */
795	error = cam_periph_runccb(ccb,
796				  (ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ?
797				  passerror : NULL,
798				  /* cam_flags */ 0,
799				  /* sense_flags */SF_RETRY_UA | SF_RETRY_SELTO,
800				  &softc->device_stats);
801
802	if (need_unmap != 0)
803		cam_periph_unmapmem(ccb, &mapinfo);
804
805	ccb->ccb_h.cbfcnp = NULL;
806	ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv;
807	bcopy(ccb, inccb, sizeof(union ccb));
808
809	return(error);
810}
811
812static int
813passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
814{
815	struct cam_periph *periph;
816	struct pass_softc *softc;
817
818	periph = xpt_path_periph(ccb->ccb_h.path);
819	softc = (struct pass_softc *)periph->softc;
820
821	return(cam_periph_error(ccb, cam_flags, sense_flags,
822				 &softc->saved_ccb));
823}
824