scsi_pass.c revision 168831
1/*-
2 * Copyright (c) 1997, 1998, 2000 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
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_pass.c 168831 2007-04-18 04:58:53Z scottl $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/types.h>
35#include <sys/bio.h>
36#include <sys/malloc.h>
37#include <sys/fcntl.h>
38#include <sys/conf.h>
39#include <sys/errno.h>
40#include <sys/devicestat.h>
41#include <sys/proc.h>
42
43#include <cam/cam.h>
44#include <cam/cam_ccb.h>
45#include <cam/cam_periph.h>
46#include <cam/cam_queue.h>
47#include <cam/cam_xpt_periph.h>
48#include <cam/cam_debug.h>
49#include <cam/cam_sim.h>
50
51#include <cam/scsi/scsi_all.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	union ccb		saved_ccb;
77	struct devstat		*device_stats;
78	struct cdev *dev;
79};
80
81
82static	d_open_t	passopen;
83static	d_close_t	passclose;
84static	d_ioctl_t	passioctl;
85
86static	periph_init_t	passinit;
87static	periph_ctor_t	passregister;
88static	periph_oninv_t	passoninvalidate;
89static	periph_dtor_t	passcleanup;
90static	periph_start_t	passstart;
91static	void		passasync(void *callback_arg, u_int32_t code,
92				  struct cam_path *path, void *arg);
93static	void		passdone(struct cam_periph *periph,
94				 union ccb *done_ccb);
95static	int		passerror(union ccb *ccb, u_int32_t cam_flags,
96				  u_int32_t sense_flags);
97static 	int		passsendccb(struct cam_periph *periph, union ccb *ccb,
98				    union ccb *inccb);
99
100static struct periph_driver passdriver =
101{
102	passinit, "pass",
103	TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0
104};
105
106PERIPHDRIVER_DECLARE(pass, passdriver);
107
108static struct cdevsw pass_cdevsw = {
109	.d_version =	D_VERSION,
110	.d_flags =	0,
111	.d_open =	passopen,
112	.d_close =	passclose,
113	.d_ioctl =	passioctl,
114	.d_name =	"pass",
115};
116
117static void
118passinit(void)
119{
120	cam_status status;
121	struct cam_path *path;
122
123	/*
124	 * Install a global async callback.  This callback will
125	 * receive async callbacks like "new device found".
126	 */
127	status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
128				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
129
130	if (status == CAM_REQ_CMP) {
131		struct ccb_setasync csa;
132
133                xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
134                csa.ccb_h.func_code = XPT_SASYNC_CB;
135                csa.event_enable = AC_FOUND_DEVICE;
136                csa.callback = passasync;
137                csa.callback_arg = NULL;
138                xpt_action((union ccb *)&csa);
139		status = csa.ccb_h.status;
140                xpt_free_path(path);
141        }
142
143	if (status != CAM_REQ_CMP) {
144		printf("pass: Failed to attach master async callback "
145		       "due to status 0x%x!\n", status);
146	}
147
148}
149
150static void
151passoninvalidate(struct cam_periph *periph)
152{
153	struct pass_softc *softc;
154	struct ccb_setasync csa;
155
156	softc = (struct pass_softc *)periph->softc;
157
158	/*
159	 * De-register any async callbacks.
160	 */
161	xpt_setup_ccb(&csa.ccb_h, periph->path,
162		      /* priority */ 5);
163	csa.ccb_h.func_code = XPT_SASYNC_CB;
164	csa.event_enable = 0;
165	csa.callback = passasync;
166	csa.callback_arg = periph;
167	xpt_action((union ccb *)&csa);
168
169	softc->flags |= PASS_FLAG_INVALID;
170
171	/*
172	 * XXX Return all queued I/O with ENXIO.
173	 * XXX Handle any transactions queued to the card
174	 *     with XPT_ABORT_CCB.
175	 */
176
177	if (bootverbose) {
178		xpt_print(periph->path, "lost device\n");
179	}
180
181}
182
183static void
184passcleanup(struct cam_periph *periph)
185{
186	struct pass_softc *softc;
187
188	softc = (struct pass_softc *)periph->softc;
189
190	devstat_remove_entry(softc->device_stats);
191
192	destroy_dev(softc->dev);
193
194	if (bootverbose) {
195		xpt_print(periph->path, "removing device entry\n");
196	}
197	free(softc, M_DEVBUF);
198}
199
200static void
201passasync(void *callback_arg, u_int32_t code,
202	  struct cam_path *path, void *arg)
203{
204	struct cam_periph *periph;
205	struct cam_sim *sim;
206
207	periph = (struct cam_periph *)callback_arg;
208
209	switch (code) {
210	case AC_FOUND_DEVICE:
211	{
212		struct ccb_getdev *cgd;
213		cam_status status;
214
215		cgd = (struct ccb_getdev *)arg;
216		if (cgd == NULL)
217			break;
218
219		/*
220		 * Allocate a peripheral instance for
221		 * this device and start the probe
222		 * process.
223		 */
224		sim = xpt_path_sim(cgd->ccb_h.path);
225		status = cam_periph_alloc(passregister, passoninvalidate,
226					  passcleanup, passstart, "pass",
227					  CAM_PERIPH_BIO, cgd->ccb_h.path,
228					  passasync, AC_FOUND_DEVICE, cgd);
229
230		if (status != CAM_REQ_CMP
231		 && status != CAM_REQ_INPROG) {
232			const struct cam_status_entry *entry;
233
234			entry = cam_fetch_status_entry(status);
235
236			printf("passasync: Unable to attach new device "
237			       "due to status %#x: %s\n", status, entry ?
238			       entry->status_text : "Unknown");
239		}
240
241		break;
242	}
243	default:
244		cam_periph_async(periph, code, path, arg);
245		break;
246	}
247}
248
249static cam_status
250passregister(struct cam_periph *periph, void *arg)
251{
252	struct pass_softc *softc;
253	struct ccb_setasync csa;
254	struct ccb_getdev *cgd;
255	int    no_tags;
256
257	cgd = (struct ccb_getdev *)arg;
258	if (periph == NULL) {
259		printf("passregister: periph was NULL!!\n");
260		return(CAM_REQ_CMP_ERR);
261	}
262
263	if (cgd == NULL) {
264		printf("passregister: no getdev CCB, can't register device\n");
265		return(CAM_REQ_CMP_ERR);
266	}
267
268	softc = (struct pass_softc *)malloc(sizeof(*softc),
269					    M_DEVBUF, M_NOWAIT);
270
271	if (softc == NULL) {
272		printf("passregister: Unable to probe new device. "
273		       "Unable to allocate softc\n");
274		return(CAM_REQ_CMP_ERR);
275	}
276
277	bzero(softc, sizeof(*softc));
278	softc->state = PASS_STATE_NORMAL;
279	softc->pd_type = SID_TYPE(&cgd->inq_data);
280
281	periph->softc = softc;
282
283	/*
284	 * We pass in 0 for a blocksize, since we don't
285	 * know what the blocksize of this device is, if
286	 * it even has a blocksize.
287	 */
288	no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
289	softc->device_stats = devstat_new_entry("pass",
290			  unit2minor(periph->unit_number), 0,
291			  DEVSTAT_NO_BLOCKSIZE
292			  | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
293			  softc->pd_type |
294			  DEVSTAT_TYPE_IF_SCSI |
295			  DEVSTAT_TYPE_PASS,
296			  DEVSTAT_PRIORITY_PASS);
297
298	/* Register the device */
299	mtx_unlock(periph->sim->mtx);
300	softc->dev = make_dev(&pass_cdevsw, unit2minor(periph->unit_number),
301			      UID_ROOT, GID_OPERATOR, 0600, "%s%d",
302			      periph->periph_name, periph->unit_number);
303	mtx_lock(periph->sim->mtx);
304	softc->dev->si_drv1 = periph;
305
306	/*
307	 * Add an async callback so that we get
308	 * notified if this device goes away.
309	 */
310	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
311	csa.ccb_h.func_code = XPT_SASYNC_CB;
312	csa.event_enable = AC_LOST_DEVICE;
313	csa.callback = passasync;
314	csa.callback_arg = periph;
315	xpt_action((union ccb *)&csa);
316
317	if (bootverbose)
318		xpt_announce_periph(periph, NULL);
319
320	return(CAM_REQ_CMP);
321}
322
323static int
324passopen(struct cdev *dev, int flags, int fmt, struct thread *td)
325{
326	struct cam_periph *periph;
327	struct pass_softc *softc;
328	int error;
329
330	error = 0; /* default to no error */
331
332	periph = (struct cam_periph *)dev->si_drv1;
333	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
334		return (ENXIO);
335
336	cam_periph_lock(periph);
337
338	softc = (struct pass_softc *)periph->softc;
339
340	if (softc->flags & PASS_FLAG_INVALID) {
341		cam_periph_unlock(periph);
342		cam_periph_release(periph);
343		return(ENXIO);
344	}
345
346	/*
347	 * Don't allow access when we're running at a high securelevel.
348	 */
349	error = securelevel_gt(td->td_ucred, 1);
350	if (error) {
351		cam_periph_unlock(periph);
352		cam_periph_release(periph);
353		return(error);
354	}
355
356	/*
357	 * Only allow read-write access.
358	 */
359	if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) {
360		cam_periph_unlock(periph);
361		cam_periph_release(periph);
362		return(EPERM);
363	}
364
365	/*
366	 * We don't allow nonblocking access.
367	 */
368	if ((flags & O_NONBLOCK) != 0) {
369		xpt_print(periph->path, "can't do nonblocking access\n");
370		cam_periph_unlock(periph);
371		cam_periph_release(periph);
372		return(EINVAL);
373	}
374
375	if ((softc->flags & PASS_FLAG_OPEN) == 0) {
376		softc->flags |= PASS_FLAG_OPEN;
377	} else {
378		/* Device closes aren't symmertical, so fix up the refcount */
379		cam_periph_release(periph);
380	}
381
382	cam_periph_unlock(periph);
383
384	return (error);
385}
386
387static int
388passclose(struct cdev *dev, int flag, int fmt, struct thread *td)
389{
390	struct 	cam_periph *periph;
391	struct	pass_softc *softc;
392
393	periph = (struct cam_periph *)dev->si_drv1;
394	if (periph == NULL)
395		return (ENXIO);
396
397	cam_periph_lock(periph);
398
399	softc = (struct pass_softc *)periph->softc;
400	softc->flags &= ~PASS_FLAG_OPEN;
401
402	cam_periph_unlock(periph);
403	cam_periph_release(periph);
404
405	return (0);
406}
407
408static void
409passstart(struct cam_periph *periph, union ccb *start_ccb)
410{
411	struct pass_softc *softc;
412
413	softc = (struct pass_softc *)periph->softc;
414
415	switch (softc->state) {
416	case PASS_STATE_NORMAL:
417		start_ccb->ccb_h.ccb_type = PASS_CCB_WAITING;
418		SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
419				  periph_links.sle);
420		periph->immediate_priority = CAM_PRIORITY_NONE;
421		wakeup(&periph->ccb_list);
422		break;
423	}
424}
425
426static void
427passdone(struct cam_periph *periph, union ccb *done_ccb)
428{
429	struct pass_softc *softc;
430	struct ccb_scsiio *csio;
431
432	softc = (struct pass_softc *)periph->softc;
433	csio = &done_ccb->csio;
434	switch (csio->ccb_h.ccb_type) {
435	case PASS_CCB_WAITING:
436		/* Caller will release the CCB */
437		wakeup(&done_ccb->ccb_h.cbfcnp);
438		return;
439	}
440	xpt_release_ccb(done_ccb);
441}
442
443static int
444passioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
445{
446	struct	cam_periph *periph;
447	struct	pass_softc *softc;
448	int	error;
449
450	periph = (struct cam_periph *)dev->si_drv1;
451	if (periph == NULL)
452		return(ENXIO);
453
454	cam_periph_lock(periph);
455	softc = (struct pass_softc *)periph->softc;
456
457	error = 0;
458
459	switch (cmd) {
460
461	case CAMIOCOMMAND:
462	{
463		union ccb *inccb;
464		union ccb *ccb;
465		int ccb_malloced;
466
467		inccb = (union ccb *)addr;
468
469		/*
470		 * Some CCB types, like scan bus and scan lun can only go
471		 * through the transport layer device.
472		 */
473		if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) {
474			xpt_print(periph->path, "CCB function code %#x is "
475			    "restricted to the XPT device\n",
476			    inccb->ccb_h.func_code);
477			error = ENODEV;
478			break;
479		}
480
481		/*
482		 * Non-immediate CCBs need a CCB from the per-device pool
483		 * of CCBs, which is scheduled by the transport layer.
484		 * Immediate CCBs and user-supplied CCBs should just be
485		 * malloced.
486		 */
487		if ((inccb->ccb_h.func_code & XPT_FC_QUEUED)
488		 && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) {
489			ccb = cam_periph_getccb(periph,
490						inccb->ccb_h.pinfo.priority);
491			ccb_malloced = 0;
492		} else {
493			ccb = xpt_alloc_ccb_nowait();
494
495			if (ccb != NULL)
496				xpt_setup_ccb(&ccb->ccb_h, periph->path,
497					      inccb->ccb_h.pinfo.priority);
498			ccb_malloced = 1;
499		}
500
501		if (ccb == NULL) {
502			xpt_print(periph->path, "unable to allocate CCB\n");
503			error = ENOMEM;
504			break;
505		}
506
507		error = passsendccb(periph, ccb, inccb);
508
509		if (ccb_malloced)
510			xpt_free_ccb(ccb);
511		else
512			xpt_release_ccb(ccb);
513
514		break;
515	}
516	default:
517		error = cam_periph_ioctl(periph, cmd, addr, passerror);
518		break;
519	}
520
521	cam_periph_unlock(periph);
522	return(error);
523}
524
525/*
526 * Generally, "ccb" should be the CCB supplied by the kernel.  "inccb"
527 * should be the CCB that is copied in from the user.
528 */
529static int
530passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
531{
532	struct pass_softc *softc;
533	struct cam_periph_map_info mapinfo;
534	int error, need_unmap;
535
536	softc = (struct pass_softc *)periph->softc;
537
538	need_unmap = 0;
539
540	/*
541	 * There are some fields in the CCB header that need to be
542	 * preserved, the rest we get from the user.
543	 */
544	xpt_merge_ccb(ccb, inccb);
545
546	/*
547	 * There's no way for the user to have a completion
548	 * function, so we put our own completion function in here.
549	 */
550	ccb->ccb_h.cbfcnp = passdone;
551
552	/*
553	 * We only attempt to map the user memory into kernel space
554	 * if they haven't passed in a physical memory pointer,
555	 * and if there is actually an I/O operation to perform.
556	 * Right now cam_periph_mapmem() only supports SCSI and device
557	 * match CCBs.  For the SCSI CCBs, we only pass the CCB in if
558	 * there's actually data to map.  cam_periph_mapmem() will do the
559	 * right thing, even if there isn't data to map, but since CCBs
560	 * without data are a reasonably common occurance (e.g. test unit
561	 * ready), it will save a few cycles if we check for it here.
562	 */
563	if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0)
564	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO)
565	    && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
566	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) {
567
568		bzero(&mapinfo, sizeof(mapinfo));
569
570		/*
571		 * cam_periph_mapmem calls into proc and vm functions that can
572		 * sleep as well as trigger I/O, so we can't hold the lock.
573		 * Dropping it here is reasonably safe.
574		 */
575		cam_periph_unlock(periph);
576		error = cam_periph_mapmem(ccb, &mapinfo);
577		cam_periph_lock(periph);
578
579		/*
580		 * cam_periph_mapmem returned an error, we can't continue.
581		 * Return the error to the user.
582		 */
583		if (error)
584			return(error);
585
586		/*
587		 * We successfully mapped the memory in, so we need to
588		 * unmap it when the transaction is done.
589		 */
590		need_unmap = 1;
591	}
592
593	/*
594	 * If the user wants us to perform any error recovery, then honor
595	 * that request.  Otherwise, it's up to the user to perform any
596	 * error recovery.
597	 */
598	error = cam_periph_runccb(ccb,
599				  (ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ?
600				  passerror : NULL,
601				  /* cam_flags */ CAM_RETRY_SELTO,
602				  /* sense_flags */SF_RETRY_UA,
603				  softc->device_stats);
604
605	if (need_unmap != 0)
606		cam_periph_unmapmem(ccb, &mapinfo);
607
608	ccb->ccb_h.cbfcnp = NULL;
609	ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv;
610	bcopy(ccb, inccb, sizeof(union ccb));
611
612	return(error);
613}
614
615static int
616passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
617{
618	struct cam_periph *periph;
619	struct pass_softc *softc;
620
621	periph = xpt_path_periph(ccb->ccb_h.path);
622	softc = (struct pass_softc *)periph->softc;
623
624	return(cam_periph_error(ccb, cam_flags, sense_flags,
625				 &softc->saved_ccb));
626}
627