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