scsi_pass.c revision 288420
1221420Sdes/*-
276259Sgreen * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
3126274Sdes * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry.
476259Sgreen * All rights reserved.
5126274Sdes *
6126274Sdes * Redistribution and use in source and binary forms, with or without
7126274Sdes * modification, are permitted provided that the following conditions
876259Sgreen * are met:
9126274Sdes * 1. Redistributions of source code must retain the above copyright
10126274Sdes *    notice, this list of conditions, and the following disclaimer,
11126274Sdes *    without modification, immediately at the beginning of the file.
12126274Sdes * 2. The name of the author may not be used to endorse or promote products
13126274Sdes *    derived from this software without specific prior written permission.
14126274Sdes *
15126274Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1676259Sgreen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1776259Sgreen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1876259Sgreen * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
1976259Sgreen * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20162852Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21162852Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22162852Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23162852Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24162852Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25162852Sdes * SUCH DAMAGE.
26162852Sdes */
27162852Sdes
28181111Sdes#include <sys/cdefs.h>
29181111Sdes__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_pass.c 288420 2015-09-30 13:31:37Z mav $");
30181111Sdes
3176259Sgreen#include <sys/param.h>
32181111Sdes#include <sys/systm.h>
33162852Sdes#include <sys/kernel.h>
34162852Sdes#include <sys/types.h>
35162852Sdes#include <sys/bio.h>
36162852Sdes#include <sys/malloc.h>
37162852Sdes#include <sys/fcntl.h>
38204917Sdes#include <sys/conf.h>
39204917Sdes#include <sys/errno.h>
40204917Sdes#include <sys/devicestat.h>
41146998Sdes#include <sys/proc.h>
42146998Sdes#include <sys/taskqueue.h>
43146998Sdes
44146998Sdes#include <cam/cam.h>
45146998Sdes#include <cam/cam_ccb.h>
46162852Sdes#include <cam/cam_periph.h>
47162852Sdes#include <cam/cam_queue.h>
48162852Sdes#include <cam/cam_xpt_periph.h>
49162852Sdes#include <cam/cam_debug.h>
50162852Sdes#include <cam/cam_sim.h>
51162852Sdes#include <cam/cam_compat.h>
52146998Sdes
53181111Sdes#include <cam/scsi/scsi_all.h>
54181111Sdes#include <cam/scsi/scsi_pass.h>
55181111Sdes
56181111Sdestypedef enum {
57181111Sdes	PASS_FLAG_OPEN			= 0x01,
58181111Sdes	PASS_FLAG_LOCKED		= 0x02,
59181111Sdes	PASS_FLAG_INVALID		= 0x04,
60181111Sdes	PASS_FLAG_INITIAL_PHYSPATH	= 0x08
6176259Sgreen} pass_flags;
6276259Sgreen
6376259Sgreentypedef enum {
6492555Sdes	PASS_STATE_NORMAL
6576259Sgreen} pass_state;
6676259Sgreen
67162852Sdestypedef enum {
6876259Sgreen	PASS_CCB_BUFFER_IO
6976259Sgreen} pass_ccb_types;
7076259Sgreen
71204917Sdes#define ccb_type	ppriv_field0
72204917Sdes#define ccb_bp		ppriv_ptr1
73204917Sdes
74126274Sdesstruct pass_softc {
75126274Sdes	pass_state	 state;
76126274Sdes	pass_flags	 flags;
77126274Sdes	u_int8_t	 pd_type;
78126274Sdes	union ccb	 saved_ccb;
79126274Sdes	int		 open_count;
80126274Sdes	u_int		 maxio;
81126274Sdes	struct devstat	*device_stats;
82126274Sdes	struct cdev	*dev;
83126274Sdes	struct cdev	*alias_dev;
84128456Sdes	struct task	 add_physpath_task;
85126274Sdes};
86204917Sdes
87204917Sdes
88204917Sdesstatic	d_open_t	passopen;
89204917Sdesstatic	d_close_t	passclose;
90204917Sdesstatic	d_ioctl_t	passioctl;
91204917Sdesstatic	d_ioctl_t	passdoioctl;
92137015Sdes
93137015Sdesstatic	periph_init_t	passinit;
94137015Sdesstatic	periph_ctor_t	passregister;
95137015Sdesstatic	periph_oninv_t	passoninvalidate;
96137015Sdesstatic	periph_dtor_t	passcleanup;
97137015Sdesstatic void		pass_add_physpath(void *context, int pending);
98204917Sdesstatic	void		passasync(void *callback_arg, u_int32_t code,
99204917Sdes				  struct cam_path *path, void *arg);
100204917Sdesstatic	int		passerror(union ccb *ccb, u_int32_t cam_flags,
101204917Sdes				  u_int32_t sense_flags);
102204917Sdesstatic 	int		passsendccb(struct cam_periph *periph, union ccb *ccb,
103204917Sdes				    union ccb *inccb);
104126274Sdes
105126274Sdesstatic struct periph_driver passdriver =
106126274Sdes{
10798937Sdes	passinit, "pass",
10898937Sdes	TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0
109126274Sdes};
110126274Sdes
11176259SgreenPERIPHDRIVER_DECLARE(pass, passdriver);
112137015Sdes
113204917Sdesstatic struct cdevsw pass_cdevsw = {
114204917Sdes	.d_version =	D_VERSION,
115204917Sdes	.d_flags =	D_TRACKCLOSE,
116204917Sdes	.d_open =	passopen,
117204917Sdes	.d_close =	passclose,
118204917Sdes	.d_ioctl =	passioctl,
119204917Sdes	.d_name =	"pass",
120204917Sdes};
121204917Sdes
122113908Sdesstatic void
123204917Sdespassinit(void)
124137015Sdes{
125137015Sdes	cam_status status;
126126274Sdes
127126274Sdes	/*
128126274Sdes	 * Install a global async callback.  This callback will
129126274Sdes	 * receive async callbacks like "new device found".
130126274Sdes	 */
131181111Sdes	status = xpt_register_async(AC_FOUND_DEVICE, passasync, NULL, NULL);
132126274Sdes
133126274Sdes	if (status != CAM_REQ_CMP) {
134126274Sdes		printf("pass: Failed to attach master async callback "
135221420Sdes		       "due to status 0x%x!\n", status);
136126274Sdes	}
137126274Sdes
138126274Sdes}
139126274Sdes
140126274Sdesstatic void
141126274Sdespassdevgonecb(void *arg)
142126274Sdes{
143126274Sdes	struct cam_periph *periph;
144126274Sdes	struct mtx *mtx;
145126274Sdes	struct pass_softc *softc;
146126274Sdes	int i;
147126274Sdes
148126274Sdes	periph = (struct cam_periph *)arg;
149126274Sdes	mtx = cam_periph_mtx(periph);
150126274Sdes	mtx_lock(mtx);
151126274Sdes
152126274Sdes	softc = (struct pass_softc *)periph->softc;
153126274Sdes	KASSERT(softc->open_count >= 0, ("Negative open count %d",
154126274Sdes		softc->open_count));
155126274Sdes
156204917Sdes	/*
157126274Sdes	 * When we get this callback, we will get no more close calls from
158126274Sdes	 * devfs.  So if we have any dangling opens, we need to release the
159204917Sdes	 * reference held for that particular context.
160204917Sdes	 */
161204917Sdes	for (i = 0; i < softc->open_count; i++)
162204917Sdes		cam_periph_release_locked(periph);
163204917Sdes
164126274Sdes	softc->open_count = 0;
165204917Sdes
166204917Sdes	/*
167204917Sdes	 * Release the reference held for the device node, it is gone now.
168204917Sdes	 */
169204917Sdes	cam_periph_release_locked(periph);
170204917Sdes
171204917Sdes	/*
172204917Sdes	 * We reference the lock directly here, instead of using
173204917Sdes	 * cam_periph_unlock().  The reason is that the final call to
174204917Sdes	 * cam_periph_release_locked() above could result in the periph
175204917Sdes	 * getting freed.  If that is the case, dereferencing the periph
176204917Sdes	 * with a cam_periph_unlock() call would cause a page fault.
177204917Sdes	 */
178204917Sdes	mtx_unlock(mtx);
179204917Sdes}
180221420Sdes
181204917Sdesstatic void
182204917Sdespassoninvalidate(struct cam_periph *periph)
183204917Sdes{
184204917Sdes	struct pass_softc *softc;
185215116Sdes
186215116Sdes	softc = (struct pass_softc *)periph->softc;
187204917Sdes
188204917Sdes	/*
189204917Sdes	 * De-register any async callbacks.
190204917Sdes	 */
191204917Sdes	xpt_register_async(0, passasync, periph, periph->path);
192204917Sdes
193204917Sdes	softc->flags |= PASS_FLAG_INVALID;
194204917Sdes
195204917Sdes	/*
196204917Sdes	 * Tell devfs this device has gone away, and ask for a callback
197204917Sdes	 * when it has cleaned up its state.
198204917Sdes	 */
199126274Sdes	destroy_dev_sched_cb(softc->dev, passdevgonecb, periph);
200126274Sdes
201204917Sdes	/*
202126274Sdes	 * XXX Return all queued I/O with ENXIO.
203181111Sdes	 * XXX Handle any transactions queued to the card
20492555Sdes	 *     with XPT_ABORT_CCB.
205137015Sdes	 */
206137015Sdes}
207146998Sdes
208137015Sdesstatic void
209146998Sdespasscleanup(struct cam_periph *periph)
210146998Sdes{
211137015Sdes	struct pass_softc *softc;
212137015Sdes
213137015Sdes	softc = (struct pass_softc *)periph->softc;
214137015Sdes
215181111Sdes	devstat_remove_entry(softc->device_stats);
216137015Sdes
217137015Sdes	cam_periph_unlock(periph);
218137015Sdes	taskqueue_drain(taskqueue_thread, &softc->add_physpath_task);
219137015Sdes
220146998Sdes	cam_periph_lock(periph);
221137015Sdes
222137015Sdes	free(softc, M_DEVBUF);
223137015Sdes}
224146998Sdes
225137015Sdesstatic void
226137015Sdespass_add_physpath(void *context, int pending)
227137015Sdes{
228126274Sdes	struct cam_periph *periph;
229126274Sdes	struct pass_softc *softc;
230192595Sdes	char *physpath;
231192595Sdes
232192595Sdes	/*
233192595Sdes	 * If we have one, create a devfs alias for our
234192595Sdes	 * physical path.
235192595Sdes	 */
236192595Sdes	periph = context;
237192595Sdes	softc = periph->softc;
238192595Sdes	physpath = malloc(MAXPATHLEN, M_DEVBUF, M_WAITOK);
239204917Sdes	cam_periph_lock(periph);
240192595Sdes	if (periph->flags & CAM_PERIPH_INVALID) {
241192595Sdes		cam_periph_unlock(periph);
242192595Sdes		goto out;
243192595Sdes	}
244221420Sdes	if (xpt_getattr(physpath, MAXPATHLEN,
245192595Sdes			"GEOM::physpath", periph->path) == 0
246204917Sdes	 && strlen(physpath) != 0) {
247192595Sdes
248192595Sdes		cam_periph_unlock(periph);
249192595Sdes		make_dev_physpath_alias(MAKEDEV_WAITOK, &softc->alias_dev,
250204917Sdes					softc->dev, softc->alias_dev, physpath);
251192595Sdes		cam_periph_lock(periph);
252192595Sdes	}
253192595Sdes
254192595Sdes	/*
255192595Sdes	 * Now that we've made our alias, we no longer have to have a
256192595Sdes	 * reference to the device.
257192595Sdes	 */
258192595Sdes	if ((softc->flags & PASS_FLAG_INITIAL_PHYSPATH) == 0) {
259192595Sdes		softc->flags |= PASS_FLAG_INITIAL_PHYSPATH;
260192595Sdes		cam_periph_unlock(periph);
261126274Sdes		dev_rel(softc->dev);
262126274Sdes	}
263126274Sdes	else
264126274Sdes		cam_periph_unlock(periph);
265126274Sdes
266126274Sdesout:
267126274Sdes	free(physpath, M_DEVBUF);
268126274Sdes}
269126274Sdes
270126274Sdesstatic void
271126274Sdespassasync(void *callback_arg, u_int32_t code,
272126274Sdes	  struct cam_path *path, void *arg)
273221420Sdes{
274126274Sdes	struct cam_periph *periph;
275126274Sdes
276126274Sdes	periph = (struct cam_periph *)callback_arg;
277126274Sdes
278126274Sdes	switch (code) {
279126274Sdes	case AC_FOUND_DEVICE:
280126274Sdes	{
281126274Sdes		struct ccb_getdev *cgd;
282126274Sdes		cam_status status;
283126274Sdes
284126274Sdes		cgd = (struct ccb_getdev *)arg;
285126274Sdes		if (cgd == NULL)
286126274Sdes			break;
287126274Sdes
288126274Sdes		/*
289126274Sdes		 * Allocate a peripheral instance for
290126274Sdes		 * this device and start the probe
291126274Sdes		 * process.
292126274Sdes		 */
293126274Sdes		status = cam_periph_alloc(passregister, passoninvalidate,
294126274Sdes					  passcleanup, NULL, "pass",
295126274Sdes					  CAM_PERIPH_BIO, path,
296162852Sdes					  passasync, AC_FOUND_DEVICE, cgd);
297126274Sdes
298126274Sdes		if (status != CAM_REQ_CMP
299126274Sdes		 && status != CAM_REQ_INPROG) {
300126274Sdes			const struct cam_status_entry *entry;
301126274Sdes
302126274Sdes			entry = cam_fetch_status_entry(status);
303126274Sdes
304126274Sdes			printf("passasync: Unable to attach new device "
305126274Sdes			       "due to status %#x: %s\n", status, entry ?
306126274Sdes			       entry->status_text : "Unknown");
307126274Sdes		}
308126274Sdes
309126274Sdes		break;
310126274Sdes	}
311126274Sdes	case AC_ADVINFO_CHANGED:
312126274Sdes	{
313126274Sdes		uintptr_t buftype;
314126274Sdes
315126274Sdes		buftype = (uintptr_t)arg;
316126274Sdes		if (buftype == CDAI_TYPE_PHYS_PATH) {
317126274Sdes			struct pass_softc *softc;
318126274Sdes
319126274Sdes			softc = (struct pass_softc *)periph->softc;
320126274Sdes			taskqueue_enqueue(taskqueue_thread,
321126274Sdes					  &softc->add_physpath_task);
322126274Sdes		}
323126274Sdes		break;
324126274Sdes	}
325126274Sdes	default:
326126274Sdes		cam_periph_async(periph, code, path, arg);
327146998Sdes		break;
328126274Sdes	}
329126274Sdes}
330126274Sdes
331126274Sdesstatic cam_status
332126274Sdespassregister(struct cam_periph *periph, void *arg)
333126274Sdes{
334126274Sdes	struct pass_softc *softc;
335126274Sdes	struct ccb_getdev *cgd;
336126274Sdes	struct ccb_pathinq cpi;
337126274Sdes	int    no_tags;
338126274Sdes
339137015Sdes	cgd = (struct ccb_getdev *)arg;
340126274Sdes	if (cgd == NULL) {
341126274Sdes		printf("%s: no getdev CCB, can't register device\n", __func__);
342126274Sdes		return(CAM_REQ_CMP_ERR);
343137015Sdes	}
344126274Sdes
345137015Sdes	softc = (struct pass_softc *)malloc(sizeof(*softc),
346126274Sdes					    M_DEVBUF, M_NOWAIT);
347126274Sdes
348126274Sdes	if (softc == NULL) {
349126274Sdes		printf("%s: Unable to probe new device. "
350126274Sdes		       "Unable to allocate softc\n", __func__);
351204917Sdes		return(CAM_REQ_CMP_ERR);
352204917Sdes	}
353126274Sdes
354181111Sdes	bzero(softc, sizeof(*softc));
355181111Sdes	softc->state = PASS_STATE_NORMAL;
356126274Sdes	if (cgd->protocol == PROTO_SCSI || cgd->protocol == PROTO_ATAPI)
357181111Sdes		softc->pd_type = SID_TYPE(&cgd->inq_data);
358181111Sdes	else if (cgd->protocol == PROTO_SATAPM)
359181111Sdes		softc->pd_type = T_ENCLOSURE;
360204917Sdes	else
361204917Sdes		softc->pd_type = T_DIRECT;
362181111Sdes
363126274Sdes	periph->softc = softc;
364126274Sdes
365126274Sdes	bzero(&cpi, sizeof(cpi));
366126274Sdes	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
367204917Sdes	cpi.ccb_h.func_code = XPT_PATH_INQ;
368204917Sdes	xpt_action((union ccb *)&cpi);
369204917Sdes
370204917Sdes	if (cpi.maxio == 0)
371126274Sdes		softc->maxio = DFLTPHYS;	/* traditional default */
372181111Sdes	else if (cpi.maxio > MAXPHYS)
373181111Sdes		softc->maxio = MAXPHYS;		/* for safety */
374126274Sdes	else
375126274Sdes		softc->maxio = cpi.maxio;	/* real value */
376126274Sdes
377181111Sdes	/*
378126274Sdes	 * We pass in 0 for a blocksize, since we don't
379126274Sdes	 * know what the blocksize of this device is, if
380126274Sdes	 * it even has a blocksize.
381221420Sdes	 */
382221420Sdes	cam_periph_unlock(periph);
383221420Sdes	no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
384221420Sdes	softc->device_stats = devstat_new_entry("pass",
385221420Sdes			  periph->unit_number, 0,
386221420Sdes			  DEVSTAT_NO_BLOCKSIZE
387221420Sdes			  | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
388221420Sdes			  softc->pd_type |
389221420Sdes			  XPORT_DEVSTAT_TYPE(cpi.transport) |
390221420Sdes			  DEVSTAT_TYPE_PASS,
391221420Sdes			  DEVSTAT_PRIORITY_PASS);
392221420Sdes
393221420Sdes	/*
394221420Sdes	 * Acquire a reference to the periph before we create the devfs
395221420Sdes	 * instance for it.  We'll release this reference once the devfs
396221420Sdes	 * instance has been freed.
397221420Sdes	 */
398221420Sdes	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
399221420Sdes		xpt_print(periph->path, "%s: lost periph during "
400221420Sdes			  "registration!\n", __func__);
401221420Sdes		cam_periph_lock(periph);
402221420Sdes		return (CAM_REQ_CMP_ERR);
403221420Sdes	}
404221420Sdes
405181111Sdes	/* Register the device */
406126274Sdes	softc->dev = make_dev(&pass_cdevsw, periph->unit_number,
407181111Sdes			      UID_ROOT, GID_OPERATOR, 0600, "%s%d",
408181111Sdes			      periph->periph_name, periph->unit_number);
409126274Sdes
410181111Sdes	/*
411181111Sdes	 * Now that we have made the devfs instance, hold a reference to it
412181111Sdes	 * until the task queue has run to setup the physical path alias.
413137015Sdes	 * That way devfs won't get rid of the device before we add our
414204917Sdes	 * alias.
415181111Sdes	 */
416181111Sdes	dev_ref(softc->dev);
417181111Sdes
418181111Sdes	cam_periph_lock(periph);
419181111Sdes	softc->dev->si_drv1 = periph;
420181111Sdes
421181111Sdes	TASK_INIT(&softc->add_physpath_task, /*priority*/0,
422181111Sdes		  pass_add_physpath, periph);
423181111Sdes
424181111Sdes	/*
425181111Sdes	 * See if physical path information is already available.
426181111Sdes	 */
427181111Sdes	taskqueue_enqueue(taskqueue_thread, &softc->add_physpath_task);
428181111Sdes
429181111Sdes	/*
430204917Sdes	 * Add an async callback so that we get notified if
431204917Sdes	 * this device goes away or its physical path
432204917Sdes	 * (stored in the advanced info data of the EDT) has
433181111Sdes	 * changed.
434204917Sdes	 */
435181111Sdes	xpt_register_async(AC_LOST_DEVICE | AC_ADVINFO_CHANGED,
436181111Sdes			   passasync, periph, periph->path);
437181111Sdes
438204917Sdes	if (bootverbose)
439181111Sdes		xpt_announce_periph(periph, NULL);
440181111Sdes
441181111Sdes	return(CAM_REQ_CMP);
442181111Sdes}
443181111Sdes
444181111Sdesstatic int
445181111Sdespassopen(struct cdev *dev, int flags, int fmt, struct thread *td)
446181111Sdes{
447181111Sdes	struct cam_periph *periph;
448181111Sdes	struct pass_softc *softc;
449181111Sdes	int error;
450181111Sdes
451126274Sdes	periph = (struct cam_periph *)dev->si_drv1;
452126274Sdes	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
453126274Sdes		return (ENXIO);
454181111Sdes
455126274Sdes	cam_periph_lock(periph);
456126274Sdes
457126274Sdes	softc = (struct pass_softc *)periph->softc;
458181111Sdes
459126274Sdes	if (softc->flags & PASS_FLAG_INVALID) {
460181111Sdes		cam_periph_release_locked(periph);
461181111Sdes		cam_periph_unlock(periph);
462126274Sdes		return(ENXIO);
463181111Sdes	}
464181111Sdes
465126274Sdes	/*
466181111Sdes	 * Don't allow access when we're running at a high securelevel.
467181111Sdes	 */
468181111Sdes	error = securelevel_gt(td->td_ucred, 1);
469181111Sdes	if (error) {
470181111Sdes		cam_periph_release_locked(periph);
471181111Sdes		cam_periph_unlock(periph);
472181111Sdes		return(error);
473181111Sdes	}
474181111Sdes
475181111Sdes	/*
476181111Sdes	 * Only allow read-write access.
477181111Sdes	 */
478126274Sdes	if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) {
479126274Sdes		cam_periph_release_locked(periph);
480126274Sdes		cam_periph_unlock(periph);
481181111Sdes		return(EPERM);
482126274Sdes	}
483126274Sdes
484126274Sdes	/*
485126274Sdes	 * We don't allow nonblocking access.
486126274Sdes	 */
487126274Sdes	if ((flags & O_NONBLOCK) != 0) {
488126274Sdes		xpt_print(periph->path, "can't do nonblocking access\n");
489126274Sdes		cam_periph_release_locked(periph);
490126274Sdes		cam_periph_unlock(periph);
491126274Sdes		return(EINVAL);
492126274Sdes	}
493162852Sdes
494126274Sdes	softc->open_count++;
495126274Sdes
496126274Sdes	cam_periph_unlock(periph);
497126274Sdes
498126274Sdes	return (error);
499126274Sdes}
500126274Sdes
501126274Sdesstatic int
502126274Sdespassclose(struct cdev *dev, int flag, int fmt, struct thread *td)
503126274Sdes{
504126274Sdes	struct 	cam_periph *periph;
505126274Sdes	struct  pass_softc *softc;
506162852Sdes	struct mtx *mtx;
507126274Sdes
508126274Sdes	periph = (struct cam_periph *)dev->si_drv1;
509204917Sdes	if (periph == NULL)
510126274Sdes		return (ENXIO);
511204917Sdes	mtx = cam_periph_mtx(periph);
512126274Sdes	mtx_lock(mtx);
513204917Sdes
514204917Sdes	softc = periph->softc;
515204917Sdes	softc->open_count--;
516204917Sdes
517204917Sdes	cam_periph_release_locked(periph);
518204917Sdes
519204917Sdes	/*
520204917Sdes	 * We reference the lock directly here, instead of using
521204917Sdes	 * cam_periph_unlock().  The reason is that the call to
522126274Sdes	 * cam_periph_release_locked() above could result in the periph
523126274Sdes	 * getting freed.  If that is the case, dereferencing the periph
524126274Sdes	 * with a cam_periph_unlock() call would cause a page fault.
525204917Sdes	 *
526204917Sdes	 * cam_periph_release() avoids this problem using the same method,
527126274Sdes	 * but we're manually acquiring and dropping the lock here to
528126274Sdes	 * protect the open count and avoid another lock acquisition and
529126274Sdes	 * release.
530204917Sdes	 */
531126274Sdes	mtx_unlock(mtx);
532126274Sdes
533204917Sdes	return (0);
534126274Sdes}
535126274Sdes
536126274Sdesstatic int
537126274Sdespassioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
538126274Sdes{
539204917Sdes	int error;
540204917Sdes
541204917Sdes	if ((error = passdoioctl(dev, cmd, addr, flag, td)) == ENOTTY) {
542204917Sdes		error = cam_compat_ioctl(dev, cmd, addr, flag, td, passdoioctl);
543204917Sdes	}
544204917Sdes	return (error);
545204917Sdes}
546126274Sdes
547126274Sdesstatic int
548126274Sdespassdoioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
549126274Sdes{
550137015Sdes	struct	cam_periph *periph;
551204917Sdes	int	error;
552204917Sdes	uint32_t priority;
553204917Sdes
554204917Sdes	periph = (struct cam_periph *)dev->si_drv1;
555126274Sdes	if (periph == NULL)
556126274Sdes		return(ENXIO);
557126274Sdes
558126274Sdes	cam_periph_lock(periph);
559126274Sdes
560126274Sdes	error = 0;
561204917Sdes
562204917Sdes	switch (cmd) {
563126274Sdes
564204917Sdes	case CAMIOCOMMAND:
565126274Sdes	{
566204917Sdes		union ccb *inccb;
567204917Sdes		union ccb *ccb;
568204917Sdes		int ccb_malloced;
569204917Sdes
570204917Sdes		inccb = (union ccb *)addr;
571126274Sdes
572126274Sdes		/*
573204917Sdes		 * Some CCB types, like scan bus and scan lun can only go
574204917Sdes		 * through the transport layer device.
575204917Sdes		 */
576204917Sdes		if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) {
577204917Sdes			xpt_print(periph->path, "CCB function code %#x is "
578204917Sdes			    "restricted to the XPT device\n",
579204917Sdes			    inccb->ccb_h.func_code);
580204917Sdes			error = ENODEV;
581204917Sdes			break;
582126274Sdes		}
583126274Sdes
584126274Sdes		/* Compatibility for RL/priority-unaware code. */
585126274Sdes		priority = inccb->ccb_h.pinfo.priority;
586126274Sdes		if (priority <= CAM_PRIORITY_OOB)
587126274Sdes		    priority += CAM_PRIORITY_OOB + 1;
588126274Sdes
589126274Sdes		/*
590126274Sdes		 * Non-immediate CCBs need a CCB from the per-device pool
591126274Sdes		 * of CCBs, which is scheduled by the transport layer.
592126274Sdes		 * Immediate CCBs and user-supplied CCBs should just be
593204917Sdes		 * malloced.
594204917Sdes		 */
595126274Sdes		if ((inccb->ccb_h.func_code & XPT_FC_QUEUED)
596126274Sdes		 && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) {
597126274Sdes			ccb = cam_periph_getccb(periph, priority);
598204917Sdes			ccb_malloced = 0;
599126274Sdes		} else {
600126274Sdes			ccb = xpt_alloc_ccb_nowait();
601204917Sdes
602181111Sdes			if (ccb != NULL)
603126274Sdes				xpt_setup_ccb(&ccb->ccb_h, periph->path,
604126274Sdes					      priority);
605126274Sdes			ccb_malloced = 1;
606126274Sdes		}
607126274Sdes
608126274Sdes		if (ccb == NULL) {
609126274Sdes			xpt_print(periph->path, "unable to allocate CCB\n");
610126274Sdes			error = ENOMEM;
611204917Sdes			break;
612126274Sdes		}
613126274Sdes
614126274Sdes		error = passsendccb(periph, ccb, inccb);
615126274Sdes
616126274Sdes		if (ccb_malloced)
617204917Sdes			xpt_free_ccb(ccb);
618204917Sdes		else
619204917Sdes			xpt_release_ccb(ccb);
620204917Sdes
621126274Sdes		break;
622204917Sdes	}
623204917Sdes	default:
624204917Sdes		error = cam_periph_ioctl(periph, cmd, addr, passerror);
625126274Sdes		break;
626126274Sdes	}
627126274Sdes
628126274Sdes	cam_periph_unlock(periph);
629137015Sdes	return(error);
630181111Sdes}
631181111Sdes
632181111Sdes/*
633181111Sdes * Generally, "ccb" should be the CCB supplied by the kernel.  "inccb"
634181111Sdes * should be the CCB that is copied in from the user.
635204917Sdes */
636204917Sdesstatic int
637204917Sdespasssendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
638204917Sdes{
639204917Sdes	struct pass_softc *softc;
640126274Sdes	struct cam_periph_map_info mapinfo;
641126274Sdes	xpt_opcode fc;
642126274Sdes	int error;
643126274Sdes
644126274Sdes	softc = (struct pass_softc *)periph->softc;
645126274Sdes
646204917Sdes	/*
647204917Sdes	 * There are some fields in the CCB header that need to be
648204917Sdes	 * preserved, the rest we get from the user.
649126274Sdes	 */
650126274Sdes	xpt_merge_ccb(ccb, inccb);
651204917Sdes
652204917Sdes	/*
653204917Sdes	 * Let cam_periph_mapmem do a sanity check on the data pointer format.
654204917Sdes	 * Even if no data transfer is needed, it's a cheap check and it
655204917Sdes	 * simplifies the code.
656126274Sdes	 */
657126274Sdes	fc = ccb->ccb_h.func_code;
658204917Sdes	if ((fc == XPT_SCSI_IO) || (fc == XPT_ATA_IO) || (fc == XPT_SMP_IO)
659204917Sdes	 || (fc == XPT_DEV_MATCH) || (fc == XPT_DEV_ADVINFO)) {
660204917Sdes		bzero(&mapinfo, sizeof(mapinfo));
661204917Sdes
662204917Sdes		/*
663204917Sdes		 * cam_periph_mapmem calls into proc and vm functions that can
664204917Sdes		 * sleep as well as trigger I/O, so we can't hold the lock.
665204917Sdes		 * Dropping it here is reasonably safe.
666204917Sdes		 */
667126274Sdes		cam_periph_unlock(periph);
668126274Sdes		error = cam_periph_mapmem(ccb, &mapinfo, softc->maxio);
669126274Sdes		cam_periph_lock(periph);
670126274Sdes
671126274Sdes		/*
672126274Sdes		 * cam_periph_mapmem returned an error, we can't continue.
673126274Sdes		 * Return the error to the user.
674126274Sdes		 */
675126274Sdes		if (error)
676126274Sdes			return(error);
677126274Sdes	} else
678126274Sdes		/* Ensure that the unmap call later on is a no-op. */
679126274Sdes		mapinfo.num_bufs_used = 0;
680126274Sdes
681126274Sdes	/*
682126274Sdes	 * If the user wants us to perform any error recovery, then honor
683137015Sdes	 * that request.  Otherwise, it's up to the user to perform any
684126274Sdes	 * error recovery.
685137015Sdes	 */
686137015Sdes	cam_periph_runccb(ccb, passerror, /* cam_flags */ CAM_RETRY_SELTO,
687137015Sdes	    /* sense_flags */ ((ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ?
688137015Sdes	     SF_RETRY_UA : SF_NO_RECOVERY) | SF_NO_PRINT,
689137015Sdes	    softc->device_stats);
690137015Sdes
691137015Sdes	cam_periph_unmapmem(ccb, &mapinfo);
692137015Sdes
693137015Sdes	ccb->ccb_h.cbfcnp = NULL;
694126274Sdes	ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv;
695126274Sdes	bcopy(ccb, inccb, sizeof(union ccb));
696126274Sdes
697126274Sdes	return(0);
698126274Sdes}
699126274Sdes
700149749Sdesstatic int
701149749Sdespasserror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
702126274Sdes{
703126274Sdes	struct cam_periph *periph;
704126274Sdes	struct pass_softc *softc;
705126274Sdes
706126274Sdes	periph = xpt_path_periph(ccb->ccb_h.path);
707137015Sdes	softc = (struct pass_softc *)periph->softc;
708149749Sdes
709126274Sdes	return(cam_periph_error(ccb, cam_flags, sense_flags,
710126274Sdes				 &softc->saved_ccb));
711126274Sdes}
712126274Sdes