| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * Generic SCSI Target Kernel Mode Driver | |
| 3 | * | |
| 4 | * Copyright (c) 2002 Nate Lawson. | |
| 5 | * Copyright (c) 1998, 1999, 2001, 2002 Justin T. Gibbs. | |
| 6 | * All rights reserved. | |
| 7 | * | |
| 8 | * Redistribution and use in source and binary forms, with or without | |
| 9 | * modification, are permitted provided that the following conditions | |
| 10 | * are met: | |
| 11 | * 1. Redistributions of source code must retain the above copyright | |
| 12 | * notice, this list of conditions, and the following disclaimer, | |
| 13 | * without modification, immediately at the beginning of the file. | |
| 14 | * 2. The name of the author may not be used to endorse or promote products | |
| 15 | * derived from this software without specific prior written permission. | |
| 16 | * | |
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | |
| 21 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 27 | * SUCH DAMAGE. | |
| 28 | * | |
| 29 | * $FreeBSD: src/sys/cam/scsi/scsi_target.c,v 1.22.2.7 2003/02/18 22:07:10 njl Exp $ | |
| 2d19cdd3 | 30 | * $DragonFly: src/sys/bus/cam/scsi/scsi_target.c,v 1.20 2008/07/18 00:07:23 dillon Exp $ |
| 984263bc MD |
31 | */ |
| 32 | ||
| 33 | #include <sys/param.h> | |
| 34 | #include <sys/systm.h> | |
| 35 | #include <sys/kernel.h> | |
| 36 | #include <sys/conf.h> | |
| 05726f07 | 37 | #include <sys/device.h> |
| 984263bc MD |
38 | #include <sys/malloc.h> |
| 39 | #include <sys/poll.h> | |
| 40 | #include <sys/vnode.h> | |
| 41 | #include <sys/devicestat.h> | |
| 4e01b467 | 42 | #include <sys/thread2.h> |
| 2c1e28dd | 43 | #include <sys/devfs.h> |
| 984263bc | 44 | |
| 1f2de5d4 MD |
45 | #include "../cam.h" |
| 46 | #include "../cam_ccb.h" | |
| 47 | #include "../cam_periph.h" | |
| 48 | #include "../cam_xpt_periph.h" | |
| 1c8b7a9a | 49 | #include "../cam_sim.h" |
| 1f2de5d4 | 50 | #include "scsi_targetio.h" |
| 984263bc MD |
51 | |
| 52 | /* Transaction information attached to each CCB sent by the user */ | |
| 53 | struct targ_cmd_descr { | |
| 54 | struct cam_periph_map_info mapinfo; | |
| 55 | TAILQ_ENTRY(targ_cmd_descr) tqe; | |
| 56 | union ccb *user_ccb; | |
| 57 | int priority; | |
| 58 | int func_code; | |
| 59 | }; | |
| 60 | ||
| 61 | /* Offset into the private CCB area for storing our descriptor */ | |
| 62 | #define targ_descr periph_priv.entries[1].ptr | |
| 63 | ||
| 64 | TAILQ_HEAD(descr_queue, targ_cmd_descr); | |
| 65 | ||
| 66 | typedef enum { | |
| 67 | TARG_STATE_RESV = 0x00, /* Invalid state */ | |
| 68 | TARG_STATE_OPENED = 0x01, /* Device opened, softc initialized */ | |
| 69 | TARG_STATE_LUN_ENABLED = 0x02 /* Device enabled for a path */ | |
| 70 | } targ_state; | |
| 71 | ||
| 72 | /* Per-instance device software context */ | |
| 73 | struct targ_softc { | |
| 74 | /* CCBs (CTIOs, ATIOs, INOTs) pending on the controller */ | |
| 75 | struct ccb_queue pending_ccb_queue; | |
| 76 | ||
| 77 | /* Command descriptors awaiting CTIO resources from the XPT */ | |
| 78 | struct descr_queue work_queue; | |
| 79 | ||
| 80 | /* Command descriptors that have been aborted back to the user. */ | |
| 81 | struct descr_queue abort_queue; | |
| 82 | ||
| 83 | /* | |
| 84 | * Queue of CCBs that have been copied out to userland, but our | |
| 85 | * userland daemon has not yet seen. | |
| 86 | */ | |
| 87 | struct ccb_queue user_ccb_queue; | |
| 88 | ||
| 89 | struct cam_periph *periph; | |
| 90 | struct cam_path *path; | |
| 91 | targ_state state; | |
| 92 | struct selinfo read_select; | |
| 93 | struct devstat device_stats; | |
| 94 | }; | |
| 95 | ||
| 96 | static d_open_t targopen; | |
| 97 | static d_close_t targclose; | |
| 98 | static d_read_t targread; | |
| 99 | static d_write_t targwrite; | |
| 100 | static d_ioctl_t targioctl; | |
| 101 | static d_poll_t targpoll; | |
| 102 | static d_kqfilter_t targkqfilter; | |
| 8be7edad MD |
103 | static d_clone_t targclone; |
| 104 | DEVFS_DECLARE_CLONE_BITMAP(targ); | |
| 105 | ||
| 984263bc MD |
106 | static void targreadfiltdetach(struct knote *kn); |
| 107 | static int targreadfilt(struct knote *kn, long hint); | |
| 108 | static struct filterops targread_filtops = | |
| 109 | { 1, NULL, targreadfiltdetach, targreadfilt }; | |
| 110 | ||
| 111 | #define TARG_CDEV_MAJOR 65 | |
| 05726f07 PA |
112 | static struct dev_ops targ_ops = { |
| 113 | { "targ", TARG_CDEV_MAJOR, D_KQFILTER }, | |
| 114 | .d_open = targopen, | |
| 115 | .d_close = targclose, | |
| 116 | .d_read = targread, | |
| 117 | .d_write = targwrite, | |
| 118 | .d_ioctl = targioctl, | |
| 119 | .d_poll = targpoll, | |
| 120 | .d_kqfilter = targkqfilter | |
| 984263bc MD |
121 | }; |
| 122 | ||
| 123 | static cam_status targendislun(struct cam_path *path, int enable, | |
| 124 | int grp6_len, int grp7_len); | |
| 125 | static cam_status targenable(struct targ_softc *softc, | |
| 126 | struct cam_path *path, | |
| 127 | int grp6_len, int grp7_len); | |
| 128 | static cam_status targdisable(struct targ_softc *softc); | |
| 129 | static periph_ctor_t targctor; | |
| 130 | static periph_dtor_t targdtor; | |
| 131 | static periph_start_t targstart; | |
| 132 | static int targusermerge(struct targ_softc *softc, | |
| 133 | struct targ_cmd_descr *descr, | |
| 134 | union ccb *ccb); | |
| 135 | static int targsendccb(struct targ_softc *softc, union ccb *ccb, | |
| 136 | struct targ_cmd_descr *descr); | |
| 137 | static void targdone(struct cam_periph *periph, | |
| 138 | union ccb *done_ccb); | |
| 139 | static int targreturnccb(struct targ_softc *softc, | |
| 140 | union ccb *ccb); | |
| 141 | static union ccb * targgetccb(struct targ_softc *softc, xpt_opcode type, | |
| 142 | int priority); | |
| 143 | static void targfreeccb(struct targ_softc *softc, union ccb *ccb); | |
| 144 | static struct targ_cmd_descr * | |
| 145 | targgetdescr(struct targ_softc *softc); | |
| 146 | static periph_init_t targinit; | |
| 147 | static void targasync(void *callback_arg, u_int32_t code, | |
| 148 | struct cam_path *path, void *arg); | |
| 149 | static void abort_all_pending(struct targ_softc *softc); | |
| 150 | static void notify_user(struct targ_softc *softc); | |
| 151 | static int targcamstatus(cam_status status); | |
| 152 | static size_t targccblen(xpt_opcode func_code); | |
| 153 | ||
| 154 | static struct periph_driver targdriver = | |
| 155 | { | |
| 156 | targinit, "targ", | |
| 157 | TAILQ_HEAD_INITIALIZER(targdriver.units), /* generation */ 0 | |
| 158 | }; | |
| 2ad14cb5 | 159 | PERIPHDRIVER_DECLARE(targ, targdriver); |
| 984263bc MD |
160 | |
| 161 | static MALLOC_DEFINE(M_TARG, "TARG", "TARG data"); | |
| 162 | ||
| 1c8b7a9a PA |
163 | /* |
| 164 | * Create softc and initialize it. Only one proc can open each targ device. | |
| 165 | * There is no locking here because a periph doesn't get created until an | |
| 166 | * ioctl is issued to do so, and that can't happen until this method returns. | |
| 167 | */ | |
| 984263bc | 168 | static int |
| 05726f07 | 169 | targopen(struct dev_open_args *ap) |
| 984263bc | 170 | { |
| 05726f07 | 171 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
172 | struct targ_softc *softc; |
| 173 | ||
| 174 | if (dev->si_drv1 != 0) { | |
| 175 | return (EBUSY); | |
| 176 | } | |
| 177 | ||
| 178 | /* Mark device busy before any potentially blocking operations */ | |
| 179 | dev->si_drv1 = (void *)~0; | |
| e4c9c0c8 | 180 | reference_dev(dev); /* save ref for later destroy_dev() */ |
| 984263bc MD |
181 | |
| 182 | /* Create the targ device, allocate its softc, initialize it */ | |
| 8be7edad | 183 | #if 0 |
| 05726f07 | 184 | make_dev(&targ_ops, minor(dev), UID_ROOT, GID_WHEEL, 0600, |
| 984263bc | 185 | "targ%d", lminor(dev)); |
| 8be7edad | 186 | #endif |
| 984263bc | 187 | MALLOC(softc, struct targ_softc *, sizeof(*softc), M_TARG, |
| ad86f6b6 | 188 | M_INTWAIT | M_ZERO); |
| 984263bc MD |
189 | dev->si_drv1 = softc; |
| 190 | softc->state = TARG_STATE_OPENED; | |
| 191 | softc->periph = NULL; | |
| 192 | softc->path = NULL; | |
| 193 | ||
| 194 | TAILQ_INIT(&softc->pending_ccb_queue); | |
| 195 | TAILQ_INIT(&softc->work_queue); | |
| 196 | TAILQ_INIT(&softc->abort_queue); | |
| 197 | TAILQ_INIT(&softc->user_ccb_queue); | |
| 198 | ||
| 199 | return (0); | |
| 200 | } | |
| 201 | ||
| 202 | /* Disable LUN if enabled and teardown softc */ | |
| 203 | static int | |
| 05726f07 | 204 | targclose(struct dev_close_args *ap) |
| 984263bc | 205 | { |
| 05726f07 | 206 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 207 | struct targ_softc *softc; |
| 1c8b7a9a | 208 | struct cam_periph *periph; |
| 984263bc MD |
209 | int error; |
| 210 | ||
| 211 | softc = (struct targ_softc *)dev->si_drv1; | |
| 1c8b7a9a PA |
212 | if ((softc->periph == NULL) || |
| 213 | (softc->state & TARG_STATE_LUN_ENABLED) == 0) { | |
| 8be7edad | 214 | devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(targ), dev->si_uminor); |
| 1c8b7a9a PA |
215 | destroy_dev(dev); |
| 216 | FREE(softc, M_TARG); | |
| 217 | return (0); | |
| 218 | } | |
| 219 | ||
| 220 | /* | |
| 221 | * Acquire a hold on the periph so that it doesn't go away before | |
| 222 | * we are ready at the end of the function. | |
| 223 | */ | |
| 224 | periph = softc->periph; | |
| 225 | cam_periph_acquire(periph); | |
| 226 | cam_periph_lock(periph); | |
| 984263bc MD |
227 | error = targdisable(softc); |
| 228 | if (error == CAM_REQ_CMP) { | |
| 229 | dev->si_drv1 = 0; | |
| 230 | if (softc->periph != NULL) { | |
| 231 | cam_periph_invalidate(softc->periph); | |
| 232 | softc->periph = NULL; | |
| 233 | } | |
| e4c9c0c8 | 234 | destroy_dev(dev); /* eats the open ref */ |
| 8be7edad | 235 | devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(targ), dev->si_uminor); |
| 984263bc | 236 | FREE(softc, M_TARG); |
| e4c9c0c8 MD |
237 | } else { |
| 238 | release_dev(dev); | |
| 984263bc | 239 | } |
| 1c8b7a9a PA |
240 | cam_periph_unlock(periph); |
| 241 | cam_periph_release(periph); | |
| 242 | ||
| 984263bc MD |
243 | return (error); |
| 244 | } | |
| 245 | ||
| 246 | /* Enable/disable LUNs, set debugging level */ | |
| 247 | static int | |
| 05726f07 | 248 | targioctl(struct dev_ioctl_args *ap) |
| 984263bc MD |
249 | { |
| 250 | struct targ_softc *softc; | |
| 251 | cam_status status; | |
| 252 | ||
| 05726f07 | 253 | softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1; |
| 984263bc | 254 | |
| 05726f07 | 255 | switch (ap->a_cmd) { |
| 984263bc MD |
256 | case TARGIOCENABLE: |
| 257 | { | |
| 258 | struct ioc_enable_lun *new_lun; | |
| 259 | struct cam_path *path; | |
| 1c8b7a9a | 260 | struct cam_sim *sim; |
| 984263bc | 261 | |
| 05726f07 | 262 | new_lun = (struct ioc_enable_lun *)ap->a_data; |
| 1c8b7a9a PA |
263 | status = xpt_create_path_unlocked(&path, /*periph*/NULL, |
| 264 | new_lun->path_id, | |
| 265 | new_lun->target_id, | |
| 266 | new_lun->lun_id); | |
| 984263bc | 267 | if (status != CAM_REQ_CMP) { |
| 85f8e2ea | 268 | kprintf("Couldn't create path, status %#x\n", status); |
| 984263bc MD |
269 | break; |
| 270 | } | |
| 1c8b7a9a PA |
271 | sim = xpt_path_sim(path); |
| 272 | CAM_SIM_LOCK(sim); | |
| 984263bc MD |
273 | status = targenable(softc, path, new_lun->grp6_len, |
| 274 | new_lun->grp7_len); | |
| 275 | xpt_free_path(path); | |
| 1c8b7a9a | 276 | CAM_SIM_UNLOCK(sim); |
| 984263bc MD |
277 | break; |
| 278 | } | |
| 279 | case TARGIOCDISABLE: | |
| 1c8b7a9a PA |
280 | if (softc->periph == NULL) { |
| 281 | status = CAM_DEV_NOT_THERE; | |
| 282 | break; | |
| 283 | } | |
| 284 | cam_periph_lock(softc->periph); | |
| 984263bc | 285 | status = targdisable(softc); |
| 1c8b7a9a | 286 | cam_periph_unlock(softc->periph); |
| 984263bc MD |
287 | break; |
| 288 | case TARGIOCDEBUG: | |
| 289 | { | |
| 290 | #ifdef CAMDEBUG | |
| 291 | struct ccb_debug cdbg; | |
| 292 | ||
| 1c8b7a9a PA |
293 | /* If no periph available, disallow debugging changes */ |
| 294 | if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) { | |
| 295 | status = CAM_DEV_NOT_THERE; | |
| 296 | break; | |
| 297 | } | |
| 984263bc | 298 | bzero(&cdbg, sizeof cdbg); |
| 5ddcc6ea | 299 | if (*((int *)ap->a_data) != 0) |
| 984263bc MD |
300 | cdbg.flags = CAM_DEBUG_PERIPH; |
| 301 | else | |
| 302 | cdbg.flags = CAM_DEBUG_NONE; | |
| 1c8b7a9a | 303 | cam_periph_lock(softc->periph); |
| 984263bc MD |
304 | xpt_setup_ccb(&cdbg.ccb_h, softc->path, /*priority*/0); |
| 305 | cdbg.ccb_h.func_code = XPT_DEBUG; | |
| 306 | cdbg.ccb_h.cbfcnp = targdone; | |
| 307 | ||
| 984263bc | 308 | xpt_action((union ccb *)&cdbg); |
| 1c8b7a9a | 309 | cam_periph_unlock(softc->periph); |
| 984263bc MD |
310 | status = cdbg.ccb_h.status & CAM_STATUS_MASK; |
| 311 | #else | |
| 312 | status = CAM_FUNC_NOTAVAIL; | |
| 313 | #endif | |
| 314 | break; | |
| 315 | } | |
| 316 | default: | |
| 317 | status = CAM_PROVIDE_FAIL; | |
| 318 | break; | |
| 319 | } | |
| 320 | ||
| 321 | return (targcamstatus(status)); | |
| 322 | } | |
| 323 | ||
| 324 | /* Writes are always ready, reads wait for user_ccb_queue or abort_queue */ | |
| 325 | static int | |
| fef8985e | 326 | targpoll(struct dev_poll_args *ap) |
| 984263bc MD |
327 | { |
| 328 | struct targ_softc *softc; | |
| 4e01b467 | 329 | int revents; |
| 984263bc | 330 | |
| 05726f07 | 331 | softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1; |
| 984263bc MD |
332 | |
| 333 | /* Poll for write() is always ok. */ | |
| fef8985e MD |
334 | revents = ap->a_events & (POLLOUT | POLLWRNORM); |
| 335 | if ((ap->a_events & (POLLIN | POLLRDNORM)) != 0) { | |
| 984263bc | 336 | /* Poll for read() depends on user and abort queues. */ |
| 1c8b7a9a | 337 | cam_periph_lock(softc->periph); |
| 984263bc MD |
338 | if (!TAILQ_EMPTY(&softc->user_ccb_queue) || |
| 339 | !TAILQ_EMPTY(&softc->abort_queue)) { | |
| fef8985e | 340 | revents |= ap->a_events & (POLLIN | POLLRDNORM); |
| 984263bc | 341 | } |
| 1c8b7a9a | 342 | cam_periph_unlock(softc->periph); |
| 984263bc MD |
343 | /* Only sleep if the user didn't poll for write. */ |
| 344 | if (revents == 0) | |
| 05726f07 | 345 | selrecord(curthread, &softc->read_select); |
| 984263bc | 346 | } |
| fef8985e MD |
347 | ap->a_events = revents; |
| 348 | return (0); | |
| 984263bc MD |
349 | } |
| 350 | ||
| 351 | static int | |
| 05726f07 | 352 | targkqfilter(struct dev_kqfilter_args *ap) |
| 984263bc | 353 | { |
| 05726f07 | 354 | struct knote *kn = ap->a_kn; |
| 984263bc | 355 | struct targ_softc *softc; |
| 984263bc | 356 | |
| 05726f07 | 357 | softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1; |
| 984263bc MD |
358 | kn->kn_hook = (caddr_t)softc; |
| 359 | kn->kn_fop = &targread_filtops; | |
| 4e01b467 | 360 | crit_enter(); |
| 984263bc | 361 | SLIST_INSERT_HEAD(&softc->read_select.si_note, kn, kn_selnext); |
| 4e01b467 | 362 | crit_exit(); |
| 984263bc MD |
363 | return (0); |
| 364 | } | |
| 365 | ||
| 366 | static void | |
| 367 | targreadfiltdetach(struct knote *kn) | |
| 368 | { | |
| 369 | struct targ_softc *softc; | |
| 984263bc MD |
370 | |
| 371 | softc = (struct targ_softc *)kn->kn_hook; | |
| 4e01b467 | 372 | crit_enter(); |
| 984263bc | 373 | SLIST_REMOVE(&softc->read_select.si_note, kn, knote, kn_selnext); |
| 4e01b467 | 374 | crit_exit(); |
| 984263bc MD |
375 | } |
| 376 | ||
| 377 | /* Notify the user's kqueue when the user queue or abort queue gets a CCB */ | |
| 378 | static int | |
| 379 | targreadfilt(struct knote *kn, long hint) | |
| 380 | { | |
| 381 | struct targ_softc *softc; | |
| 4e01b467 | 382 | int retval; |
| 984263bc MD |
383 | |
| 384 | softc = (struct targ_softc *)kn->kn_hook; | |
| 2d19cdd3 | 385 | cam_periph_lock(softc->periph); |
| 984263bc MD |
386 | retval = !TAILQ_EMPTY(&softc->user_ccb_queue) || |
| 387 | !TAILQ_EMPTY(&softc->abort_queue); | |
| 1c8b7a9a | 388 | cam_periph_unlock(softc->periph); |
| 984263bc MD |
389 | return (retval); |
| 390 | } | |
| 391 | ||
| 392 | /* Send the HBA the enable/disable message */ | |
| 393 | static cam_status | |
| 394 | targendislun(struct cam_path *path, int enable, int grp6_len, int grp7_len) | |
| 395 | { | |
| 396 | struct ccb_en_lun en_ccb; | |
| 397 | cam_status status; | |
| 398 | ||
| 399 | /* Tell the lun to begin answering selects */ | |
| 400 | xpt_setup_ccb(&en_ccb.ccb_h, path, /*priority*/1); | |
| 401 | en_ccb.ccb_h.func_code = XPT_EN_LUN; | |
| 402 | /* Don't need support for any vendor specific commands */ | |
| 403 | en_ccb.grp6_len = grp6_len; | |
| 404 | en_ccb.grp7_len = grp7_len; | |
| 405 | en_ccb.enable = enable ? 1 : 0; | |
| 406 | xpt_action((union ccb *)&en_ccb); | |
| 407 | status = en_ccb.ccb_h.status & CAM_STATUS_MASK; | |
| 408 | if (status != CAM_REQ_CMP) { | |
| 1c8b7a9a PA |
409 | xpt_print(path, "%sable lun CCB rejected, status %#x\n", |
| 410 | enable ? "en" : "dis", status); | |
| 984263bc MD |
411 | } |
| 412 | return (status); | |
| 413 | } | |
| 414 | ||
| 415 | /* Enable target mode on a LUN, given its path */ | |
| 416 | static cam_status | |
| 417 | targenable(struct targ_softc *softc, struct cam_path *path, int grp6_len, | |
| 418 | int grp7_len) | |
| 419 | { | |
| 420 | struct cam_periph *periph; | |
| 421 | struct ccb_pathinq cpi; | |
| 422 | cam_status status; | |
| 423 | ||
| 424 | if ((softc->state & TARG_STATE_LUN_ENABLED) != 0) | |
| 425 | return (CAM_LUN_ALRDY_ENA); | |
| 426 | ||
| 427 | /* Make sure SIM supports target mode */ | |
| 428 | xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1); | |
| 429 | cpi.ccb_h.func_code = XPT_PATH_INQ; | |
| 430 | xpt_action((union ccb *)&cpi); | |
| 431 | status = cpi.ccb_h.status & CAM_STATUS_MASK; | |
| 432 | if (status != CAM_REQ_CMP) { | |
| 85f8e2ea | 433 | kprintf("pathinq failed, status %#x\n", status); |
| 984263bc MD |
434 | goto enable_fail; |
| 435 | } | |
| 436 | if ((cpi.target_sprt & PIT_PROCESSOR) == 0) { | |
| 85f8e2ea | 437 | kprintf("controller does not support target mode\n"); |
| 984263bc MD |
438 | status = CAM_FUNC_NOTAVAIL; |
| 439 | goto enable_fail; | |
| 440 | } | |
| 441 | ||
| 442 | /* Destroy any periph on our path if it is disabled */ | |
| 443 | periph = cam_periph_find(path, "targ"); | |
| 444 | if (periph != NULL) { | |
| 445 | struct targ_softc *del_softc; | |
| 446 | ||
| 447 | del_softc = (struct targ_softc *)periph->softc; | |
| 448 | if ((del_softc->state & TARG_STATE_LUN_ENABLED) == 0) { | |
| 449 | cam_periph_invalidate(del_softc->periph); | |
| 450 | del_softc->periph = NULL; | |
| 451 | } else { | |
| 85f8e2ea | 452 | kprintf("Requested path still in use by targ%d\n", |
| 984263bc MD |
453 | periph->unit_number); |
| 454 | status = CAM_LUN_ALRDY_ENA; | |
| 455 | goto enable_fail; | |
| 456 | } | |
| 457 | } | |
| 458 | ||
| 459 | /* Create a periph instance attached to this path */ | |
| 460 | status = cam_periph_alloc(targctor, NULL, targdtor, targstart, | |
| 461 | "targ", CAM_PERIPH_BIO, path, targasync, 0, softc); | |
| 462 | if (status != CAM_REQ_CMP) { | |
| 85f8e2ea | 463 | kprintf("cam_periph_alloc failed, status %#x\n", status); |
| 984263bc MD |
464 | goto enable_fail; |
| 465 | } | |
| 466 | ||
| 467 | /* Ensure that the periph now exists. */ | |
| 468 | if (cam_periph_find(path, "targ") == NULL) { | |
| 469 | panic("targenable: succeeded but no periph?"); | |
| 470 | /* NOTREACHED */ | |
| 471 | } | |
| 472 | ||
| 473 | /* Send the enable lun message */ | |
| 474 | status = targendislun(path, /*enable*/1, grp6_len, grp7_len); | |
| 475 | if (status != CAM_REQ_CMP) { | |
| 85f8e2ea | 476 | kprintf("enable lun failed, status %#x\n", status); |
| 984263bc MD |
477 | goto enable_fail; |
| 478 | } | |
| 479 | softc->state |= TARG_STATE_LUN_ENABLED; | |
| 480 | ||
| 481 | enable_fail: | |
| 482 | return (status); | |
| 483 | } | |
| 484 | ||
| 485 | /* Disable this softc's target instance if enabled */ | |
| 486 | static cam_status | |
| 487 | targdisable(struct targ_softc *softc) | |
| 488 | { | |
| 489 | cam_status status; | |
| 984263bc MD |
490 | |
| 491 | if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) | |
| 492 | return (CAM_REQ_CMP); | |
| 493 | ||
| 494 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targdisable\n")); | |
| 495 | ||
| 496 | /* Abort any ccbs pending on the controller */ | |
| 4e01b467 | 497 | crit_enter(); |
| 984263bc | 498 | abort_all_pending(softc); |
| 4e01b467 | 499 | crit_exit(); |
| 984263bc MD |
500 | |
| 501 | /* Disable this lun */ | |
| 502 | status = targendislun(softc->path, /*enable*/0, | |
| 503 | /*grp6_len*/0, /*grp7_len*/0); | |
| 504 | if (status == CAM_REQ_CMP) | |
| 505 | softc->state &= ~TARG_STATE_LUN_ENABLED; | |
| 506 | else | |
| 85f8e2ea | 507 | kprintf("Disable lun failed, status %#x\n", status); |
| 984263bc MD |
508 | |
| 509 | return (status); | |
| 510 | } | |
| 511 | ||
| 512 | /* Initialize a periph (called from cam_periph_alloc) */ | |
| 513 | static cam_status | |
| 514 | targctor(struct cam_periph *periph, void *arg) | |
| 515 | { | |
| 516 | struct targ_softc *softc; | |
| 517 | ||
| 518 | /* Store pointer to softc for periph-driven routines */ | |
| 519 | softc = (struct targ_softc *)arg; | |
| 520 | periph->softc = softc; | |
| 521 | softc->periph = periph; | |
| 522 | softc->path = periph->path; | |
| 523 | return (CAM_REQ_CMP); | |
| 524 | } | |
| 525 | ||
| 526 | static void | |
| 527 | targdtor(struct cam_periph *periph) | |
| 528 | { | |
| 529 | struct targ_softc *softc; | |
| 530 | struct ccb_hdr *ccb_h; | |
| 531 | struct targ_cmd_descr *descr; | |
| 532 | ||
| 533 | softc = (struct targ_softc *)periph->softc; | |
| 534 | ||
| 535 | /* | |
| 536 | * targdisable() aborts CCBs back to the user and leaves them | |
| 537 | * on user_ccb_queue and abort_queue in case the user is still | |
| 538 | * interested in them. We free them now. | |
| 539 | */ | |
| 540 | while ((ccb_h = TAILQ_FIRST(&softc->user_ccb_queue)) != NULL) { | |
| 541 | TAILQ_REMOVE(&softc->user_ccb_queue, ccb_h, periph_links.tqe); | |
| 542 | targfreeccb(softc, (union ccb *)ccb_h); | |
| 543 | } | |
| 544 | while ((descr = TAILQ_FIRST(&softc->abort_queue)) != NULL) { | |
| 545 | TAILQ_REMOVE(&softc->abort_queue, descr, tqe); | |
| 546 | FREE(descr, M_TARG); | |
| 547 | } | |
| 548 | ||
| 549 | softc->periph = NULL; | |
| 550 | softc->path = NULL; | |
| 551 | periph->softc = NULL; | |
| 552 | } | |
| 553 | ||
| 554 | /* Receive CCBs from user mode proc and send them to the HBA */ | |
| 555 | static int | |
| 05726f07 | 556 | targwrite(struct dev_write_args *ap) |
| 984263bc | 557 | { |
| 05726f07 | 558 | struct uio *uio = ap->a_uio; |
| 984263bc MD |
559 | union ccb *user_ccb; |
| 560 | struct targ_softc *softc; | |
| 561 | struct targ_cmd_descr *descr; | |
| 4e01b467 | 562 | int write_len, error; |
| 984263bc MD |
563 | int func_code, priority; |
| 564 | ||
| 05726f07 | 565 | softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1; |
| 984263bc MD |
566 | write_len = error = 0; |
| 567 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| e54488bb | 568 | ("write - uio_resid %ld\n", uio->uio_resid)); |
| 984263bc MD |
569 | while (uio->uio_resid >= sizeof(user_ccb) && error == 0) { |
| 570 | union ccb *ccb; | |
| 984263bc MD |
571 | |
| 572 | error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio); | |
| 573 | if (error != 0) { | |
| 574 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 575 | ("write - uiomove failed (%d)\n", error)); | |
| 576 | break; | |
| 577 | } | |
| 578 | priority = fuword(&user_ccb->ccb_h.pinfo.priority); | |
| 579 | if (priority == -1) { | |
| 580 | error = EINVAL; | |
| 581 | break; | |
| 582 | } | |
| 583 | func_code = fuword(&user_ccb->ccb_h.func_code); | |
| 584 | switch (func_code) { | |
| 585 | case XPT_ACCEPT_TARGET_IO: | |
| 586 | case XPT_IMMED_NOTIFY: | |
| 1c8b7a9a | 587 | cam_periph_lock(softc->periph); |
| 984263bc MD |
588 | ccb = targgetccb(softc, func_code, priority); |
| 589 | descr = (struct targ_cmd_descr *)ccb->ccb_h.targ_descr; | |
| 590 | descr->user_ccb = user_ccb; | |
| 591 | descr->func_code = func_code; | |
| 592 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 593 | ("Sent ATIO/INOT (%p)\n", user_ccb)); | |
| 594 | xpt_action(ccb); | |
| 984263bc MD |
595 | TAILQ_INSERT_TAIL(&softc->pending_ccb_queue, |
| 596 | &ccb->ccb_h, | |
| 597 | periph_links.tqe); | |
| 1c8b7a9a | 598 | cam_periph_unlock(softc->periph); |
| 984263bc MD |
599 | break; |
| 600 | default: | |
| 1c8b7a9a | 601 | cam_periph_lock(softc->periph); |
| 984263bc MD |
602 | if ((func_code & XPT_FC_QUEUED) != 0) { |
| 603 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 604 | ("Sending queued ccb %#x (%p)\n", | |
| 605 | func_code, user_ccb)); | |
| 606 | descr = targgetdescr(softc); | |
| 607 | descr->user_ccb = user_ccb; | |
| 608 | descr->priority = priority; | |
| 609 | descr->func_code = func_code; | |
| 4e01b467 | 610 | crit_enter(); |
| 984263bc MD |
611 | TAILQ_INSERT_TAIL(&softc->work_queue, |
| 612 | descr, tqe); | |
| 4e01b467 | 613 | crit_exit(); |
| 984263bc MD |
614 | xpt_schedule(softc->periph, priority); |
| 615 | } else { | |
| 616 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 617 | ("Sending inline ccb %#x (%p)\n", | |
| 618 | func_code, user_ccb)); | |
| 619 | ccb = targgetccb(softc, func_code, priority); | |
| 620 | descr = (struct targ_cmd_descr *) | |
| 621 | ccb->ccb_h.targ_descr; | |
| 622 | descr->user_ccb = user_ccb; | |
| 623 | descr->priority = priority; | |
| 624 | descr->func_code = func_code; | |
| 625 | if (targusermerge(softc, descr, ccb) != EFAULT) | |
| 626 | targsendccb(softc, ccb, descr); | |
| 627 | targreturnccb(softc, ccb); | |
| 628 | } | |
| 1c8b7a9a | 629 | cam_periph_unlock(softc->periph); |
| 984263bc MD |
630 | break; |
| 631 | } | |
| 632 | write_len += sizeof(user_ccb); | |
| 633 | } | |
| 634 | ||
| 635 | /* | |
| 636 | * If we've successfully taken in some amount of | |
| 637 | * data, return success for that data first. If | |
| 638 | * an error is persistent, it will be reported | |
| 639 | * on the next write. | |
| 640 | */ | |
| 641 | if (error != 0 && write_len == 0) | |
| 642 | return (error); | |
| 643 | if (write_len == 0 && uio->uio_resid != 0) | |
| 644 | return (ENOSPC); | |
| 645 | return (0); | |
| 646 | } | |
| 647 | ||
| 648 | /* Process requests (descrs) via the periph-supplied CCBs */ | |
| 649 | static void | |
| 650 | targstart(struct cam_periph *periph, union ccb *start_ccb) | |
| 651 | { | |
| 652 | struct targ_softc *softc; | |
| 653 | struct targ_cmd_descr *descr, *next_descr; | |
| 4e01b467 | 654 | int error; |
| 984263bc MD |
655 | |
| 656 | softc = (struct targ_softc *)periph->softc; | |
| 657 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targstart %p\n", start_ccb)); | |
| 658 | ||
| 4e01b467 | 659 | crit_enter(); |
| 984263bc MD |
660 | descr = TAILQ_FIRST(&softc->work_queue); |
| 661 | if (descr == NULL) { | |
| 4e01b467 | 662 | crit_exit(); |
| 984263bc MD |
663 | xpt_release_ccb(start_ccb); |
| 664 | } else { | |
| 665 | TAILQ_REMOVE(&softc->work_queue, descr, tqe); | |
| 666 | next_descr = TAILQ_FIRST(&softc->work_queue); | |
| 4e01b467 | 667 | crit_exit(); |
| 984263bc MD |
668 | |
| 669 | /* Initiate a transaction using the descr and supplied CCB */ | |
| 670 | error = targusermerge(softc, descr, start_ccb); | |
| 671 | if (error == 0) | |
| 672 | error = targsendccb(softc, start_ccb, descr); | |
| 673 | if (error != 0) { | |
| 1c8b7a9a PA |
674 | xpt_print(periph->path, |
| 675 | "targsendccb failed, err %d\n", error); | |
| 984263bc MD |
676 | xpt_release_ccb(start_ccb); |
| 677 | suword(&descr->user_ccb->ccb_h.status, | |
| 678 | CAM_REQ_CMP_ERR); | |
| 4e01b467 | 679 | crit_enter(); |
| 984263bc | 680 | TAILQ_INSERT_TAIL(&softc->abort_queue, descr, tqe); |
| 4e01b467 | 681 | crit_exit(); |
| 984263bc MD |
682 | notify_user(softc); |
| 683 | } | |
| 684 | ||
| 685 | /* If we have more work to do, stay scheduled */ | |
| 686 | if (next_descr != NULL) | |
| 687 | xpt_schedule(periph, next_descr->priority); | |
| 688 | } | |
| 689 | } | |
| 690 | ||
| 691 | static int | |
| 692 | targusermerge(struct targ_softc *softc, struct targ_cmd_descr *descr, | |
| 693 | union ccb *ccb) | |
| 694 | { | |
| 695 | struct ccb_hdr *u_ccbh, *k_ccbh; | |
| 696 | size_t ccb_len; | |
| 697 | int error; | |
| 698 | ||
| 699 | u_ccbh = &descr->user_ccb->ccb_h; | |
| 700 | k_ccbh = &ccb->ccb_h; | |
| 701 | ||
| 702 | /* | |
| 703 | * There are some fields in the CCB header that need to be | |
| 704 | * preserved, the rest we get from the user ccb. (See xpt_merge_ccb) | |
| 705 | */ | |
| 706 | xpt_setup_ccb(k_ccbh, softc->path, descr->priority); | |
| 707 | k_ccbh->retry_count = fuword(&u_ccbh->retry_count); | |
| 708 | k_ccbh->func_code = descr->func_code; | |
| 709 | k_ccbh->flags = fuword(&u_ccbh->flags); | |
| 710 | k_ccbh->timeout = fuword(&u_ccbh->timeout); | |
| 711 | ccb_len = targccblen(k_ccbh->func_code) - sizeof(struct ccb_hdr); | |
| 712 | error = copyin(u_ccbh + 1, k_ccbh + 1, ccb_len); | |
| 713 | if (error != 0) { | |
| 714 | k_ccbh->status = CAM_REQ_CMP_ERR; | |
| 715 | return (error); | |
| 716 | } | |
| 717 | ||
| 718 | /* Translate usermode abort_ccb pointer to its kernel counterpart */ | |
| 719 | if (k_ccbh->func_code == XPT_ABORT) { | |
| 720 | struct ccb_abort *cab; | |
| 721 | struct ccb_hdr *ccb_h; | |
| 984263bc MD |
722 | |
| 723 | cab = (struct ccb_abort *)ccb; | |
| 4e01b467 | 724 | crit_enter(); |
| 984263bc MD |
725 | TAILQ_FOREACH(ccb_h, &softc->pending_ccb_queue, |
| 726 | periph_links.tqe) { | |
| 727 | struct targ_cmd_descr *ab_descr; | |
| 728 | ||
| 729 | ab_descr = (struct targ_cmd_descr *)ccb_h->targ_descr; | |
| 730 | if (ab_descr->user_ccb == cab->abort_ccb) { | |
| 731 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 732 | ("Changing abort for %p to %p\n", | |
| 733 | cab->abort_ccb, ccb_h)); | |
| 734 | cab->abort_ccb = (union ccb *)ccb_h; | |
| 735 | break; | |
| 736 | } | |
| 737 | } | |
| 4e01b467 | 738 | crit_exit(); |
| 984263bc MD |
739 | /* CCB not found, set appropriate status */ |
| 740 | if (ccb_h == NULL) { | |
| 741 | k_ccbh->status = CAM_PATH_INVALID; | |
| 742 | error = ESRCH; | |
| 743 | } | |
| 744 | } | |
| 745 | ||
| 746 | return (error); | |
| 747 | } | |
| 748 | ||
| 749 | /* Build and send a kernel CCB formed from descr->user_ccb */ | |
| 750 | static int | |
| 751 | targsendccb(struct targ_softc *softc, union ccb *ccb, | |
| 752 | struct targ_cmd_descr *descr) | |
| 753 | { | |
| 754 | struct cam_periph_map_info *mapinfo; | |
| 755 | struct ccb_hdr *ccb_h; | |
| 756 | int error; | |
| 757 | ||
| 758 | ccb_h = &ccb->ccb_h; | |
| 759 | mapinfo = &descr->mapinfo; | |
| 760 | mapinfo->num_bufs_used = 0; | |
| 761 | ||
| 762 | /* | |
| 763 | * There's no way for the user to have a completion | |
| 764 | * function, so we put our own completion function in here. | |
| 765 | * We also stash in a reference to our descriptor so targreturnccb() | |
| 766 | * can find our mapping info. | |
| 767 | */ | |
| 768 | ccb_h->cbfcnp = targdone; | |
| 769 | ccb_h->targ_descr = descr; | |
| 770 | ||
| 771 | /* | |
| 772 | * We only attempt to map the user memory into kernel space | |
| 773 | * if they haven't passed in a physical memory pointer, | |
| 774 | * and if there is actually an I/O operation to perform. | |
| 775 | * Right now cam_periph_mapmem() only supports SCSI and device | |
| 776 | * match CCBs. For the SCSI CCBs, we only pass the CCB in if | |
| 777 | * there's actually data to map. cam_periph_mapmem() will do the | |
| 778 | * right thing, even if there isn't data to map, but since CCBs | |
| 779 | * without data are a reasonably common occurance (e.g. test unit | |
| 780 | * ready), it will save a few cycles if we check for it here. | |
| 781 | */ | |
| 782 | if (((ccb_h->flags & CAM_DATA_PHYS) == 0) | |
| 783 | && (((ccb_h->func_code == XPT_CONT_TARGET_IO) | |
| 784 | && ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE)) | |
| 785 | || (ccb_h->func_code == XPT_DEV_MATCH))) { | |
| 786 | ||
| 787 | error = cam_periph_mapmem(ccb, mapinfo); | |
| 788 | ||
| 789 | /* | |
| 790 | * cam_periph_mapmem returned an error, we can't continue. | |
| 791 | * Return the error to the user. | |
| 792 | */ | |
| 793 | if (error) { | |
| 794 | ccb_h->status = CAM_REQ_CMP_ERR; | |
| 795 | mapinfo->num_bufs_used = 0; | |
| 796 | return (error); | |
| 797 | } | |
| 798 | } | |
| 799 | ||
| 800 | /* | |
| 801 | * Once queued on the pending CCB list, this CCB will be protected | |
| 802 | * by our error recovery handler. | |
| 803 | */ | |
| 804 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("sendccb %p\n", ccb)); | |
| 805 | if (XPT_FC_IS_QUEUED(ccb)) { | |
| 4e01b467 | 806 | crit_enter(); |
| 984263bc MD |
807 | TAILQ_INSERT_TAIL(&softc->pending_ccb_queue, ccb_h, |
| 808 | periph_links.tqe); | |
| 4e01b467 | 809 | crit_exit(); |
| 984263bc MD |
810 | } |
| 811 | xpt_action(ccb); | |
| 812 | ||
| 813 | return (0); | |
| 814 | } | |
| 815 | ||
| 4e01b467 | 816 | /* Completion routine for CCBs (called in a critical section) */ |
| 984263bc MD |
817 | static void |
| 818 | targdone(struct cam_periph *periph, union ccb *done_ccb) | |
| 819 | { | |
| 820 | struct targ_softc *softc; | |
| 821 | cam_status status; | |
| 822 | ||
| 823 | CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("targdone %p\n", done_ccb)); | |
| 824 | softc = (struct targ_softc *)periph->softc; | |
| 825 | TAILQ_REMOVE(&softc->pending_ccb_queue, &done_ccb->ccb_h, | |
| 826 | periph_links.tqe); | |
| 827 | status = done_ccb->ccb_h.status & CAM_STATUS_MASK; | |
| 828 | ||
| 829 | /* If we're no longer enabled, throw away CCB */ | |
| 830 | if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) { | |
| 831 | targfreeccb(softc, done_ccb); | |
| 832 | return; | |
| 833 | } | |
| 834 | /* abort_all_pending() waits for pending queue to be empty */ | |
| 835 | if (TAILQ_EMPTY(&softc->pending_ccb_queue)) | |
| 836 | wakeup(&softc->pending_ccb_queue); | |
| 837 | ||
| 838 | switch (done_ccb->ccb_h.func_code) { | |
| 839 | /* All FC_*_QUEUED CCBs go back to userland */ | |
| 840 | case XPT_IMMED_NOTIFY: | |
| 841 | case XPT_ACCEPT_TARGET_IO: | |
| 842 | case XPT_CONT_TARGET_IO: | |
| 843 | TAILQ_INSERT_TAIL(&softc->user_ccb_queue, &done_ccb->ccb_h, | |
| 844 | periph_links.tqe); | |
| 845 | notify_user(softc); | |
| 846 | break; | |
| 847 | default: | |
| 848 | panic("targdone: impossible xpt opcode %#x", | |
| 849 | done_ccb->ccb_h.func_code); | |
| 850 | /* NOTREACHED */ | |
| 851 | } | |
| 852 | } | |
| 853 | ||
| 854 | /* Return CCBs to the user from the user queue and abort queue */ | |
| 855 | static int | |
| 05726f07 | 856 | targread(struct dev_read_args *ap) |
| 984263bc | 857 | { |
| 05726f07 | 858 | struct uio *uio = ap->a_uio; |
| 984263bc MD |
859 | struct descr_queue *abort_queue; |
| 860 | struct targ_cmd_descr *user_descr; | |
| 861 | struct targ_softc *softc; | |
| 862 | struct ccb_queue *user_queue; | |
| 863 | struct ccb_hdr *ccb_h; | |
| 864 | union ccb *user_ccb; | |
| 4e01b467 | 865 | int read_len, error; |
| 984263bc MD |
866 | |
| 867 | error = 0; | |
| 868 | read_len = 0; | |
| 05726f07 | 869 | softc = (struct targ_softc *)ap->a_head.a_dev->si_drv1; |
| 984263bc MD |
870 | user_queue = &softc->user_ccb_queue; |
| 871 | abort_queue = &softc->abort_queue; | |
| 872 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targread\n")); | |
| 873 | ||
| 874 | /* If no data is available, wait or return immediately */ | |
| 1c8b7a9a | 875 | cam_periph_lock(softc->periph); |
| 984263bc MD |
876 | ccb_h = TAILQ_FIRST(user_queue); |
| 877 | user_descr = TAILQ_FIRST(abort_queue); | |
| 878 | while (ccb_h == NULL && user_descr == NULL) { | |
| 05726f07 | 879 | if ((ap->a_ioflag & IO_NDELAY) == 0) { |
| 1c8b7a9a PA |
880 | error = sim_lock_sleep(user_queue, PCATCH, "targrd", 0, |
| 881 | softc->periph->sim->lock); | |
| 984263bc MD |
882 | ccb_h = TAILQ_FIRST(user_queue); |
| 883 | user_descr = TAILQ_FIRST(abort_queue); | |
| 884 | if (error != 0) { | |
| 885 | if (error == ERESTART) { | |
| 886 | continue; | |
| 887 | } else { | |
| 984263bc MD |
888 | goto read_fail; |
| 889 | } | |
| 890 | } | |
| 891 | } else { | |
| 1c8b7a9a | 892 | cam_periph_unlock(softc->periph); |
| 984263bc MD |
893 | return (EAGAIN); |
| 894 | } | |
| 895 | } | |
| 896 | ||
| 897 | /* Data is available so fill the user's buffer */ | |
| 898 | while (ccb_h != NULL) { | |
| 899 | struct targ_cmd_descr *descr; | |
| 900 | ||
| 901 | if (uio->uio_resid < sizeof(user_ccb)) | |
| 902 | break; | |
| 903 | TAILQ_REMOVE(user_queue, ccb_h, periph_links.tqe); | |
| 984263bc MD |
904 | descr = (struct targ_cmd_descr *)ccb_h->targ_descr; |
| 905 | user_ccb = descr->user_ccb; | |
| 906 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 907 | ("targread ccb %p (%p)\n", ccb_h, user_ccb)); | |
| 908 | error = targreturnccb(softc, (union ccb *)ccb_h); | |
| 909 | if (error != 0) | |
| 910 | goto read_fail; | |
| 1c8b7a9a | 911 | cam_periph_unlock(softc->periph); |
| 984263bc | 912 | error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio); |
| 1c8b7a9a | 913 | cam_periph_lock(softc->periph); |
| 984263bc MD |
914 | if (error != 0) |
| 915 | goto read_fail; | |
| 916 | read_len += sizeof(user_ccb); | |
| 917 | ||
| 984263bc MD |
918 | ccb_h = TAILQ_FIRST(user_queue); |
| 919 | } | |
| 920 | ||
| 921 | /* Flush out any aborted descriptors */ | |
| 922 | while (user_descr != NULL) { | |
| 923 | if (uio->uio_resid < sizeof(user_ccb)) | |
| 924 | break; | |
| 925 | TAILQ_REMOVE(abort_queue, user_descr, tqe); | |
| 984263bc MD |
926 | user_ccb = user_descr->user_ccb; |
| 927 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 928 | ("targread aborted descr %p (%p)\n", | |
| 929 | user_descr, user_ccb)); | |
| 930 | suword(&user_ccb->ccb_h.status, CAM_REQ_ABORTED); | |
| 1c8b7a9a | 931 | cam_periph_unlock(softc->periph); |
| 984263bc | 932 | error = uiomove((caddr_t)&user_ccb, sizeof(user_ccb), uio); |
| 1c8b7a9a | 933 | cam_periph_lock(softc->periph); |
| 984263bc MD |
934 | if (error != 0) |
| 935 | goto read_fail; | |
| 936 | read_len += sizeof(user_ccb); | |
| 937 | ||
| 984263bc MD |
938 | user_descr = TAILQ_FIRST(abort_queue); |
| 939 | } | |
| 984263bc MD |
940 | |
| 941 | /* | |
| 942 | * If we've successfully read some amount of data, don't report an | |
| 943 | * error. If the error is persistent, it will be reported on the | |
| 944 | * next read(). | |
| 945 | */ | |
| 946 | if (read_len == 0 && uio->uio_resid != 0) | |
| 947 | error = ENOSPC; | |
| 948 | ||
| 949 | read_fail: | |
| 1c8b7a9a | 950 | cam_periph_unlock(softc->periph); |
| 984263bc MD |
951 | return (error); |
| 952 | } | |
| 953 | ||
| 954 | /* Copy completed ccb back to the user */ | |
| 955 | static int | |
| 956 | targreturnccb(struct targ_softc *softc, union ccb *ccb) | |
| 957 | { | |
| 958 | struct targ_cmd_descr *descr; | |
| 959 | struct ccb_hdr *u_ccbh; | |
| 960 | size_t ccb_len; | |
| 961 | int error; | |
| 962 | ||
| 963 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("targreturnccb %p\n", ccb)); | |
| 964 | descr = (struct targ_cmd_descr *)ccb->ccb_h.targ_descr; | |
| 965 | u_ccbh = &descr->user_ccb->ccb_h; | |
| 966 | ||
| 967 | /* Copy out the central portion of the ccb_hdr */ | |
| 968 | copyout(&ccb->ccb_h.retry_count, &u_ccbh->retry_count, | |
| 969 | offsetof(struct ccb_hdr, periph_priv) - | |
| 970 | offsetof(struct ccb_hdr, retry_count)); | |
| 971 | ||
| 972 | /* Copy out the rest of the ccb (after the ccb_hdr) */ | |
| 973 | ccb_len = targccblen(ccb->ccb_h.func_code) - sizeof(struct ccb_hdr); | |
| 974 | if (descr->mapinfo.num_bufs_used != 0) | |
| 975 | cam_periph_unmapmem(ccb, &descr->mapinfo); | |
| 976 | error = copyout(&ccb->ccb_h + 1, u_ccbh + 1, ccb_len); | |
| 977 | if (error != 0) { | |
| 1c8b7a9a PA |
978 | xpt_print(softc->path, |
| 979 | "targreturnccb - CCB copyout failed (%d)\n", error); | |
| 984263bc MD |
980 | } |
| 981 | /* Free CCB or send back to devq. */ | |
| 982 | targfreeccb(softc, ccb); | |
| 983 | ||
| 984 | return (error); | |
| 985 | } | |
| 986 | ||
| 987 | static union ccb * | |
| 988 | targgetccb(struct targ_softc *softc, xpt_opcode type, int priority) | |
| 989 | { | |
| 990 | union ccb *ccb; | |
| 991 | int ccb_len; | |
| 992 | ||
| 993 | ccb_len = targccblen(type); | |
| 898d961b | 994 | MALLOC(ccb, union ccb *, ccb_len, M_TARG, M_INTWAIT); |
| 984263bc MD |
995 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("getccb %p\n", ccb)); |
| 996 | ||
| 997 | xpt_setup_ccb(&ccb->ccb_h, softc->path, priority); | |
| 998 | ccb->ccb_h.func_code = type; | |
| 999 | ccb->ccb_h.cbfcnp = targdone; | |
| 1000 | ccb->ccb_h.targ_descr = targgetdescr(softc); | |
| 1001 | return (ccb); | |
| 1002 | } | |
| 1003 | ||
| 1004 | static void | |
| 1005 | targfreeccb(struct targ_softc *softc, union ccb *ccb) | |
| 1006 | { | |
| 1007 | CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, ("targfreeccb descr %p and\n", | |
| 1008 | ccb->ccb_h.targ_descr)); | |
| 1009 | FREE(ccb->ccb_h.targ_descr, M_TARG); | |
| 1010 | ||
| 1011 | switch (ccb->ccb_h.func_code) { | |
| 1012 | case XPT_ACCEPT_TARGET_IO: | |
| 1013 | case XPT_IMMED_NOTIFY: | |
| 1014 | CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, ("freeing ccb %p\n", ccb)); | |
| 1015 | FREE(ccb, M_TARG); | |
| 1016 | break; | |
| 1017 | default: | |
| 1018 | /* Send back CCB if we got it from the periph */ | |
| 1019 | if (XPT_FC_IS_QUEUED(ccb)) { | |
| 1020 | CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, | |
| 1021 | ("returning queued ccb %p\n", ccb)); | |
| 1022 | xpt_release_ccb(ccb); | |
| 1023 | } else { | |
| 1024 | CAM_DEBUG_PRINT(CAM_DEBUG_PERIPH, | |
| 1025 | ("freeing ccb %p\n", ccb)); | |
| 1026 | FREE(ccb, M_TARG); | |
| 1027 | } | |
| 1028 | break; | |
| 1029 | } | |
| 1030 | } | |
| 1031 | ||
| 1032 | static struct targ_cmd_descr * | |
| 1033 | targgetdescr(struct targ_softc *softc) | |
| 1034 | { | |
| 1035 | struct targ_cmd_descr *descr; | |
| 1036 | ||
| 898d961b MD |
1037 | MALLOC(descr, struct targ_cmd_descr *, sizeof(*descr), |
| 1038 | M_TARG, M_INTWAIT); | |
| 984263bc MD |
1039 | descr->mapinfo.num_bufs_used = 0; |
| 1040 | return (descr); | |
| 1041 | } | |
| 1042 | ||
| 8be7edad MD |
1043 | static int |
| 1044 | targclone(struct dev_clone_args *ap) | |
| 1045 | { | |
| 1046 | int unit; | |
| 1047 | ||
| 1048 | unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(targ), 0); | |
| 1049 | ap->a_dev = make_only_dev(&targ_ops, unit, UID_ROOT, GID_WHEEL, | |
| 1050 | 0600, "targ%d", unit); | |
| 1051 | return 0; | |
| 1052 | } | |
| 1053 | ||
| 984263bc MD |
1054 | static void |
| 1055 | targinit(void) | |
| 1056 | { | |
| b96f3782 AH |
1057 | make_autoclone_dev(&targ_ops, &DEVFS_CLONE_BITMAP(targ), |
| 1058 | targclone, UID_ROOT, GID_WHEEL, 0600, "targ"); | |
| 8be7edad | 1059 | /* XXX: need uninit or so? */ |
| 984263bc MD |
1060 | } |
| 1061 | ||
| 1062 | static void | |
| 1063 | targasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) | |
| 1064 | { | |
| 1065 | /* All events are handled in usermode by INOTs */ | |
| 1066 | panic("targasync() called, should be an INOT instead"); | |
| 1067 | } | |
| 1068 | ||
| 1069 | /* Cancel all pending requests and CCBs awaiting work. */ | |
| 1070 | static void | |
| 1071 | abort_all_pending(struct targ_softc *softc) | |
| 1072 | { | |
| 1073 | struct targ_cmd_descr *descr; | |
| 1074 | struct ccb_abort cab; | |
| 1075 | struct ccb_hdr *ccb_h; | |
| 1c8b7a9a | 1076 | struct cam_sim *sim; |
| 984263bc MD |
1077 | |
| 1078 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, ("abort_all_pending\n")); | |
| 1079 | ||
| 1080 | /* First abort the descriptors awaiting resources */ | |
| 1081 | while ((descr = TAILQ_FIRST(&softc->work_queue)) != NULL) { | |
| 1082 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 1083 | ("Aborting descr from workq %p\n", descr)); | |
| 1084 | TAILQ_REMOVE(&softc->work_queue, descr, tqe); | |
| 1085 | TAILQ_INSERT_TAIL(&softc->abort_queue, descr, tqe); | |
| 1086 | } | |
| 1087 | ||
| 1088 | /* | |
| 1089 | * Then abort all pending CCBs. | |
| 1090 | * targdone() will return the aborted CCB via user_ccb_queue | |
| 1091 | */ | |
| 1092 | xpt_setup_ccb(&cab.ccb_h, softc->path, /*priority*/0); | |
| 1093 | cab.ccb_h.func_code = XPT_ABORT; | |
| 1094 | cab.ccb_h.status = CAM_REQ_CMP_ERR; | |
| 1095 | TAILQ_FOREACH(ccb_h, &softc->pending_ccb_queue, periph_links.tqe) { | |
| 1096 | CAM_DEBUG(softc->path, CAM_DEBUG_PERIPH, | |
| 1097 | ("Aborting pending CCB %p\n", ccb_h)); | |
| 1098 | cab.abort_ccb = (union ccb *)ccb_h; | |
| 1099 | xpt_action((union ccb *)&cab); | |
| 1100 | if (cab.ccb_h.status != CAM_REQ_CMP) { | |
| 1c8b7a9a PA |
1101 | xpt_print(cab.ccb_h.path, |
| 1102 | "Unable to abort CCB, status %#x\n", | |
| 1103 | cab.ccb_h.status); | |
| 984263bc MD |
1104 | } |
| 1105 | } | |
| 1106 | ||
| 1107 | /* If we aborted at least one pending CCB ok, wait for it. */ | |
| 1108 | if (cab.ccb_h.status == CAM_REQ_CMP) { | |
| 1c8b7a9a PA |
1109 | sim = xpt_path_sim(softc->path); |
| 1110 | sim_lock_sleep(&softc->pending_ccb_queue, PCATCH, "tgabrt", 0, | |
| 1111 | sim->lock); | |
| 984263bc MD |
1112 | } |
| 1113 | ||
| 1114 | /* If we aborted anything from the work queue, wakeup user. */ | |
| 1115 | if (!TAILQ_EMPTY(&softc->user_ccb_queue) | |
| 1116 | || !TAILQ_EMPTY(&softc->abort_queue)) | |
| 1117 | notify_user(softc); | |
| 1118 | } | |
| 1119 | ||
| 1120 | /* Notify the user that data is ready */ | |
| 1121 | static void | |
| 1122 | notify_user(struct targ_softc *softc) | |
| 1123 | { | |
| 1124 | /* | |
| 1125 | * Notify users sleeping via poll(), kqueue(), and | |
| 1126 | * blocking read(). | |
| 1127 | */ | |
| 1128 | selwakeup(&softc->read_select); | |
| 1129 | KNOTE(&softc->read_select.si_note, 0); | |
| 1130 | wakeup(&softc->user_ccb_queue); | |
| 1131 | } | |
| 1132 | ||
| 1133 | /* Convert CAM status to errno values */ | |
| 1134 | static int | |
| 1135 | targcamstatus(cam_status status) | |
| 1136 | { | |
| 1137 | switch (status & CAM_STATUS_MASK) { | |
| 1138 | case CAM_REQ_CMP: /* CCB request completed without error */ | |
| 1139 | return (0); | |
| 1140 | case CAM_REQ_INPROG: /* CCB request is in progress */ | |
| 1141 | return (EINPROGRESS); | |
| 1142 | case CAM_REQ_CMP_ERR: /* CCB request completed with an error */ | |
| 1143 | return (EIO); | |
| 1144 | case CAM_PROVIDE_FAIL: /* Unable to provide requested capability */ | |
| 1145 | return (ENOTTY); | |
| 1146 | case CAM_FUNC_NOTAVAIL: /* The requested function is not available */ | |
| 1147 | return (ENOTSUP); | |
| 1148 | case CAM_LUN_ALRDY_ENA: /* LUN is already enabled for target mode */ | |
| 1149 | return (EADDRINUSE); | |
| 1150 | case CAM_PATH_INVALID: /* Supplied Path ID is invalid */ | |
| 1151 | case CAM_DEV_NOT_THERE: /* SCSI Device Not Installed/there */ | |
| 1152 | return (ENOENT); | |
| 1153 | case CAM_REQ_ABORTED: /* CCB request aborted by the host */ | |
| 1154 | return (ECANCELED); | |
| 1155 | case CAM_CMD_TIMEOUT: /* Command timeout */ | |
| 1156 | return (ETIMEDOUT); | |
| 1157 | case CAM_REQUEUE_REQ: /* Requeue to preserve transaction ordering */ | |
| 1158 | return (EAGAIN); | |
| 1159 | case CAM_REQ_INVALID: /* CCB request was invalid */ | |
| 1160 | return (EINVAL); | |
| 1161 | case CAM_RESRC_UNAVAIL: /* Resource Unavailable */ | |
| 1162 | return (ENOMEM); | |
| 1163 | case CAM_BUSY: /* CAM subsytem is busy */ | |
| 1164 | case CAM_UA_ABORT: /* Unable to abort CCB request */ | |
| 1165 | return (EBUSY); | |
| 1166 | default: | |
| 1167 | return (ENXIO); | |
| 1168 | } | |
| 1169 | } | |
| 1170 | ||
| 1171 | static size_t | |
| 1172 | targccblen(xpt_opcode func_code) | |
| 1173 | { | |
| 1174 | int len; | |
| 1175 | ||
| 1176 | /* Codes we expect to see as a target */ | |
| 1177 | switch (func_code) { | |
| 1178 | case XPT_CONT_TARGET_IO: | |
| 1179 | case XPT_SCSI_IO: | |
| 1180 | len = sizeof(struct ccb_scsiio); | |
| 1181 | break; | |
| 1182 | case XPT_ACCEPT_TARGET_IO: | |
| 1183 | len = sizeof(struct ccb_accept_tio); | |
| 1184 | break; | |
| 1185 | case XPT_IMMED_NOTIFY: | |
| 1186 | len = sizeof(struct ccb_immed_notify); | |
| 1187 | break; | |
| 1188 | case XPT_REL_SIMQ: | |
| 1189 | len = sizeof(struct ccb_relsim); | |
| 1190 | break; | |
| 1191 | case XPT_PATH_INQ: | |
| 1192 | len = sizeof(struct ccb_pathinq); | |
| 1193 | break; | |
| 1194 | case XPT_DEBUG: | |
| 1195 | len = sizeof(struct ccb_debug); | |
| 1196 | break; | |
| 1197 | case XPT_ABORT: | |
| 1198 | len = sizeof(struct ccb_abort); | |
| 1199 | break; | |
| 1200 | case XPT_EN_LUN: | |
| 1201 | len = sizeof(struct ccb_en_lun); | |
| 1202 | break; | |
| 1203 | default: | |
| 1204 | len = sizeof(union ccb); | |
| 1205 | break; | |
| 1206 | } | |
| 1207 | ||
| 1208 | return (len); | |
| 1209 | } |