scsi_pass.c revision 110232
10SN/A/*
2157SN/A * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
30SN/A * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry.
40SN/A * All rights reserved.
50SN/A *
60SN/A * Redistribution and use in source and binary forms, with or without
7157SN/A * modification, are permitted provided that the following conditions
80SN/A * are met:
9157SN/A * 1. Redistributions of source code must retain the above copyright
100SN/A *    notice, this list of conditions, and the following disclaimer,
110SN/A *    without modification, immediately at the beginning of the file.
120SN/A * 2. The name of the author may not be used to endorse or promote products
130SN/A *    derived from this software without specific prior written permission.
140SN/A *
150SN/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
160SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
180SN/A * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
190SN/A * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
200SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21157SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22157SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23157SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
240SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
250SN/A * SUCH DAMAGE.
260SN/A *
270SN/A * $FreeBSD: head/sys/cam/scsi/scsi_pass.c 110232 2003-02-02 13:17:30Z alfred $
280SN/A */
290SN/A
300SN/A#include <sys/param.h>
310SN/A#include <sys/systm.h>
320SN/A#include <sys/kernel.h>
330SN/A#include <sys/types.h>
340SN/A#include <sys/bio.h>
350SN/A#include <sys/malloc.h>
360SN/A#include <sys/fcntl.h>
370SN/A#include <sys/conf.h>
380SN/A#include <sys/errno.h>
390SN/A#include <sys/devicestat.h>
400SN/A#include <sys/proc.h>
410SN/A
42#include <cam/cam.h>
43#include <cam/cam_ccb.h>
44#include <cam/cam_periph.h>
45#include <cam/cam_queue.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_pass.h>
51
52typedef enum {
53	PASS_FLAG_OPEN			= 0x01,
54	PASS_FLAG_LOCKED		= 0x02,
55	PASS_FLAG_INVALID		= 0x04
56} pass_flags;
57
58typedef enum {
59	PASS_STATE_NORMAL
60} pass_state;
61
62typedef enum {
63	PASS_CCB_BUFFER_IO,
64	PASS_CCB_WAITING
65} pass_ccb_types;
66
67#define ccb_type	ppriv_field0
68#define ccb_bp		ppriv_ptr1
69
70struct pass_softc {
71	pass_state		state;
72	pass_flags		flags;
73	u_int8_t		pd_type;
74	union ccb		saved_ccb;
75	struct devstat		device_stats;
76	dev_t			dev;
77};
78
79#define PASS_CDEV_MAJOR 31
80
81static	d_open_t	passopen;
82static	d_close_t	passclose;
83static	d_ioctl_t	passioctl;
84
85static	periph_init_t	passinit;
86static	periph_ctor_t	passregister;
87static	periph_oninv_t	passoninvalidate;
88static	periph_dtor_t	passcleanup;
89static	periph_start_t	passstart;
90static	void		passasync(void *callback_arg, u_int32_t code,
91				  struct cam_path *path, void *arg);
92static	void		passdone(struct cam_periph *periph,
93				 union ccb *done_ccb);
94static	int		passerror(union ccb *ccb, u_int32_t cam_flags,
95				  u_int32_t sense_flags);
96static 	int		passsendccb(struct cam_periph *periph, union ccb *ccb,
97				    union ccb *inccb);
98
99static struct periph_driver passdriver =
100{
101	passinit, "pass",
102	TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0
103};
104
105PERIPHDRIVER_DECLARE(pass, passdriver);
106
107static struct cdevsw pass_cdevsw = {
108	/* open */	passopen,
109	/* close */	passclose,
110	/* read */	noread,
111	/* write */	nowrite,
112	/* ioctl */	passioctl,
113	/* poll */	nopoll,
114	/* mmap */	nommap,
115	/* strategy */	nostrategy,
116	/* name */	"pass",
117	/* maj */	PASS_CDEV_MAJOR,
118	/* dump */	nodump,
119	/* psize */	nopsize,
120	/* flags */	0,
121};
122
123static void
124passinit(void)
125{
126	cam_status status;
127	struct cam_path *path;
128
129	/*
130	 * Install a global async callback.  This callback will
131	 * receive async callbacks like "new device found".
132	 */
133	status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
134				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
135
136	if (status == CAM_REQ_CMP) {
137		struct ccb_setasync csa;
138
139                xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
140                csa.ccb_h.func_code = XPT_SASYNC_CB;
141                csa.event_enable = AC_FOUND_DEVICE;
142                csa.callback = passasync;
143                csa.callback_arg = NULL;
144                xpt_action((union ccb *)&csa);
145		status = csa.ccb_h.status;
146                xpt_free_path(path);
147        }
148
149	if (status != CAM_REQ_CMP) {
150		printf("pass: Failed to attach master async callback "
151		       "due to status 0x%x!\n", status);
152	}
153
154}
155
156static void
157passoninvalidate(struct cam_periph *periph)
158{
159	struct pass_softc *softc;
160	struct ccb_setasync csa;
161
162	softc = (struct pass_softc *)periph->softc;
163
164	/*
165	 * De-register any async callbacks.
166	 */
167	xpt_setup_ccb(&csa.ccb_h, periph->path,
168		      /* priority */ 5);
169	csa.ccb_h.func_code = XPT_SASYNC_CB;
170	csa.event_enable = 0;
171	csa.callback = passasync;
172	csa.callback_arg = periph;
173	xpt_action((union ccb *)&csa);
174
175	softc->flags |= PASS_FLAG_INVALID;
176
177	/*
178	 * XXX Return all queued I/O with ENXIO.
179	 * XXX Handle any transactions queued to the card
180	 *     with XPT_ABORT_CCB.
181	 */
182
183	if (bootverbose) {
184		xpt_print_path(periph->path);
185		printf("lost device\n");
186	}
187
188}
189
190static void
191passcleanup(struct cam_periph *periph)
192{
193	struct pass_softc *softc;
194
195	softc = (struct pass_softc *)periph->softc;
196
197	devstat_remove_entry(&softc->device_stats);
198
199	destroy_dev(softc->dev);
200
201	if (bootverbose) {
202		xpt_print_path(periph->path);
203		printf("removing device entry\n");
204	}
205	free(softc, M_DEVBUF);
206}
207
208static void
209passasync(void *callback_arg, u_int32_t code,
210	  struct cam_path *path, void *arg)
211{
212	struct cam_periph *periph;
213
214	periph = (struct cam_periph *)callback_arg;
215
216	switch (code) {
217	case AC_FOUND_DEVICE:
218	{
219		struct ccb_getdev *cgd;
220		cam_status status;
221
222		cgd = (struct ccb_getdev *)arg;
223		if (cgd == NULL)
224			break;
225
226		/*
227		 * Allocate a peripheral instance for
228		 * this device and start the probe
229		 * process.
230		 */
231		status = cam_periph_alloc(passregister, passoninvalidate,
232					  passcleanup, passstart, "pass",
233					  CAM_PERIPH_BIO, cgd->ccb_h.path,
234					  passasync, AC_FOUND_DEVICE, cgd);
235
236		if (status != CAM_REQ_CMP
237		 && status != CAM_REQ_INPROG) {
238			const struct cam_status_entry *entry;
239
240			entry = cam_fetch_status_entry(status);
241
242			printf("passasync: Unable to attach new device "
243			       "due to status %#x: %s\n", status, entry ?
244			       entry->status_text : "Unknown");
245		}
246
247		break;
248	}
249	default:
250		cam_periph_async(periph, code, path, arg);
251		break;
252	}
253}
254
255static cam_status
256passregister(struct cam_periph *periph, void *arg)
257{
258	struct pass_softc *softc;
259	struct ccb_setasync csa;
260	struct ccb_getdev *cgd;
261	int    no_tags;
262
263	cgd = (struct ccb_getdev *)arg;
264	if (periph == NULL) {
265		printf("passregister: periph was NULL!!\n");
266		return(CAM_REQ_CMP_ERR);
267	}
268
269	if (cgd == NULL) {
270		printf("passregister: no getdev CCB, can't register device\n");
271		return(CAM_REQ_CMP_ERR);
272	}
273
274	softc = (struct pass_softc *)malloc(sizeof(*softc),
275					    M_DEVBUF, M_NOWAIT);
276
277	if (softc == NULL) {
278		printf("passregister: Unable to probe new device. "
279		       "Unable to allocate softc\n");
280		return(CAM_REQ_CMP_ERR);
281	}
282
283	bzero(softc, sizeof(*softc));
284	softc->state = PASS_STATE_NORMAL;
285	softc->pd_type = SID_TYPE(&cgd->inq_data);
286
287	periph->softc = softc;
288
289	/*
290	 * We pass in 0 for a blocksize, since we don't
291	 * know what the blocksize of this device is, if
292	 * it even has a blocksize.
293	 */
294	no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
295	devstat_add_entry(&softc->device_stats, "pass", periph->unit_number, 0,
296			  DEVSTAT_NO_BLOCKSIZE
297			  | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
298			  softc->pd_type |
299			  DEVSTAT_TYPE_IF_SCSI |
300			  DEVSTAT_TYPE_PASS,
301			  DEVSTAT_PRIORITY_PASS);
302
303	/* Register the device */
304	softc->dev = make_dev(&pass_cdevsw, periph->unit_number, UID_ROOT,
305			      GID_OPERATOR, 0600, "%s%d", periph->periph_name,
306			      periph->unit_number);
307	softc->dev->si_drv1 = periph;
308
309	/*
310	 * Add an async callback so that we get
311	 * notified if this device goes away.
312	 */
313	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
314	csa.ccb_h.func_code = XPT_SASYNC_CB;
315	csa.event_enable = AC_LOST_DEVICE;
316	csa.callback = passasync;
317	csa.callback_arg = periph;
318	xpt_action((union ccb *)&csa);
319
320	if (bootverbose)
321		xpt_announce_periph(periph, NULL);
322
323	return(CAM_REQ_CMP);
324}
325
326static int
327passopen(dev_t dev, int flags, int fmt, struct thread *td)
328{
329	struct cam_periph *periph;
330	struct pass_softc *softc;
331	int error;
332	int s;
333
334	error = 0; /* default to no error */
335
336	periph = (struct cam_periph *)dev->si_drv1;
337	if (periph == NULL)
338		return (ENXIO);
339
340	softc = (struct pass_softc *)periph->softc;
341
342	s = splsoftcam();
343	if (softc->flags & PASS_FLAG_INVALID) {
344		splx(s);
345		return(ENXIO);
346	}
347
348	/*
349	 * Don't allow access when we're running at a high securelevel.
350	 */
351	error = securelevel_gt(td->td_ucred, 1);
352	if (error) {
353		splx(s);
354		return(error);
355	}
356
357	/*
358	 * Only allow read-write access.
359	 */
360	if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) {
361		splx(s);
362		return(EPERM);
363	}
364
365	/*
366	 * We don't allow nonblocking access.
367	 */
368	if ((flags & O_NONBLOCK) != 0) {
369		xpt_print_path(periph->path);
370		printf("can't do nonblocking accesss\n");
371		splx(s);
372		return(EINVAL);
373	}
374
375	if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) {
376		splx(s);
377		return (error);
378	}
379
380	splx(s);
381
382	if ((softc->flags & PASS_FLAG_OPEN) == 0) {
383		if (cam_periph_acquire(periph) != CAM_REQ_CMP)
384			return(ENXIO);
385		softc->flags |= PASS_FLAG_OPEN;
386	}
387
388	cam_periph_unlock(periph);
389
390	return (error);
391}
392
393static int
394passclose(dev_t dev, int flag, int fmt, struct thread *td)
395{
396	struct 	cam_periph *periph;
397	struct	pass_softc *softc;
398	int	error;
399
400	periph = (struct cam_periph *)dev->si_drv1;
401	if (periph == NULL)
402		return (ENXIO);
403
404	softc = (struct pass_softc *)periph->softc;
405
406	if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
407		return (error);
408
409	softc->flags &= ~PASS_FLAG_OPEN;
410
411	cam_periph_unlock(periph);
412	cam_periph_release(periph);
413
414	return (0);
415}
416
417static void
418passstart(struct cam_periph *periph, union ccb *start_ccb)
419{
420	struct pass_softc *softc;
421	int s;
422
423	softc = (struct pass_softc *)periph->softc;
424
425	switch (softc->state) {
426	case PASS_STATE_NORMAL:
427		s = splbio();
428		start_ccb->ccb_h.ccb_type = PASS_CCB_WAITING;
429		SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
430				  periph_links.sle);
431		periph->immediate_priority = CAM_PRIORITY_NONE;
432		splx(s);
433		wakeup(&periph->ccb_list);
434		break;
435	}
436}
437
438static void
439passdone(struct cam_periph *periph, union ccb *done_ccb)
440{
441	struct pass_softc *softc;
442	struct ccb_scsiio *csio;
443
444	softc = (struct pass_softc *)periph->softc;
445	csio = &done_ccb->csio;
446	switch (csio->ccb_h.ccb_type) {
447	case PASS_CCB_WAITING:
448		/* Caller will release the CCB */
449		wakeup(&done_ccb->ccb_h.cbfcnp);
450		return;
451	}
452	xpt_release_ccb(done_ccb);
453}
454
455static int
456passioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
457{
458	struct	cam_periph *periph;
459	struct	pass_softc *softc;
460	int	error;
461
462	periph = (struct cam_periph *)dev->si_drv1;
463	if (periph == NULL)
464		return(ENXIO);
465
466	softc = (struct pass_softc *)periph->softc;
467
468	error = 0;
469
470	switch (cmd) {
471
472	case CAMIOCOMMAND:
473	{
474		union ccb *inccb;
475		union ccb *ccb;
476		int ccb_malloced;
477
478		inccb = (union ccb *)addr;
479
480		/*
481		 * Some CCB types, like scan bus and scan lun can only go
482		 * through the transport layer device.
483		 */
484		if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) {
485			xpt_print_path(periph->path);
486			printf("CCB function code %#x is restricted to the "
487			       "XPT device\n", inccb->ccb_h.func_code);
488			error = ENODEV;
489			break;
490		}
491
492		/*
493		 * Non-immediate CCBs need a CCB from the per-device pool
494		 * of CCBs, which is scheduled by the transport layer.
495		 * Immediate CCBs and user-supplied CCBs should just be
496		 * malloced.
497		 */
498		if ((inccb->ccb_h.func_code & XPT_FC_QUEUED)
499		 && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) {
500			ccb = cam_periph_getccb(periph,
501						inccb->ccb_h.pinfo.priority);
502			ccb_malloced = 0;
503		} else {
504			ccb = xpt_alloc_ccb();
505
506			if (ccb != NULL)
507				xpt_setup_ccb(&ccb->ccb_h, periph->path,
508					      inccb->ccb_h.pinfo.priority);
509			ccb_malloced = 1;
510		}
511
512		if (ccb == NULL) {
513			xpt_print_path(periph->path);
514			printf("unable to allocate CCB\n");
515			error = ENOMEM;
516			break;
517		}
518
519		error = passsendccb(periph, ccb, inccb);
520
521		if (ccb_malloced)
522			xpt_free_ccb(ccb);
523		else
524			xpt_release_ccb(ccb);
525
526		break;
527	}
528	default:
529		error = cam_periph_ioctl(periph, cmd, addr, passerror);
530		break;
531	}
532
533	return(error);
534}
535
536/*
537 * Generally, "ccb" should be the CCB supplied by the kernel.  "inccb"
538 * should be the CCB that is copied in from the user.
539 */
540static int
541passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
542{
543	struct pass_softc *softc;
544	struct cam_periph_map_info mapinfo;
545	int error, need_unmap;
546
547	softc = (struct pass_softc *)periph->softc;
548
549	need_unmap = 0;
550
551	/*
552	 * There are some fields in the CCB header that need to be
553	 * preserved, the rest we get from the user.
554	 */
555	xpt_merge_ccb(ccb, inccb);
556
557	/*
558	 * There's no way for the user to have a completion
559	 * function, so we put our own completion function in here.
560	 */
561	ccb->ccb_h.cbfcnp = passdone;
562
563	/*
564	 * We only attempt to map the user memory into kernel space
565	 * if they haven't passed in a physical memory pointer,
566	 * and if there is actually an I/O operation to perform.
567	 * Right now cam_periph_mapmem() only supports SCSI and device
568	 * match CCBs.  For the SCSI CCBs, we only pass the CCB in if
569	 * there's actually data to map.  cam_periph_mapmem() will do the
570	 * right thing, even if there isn't data to map, but since CCBs
571	 * without data are a reasonably common occurance (e.g. test unit
572	 * ready), it will save a few cycles if we check for it here.
573	 */
574	if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0)
575	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO)
576	    && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
577	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) {
578
579		bzero(&mapinfo, sizeof(mapinfo));
580
581		error = cam_periph_mapmem(ccb, &mapinfo);
582
583		/*
584		 * cam_periph_mapmem returned an error, we can't continue.
585		 * Return the error to the user.
586		 */
587		if (error)
588			return(error);
589
590		/*
591		 * We successfully mapped the memory in, so we need to
592		 * unmap it when the transaction is done.
593		 */
594		need_unmap = 1;
595	}
596
597	/*
598	 * If the user wants us to perform any error recovery, then honor
599	 * that request.  Otherwise, it's up to the user to perform any
600	 * error recovery.
601	 */
602	error = cam_periph_runccb(ccb,
603				  (ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ?
604				  passerror : NULL,
605				  /* cam_flags */ CAM_RETRY_SELTO,
606				  /* sense_flags */SF_RETRY_UA,
607				  &softc->device_stats);
608
609	if (need_unmap != 0)
610		cam_periph_unmapmem(ccb, &mapinfo);
611
612	ccb->ccb_h.cbfcnp = NULL;
613	ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv;
614	bcopy(ccb, inccb, sizeof(union ccb));
615
616	return(error);
617}
618
619static int
620passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
621{
622	struct cam_periph *periph;
623	struct pass_softc *softc;
624
625	periph = xpt_path_periph(ccb->ccb_h.path);
626	softc = (struct pass_softc *)periph->softc;
627
628	return(cam_periph_error(ccb, cam_flags, sense_flags,
629				 &softc->saved_ccb));
630}
631