| Commit | Line | Data |
|---|---|---|
| 191d7ec1 SW |
1 | /*- |
| 2 | * Copyright (c) 1997-2009 by Matthew Jacob | |
| 3 | * All rights reserved. | |
| 984263bc MD |
4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice immediately at the beginning of the file, without modification, | |
| 10 | * this list of conditions, and the following disclaimer. | |
| 11 | * 2. The name of the author may not be used to endorse or promote products | |
| 12 | * derived from this software without specific prior written permission. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | |
| 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 191d7ec1 SW |
25 | * |
| 26 | * $FreeBSD: src/sys/dev/isp/isp_freebsd.c,v 1.176 2011/11/06 00:44:40 mjacob Exp $ | |
| 27 | */ | |
| 28 | ||
| 29 | /* | |
| 30 | * Platform (FreeBSD) dependent common attachment code for Qlogic adapters. | |
| 984263bc | 31 | */ |
| 191d7ec1 | 32 | #include <dev/disk/isp/isp_freebsd.h> |
| 984263bc MD |
33 | #include <sys/unistd.h> |
| 34 | #include <sys/kthread.h> | |
| 984263bc | 35 | #include <sys/conf.h> |
| 191d7ec1 SW |
36 | #include <sys/module.h> |
| 37 | #include <dev/disk/isp/isp_ioctl.h> | |
| 38 | #include <sys/devicestat.h> | |
| 39 | #include <bus/cam/cam_periph.h> | |
| 40 | #include <bus/cam/cam_xpt_periph.h> | |
| fef8985e | 41 | #include <sys/device.h> |
| 984263bc | 42 | |
| 191d7ec1 SW |
43 | #define THREAD_CREATE kthread_create |
| 44 | ||
| 45 | MODULE_VERSION(isp, 1); | |
| 46 | MODULE_DEPEND(isp, cam, 1, 1, 1); | |
| 47 | int isp_announced = 0; | |
| 48 | int isp_fabric_hysteresis = 5; | |
| 49 | int isp_loop_down_limit = 60; /* default loop down limit */ | |
| 50 | int isp_change_is_bad = 0; /* "changed" devices are bad */ | |
| 51 | int isp_quickboot_time = 7; /* don't wait more than N secs for loop up */ | |
| 52 | int isp_gone_device_time = 30; /* grace time before reporting device lost */ | |
| 53 | int isp_autoconfig = 1; /* automatically attach/detach devices */ | |
| 54 | static const char *roles[4] = { | |
| 55 | "(none)", "Target", "Initiator", "Target/Initiator" | |
| 56 | }; | |
| 57 | static const char prom3[] = "Chan %d PortID 0x%06x Departed from Target %u because of %s"; | |
| 58 | static const char rqo[] = "%s: Request Queue Overflow\n"; | |
| 984263bc | 59 | |
| 191d7ec1 | 60 | static void isp_freeze_loopdown(ispsoftc_t *, int, char *); |
| 984263bc MD |
61 | static d_ioctl_t ispioctl; |
| 62 | static void isp_intr_enable(void *); | |
| 191d7ec1 | 63 | static void isp_cam_async(void *, uint32_t, struct cam_path *, void *); |
| 984263bc MD |
64 | static void isp_poll(struct cam_sim *); |
| 65 | static timeout_t isp_watchdog; | |
| 191d7ec1 SW |
66 | static timeout_t isp_gdt; |
| 67 | static task_fn_t isp_gdt_task; | |
| 68 | static timeout_t isp_ldt; | |
| 69 | static task_fn_t isp_ldt_task; | |
| 984263bc MD |
70 | static void isp_kthread(void *); |
| 71 | static void isp_action(struct cam_sim *, union ccb *); | |
| 191d7ec1 SW |
72 | #ifdef ISP_INTERNAL_TARGET |
| 73 | static void isp_target_thread_pi(void *); | |
| 74 | static void isp_target_thread_fc(void *); | |
| 75 | #endif | |
| 76 | static void isp_timer(void *); | |
| 984263bc | 77 | |
| fef8985e | 78 | static struct dev_ops isp_ops = { |
| 191d7ec1 | 79 | { "isp", 0, D_DISK }, |
| fef8985e | 80 | .d_ioctl = ispioctl, |
| 984263bc MD |
81 | }; |
| 82 | ||
| 191d7ec1 SW |
83 | static int |
| 84 | isp_attach_chan(ispsoftc_t *isp, struct cam_devq *devq, int chan) | |
| 984263bc | 85 | { |
| 984263bc | 86 | struct ccb_setasync csa; |
| 984263bc MD |
87 | struct cam_sim *sim; |
| 88 | struct cam_path *path; | |
| 89 | ||
| 90 | /* | |
| 984263bc MD |
91 | * Construct our SIM entry. |
| 92 | */ | |
| 191d7ec1 SW |
93 | sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp, device_get_unit(isp->isp_dev), &isp->isp_osinfo.lock, isp->isp_maxcmds, isp->isp_maxcmds, devq); |
| 94 | cam_simq_release(devq); | |
| 984263bc | 95 | |
| 191d7ec1 SW |
96 | if (sim == NULL) { |
| 97 | return (ENOMEM); | |
| 984263bc MD |
98 | } |
| 99 | ||
| 191d7ec1 SW |
100 | ISP_LOCK(isp); |
| 101 | if (xpt_bus_register(sim, chan) != CAM_SUCCESS) { | |
| 102 | ISP_UNLOCK(isp); | |
| 3aed1355 | 103 | cam_sim_free(sim); |
| 191d7ec1 | 104 | return (EIO); |
| 984263bc | 105 | } |
| 191d7ec1 SW |
106 | ISP_UNLOCK(isp); |
| 107 | if (xpt_create_path_unlocked(&path, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { | |
| 108 | ISP_LOCK(isp); | |
| 984263bc | 109 | xpt_bus_deregister(cam_sim_path(sim)); |
| 191d7ec1 | 110 | ISP_UNLOCK(isp); |
| 3aed1355 | 111 | cam_sim_free(sim); |
| 191d7ec1 | 112 | return (ENXIO); |
| 984263bc | 113 | } |
| 984263bc MD |
114 | xpt_setup_ccb(&csa.ccb_h, path, 5); |
| 115 | csa.ccb_h.func_code = XPT_SASYNC_CB; | |
| 116 | csa.event_enable = AC_LOST_DEVICE; | |
| 117 | csa.callback = isp_cam_async; | |
| 118 | csa.callback_arg = sim; | |
| 119 | xpt_action((union ccb *)&csa); | |
| 191d7ec1 SW |
120 | |
| 121 | if (IS_SCSI(isp)) { | |
| 122 | struct isp_spi *spi = ISP_SPI_PC(isp, chan); | |
| 123 | spi->sim = sim; | |
| 124 | spi->path = path; | |
| 125 | #ifdef ISP_INTERNAL_TARGET | |
| 126 | ISP_SET_PC(isp, chan, proc_active, 1); | |
| 127 | if (THREAD_CREATE(isp_target_thread_pi, spi, &spi->target_proc, "%s: isp_test_tgt%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) { | |
| 128 | ISP_SET_PC(isp, chan, proc_active, 0); | |
| 129 | isp_prt(isp, ISP_LOGERR, "cannot create test target thread"); | |
| 130 | } | |
| 131 | #endif | |
| 132 | } else { | |
| 133 | fcparam *fcp = FCPARAM(isp, chan); | |
| 134 | struct isp_fc *fc = ISP_FC_PC(isp, chan); | |
| 135 | ||
| 136 | ISP_LOCK(isp); | |
| 137 | fc->sim = sim; | |
| 138 | fc->path = path; | |
| 139 | fc->isp = isp; | |
| 140 | fc->ready = 1; | |
| 141 | ||
| 142 | callout_init(&fc->ldt); | |
| 143 | callout_init(&fc->gdt); | |
| 144 | TASK_INIT(&fc->ltask, 1, isp_ldt_task, fc); | |
| 145 | TASK_INIT(&fc->gtask, 1, isp_gdt_task, fc); | |
| 146 | ||
| 147 | /* | |
| 148 | * We start by being "loop down" if we have an initiator role | |
| 149 | */ | |
| 150 | if (fcp->role & ISP_ROLE_INITIATOR) { | |
| 151 | isp_freeze_loopdown(isp, chan, "isp_attach"); | |
| 152 | callout_reset(&fc->ldt, isp_quickboot_time * hz, isp_ldt, fc); | |
| 153 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Starting Initial Loop Down Timer @ %lu", (unsigned long) time_second); | |
| 154 | } | |
| 155 | ISP_UNLOCK(isp); | |
| 156 | if (THREAD_CREATE(isp_kthread, fc, &fc->kthread, "%s: fc_thrd%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) { | |
| 157 | xpt_free_path(fc->path); | |
| 158 | ISP_LOCK(isp); | |
| 159 | if (callout_active(&fc->ldt)) { | |
| 160 | callout_stop(&fc->ldt); | |
| 161 | } | |
| 162 | xpt_bus_deregister(cam_sim_path(fc->sim)); | |
| 163 | ISP_UNLOCK(isp); | |
| 164 | cam_sim_free(fc->sim); | |
| 165 | return (ENOMEM); | |
| 166 | } | |
| 167 | #ifdef ISP_INTERNAL_TARGET | |
| 168 | ISP_SET_PC(isp, chan, proc_active, 1); | |
| 169 | if (THREAD_CREATE(isp_target_thread_fc, fc, &fc->target_proc, "%s: isp_test_tgt%d", device_get_nameunit(isp->isp_osinfo.dev), chan)) { | |
| 170 | ISP_SET_PC(isp, chan, proc_active, 0); | |
| 171 | isp_prt(isp, ISP_LOGERR, "cannot create test target thread"); | |
| 172 | } | |
| 173 | #endif | |
| 174 | if (chan == 0) { | |
| 175 | SYSCTL_ADD_QUAD(&isp->isp_sysctl_ctx, SYSCTL_CHILDREN(isp->isp_sysctl_tree), OID_AUTO, "wwnn", CTLFLAG_RD, &FCPARAM(isp, 0)->isp_wwnn, 0, "World Wide Node Name"); | |
| 176 | SYSCTL_ADD_QUAD(&isp->isp_sysctl_ctx, SYSCTL_CHILDREN(isp->isp_sysctl_tree), OID_AUTO, "wwpn", CTLFLAG_RD, &FCPARAM(isp, 0)->isp_wwpn, 0, "World Wide Port Name"); | |
| 177 | SYSCTL_ADD_UINT(&isp->isp_sysctl_ctx, SYSCTL_CHILDREN(isp->isp_sysctl_tree), OID_AUTO, "loop_down_limit", CTLFLAG_RW, &ISP_FC_PC(isp, 0)->loop_down_limit, 0, "Loop Down Limit"); | |
| 178 | SYSCTL_ADD_UINT(&isp->isp_sysctl_ctx, SYSCTL_CHILDREN(isp->isp_sysctl_tree), OID_AUTO, "gone_device_time", CTLFLAG_RW, &ISP_FC_PC(isp, 0)->gone_device_time, 0, "Gone Device Time"); | |
| 984263bc | 179 | } |
| 984263bc | 180 | } |
| 191d7ec1 SW |
181 | return (0); |
| 182 | } | |
| 984263bc | 183 | |
| 191d7ec1 SW |
184 | int |
| 185 | isp_attach(ispsoftc_t *isp) | |
| 186 | { | |
| 187 | const char *nu = device_get_nameunit(isp->isp_osinfo.dev); | |
| 188 | int du = device_get_unit(isp->isp_dev); | |
| 189 | int chan; | |
| 984263bc | 190 | |
| 191d7ec1 SW |
191 | isp->isp_osinfo.ehook.ich_func = isp_intr_enable; |
| 192 | isp->isp_osinfo.ehook.ich_arg = isp; | |
| 52800c9d | 193 | isp->isp_osinfo.ehook.ich_desc = "isp"; |
| 984263bc | 194 | /* |
| 191d7ec1 SW |
195 | * Haha. Set this first, because if we're loaded as a module isp_intr_enable |
| 196 | * will be called right awawy, which will clear isp_osinfo.ehook_active, | |
| 197 | * which would be unwise to then set again later. | |
| 984263bc | 198 | */ |
| 191d7ec1 SW |
199 | isp->isp_osinfo.ehook_active = 1; |
| 200 | if (config_intrhook_establish(&isp->isp_osinfo.ehook) != 0) { | |
| 201 | isp_prt(isp, ISP_LOGERR, "could not establish interrupt enable hook"); | |
| 202 | return (-EIO); | |
| 203 | } | |
| 204 | ||
| 205 | /* | |
| 206 | * Create the device queue for our SIM(s). | |
| 207 | */ | |
| 208 | isp->isp_osinfo.devq = cam_simq_alloc(isp->isp_maxcmds); | |
| 209 | if (isp->isp_osinfo.devq == NULL) { | |
| 210 | config_intrhook_disestablish(&isp->isp_osinfo.ehook); | |
| 211 | return (EIO); | |
| 212 | } | |
| 213 | ||
| 214 | sysctl_ctx_init(&isp->isp_sysctl_ctx); | |
| 215 | isp->isp_sysctl_tree = SYSCTL_ADD_NODE(&isp->isp_sysctl_ctx, | |
| 216 | SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, | |
| 217 | device_get_nameunit(isp->isp_dev), CTLFLAG_RD, 0, ""); | |
| 218 | if (isp->isp_sysctl_tree == NULL) { | |
| 219 | device_printf(isp->isp_dev, "can't add sysctl node\n"); | |
| 220 | return (EINVAL); | |
| 221 | } | |
| 222 | for (chan = 0; chan < isp->isp_nchan; chan++) { | |
| 223 | if (isp_attach_chan(isp, isp->isp_osinfo.devq, chan)) { | |
| 224 | goto unwind; | |
| 984263bc | 225 | } |
| 191d7ec1 | 226 | } |
| 984263bc | 227 | |
| 191d7ec1 SW |
228 | callout_init(&isp->isp_osinfo.tmo); |
| 229 | callout_reset(&isp->isp_osinfo.tmo, hz, isp_timer, isp); | |
| 230 | isp->isp_osinfo.timer_active = 1; | |
| 231 | ||
| 232 | isp->isp_osinfo.cdev = make_dev(&isp_ops, du, UID_ROOT, GID_OPERATOR, 0600, "%s", nu); | |
| 233 | if (isp->isp_osinfo.cdev) { | |
| 234 | isp->isp_osinfo.cdev->si_drv1 = isp; | |
| 235 | } | |
| 236 | return (0); | |
| 237 | ||
| 238 | unwind: | |
| 239 | while (--chan >= 0) { | |
| 240 | struct cam_sim *sim; | |
| 241 | struct cam_path *path; | |
| 242 | if (IS_FC(isp)) { | |
| 243 | sim = ISP_FC_PC(isp, chan)->sim; | |
| 244 | path = ISP_FC_PC(isp, chan)->path; | |
| 245 | } else { | |
| 246 | sim = ISP_SPI_PC(isp, chan)->sim; | |
| 247 | path = ISP_SPI_PC(isp, chan)->path; | |
| 984263bc | 248 | } |
| 191d7ec1 SW |
249 | xpt_free_path(path); |
| 250 | ISP_LOCK(isp); | |
| 251 | xpt_bus_deregister(cam_sim_path(sim)); | |
| 252 | ISP_UNLOCK(isp); | |
| 253 | cam_sim_free(sim); | |
| 254 | } | |
| 255 | if (isp->isp_osinfo.ehook_active) { | |
| 256 | config_intrhook_disestablish(&isp->isp_osinfo.ehook); | |
| 257 | isp->isp_osinfo.ehook_active = 0; | |
| 258 | } | |
| 259 | if (isp->isp_osinfo.cdev) { | |
| 260 | destroy_dev(isp->isp_osinfo.cdev); | |
| 261 | isp->isp_osinfo.cdev = NULL; | |
| 262 | } | |
| 263 | isp->isp_osinfo.devq = NULL; | |
| 264 | return (-1); | |
| 265 | } | |
| 266 | ||
| 267 | int | |
| 268 | isp_detach(ispsoftc_t *isp) | |
| 269 | { | |
| 270 | struct cam_sim *sim; | |
| 271 | struct cam_path *path; | |
| 272 | struct ccb_setasync csa; | |
| 273 | int chan; | |
| 984263bc | 274 | |
| 191d7ec1 SW |
275 | ISP_LOCK(isp); |
| 276 | for (chan = isp->isp_nchan - 1; chan >= 0; chan -= 1) { | |
| 277 | if (IS_FC(isp)) { | |
| 278 | sim = ISP_FC_PC(isp, chan)->sim; | |
| 279 | path = ISP_FC_PC(isp, chan)->path; | |
| 280 | } else { | |
| 281 | sim = ISP_SPI_PC(isp, chan)->sim; | |
| 282 | path = ISP_SPI_PC(isp, chan)->path; | |
| 283 | } | |
| 284 | if (sim->refcount > 2) { | |
| 285 | ISP_UNLOCK(isp); | |
| 286 | return (EBUSY); | |
| 287 | } | |
| 288 | } | |
| 289 | if (isp->isp_osinfo.timer_active) { | |
| 290 | callout_stop(&isp->isp_osinfo.tmo); | |
| 291 | isp->isp_osinfo.timer_active = 0; | |
| 292 | } | |
| 293 | for (chan = isp->isp_nchan - 1; chan >= 0; chan -= 1) { | |
| 294 | if (IS_FC(isp)) { | |
| 295 | sim = ISP_FC_PC(isp, chan)->sim; | |
| 296 | path = ISP_FC_PC(isp, chan)->path; | |
| 297 | } else { | |
| 298 | sim = ISP_SPI_PC(isp, chan)->sim; | |
| 299 | path = ISP_SPI_PC(isp, chan)->path; | |
| 300 | } | |
| 984263bc MD |
301 | xpt_setup_ccb(&csa.ccb_h, path, 5); |
| 302 | csa.ccb_h.func_code = XPT_SASYNC_CB; | |
| 191d7ec1 | 303 | csa.event_enable = 0; |
| 984263bc MD |
304 | csa.callback = isp_cam_async; |
| 305 | csa.callback_arg = sim; | |
| 306 | xpt_action((union ccb *)&csa); | |
| 191d7ec1 SW |
307 | xpt_free_path(path); |
| 308 | xpt_bus_deregister(cam_sim_path(sim)); | |
| 309 | cam_sim_free(sim); | |
| 984263bc | 310 | } |
| 191d7ec1 SW |
311 | ISP_UNLOCK(isp); |
| 312 | if (isp->isp_osinfo.cdev) { | |
| 313 | destroy_dev(isp->isp_osinfo.cdev); | |
| 314 | isp->isp_osinfo.cdev = NULL; | |
| 984263bc | 315 | } |
| 191d7ec1 SW |
316 | if (isp->isp_osinfo.ehook_active) { |
| 317 | config_intrhook_disestablish(&isp->isp_osinfo.ehook); | |
| 318 | isp->isp_osinfo.ehook_active = 0; | |
| 319 | } | |
| 320 | if (isp->isp_osinfo.devq != NULL) { | |
| 321 | isp->isp_osinfo.devq = NULL; | |
| 984263bc | 322 | } |
| 191d7ec1 SW |
323 | if (isp->isp_sysctl_tree != NULL) |
| 324 | sysctl_ctx_free(&isp->isp_sysctl_ctx); | |
| 325 | return (0); | |
| 326 | } | |
| 984263bc | 327 | |
| 191d7ec1 SW |
328 | static void |
| 329 | isp_freeze_loopdown(ispsoftc_t *isp, int chan, char *msg) | |
| 330 | { | |
| 331 | if (IS_FC(isp)) { | |
| 332 | struct isp_fc *fc = ISP_FC_PC(isp, chan); | |
| 333 | if (fc->simqfrozen == 0) { | |
| 334 | isp_prt(isp, ISP_LOGDEBUG0, "%s: freeze simq (loopdown) chan %d", msg, chan); | |
| 335 | fc->simqfrozen = SIMQFRZ_LOOPDOWN; | |
| 336 | xpt_freeze_simq(fc->sim, 1); | |
| 337 | } else { | |
| 338 | isp_prt(isp, ISP_LOGDEBUG0, "%s: mark frozen (loopdown) chan %d", msg, chan); | |
| 339 | fc->simqfrozen |= SIMQFRZ_LOOPDOWN; | |
| 340 | } | |
| 341 | } | |
| 984263bc MD |
342 | } |
| 343 | ||
| 191d7ec1 SW |
344 | static void |
| 345 | isp_unfreeze_loopdown(ispsoftc_t *isp, int chan) | |
| 984263bc | 346 | { |
| 191d7ec1 SW |
347 | if (IS_FC(isp)) { |
| 348 | struct isp_fc *fc = ISP_FC_PC(isp, chan); | |
| 349 | int wasfrozen = fc->simqfrozen & SIMQFRZ_LOOPDOWN; | |
| 350 | fc->simqfrozen &= ~SIMQFRZ_LOOPDOWN; | |
| 351 | if (wasfrozen && fc->simqfrozen == 0) { | |
| 352 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d releasing simq", __func__, chan); | |
| 353 | xpt_release_simq(fc->sim, 1); | |
| 354 | } | |
| 984263bc MD |
355 | } |
| 356 | } | |
| 357 | ||
| 191d7ec1 | 358 | |
| 984263bc | 359 | static int |
| fef8985e | 360 | ispioctl(struct dev_ioctl_args *ap) |
| 984263bc | 361 | { |
| b13267a5 | 362 | cdev_t dev = ap->a_head.a_dev; |
| 191d7ec1 SW |
363 | caddr_t addr = ap->a_data; |
| 364 | u_long c = ap->a_cmd; | |
| 365 | ispsoftc_t *isp; | |
| 366 | int nr, chan, retval = ENOTTY; | |
| 984263bc | 367 | |
| 191d7ec1 | 368 | isp = dev->si_drv1; |
| 984263bc | 369 | |
| 191d7ec1 SW |
370 | switch (c) { |
| 371 | case ISP_SDBLEV: | |
| 372 | { | |
| 373 | int olddblev = isp->isp_dblev; | |
| 374 | isp->isp_dblev = *(int *)addr; | |
| 375 | *(int *)addr = olddblev; | |
| 984263bc | 376 | retval = 0; |
| 984263bc MD |
377 | break; |
| 378 | } | |
| 191d7ec1 SW |
379 | case ISP_GETROLE: |
| 380 | chan = *(int *)addr; | |
| 381 | if (chan < 0 || chan >= isp->isp_nchan) { | |
| 382 | retval = -ENXIO; | |
| 383 | break; | |
| 384 | } | |
| 385 | if (IS_FC(isp)) { | |
| 386 | *(int *)addr = FCPARAM(isp, chan)->role; | |
| 387 | } else { | |
| 388 | *(int *)addr = SDPARAM(isp, chan)->role; | |
| 389 | } | |
| 984263bc MD |
390 | retval = 0; |
| 391 | break; | |
| 191d7ec1 SW |
392 | case ISP_SETROLE: |
| 393 | nr = *(int *)addr; | |
| 394 | chan = nr >> 8; | |
| 395 | if (chan < 0 || chan >= isp->isp_nchan) { | |
| 396 | retval = -ENXIO; | |
| 397 | break; | |
| 398 | } | |
| 399 | nr &= 0xff; | |
| 400 | if (nr & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) { | |
| 401 | retval = EINVAL; | |
| 402 | break; | |
| 403 | } | |
| 404 | if (IS_FC(isp)) { | |
| 405 | /* | |
| 406 | * We don't really support dual role at present on FC cards. | |
| 407 | * | |
| 408 | * We should, but a bunch of things are currently broken, | |
| 409 | * so don't allow it. | |
| 410 | */ | |
| 411 | if (nr == ISP_ROLE_BOTH) { | |
| 412 | isp_prt(isp, ISP_LOGERR, "cannot support dual role at present"); | |
| 413 | retval = EINVAL; | |
| 414 | break; | |
| 415 | } | |
| 416 | *(int *)addr = FCPARAM(isp, chan)->role; | |
| 417 | #ifdef ISP_INTERNAL_TARGET | |
| 418 | ISP_LOCK(isp); | |
| 419 | retval = isp_fc_change_role(isp, chan, nr); | |
| 420 | ISP_UNLOCK(isp); | |
| 421 | #else | |
| 422 | FCPARAM(isp, chan)->role = nr; | |
| 984263bc | 423 | #endif |
| 191d7ec1 SW |
424 | } else { |
| 425 | *(int *)addr = SDPARAM(isp, chan)->role; | |
| 426 | SDPARAM(isp, chan)->role = nr; | |
| 427 | } | |
| 984263bc MD |
428 | retval = 0; |
| 429 | break; | |
| 191d7ec1 | 430 | |
| 984263bc MD |
431 | case ISP_RESETHBA: |
| 432 | ISP_LOCK(isp); | |
| 191d7ec1 SW |
433 | #ifdef ISP_TARGET_MODE |
| 434 | isp_del_all_wwn_entries(isp, ISP_NOCHAN); | |
| 435 | #endif | |
| 436 | isp_reinit(isp, 0); | |
| 984263bc MD |
437 | ISP_UNLOCK(isp); |
| 438 | retval = 0; | |
| 439 | break; | |
| 191d7ec1 | 440 | |
| 984263bc MD |
441 | case ISP_RESCAN: |
| 442 | if (IS_FC(isp)) { | |
| 191d7ec1 SW |
443 | chan = *(int *)addr; |
| 444 | if (chan < 0 || chan >= isp->isp_nchan) { | |
| 445 | retval = -ENXIO; | |
| 446 | break; | |
| 447 | } | |
| 984263bc | 448 | ISP_LOCK(isp); |
| 191d7ec1 | 449 | if (isp_fc_runstate(isp, chan, 5 * 1000000)) { |
| 984263bc MD |
450 | retval = EIO; |
| 451 | } else { | |
| 452 | retval = 0; | |
| 453 | } | |
| 454 | ISP_UNLOCK(isp); | |
| 455 | } | |
| 456 | break; | |
| 191d7ec1 | 457 | |
| 984263bc MD |
458 | case ISP_FC_LIP: |
| 459 | if (IS_FC(isp)) { | |
| 191d7ec1 SW |
460 | chan = *(int *)addr; |
| 461 | if (chan < 0 || chan >= isp->isp_nchan) { | |
| 462 | retval = -ENXIO; | |
| 463 | break; | |
| 464 | } | |
| 984263bc | 465 | ISP_LOCK(isp); |
| 191d7ec1 | 466 | if (isp_control(isp, ISPCTL_SEND_LIP, chan)) { |
| 984263bc MD |
467 | retval = EIO; |
| 468 | } else { | |
| 469 | retval = 0; | |
| 470 | } | |
| 471 | ISP_UNLOCK(isp); | |
| 472 | } | |
| 473 | break; | |
| 474 | case ISP_FC_GETDINFO: | |
| 475 | { | |
| 191d7ec1 SW |
476 | struct isp_fc_device *ifc = (struct isp_fc_device *) addr; |
| 477 | fcportdb_t *lp; | |
| 984263bc | 478 | |
| 191d7ec1 SW |
479 | if (IS_SCSI(isp)) { |
| 480 | break; | |
| 481 | } | |
| 482 | if (ifc->loopid >= MAX_FC_TARG) { | |
| 984263bc MD |
483 | retval = EINVAL; |
| 484 | break; | |
| 485 | } | |
| 191d7ec1 SW |
486 | lp = &FCPARAM(isp, ifc->chan)->portdb[ifc->loopid]; |
| 487 | if (lp->state == FC_PORTDB_STATE_VALID || lp->target_mode) { | |
| 488 | ifc->role = lp->roles; | |
| 489 | ifc->loopid = lp->handle; | |
| 984263bc MD |
490 | ifc->portid = lp->portid; |
| 491 | ifc->node_wwn = lp->node_wwn; | |
| 492 | ifc->port_wwn = lp->port_wwn; | |
| 493 | retval = 0; | |
| 494 | } else { | |
| 495 | retval = ENODEV; | |
| 496 | } | |
| 984263bc MD |
497 | break; |
| 498 | } | |
| 499 | case ISP_GET_STATS: | |
| 500 | { | |
| 191d7ec1 | 501 | isp_stats_t *sp = (isp_stats_t *) addr; |
| 984263bc | 502 | |
| 191d7ec1 | 503 | ISP_MEMZERO(sp, sizeof (*sp)); |
| 984263bc MD |
504 | sp->isp_stat_version = ISP_STATS_VERSION; |
| 505 | sp->isp_type = isp->isp_type; | |
| 506 | sp->isp_revision = isp->isp_revision; | |
| 507 | ISP_LOCK(isp); | |
| 508 | sp->isp_stats[ISP_INTCNT] = isp->isp_intcnt; | |
| 509 | sp->isp_stats[ISP_INTBOGUS] = isp->isp_intbogus; | |
| 510 | sp->isp_stats[ISP_INTMBOXC] = isp->isp_intmboxc; | |
| 511 | sp->isp_stats[ISP_INGOASYNC] = isp->isp_intoasync; | |
| 512 | sp->isp_stats[ISP_RSLTCCMPLT] = isp->isp_rsltccmplt; | |
| 513 | sp->isp_stats[ISP_FPHCCMCPLT] = isp->isp_fphccmplt; | |
| 514 | sp->isp_stats[ISP_RSCCHIWAT] = isp->isp_rscchiwater; | |
| 515 | sp->isp_stats[ISP_FPCCHIWAT] = isp->isp_fpcchiwater; | |
| 516 | ISP_UNLOCK(isp); | |
| 517 | retval = 0; | |
| 518 | break; | |
| 519 | } | |
| 520 | case ISP_CLR_STATS: | |
| 521 | ISP_LOCK(isp); | |
| 522 | isp->isp_intcnt = 0; | |
| 523 | isp->isp_intbogus = 0; | |
| 524 | isp->isp_intmboxc = 0; | |
| 525 | isp->isp_intoasync = 0; | |
| 526 | isp->isp_rsltccmplt = 0; | |
| 527 | isp->isp_fphccmplt = 0; | |
| 528 | isp->isp_rscchiwater = 0; | |
| 529 | isp->isp_fpcchiwater = 0; | |
| 530 | ISP_UNLOCK(isp); | |
| 531 | retval = 0; | |
| 532 | break; | |
| 533 | case ISP_FC_GETHINFO: | |
| 534 | { | |
| 191d7ec1 SW |
535 | struct isp_hba_device *hba = (struct isp_hba_device *) addr; |
| 536 | int chan = hba->fc_channel; | |
| 984263bc | 537 | |
| 191d7ec1 SW |
538 | if (chan < 0 || chan >= isp->isp_nchan) { |
| 539 | retval = ENXIO; | |
| 984263bc MD |
540 | break; |
| 541 | } | |
| 191d7ec1 SW |
542 | hba->fc_fw_major = ISP_FW_MAJORX(isp->isp_fwrev); |
| 543 | hba->fc_fw_minor = ISP_FW_MINORX(isp->isp_fwrev); | |
| 544 | hba->fc_fw_micro = ISP_FW_MICROX(isp->isp_fwrev); | |
| 545 | hba->fc_nchannels = isp->isp_nchan; | |
| 546 | if (IS_FC(isp)) { | |
| 547 | hba->fc_nports = MAX_FC_TARG; | |
| 548 | hba->fc_speed = FCPARAM(isp, hba->fc_channel)->isp_gbspeed; | |
| 549 | hba->fc_topology = FCPARAM(isp, chan)->isp_topo + 1; | |
| 550 | hba->fc_loopid = FCPARAM(isp, chan)->isp_loopid; | |
| 551 | hba->nvram_node_wwn = FCPARAM(isp, chan)->isp_wwnn_nvram; | |
| 552 | hba->nvram_port_wwn = FCPARAM(isp, chan)->isp_wwpn_nvram; | |
| 553 | hba->active_node_wwn = FCPARAM(isp, chan)->isp_wwnn; | |
| 554 | hba->active_port_wwn = FCPARAM(isp, chan)->isp_wwpn; | |
| 555 | } else { | |
| 556 | hba->fc_nports = MAX_TARGETS; | |
| 557 | hba->fc_speed = 0; | |
| 558 | hba->fc_topology = 0; | |
| 559 | hba->nvram_node_wwn = 0ull; | |
| 560 | hba->nvram_port_wwn = 0ull; | |
| 561 | hba->active_node_wwn = 0ull; | |
| 562 | hba->active_port_wwn = 0ull; | |
| 984263bc | 563 | } |
| 191d7ec1 | 564 | retval = 0; |
| 984263bc MD |
565 | break; |
| 566 | } | |
| 191d7ec1 | 567 | case ISP_TSK_MGMT: |
| 984263bc | 568 | { |
| 191d7ec1 SW |
569 | int needmarker; |
| 570 | struct isp_fc_tsk_mgmt *fct = (struct isp_fc_tsk_mgmt *) addr; | |
| 571 | uint16_t loopid; | |
| 572 | mbreg_t mbs; | |
| 984263bc | 573 | |
| 191d7ec1 | 574 | if (IS_SCSI(isp)) { |
| 984263bc MD |
575 | break; |
| 576 | } | |
| 191d7ec1 SW |
577 | |
| 578 | chan = fct->chan; | |
| 579 | if (chan < 0 || chan >= isp->isp_nchan) { | |
| 580 | retval = -ENXIO; | |
| 984263bc MD |
581 | break; |
| 582 | } | |
| 191d7ec1 SW |
583 | |
| 584 | needmarker = retval = 0; | |
| 585 | loopid = fct->loopid; | |
| 586 | ISP_LOCK(isp); | |
| 587 | if (IS_24XX(isp)) { | |
| 588 | uint8_t local[QENTRY_LEN]; | |
| 589 | isp24xx_tmf_t *tmf; | |
| 590 | isp24xx_statusreq_t *sp; | |
| 591 | fcparam *fcp = FCPARAM(isp, chan); | |
| 592 | fcportdb_t *lp; | |
| 593 | int i; | |
| 594 | ||
| 595 | for (i = 0; i < MAX_FC_TARG; i++) { | |
| 596 | lp = &fcp->portdb[i]; | |
| 597 | if (lp->handle == loopid) { | |
| 598 | break; | |
| 599 | } | |
| 600 | } | |
| 601 | if (i == MAX_FC_TARG) { | |
| 602 | retval = ENXIO; | |
| 603 | ISP_UNLOCK(isp); | |
| 984263bc MD |
604 | break; |
| 605 | } | |
| 191d7ec1 SW |
606 | /* XXX VALIDATE LP XXX */ |
| 607 | tmf = (isp24xx_tmf_t *) local; | |
| 608 | ISP_MEMZERO(tmf, QENTRY_LEN); | |
| 609 | tmf->tmf_header.rqs_entry_type = RQSTYPE_TSK_MGMT; | |
| 610 | tmf->tmf_header.rqs_entry_count = 1; | |
| 611 | tmf->tmf_nphdl = lp->handle; | |
| 612 | tmf->tmf_delay = 2; | |
| 613 | tmf->tmf_timeout = 2; | |
| 614 | tmf->tmf_tidlo = lp->portid; | |
| 615 | tmf->tmf_tidhi = lp->portid >> 16; | |
| 616 | tmf->tmf_vpidx = ISP_GET_VPIDX(isp, chan); | |
| 617 | tmf->tmf_lun[1] = fct->lun & 0xff; | |
| 618 | if (fct->lun >= 256) { | |
| 619 | tmf->tmf_lun[0] = 0x40 | (fct->lun >> 8); | |
| 620 | } | |
| 621 | switch (fct->action) { | |
| 622 | case IPT_CLEAR_ACA: | |
| 623 | tmf->tmf_flags = ISP24XX_TMF_CLEAR_ACA; | |
| 624 | break; | |
| 625 | case IPT_TARGET_RESET: | |
| 626 | tmf->tmf_flags = ISP24XX_TMF_TARGET_RESET; | |
| 627 | needmarker = 1; | |
| 628 | break; | |
| 629 | case IPT_LUN_RESET: | |
| 630 | tmf->tmf_flags = ISP24XX_TMF_LUN_RESET; | |
| 631 | needmarker = 1; | |
| 632 | break; | |
| 633 | case IPT_CLEAR_TASK_SET: | |
| 634 | tmf->tmf_flags = ISP24XX_TMF_CLEAR_TASK_SET; | |
| 635 | needmarker = 1; | |
| 636 | break; | |
| 637 | case IPT_ABORT_TASK_SET: | |
| 638 | tmf->tmf_flags = ISP24XX_TMF_ABORT_TASK_SET; | |
| 639 | needmarker = 1; | |
| 640 | break; | |
| 641 | default: | |
| 984263bc MD |
642 | retval = EINVAL; |
| 643 | break; | |
| 644 | } | |
| 191d7ec1 SW |
645 | if (retval) { |
| 646 | ISP_UNLOCK(isp); | |
| 647 | break; | |
| 984263bc | 648 | } |
| 191d7ec1 SW |
649 | MBSINIT(&mbs, MBOX_EXEC_COMMAND_IOCB_A64, MBLOGALL, 5000000); |
| 650 | mbs.param[1] = QENTRY_LEN; | |
| 651 | mbs.param[2] = DMA_WD1(fcp->isp_scdma); | |
| 652 | mbs.param[3] = DMA_WD0(fcp->isp_scdma); | |
| 653 | mbs.param[6] = DMA_WD3(fcp->isp_scdma); | |
| 654 | mbs.param[7] = DMA_WD2(fcp->isp_scdma); | |
| 655 | ||
| 656 | if (FC_SCRATCH_ACQUIRE(isp, chan)) { | |
| 657 | ISP_UNLOCK(isp); | |
| 658 | retval = ENOMEM; | |
| 984263bc MD |
659 | break; |
| 660 | } | |
| 191d7ec1 SW |
661 | isp_put_24xx_tmf(isp, tmf, fcp->isp_scratch); |
| 662 | MEMORYBARRIER(isp, SYNC_SFORDEV, 0, QENTRY_LEN, chan); | |
| 663 | sp = (isp24xx_statusreq_t *) local; | |
| 664 | sp->req_completion_status = 1; | |
| 665 | retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); | |
| 666 | MEMORYBARRIER(isp, SYNC_SFORCPU, QENTRY_LEN, QENTRY_LEN, chan); | |
| 667 | isp_get_24xx_response(isp, &((isp24xx_statusreq_t *)fcp->isp_scratch)[1], sp); | |
| 668 | FC_SCRATCH_RELEASE(isp, chan); | |
| 669 | if (retval || sp->req_completion_status != 0) { | |
| 670 | FC_SCRATCH_RELEASE(isp, chan); | |
| 671 | retval = EIO; | |
| 672 | } | |
| 673 | if (retval == 0) { | |
| 674 | if (needmarker) { | |
| 675 | fcp->sendmarker = 1; | |
| 676 | } | |
| 677 | } | |
| 678 | } else { | |
| 679 | MBSINIT(&mbs, 0, MBLOGALL, 0); | |
| 680 | if (ISP_CAP_2KLOGIN(isp) == 0) { | |
| 681 | loopid <<= 8; | |
| 682 | } | |
| 683 | switch (fct->action) { | |
| 684 | case IPT_CLEAR_ACA: | |
| 685 | mbs.param[0] = MBOX_CLEAR_ACA; | |
| 686 | mbs.param[1] = loopid; | |
| 687 | mbs.param[2] = fct->lun; | |
| 688 | break; | |
| 689 | case IPT_TARGET_RESET: | |
| 690 | mbs.param[0] = MBOX_TARGET_RESET; | |
| 691 | mbs.param[1] = loopid; | |
| 692 | needmarker = 1; | |
| 693 | break; | |
| 694 | case IPT_LUN_RESET: | |
| 695 | mbs.param[0] = MBOX_LUN_RESET; | |
| 696 | mbs.param[1] = loopid; | |
| 697 | mbs.param[2] = fct->lun; | |
| 698 | needmarker = 1; | |
| 699 | break; | |
| 700 | case IPT_CLEAR_TASK_SET: | |
| 701 | mbs.param[0] = MBOX_CLEAR_TASK_SET; | |
| 702 | mbs.param[1] = loopid; | |
| 703 | mbs.param[2] = fct->lun; | |
| 704 | needmarker = 1; | |
| 705 | break; | |
| 706 | case IPT_ABORT_TASK_SET: | |
| 707 | mbs.param[0] = MBOX_ABORT_TASK_SET; | |
| 708 | mbs.param[1] = loopid; | |
| 709 | mbs.param[2] = fct->lun; | |
| 710 | needmarker = 1; | |
| 711 | break; | |
| 712 | default: | |
| 713 | retval = EINVAL; | |
| 714 | break; | |
| 715 | } | |
| 716 | if (retval == 0) { | |
| 717 | if (needmarker) { | |
| 718 | FCPARAM(isp, chan)->sendmarker = 1; | |
| 719 | } | |
| 720 | retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); | |
| 721 | if (retval) { | |
| 722 | retval = EIO; | |
| 723 | } | |
| 724 | } | |
| 984263bc | 725 | } |
| 191d7ec1 | 726 | ISP_UNLOCK(isp); |
| 984263bc MD |
727 | break; |
| 728 | } | |
| 729 | default: | |
| 730 | break; | |
| 731 | } | |
| 732 | return (retval); | |
| 733 | } | |
| 734 | ||
| 735 | static void | |
| 736 | isp_intr_enable(void *arg) | |
| 737 | { | |
| 191d7ec1 SW |
738 | int chan; |
| 739 | ispsoftc_t *isp = arg; | |
| 740 | ISP_LOCK(isp); | |
| 741 | for (chan = 0; chan < isp->isp_nchan; chan++) { | |
| 742 | if (IS_FC(isp)) { | |
| 743 | if (FCPARAM(isp, chan)->role != ISP_ROLE_NONE) { | |
| 744 | ISP_ENABLE_INTS(isp); | |
| 745 | break; | |
| 746 | } | |
| 747 | } else { | |
| 748 | if (SDPARAM(isp, chan)->role != ISP_ROLE_NONE) { | |
| 749 | ISP_ENABLE_INTS(isp); | |
| 750 | break; | |
| 751 | } | |
| 752 | } | |
| 984263bc | 753 | } |
| 191d7ec1 SW |
754 | isp->isp_osinfo.ehook_active = 0; |
| 755 | ISP_UNLOCK(isp); | |
| 984263bc MD |
756 | /* Release our hook so that the boot can continue. */ |
| 757 | config_intrhook_disestablish(&isp->isp_osinfo.ehook); | |
| 758 | } | |
| 759 | ||
| 760 | /* | |
| 191d7ec1 SW |
761 | * Local Inlines |
| 762 | */ | |
| 763 | ||
| 764 | static ISP_INLINE int isp_get_pcmd(ispsoftc_t *, union ccb *); | |
| 765 | static ISP_INLINE void isp_free_pcmd(ispsoftc_t *, union ccb *); | |
| 766 | ||
| 767 | static ISP_INLINE int | |
| 768 | isp_get_pcmd(ispsoftc_t *isp, union ccb *ccb) | |
| 769 | { | |
| 770 | ISP_PCMD(ccb) = isp->isp_osinfo.pcmd_free; | |
| 771 | if (ISP_PCMD(ccb) == NULL) { | |
| 772 | return (-1); | |
| 773 | } | |
| 774 | isp->isp_osinfo.pcmd_free = ((struct isp_pcmd *)ISP_PCMD(ccb))->next; | |
| 775 | return (0); | |
| 776 | } | |
| 777 | ||
| 778 | static ISP_INLINE void | |
| 779 | isp_free_pcmd(ispsoftc_t *isp, union ccb *ccb) | |
| 780 | { | |
| 781 | ((struct isp_pcmd *)ISP_PCMD(ccb))->next = isp->isp_osinfo.pcmd_free; | |
| 782 | isp->isp_osinfo.pcmd_free = ISP_PCMD(ccb); | |
| 783 | ISP_PCMD(ccb) = NULL; | |
| 784 | } | |
| 785 | /* | |
| 984263bc MD |
786 | * Put the target mode functions here, because some are inlines |
| 787 | */ | |
| 788 | ||
| 789 | #ifdef ISP_TARGET_MODE | |
| 191d7ec1 SW |
790 | static ISP_INLINE int is_lun_enabled(ispsoftc_t *, int, lun_id_t); |
| 791 | static ISP_INLINE tstate_t *get_lun_statep(ispsoftc_t *, int, lun_id_t); | |
| 792 | static ISP_INLINE tstate_t *get_lun_statep_from_tag(ispsoftc_t *, int, uint32_t); | |
| 793 | static ISP_INLINE void rls_lun_statep(ispsoftc_t *, tstate_t *); | |
| 794 | static ISP_INLINE inot_private_data_t *get_ntp_from_tagdata(ispsoftc_t *, uint32_t, uint32_t, tstate_t **); | |
| 795 | static ISP_INLINE atio_private_data_t *isp_get_atpd(ispsoftc_t *, tstate_t *, uint32_t); | |
| 796 | static ISP_INLINE void isp_put_atpd(ispsoftc_t *, tstate_t *, atio_private_data_t *); | |
| 797 | static ISP_INLINE inot_private_data_t *isp_get_ntpd(ispsoftc_t *, tstate_t *); | |
| 798 | static ISP_INLINE inot_private_data_t *isp_find_ntpd(ispsoftc_t *, tstate_t *, uint32_t, uint32_t); | |
| 799 | static ISP_INLINE void isp_put_ntpd(ispsoftc_t *, tstate_t *, inot_private_data_t *); | |
| 800 | static cam_status create_lun_state(ispsoftc_t *, int, struct cam_path *, tstate_t **); | |
| 801 | static void destroy_lun_state(ispsoftc_t *, tstate_t *); | |
| 802 | static void isp_enable_lun(ispsoftc_t *, union ccb *); | |
| 803 | static void isp_enable_deferred_luns(ispsoftc_t *, int); | |
| 804 | static cam_status isp_enable_deferred(ispsoftc_t *, int, lun_id_t); | |
| 805 | static void isp_disable_lun(ispsoftc_t *, union ccb *); | |
| 806 | static int isp_enable_target_mode(ispsoftc_t *, int); | |
| 807 | static void isp_ledone(ispsoftc_t *, lun_entry_t *); | |
| 984263bc MD |
808 | static timeout_t isp_refire_putback_atio; |
| 809 | static void isp_complete_ctio(union ccb *); | |
| 810 | static void isp_target_putback_atio(union ccb *); | |
| 191d7ec1 SW |
811 | static void isp_target_start_ctio(ispsoftc_t *, union ccb *); |
| 812 | static void isp_handle_platform_atio(ispsoftc_t *, at_entry_t *); | |
| 813 | static void isp_handle_platform_atio2(ispsoftc_t *, at2_entry_t *); | |
| 814 | static void isp_handle_platform_atio7(ispsoftc_t *, at7_entry_t *); | |
| 815 | static void isp_handle_platform_ctio(ispsoftc_t *, void *); | |
| 816 | static void isp_handle_platform_notify_scsi(ispsoftc_t *, in_entry_t *); | |
| 817 | static void isp_handle_platform_notify_fc(ispsoftc_t *, in_fcentry_t *); | |
| 818 | static void isp_handle_platform_notify_24xx(ispsoftc_t *, in_fcentry_24xx_t *); | |
| 819 | static int isp_handle_platform_target_notify_ack(ispsoftc_t *, isp_notify_t *); | |
| 820 | static void isp_handle_platform_target_tmf(ispsoftc_t *, isp_notify_t *); | |
| 821 | static void isp_target_mark_aborted(ispsoftc_t *, union ccb *); | |
| 822 | static void isp_target_mark_aborted_early(ispsoftc_t *, tstate_t *, uint32_t); | |
| 823 | ||
| 824 | static ISP_INLINE int | |
| 825 | is_lun_enabled(ispsoftc_t *isp, int bus, lun_id_t lun) | |
| 984263bc MD |
826 | { |
| 827 | tstate_t *tptr; | |
| 191d7ec1 SW |
828 | struct tslist *lhp; |
| 829 | ||
| 830 | ISP_GET_PC_ADDR(isp, bus, lun_hash[LUN_HASH_FUNC(lun)], lhp); | |
| 831 | SLIST_FOREACH(tptr, lhp, next) { | |
| 832 | if (xpt_path_lun_id(tptr->owner) == lun) { | |
| 984263bc MD |
833 | return (1); |
| 834 | } | |
| 191d7ec1 | 835 | } |
| 984263bc MD |
836 | return (0); |
| 837 | } | |
| 838 | ||
| 191d7ec1 SW |
839 | static void |
| 840 | dump_tstates(ispsoftc_t *isp, int bus) | |
| 984263bc | 841 | { |
| 191d7ec1 SW |
842 | int i, j; |
| 843 | struct tslist *lhp; | |
| 844 | tstate_t *tptr = NULL; | |
| 845 | ||
| 846 | if (bus >= isp->isp_nchan) { | |
| 847 | return; | |
| 984263bc | 848 | } |
| 191d7ec1 SW |
849 | for (i = 0; i < LUN_HASH_SIZE; i++) { |
| 850 | ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); | |
| 851 | j = 0; | |
| 852 | SLIST_FOREACH(tptr, lhp, next) { | |
| 853 | xpt_print(tptr->owner, "[%d, %d] atio_cnt=%d inot_cnt=%d\n", i, j, tptr->atio_count, tptr->inot_count); | |
| 854 | j++; | |
| 984263bc MD |
855 | } |
| 856 | } | |
| 984263bc MD |
857 | } |
| 858 | ||
| 191d7ec1 SW |
859 | static ISP_INLINE tstate_t * |
| 860 | get_lun_statep(ispsoftc_t *isp, int bus, lun_id_t lun) | |
| 984263bc MD |
861 | { |
| 862 | tstate_t *tptr = NULL; | |
| 191d7ec1 SW |
863 | struct tslist *lhp; |
| 864 | int i; | |
| 865 | ||
| 866 | if (bus < isp->isp_nchan) { | |
| 867 | for (i = 0; i < LUN_HASH_SIZE; i++) { | |
| 868 | ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); | |
| 869 | SLIST_FOREACH(tptr, lhp, next) { | |
| 870 | if (xpt_path_lun_id(tptr->owner) == lun) { | |
| 871 | tptr->hold++; | |
| 872 | return (tptr); | |
| 873 | } | |
| 874 | } | |
| 984263bc MD |
875 | } |
| 876 | } | |
| 191d7ec1 SW |
877 | return (NULL); |
| 878 | } | |
| 984263bc | 879 | |
| 191d7ec1 SW |
880 | static ISP_INLINE tstate_t * |
| 881 | get_lun_statep_from_tag(ispsoftc_t *isp, int bus, uint32_t tagval) | |
| 882 | { | |
| 883 | tstate_t *tptr = NULL; | |
| 884 | atio_private_data_t *atp; | |
| 885 | struct tslist *lhp; | |
| 886 | int i; | |
| 887 | ||
| 888 | if (bus < isp->isp_nchan && tagval != 0) { | |
| 889 | for (i = 0; i < LUN_HASH_SIZE; i++) { | |
| 890 | ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); | |
| 891 | SLIST_FOREACH(tptr, lhp, next) { | |
| 892 | atp = isp_get_atpd(isp, tptr, tagval); | |
| 893 | if (atp && atp->tag == tagval) { | |
| 894 | tptr->hold++; | |
| 895 | return (tptr); | |
| 896 | } | |
| 897 | } | |
| 984263bc | 898 | } |
| 191d7ec1 SW |
899 | } |
| 900 | return (NULL); | |
| 984263bc MD |
901 | } |
| 902 | ||
| 191d7ec1 SW |
903 | static ISP_INLINE inot_private_data_t * |
| 904 | get_ntp_from_tagdata(ispsoftc_t *isp, uint32_t tag_id, uint32_t seq_id, tstate_t **rslt) | |
| 905 | { | |
| 906 | inot_private_data_t *ntp; | |
| 907 | tstate_t *tptr; | |
| 908 | struct tslist *lhp; | |
| 909 | int bus, i; | |
| 910 | ||
| 911 | for (bus = 0; bus < isp->isp_nchan; bus++) { | |
| 912 | for (i = 0; i < LUN_HASH_SIZE; i++) { | |
| 913 | ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); | |
| 914 | SLIST_FOREACH(tptr, lhp, next) { | |
| 915 | ntp = isp_find_ntpd(isp, tptr, tag_id, seq_id); | |
| 916 | if (ntp) { | |
| 917 | *rslt = tptr; | |
| 918 | tptr->hold++; | |
| 919 | return (ntp); | |
| 920 | } | |
| 921 | } | |
| 922 | } | |
| 923 | } | |
| 924 | return (NULL); | |
| 925 | } | |
| 926 | static ISP_INLINE void | |
| 927 | rls_lun_statep(ispsoftc_t *isp, tstate_t *tptr) | |
| 984263bc | 928 | { |
| 191d7ec1 SW |
929 | KASSERT((tptr->hold), ("tptr not held")); |
| 930 | tptr->hold--; | |
| 984263bc MD |
931 | } |
| 932 | ||
| 191d7ec1 SW |
933 | static void |
| 934 | isp_tmcmd_restart(ispsoftc_t *isp) | |
| 984263bc | 935 | { |
| 191d7ec1 SW |
936 | inot_private_data_t *ntp; |
| 937 | tstate_t *tptr; | |
| 938 | struct tslist *lhp; | |
| 939 | int bus, i; | |
| 940 | ||
| 941 | for (bus = 0; bus < isp->isp_nchan; bus++) { | |
| 942 | for (i = 0; i < LUN_HASH_SIZE; i++) { | |
| 943 | ISP_GET_PC_ADDR(isp, bus, lun_hash[i], lhp); | |
| 944 | SLIST_FOREACH(tptr, lhp, next) { | |
| 945 | inot_private_data_t *restart_queue = tptr->restart_queue; | |
| 946 | tptr->restart_queue = NULL; | |
| 947 | while (restart_queue) { | |
| 948 | ntp = restart_queue; | |
| 949 | restart_queue = ntp->rd.nt.nt_hba; | |
| 950 | if (IS_24XX(isp)) { | |
| 951 | isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at7_entry_t *)ntp->rd.data)->at_rxid); | |
| 952 | isp_handle_platform_atio7(isp, (at7_entry_t *) ntp->rd.data); | |
| 953 | } else { | |
| 954 | isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at2_entry_t *)ntp->rd.data)->at_rxid); | |
| 955 | isp_handle_platform_atio2(isp, (at2_entry_t *) ntp->rd.data); | |
| 956 | } | |
| 957 | isp_put_ntpd(isp, tptr, ntp); | |
| 958 | if (tptr->restart_queue && restart_queue != NULL) { | |
| 959 | ntp = tptr->restart_queue; | |
| 960 | tptr->restart_queue = restart_queue; | |
| 961 | while (restart_queue->rd.nt.nt_hba) { | |
| 962 | restart_queue = restart_queue->rd.nt.nt_hba; | |
| 963 | } | |
| 964 | restart_queue->rd.nt.nt_hba = ntp; | |
| 965 | break; | |
| 966 | } | |
| 967 | } | |
| 968 | } | |
| 984263bc | 969 | } |
| 984263bc | 970 | } |
| 984263bc MD |
971 | } |
| 972 | ||
| 191d7ec1 SW |
973 | static ISP_INLINE atio_private_data_t * |
| 974 | isp_get_atpd(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag) | |
| 984263bc | 975 | { |
| 191d7ec1 SW |
976 | atio_private_data_t *atp; |
| 977 | ||
| 978 | if (tag == 0) { | |
| 979 | atp = tptr->atfree; | |
| 980 | if (atp) { | |
| 981 | tptr->atfree = atp->next; | |
| 982 | } | |
| 983 | return (atp); | |
| 984263bc | 984 | } |
| 191d7ec1 SW |
985 | for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) { |
| 986 | if (atp->tag == tag) { | |
| 987 | return (atp); | |
| 988 | } | |
| 989 | } | |
| 990 | return (NULL); | |
| 991 | } | |
| 992 | ||
| 993 | static ISP_INLINE void | |
| 994 | isp_put_atpd(ispsoftc_t *isp, tstate_t *tptr, atio_private_data_t *atp) | |
| 995 | { | |
| 996 | atp->tag = 0; | |
| 997 | atp->dead = 0; | |
| 998 | atp->next = tptr->atfree; | |
| 999 | tptr->atfree = atp; | |
| 984263bc MD |
1000 | } |
| 1001 | ||
| 191d7ec1 SW |
1002 | static void |
| 1003 | isp_dump_atpd(ispsoftc_t *isp, tstate_t *tptr) | |
| 984263bc | 1004 | { |
| 191d7ec1 SW |
1005 | atio_private_data_t *atp; |
| 1006 | const char *states[8] = { "Free", "ATIO", "CAM", "CTIO", "LAST_CTIO", "PDON", "?6", "7" }; | |
| 1007 | ||
| 1008 | for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) { | |
| 1009 | if (atp->tag == 0) { | |
| 1010 | continue; | |
| 1011 | } | |
| 1012 | xpt_print(tptr->owner, "ATP: [0x%x] origdlen %u bytes_xfrd %u last_xfr %u lun %u nphdl 0x%04x s_id 0x%06x d_id 0x%06x oxid 0x%04x state %s\n", | |
| 1013 | atp->tag, atp->orig_datalen, atp->bytes_xfered, atp->last_xframt, atp->lun, atp->nphdl, atp->sid, atp->portid, atp->oxid, states[atp->state & 0x7]); | |
| 1014 | } | |
| 984263bc MD |
1015 | } |
| 1016 | ||
| 191d7ec1 SW |
1017 | |
| 1018 | static ISP_INLINE inot_private_data_t * | |
| 1019 | isp_get_ntpd(ispsoftc_t *isp, tstate_t *tptr) | |
| 984263bc | 1020 | { |
| 191d7ec1 SW |
1021 | inot_private_data_t *ntp; |
| 1022 | ntp = tptr->ntfree; | |
| 1023 | if (ntp) { | |
| 1024 | tptr->ntfree = ntp->next; | |
| 984263bc | 1025 | } |
| 191d7ec1 | 1026 | return (ntp); |
| 984263bc MD |
1027 | } |
| 1028 | ||
| 191d7ec1 SW |
1029 | static ISP_INLINE inot_private_data_t * |
| 1030 | isp_find_ntpd(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag_id, uint32_t seq_id) | |
| 984263bc | 1031 | { |
| 191d7ec1 SW |
1032 | inot_private_data_t *ntp; |
| 1033 | for (ntp = tptr->ntpool; ntp < &tptr->ntpool[ATPDPSIZE]; ntp++) { | |
| 1034 | if (ntp->rd.tag_id == tag_id && ntp->rd.seq_id == seq_id) { | |
| 1035 | return (ntp); | |
| 1036 | } | |
| 984263bc MD |
1037 | } |
| 1038 | return (NULL); | |
| 1039 | } | |
| 1040 | ||
| 191d7ec1 SW |
1041 | static ISP_INLINE void |
| 1042 | isp_put_ntpd(ispsoftc_t *isp, tstate_t *tptr, inot_private_data_t *ntp) | |
| 1043 | { | |
| 1044 | ntp->rd.tag_id = ntp->rd.seq_id = 0; | |
| 1045 | ntp->next = tptr->ntfree; | |
| 1046 | tptr->ntfree = ntp; | |
| 1047 | } | |
| 1048 | ||
| 984263bc | 1049 | static cam_status |
| 191d7ec1 | 1050 | create_lun_state(ispsoftc_t *isp, int bus, struct cam_path *path, tstate_t **rslt) |
| 984263bc MD |
1051 | { |
| 1052 | cam_status status; | |
| 1053 | lun_id_t lun; | |
| 191d7ec1 SW |
1054 | struct tslist *lhp; |
| 1055 | tstate_t *tptr; | |
| 1056 | int i; | |
| 984263bc MD |
1057 | |
| 1058 | lun = xpt_path_lun_id(path); | |
| 191d7ec1 SW |
1059 | if (lun != CAM_LUN_WILDCARD) { |
| 1060 | if (lun >= ISP_MAX_LUNS(isp)) { | |
| 1061 | return (CAM_LUN_INVALID); | |
| 1062 | } | |
| 984263bc MD |
1063 | } |
| 1064 | if (is_lun_enabled(isp, bus, lun)) { | |
| 1065 | return (CAM_LUN_ALRDY_ENA); | |
| 1066 | } | |
| 191d7ec1 SW |
1067 | tptr = kmalloc(sizeof (tstate_t), M_DEVBUF, M_WAITOK | M_ZERO); |
| 1068 | status = xpt_create_path(&tptr->owner, NULL, xpt_path_path_id(path), xpt_path_target_id(path), lun); | |
| 984263bc | 1069 | if (status != CAM_REQ_CMP) { |
| 191d7ec1 | 1070 | kfree(tptr, M_DEVBUF); |
| 984263bc MD |
1071 | return (status); |
| 1072 | } | |
| 191d7ec1 SW |
1073 | SLIST_INIT(&tptr->atios); |
| 1074 | SLIST_INIT(&tptr->inots); | |
| 1075 | for (i = 0; i < ATPDPSIZE-1; i++) { | |
| 1076 | tptr->atpool[i].next = &tptr->atpool[i+1]; | |
| 1077 | tptr->ntpool[i].next = &tptr->ntpool[i+1]; | |
| 984263bc | 1078 | } |
| 191d7ec1 SW |
1079 | tptr->atfree = tptr->atpool; |
| 1080 | tptr->ntfree = tptr->ntpool; | |
| 1081 | tptr->hold = 1; | |
| 1082 | ISP_GET_PC_ADDR(isp, bus, lun_hash[LUN_HASH_FUNC(xpt_path_lun_id(tptr->owner))], lhp); | |
| 1083 | SLIST_INSERT_HEAD(lhp, tptr, next); | |
| 1084 | *rslt = tptr; | |
| 1085 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, path, "created tstate\n"); | |
| 984263bc MD |
1086 | return (CAM_REQ_CMP); |
| 1087 | } | |
| 1088 | ||
| 191d7ec1 SW |
1089 | static ISP_INLINE void |
| 1090 | destroy_lun_state(ispsoftc_t *isp, tstate_t *tptr) | |
| 984263bc | 1091 | { |
| 191d7ec1 SW |
1092 | struct tslist *lhp; |
| 1093 | KASSERT((tptr->hold == 0), ("tptr still held")); | |
| 1094 | ISP_GET_PC_ADDR(isp, xpt_path_path_id(tptr->owner), lun_hash[LUN_HASH_FUNC(xpt_path_lun_id(tptr->owner))], lhp); | |
| 1095 | SLIST_REMOVE(lhp, tptr, tstate, next); | |
| 1096 | xpt_free_path(tptr->owner); | |
| efda3bd0 | 1097 | kfree(tptr, M_DEVBUF); |
| 984263bc MD |
1098 | } |
| 1099 | ||
| 1100 | /* | |
| 191d7ec1 | 1101 | * Enable a lun. |
| 984263bc MD |
1102 | */ |
| 1103 | static void | |
| 191d7ec1 | 1104 | isp_enable_lun(ispsoftc_t *isp, union ccb *ccb) |
| 984263bc | 1105 | { |
| 191d7ec1 SW |
1106 | tstate_t *tptr = NULL; |
| 1107 | int bus, tm_enabled, target_role; | |
| 1108 | target_id_t target; | |
| 984263bc | 1109 | lun_id_t lun; |
| 984263bc MD |
1110 | |
| 1111 | /* | |
| 191d7ec1 | 1112 | * We only support either a wildcard target/lun or a target ID of zero and a non-wildcard lun |
| 984263bc | 1113 | */ |
| 191d7ec1 SW |
1114 | bus = XS_CHANNEL(ccb); |
| 1115 | target = ccb->ccb_h.target_id; | |
| 1116 | lun = ccb->ccb_h.target_lun; | |
| 1117 | if (target != CAM_TARGET_WILDCARD && target != 0) { | |
| 1118 | ccb->ccb_h.status = CAM_TID_INVALID; | |
| 1119 | xpt_done(ccb); | |
| 1120 | return; | |
| 1121 | } | |
| 1122 | if (target == CAM_TARGET_WILDCARD && lun != CAM_LUN_WILDCARD) { | |
| 1123 | ccb->ccb_h.status = CAM_LUN_INVALID; | |
| 1124 | xpt_done(ccb); | |
| 1125 | return; | |
| 1126 | } | |
| 984263bc | 1127 | |
| 191d7ec1 | 1128 | if (target != CAM_TARGET_WILDCARD && lun == CAM_LUN_WILDCARD) { |
| 984263bc | 1129 | ccb->ccb_h.status = CAM_LUN_INVALID; |
| 191d7ec1 | 1130 | xpt_done(ccb); |
| 984263bc MD |
1131 | return; |
| 1132 | } | |
| 191d7ec1 SW |
1133 | if (isp->isp_dblev & ISP_LOGTDEBUG0) { |
| 1134 | xpt_print(ccb->ccb_h.path, "enabling lun 0x%x on channel %d\n", lun, bus); | |
| 1135 | } | |
| 984263bc | 1136 | |
| 191d7ec1 SW |
1137 | /* |
| 1138 | * Wait until we're not busy with the lun enables subsystem | |
| 1139 | */ | |
| 1140 | while (isp->isp_osinfo.tmbusy) { | |
| 1141 | isp->isp_osinfo.tmwanted = 1; | |
| 1142 | mtx_sleep(isp, &isp->isp_lock, 0, "want_isp_enable_lun", 0); | |
| 1143 | } | |
| 1144 | isp->isp_osinfo.tmbusy = 1; | |
| 1145 | ||
| 1146 | /* | |
| 1147 | * This is as a good a place as any to check f/w capabilities. | |
| 1148 | */ | |
| 1149 | ||
| 1150 | if (IS_FC(isp)) { | |
| 1151 | if (ISP_CAP_TMODE(isp) == 0) { | |
| 1152 | xpt_print(ccb->ccb_h.path, "firmware does not support target mode\n"); | |
| 984263bc | 1153 | ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; |
| 191d7ec1 | 1154 | goto done; |
| 984263bc MD |
1155 | } |
| 1156 | /* | |
| 191d7ec1 SW |
1157 | * We *could* handle non-SCCLUN f/w, but we'd have to |
| 1158 | * dork with our already fragile enable/disable code. | |
| 984263bc | 1159 | */ |
| 191d7ec1 SW |
1160 | if (ISP_CAP_SCCFW(isp) == 0) { |
| 1161 | xpt_print(ccb->ccb_h.path, "firmware not SCCLUN capable\n"); | |
| 1162 | ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; | |
| 1163 | goto done; | |
| 984263bc | 1164 | } |
| 984263bc | 1165 | |
| 191d7ec1 SW |
1166 | target_role = (FCPARAM(isp, bus)->role & ISP_ROLE_TARGET) != 0; |
| 1167 | ||
| 984263bc | 1168 | } else { |
| 191d7ec1 | 1169 | target_role = (SDPARAM(isp, bus)->role & ISP_ROLE_TARGET) != 0; |
| 984263bc MD |
1170 | } |
| 1171 | ||
| 1172 | /* | |
| 191d7ec1 SW |
1173 | * Create the state pointer. |
| 1174 | * It should not already exist. | |
| 984263bc | 1175 | */ |
| 191d7ec1 SW |
1176 | tptr = get_lun_statep(isp, bus, lun); |
| 1177 | if (tptr) { | |
| 1178 | ccb->ccb_h.status = CAM_LUN_ALRDY_ENA; | |
| 1179 | goto done; | |
| 1180 | } | |
| 1181 | ccb->ccb_h.status = create_lun_state(isp, bus, ccb->ccb_h.path, &tptr); | |
| 1182 | if (ccb->ccb_h.status != CAM_REQ_CMP) { | |
| 1183 | goto done; | |
| 984263bc MD |
1184 | } |
| 1185 | ||
| 1186 | /* | |
| 191d7ec1 SW |
1187 | * We have a tricky maneuver to perform here. |
| 1188 | * | |
| 1189 | * If target mode isn't already enabled here, | |
| 1190 | * *and* our current role includes target mode, | |
| 1191 | * we enable target mode here. | |
| 1192 | * | |
| 984263bc | 1193 | */ |
| 191d7ec1 SW |
1194 | ISP_GET_PC(isp, bus, tm_enabled, tm_enabled); |
| 1195 | if (tm_enabled == 0 && target_role != 0) { | |
| 1196 | if (isp_enable_target_mode(isp, bus)) { | |
| 1197 | ccb->ccb_h.status = CAM_REQ_CMP_ERR; | |
| 1198 | destroy_lun_state(isp, tptr); | |
| 1199 | tptr = NULL; | |
| 1200 | goto done; | |
| 984263bc | 1201 | } |
| 191d7ec1 | 1202 | tm_enabled = 1; |
| 984263bc MD |
1203 | } |
| 1204 | ||
| 191d7ec1 SW |
1205 | /* |
| 1206 | * Now check to see whether this bus is in target mode already. | |
| 1207 | * | |
| 1208 | * If not, a later role change into target mode will finish the job. | |
| 1209 | */ | |
| 1210 | if (tm_enabled == 0) { | |
| 1211 | ISP_SET_PC(isp, bus, tm_enable_defer, 1); | |
| 984263bc | 1212 | ccb->ccb_h.status = CAM_REQ_CMP; |
| 191d7ec1 SW |
1213 | xpt_print(ccb->ccb_h.path, "Target Mode Not Enabled Yet- Lun Enables Deferred\n"); |
| 1214 | goto done; | |
| 984263bc MD |
1215 | } |
| 1216 | ||
| 191d7ec1 SW |
1217 | /* |
| 1218 | * Enable the lun. | |
| 1219 | */ | |
| 1220 | ccb->ccb_h.status = isp_enable_deferred(isp, bus, lun); | |
| 984263bc | 1221 | |
| 191d7ec1 SW |
1222 | done: |
| 1223 | if (ccb->ccb_h.status != CAM_REQ_CMP && tptr) { | |
| 1224 | destroy_lun_state(isp, tptr); | |
| 1225 | tptr = NULL; | |
| 1226 | } | |
| 1227 | if (tptr) { | |
| 984263bc | 1228 | rls_lun_statep(isp, tptr); |
| 984263bc | 1229 | } |
| 191d7ec1 SW |
1230 | isp->isp_osinfo.tmbusy = 0; |
| 1231 | if (isp->isp_osinfo.tmwanted) { | |
| 1232 | isp->isp_osinfo.tmwanted = 0; | |
| 1233 | wakeup(isp); | |
| 1234 | } | |
| 1235 | xpt_done(ccb); | |
| 1236 | } | |
| 984263bc | 1237 | |
| 191d7ec1 SW |
1238 | static void |
| 1239 | isp_enable_deferred_luns(ispsoftc_t *isp, int bus) | |
| 1240 | { | |
| 1241 | /* | |
| 1242 | * XXX: not entirely implemented yet | |
| 1243 | */ | |
| 1244 | (void) isp_enable_deferred(isp, bus, 0); | |
| 1245 | } | |
| 984263bc | 1246 | |
| 191d7ec1 SW |
1247 | static uint32_t |
| 1248 | isp_enable_deferred(ispsoftc_t *isp, int bus, lun_id_t lun) | |
| 1249 | { | |
| 1250 | cam_status status; | |
| 984263bc | 1251 | |
| 191d7ec1 SW |
1252 | isp_prt(isp, ISP_LOGTINFO, "%s: bus %d lun %u", __func__, bus, lun); |
| 1253 | if (IS_24XX(isp) || (IS_FC(isp) && ISP_FC_PC(isp, bus)->tm_luns_enabled)) { | |
| 1254 | status = CAM_REQ_CMP; | |
| 1255 | } else { | |
| 1256 | int cmd_cnt, not_cnt; | |
| 984263bc | 1257 | |
| 191d7ec1 SW |
1258 | if (IS_23XX(isp)) { |
| 1259 | cmd_cnt = DFLT_CMND_CNT; | |
| 1260 | not_cnt = DFLT_INOT_CNT; | |
| 1261 | } else { | |
| 1262 | cmd_cnt = 64; | |
| 1263 | not_cnt = 8; | |
| 984263bc | 1264 | } |
| 191d7ec1 SW |
1265 | status = CAM_REQ_INPROG; |
| 1266 | isp->isp_osinfo.rptr = &status; | |
| 1267 | if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, lun, DFLT_CMND_CNT, DFLT_INOT_CNT)) { | |
| 1268 | status = CAM_RESRC_UNAVAIL; | |
| 1269 | } else { | |
| 1270 | mtx_sleep(&status, &isp->isp_lock, PRIBIO, "isp_enable_deferred", 0); | |
| 984263bc | 1271 | } |
| 191d7ec1 SW |
1272 | isp->isp_osinfo.rptr = NULL; |
| 1273 | } | |
| 984263bc | 1274 | |
| 191d7ec1 SW |
1275 | if (status == CAM_REQ_CMP) { |
| 1276 | ISP_SET_PC(isp, bus, tm_luns_enabled, 1); | |
| 1277 | isp_prt(isp, ISP_LOGTINFO, "bus %d lun %u now enabled for target mode", bus, lun); | |
| 984263bc | 1278 | } |
| 191d7ec1 SW |
1279 | return (status); |
| 1280 | } | |
| 984263bc | 1281 | |
| 191d7ec1 SW |
1282 | static void |
| 1283 | isp_disable_lun(ispsoftc_t *isp, union ccb *ccb) | |
| 1284 | { | |
| 1285 | tstate_t *tptr = NULL; | |
| 1286 | int bus; | |
| 1287 | cam_status status; | |
| 1288 | target_id_t target; | |
| 1289 | lun_id_t lun; | |
| 984263bc | 1290 | |
| 191d7ec1 SW |
1291 | bus = XS_CHANNEL(ccb); |
| 1292 | target = ccb->ccb_h.target_id; | |
| 1293 | lun = ccb->ccb_h.target_lun; | |
| 1294 | if (target != CAM_TARGET_WILDCARD && target != 0) { | |
| 1295 | ccb->ccb_h.status = CAM_TID_INVALID; | |
| 1296 | xpt_done(ccb); | |
| 1297 | return; | |
| 1298 | } | |
| 1299 | if (target == CAM_TARGET_WILDCARD && lun != CAM_LUN_WILDCARD) { | |
| 1300 | ccb->ccb_h.status = CAM_LUN_INVALID; | |
| 1301 | xpt_done(ccb); | |
| 1302 | return; | |
| 1303 | } | |
| 1304 | ||
| 1305 | if (target != CAM_TARGET_WILDCARD && lun == CAM_LUN_WILDCARD) { | |
| 1306 | ccb->ccb_h.status = CAM_LUN_INVALID; | |
| 1307 | xpt_done(ccb); | |
| 1308 | return; | |
| 1309 | } | |
| 1310 | if (isp->isp_dblev & ISP_LOGTDEBUG0) { | |
| 1311 | xpt_print(ccb->ccb_h.path, "enabling lun 0x%x on channel %d\n", lun, bus); | |
| 1312 | } | |
| 1313 | ||
| 1314 | /* | |
| 1315 | * See if we're busy disabling a lun now. | |
| 1316 | */ | |
| 1317 | while (isp->isp_osinfo.tmbusy) { | |
| 1318 | isp->isp_osinfo.tmwanted = 1; | |
| 1319 | mtx_sleep(isp, &isp->isp_lock, PRIBIO, "want_isp_disable_lun", 0); | |
| 1320 | } | |
| 1321 | isp->isp_osinfo.tmbusy = 1; | |
| 1322 | ||
| 1323 | /* | |
| 1324 | * Find the state pointer. | |
| 1325 | */ | |
| 1326 | if ((tptr = get_lun_statep(isp, bus, lun)) == NULL) { | |
| 1327 | ccb->ccb_h.status = CAM_PATH_INVALID; | |
| 1328 | goto done; | |
| 1329 | } | |
| 1330 | ||
| 1331 | /* | |
| 1332 | * If we're a 24XX card, we're done. | |
| 1333 | */ | |
| 1334 | if (IS_24XX(isp)) { | |
| 1335 | status = CAM_REQ_CMP; | |
| 1336 | goto done; | |
| 1337 | } | |
| 1338 | ||
| 1339 | /* | |
| 1340 | * For SCC FW, we only deal with lun zero. | |
| 1341 | */ | |
| 1342 | if (IS_FC(isp)) { | |
| 1343 | lun = 0; | |
| 1344 | } | |
| 1345 | ||
| 1346 | isp->isp_osinfo.rptr = &status; | |
| 1347 | status = CAM_REQ_INPROG; | |
| 1348 | if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, lun, 0, 0)) { | |
| 1349 | status = CAM_RESRC_UNAVAIL; | |
| 984263bc | 1350 | } else { |
| 191d7ec1 SW |
1351 | mtx_sleep(ccb, &isp->isp_lock, PRIBIO, "isp_disable_lun", 0); |
| 1352 | } | |
| 1353 | done: | |
| 1354 | if (status == CAM_REQ_CMP) { | |
| 1355 | xpt_print(ccb->ccb_h.path, "now disabled for target mode\n"); | |
| 1356 | } | |
| 1357 | if (tptr) { | |
| 1358 | destroy_lun_state(isp, tptr); | |
| 984263bc | 1359 | } |
| 191d7ec1 SW |
1360 | isp->isp_osinfo.rptr = NULL; |
| 1361 | isp->isp_osinfo.tmbusy = 0; | |
| 1362 | if (isp->isp_osinfo.tmwanted) { | |
| 1363 | isp->isp_osinfo.tmwanted = 0; | |
| 1364 | wakeup(isp); | |
| 1365 | } | |
| 1366 | xpt_done(ccb); | |
| 984263bc MD |
1367 | } |
| 1368 | ||
| 191d7ec1 SW |
1369 | static int |
| 1370 | isp_enable_target_mode(ispsoftc_t *isp, int bus) | |
| 984263bc | 1371 | { |
| 191d7ec1 SW |
1372 | int ct; |
| 1373 | ||
| 1374 | ISP_GET_PC(isp, bus, tm_enabled, ct); | |
| 1375 | if (ct != 0) { | |
| 1376 | return (0); | |
| 1377 | } | |
| 984263bc | 1378 | |
| 191d7ec1 SW |
1379 | if (IS_SCSI(isp)) { |
| 1380 | mbreg_t mbs; | |
| 1381 | ||
| 1382 | MBSINIT(&mbs, MBOX_ENABLE_TARGET_MODE, MBLOGALL, 0); | |
| 1383 | mbs.param[0] = MBOX_ENABLE_TARGET_MODE; | |
| 1384 | mbs.param[1] = ENABLE_TARGET_FLAG|ENABLE_TQING_FLAG; | |
| 1385 | mbs.param[2] = bus << 7; | |
| 1386 | if (isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs) < 0 || mbs.param[0] != MBOX_COMMAND_COMPLETE) { | |
| 1387 | isp_prt(isp, ISP_LOGERR, "Unable to add Target Role to Bus %d", bus); | |
| 1388 | return (EIO); | |
| 984263bc | 1389 | } |
| 191d7ec1 | 1390 | SDPARAM(isp, bus)->role |= ISP_ROLE_TARGET; |
| 984263bc | 1391 | } |
| 191d7ec1 SW |
1392 | ISP_SET_PC(isp, bus, tm_enabled, 1); |
| 1393 | isp_prt(isp, ISP_LOGINFO, "Target Role added to Bus %d", bus); | |
| 1394 | return (0); | |
| 1395 | } | |
| 1396 | ||
| 1397 | #ifdef NEEDED | |
| 1398 | static int | |
| 1399 | isp_disable_target_mode(ispsoftc_t *isp, int bus) | |
| 1400 | { | |
| 1401 | int ct; | |
| 1402 | ||
| 1403 | ISP_GET_PC(isp, bus, tm_enabled, ct); | |
| 1404 | if (ct == 0) { | |
| 1405 | return (0); | |
| 984263bc | 1406 | } |
| 191d7ec1 SW |
1407 | |
| 1408 | if (IS_SCSI(isp)) { | |
| 1409 | mbreg_t mbs; | |
| 1410 | ||
| 1411 | MBSINIT(&mbs, MBOX_ENABLE_TARGET_MODE, MBLOGALL, 0); | |
| 1412 | mbs.param[2] = bus << 7; | |
| 1413 | if (isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs) < 0 || mbs.param[0] != MBOX_COMMAND_COMPLETE) { | |
| 1414 | isp_prt(isp, ISP_LOGERR, "Unable to subtract Target Role to Bus %d", bus); | |
| 1415 | return (EIO); | |
| 1416 | } | |
| 1417 | SDPARAM(isp, bus)->role &= ~ISP_ROLE_TARGET; | |
| 984263bc | 1418 | } |
| 191d7ec1 SW |
1419 | ISP_SET_PC(isp, bus, tm_enabled, 0); |
| 1420 | isp_prt(isp, ISP_LOGINFO, "Target Role subtracted from Bus %d", bus); | |
| 1421 | return (0); | |
| 1422 | } | |
| 1423 | #endif | |
| 1424 | ||
| 1425 | static void | |
| 1426 | isp_ledone(ispsoftc_t *isp, lun_entry_t *lep) | |
| 1427 | { | |
| 1428 | uint32_t *rptr; | |
| 1429 | ||
| 1430 | rptr = isp->isp_osinfo.rptr; | |
| 1431 | if (lep->le_status != LUN_OK) { | |
| 1432 | isp_prt(isp, ISP_LOGERR, "ENABLE/MODIFY LUN returned 0x%x", lep->le_status); | |
| 1433 | if (rptr) { | |
| 1434 | *rptr = CAM_REQ_CMP_ERR; | |
| 1435 | wakeup_one(rptr); | |
| 1436 | } | |
| 984263bc | 1437 | } else { |
| 191d7ec1 SW |
1438 | if (rptr) { |
| 1439 | *rptr = CAM_REQ_CMP; | |
| 1440 | wakeup_one(rptr); | |
| 984263bc MD |
1441 | } |
| 1442 | } | |
| 984263bc MD |
1443 | } |
| 1444 | ||
| 191d7ec1 SW |
1445 | static void |
| 1446 | isp_target_start_ctio(ispsoftc_t *isp, union ccb *ccb) | |
| 984263bc MD |
1447 | { |
| 1448 | void *qe; | |
| 191d7ec1 SW |
1449 | tstate_t *tptr; |
| 1450 | atio_private_data_t *atp; | |
| 984263bc | 1451 | struct ccb_scsiio *cso = &ccb->csio; |
| 191d7ec1 SW |
1452 | uint32_t dmaresult, handle; |
| 1453 | uint8_t local[QENTRY_LEN]; | |
| 1454 | ||
| 1455 | /* | |
| 1456 | * Do some sanity checks. | |
| 1457 | */ | |
| 1458 | if (cso->dxfer_len == 0) { | |
| 1459 | if ((ccb->ccb_h.flags & CAM_SEND_STATUS) == 0) { | |
| 1460 | xpt_print(ccb->ccb_h.path, "a data transfer length of zero but no status to send is wrong\n"); | |
| 1461 | ccb->ccb_h.status = CAM_REQ_INVALID; | |
| 1462 | xpt_done(ccb); | |
| 1463 | return; | |
| 1464 | } | |
| 1465 | } | |
| 1466 | ||
| 1467 | tptr = get_lun_statep(isp, XS_CHANNEL(ccb), XS_LUN(ccb)); | |
| 1468 | if (tptr == NULL) { | |
| 1469 | tptr = get_lun_statep(isp, XS_CHANNEL(ccb), CAM_LUN_WILDCARD); | |
| 1470 | if (tptr == NULL) { | |
| 1471 | xpt_print(ccb->ccb_h.path, "%s: [0x%x] cannot find tstate pointer in %s\n", __func__, cso->tag_id); | |
| 1472 | dump_tstates(isp, XS_CHANNEL(ccb)); | |
| 1473 | ccb->ccb_h.status = CAM_DEV_NOT_THERE; | |
| 1474 | xpt_done(ccb); | |
| 1475 | return; | |
| 1476 | } | |
| 1477 | } | |
| 1478 | ||
| 1479 | atp = isp_get_atpd(isp, tptr, cso->tag_id); | |
| 1480 | if (atp == NULL) { | |
| 1481 | xpt_print(ccb->ccb_h.path, "%s: [0x%x] cannot find private data adjunct\n", __func__, cso->tag_id); | |
| 1482 | isp_dump_atpd(isp, tptr); | |
| 1483 | ccb->ccb_h.status = CAM_REQ_CMP_ERR; | |
| 1484 | xpt_done(ccb); | |
| 1485 | return; | |
| 1486 | } | |
| 1487 | if (atp->dead) { | |
| 1488 | xpt_print(ccb->ccb_h.path, "%s: [0x%x] stopping sending a CTIO for a dead command\n", __func__, cso->tag_id); | |
| 1489 | ccb->ccb_h.status = CAM_REQ_ABORTED; | |
| 1490 | xpt_done(ccb); | |
| 1491 | return; | |
| 1492 | } | |
| 984263bc | 1493 | |
| 191d7ec1 SW |
1494 | /* |
| 1495 | * Check to make sure we're still in target mode. | |
| 1496 | */ | |
| 1497 | if ((FCPARAM(isp, XS_CHANNEL(ccb))->role & ISP_ROLE_TARGET) == 0) { | |
| 1498 | xpt_print(ccb->ccb_h.path, "%s: [0x%x] stopping sending a CTIO because we're no longer in target mode\n", __func__, cso->tag_id); | |
| 1499 | ccb->ccb_h.status = CAM_PROVIDE_FAIL; | |
| 1500 | xpt_done(ccb); | |
| 1501 | return; | |
| 1502 | } | |
| 984263bc | 1503 | |
| 191d7ec1 SW |
1504 | /* |
| 1505 | * Get some resources | |
| 1506 | */ | |
| 1507 | if (isp_get_pcmd(isp, ccb)) { | |
| 1508 | rls_lun_statep(isp, tptr); | |
| 1509 | xpt_print(ccb->ccb_h.path, "out of PCMDs\n"); | |
| 1510 | cam_freeze_devq(ccb->ccb_h.path); | |
| 1511 | cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 250, 0); | |
| 1512 | ccb->ccb_h.status = CAM_REQUEUE_REQ; | |
| 1513 | xpt_done(ccb); | |
| 1514 | return; | |
| 1515 | } | |
| 1516 | qe = isp_getrqentry(isp); | |
| 1517 | if (qe == NULL) { | |
| 1518 | xpt_print(ccb->ccb_h.path, rqo, __func__); | |
| 1519 | cam_freeze_devq(ccb->ccb_h.path); | |
| 1520 | cam_release_devq(ccb->ccb_h.path, RELSIM_RELEASE_AFTER_TIMEOUT, 0, 250, 0); | |
| 1521 | ccb->ccb_h.status = CAM_REQUEUE_REQ; | |
| 1522 | goto out; | |
| 984263bc | 1523 | } |
| 191d7ec1 | 1524 | memset(local, 0, QENTRY_LEN); |
| 984263bc MD |
1525 | |
| 1526 | /* | |
| 1527 | * We're either moving data or completing a command here. | |
| 1528 | */ | |
| 191d7ec1 SW |
1529 | if (IS_24XX(isp)) { |
| 1530 | ct7_entry_t *cto = (ct7_entry_t *) local; | |
| 984263bc | 1531 | |
| 191d7ec1 SW |
1532 | cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7; |
| 1533 | cto->ct_header.rqs_entry_count = 1; | |
| 1534 | cto->ct_header.rqs_seqno = 1; | |
| 1535 | cto->ct_nphdl = atp->nphdl; | |
| 1536 | cto->ct_rxid = atp->tag; | |
| 1537 | cto->ct_iid_lo = atp->portid; | |
| 1538 | cto->ct_iid_hi = atp->portid >> 16; | |
| 1539 | cto->ct_oxid = atp->oxid; | |
| 1540 | cto->ct_vpidx = ISP_GET_VPIDX(isp, XS_CHANNEL(ccb)); | |
| 1541 | cto->ct_scsi_status = cso->scsi_status; | |
| 1542 | cto->ct_timeout = 120; | |
| 1543 | cto->ct_flags = atp->tattr << CT7_TASK_ATTR_SHIFT; | |
| 1544 | if (ccb->ccb_h.flags & CAM_SEND_STATUS) { | |
| 1545 | cto->ct_flags |= CT7_SENDSTATUS; | |
| 1546 | } | |
| 1547 | if (cso->dxfer_len == 0) { | |
| 1548 | cto->ct_flags |= CT7_FLAG_MODE1 | CT7_NO_DATA; | |
| 1549 | if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) { | |
| 1550 | int m = min(cso->sense_len, sizeof (struct scsi_sense_data)); | |
| 1551 | cto->rsp.m1.ct_resplen = cto->ct_senselen = min(m, MAXRESPLEN_24XX); | |
| 1552 | memcpy(cto->rsp.m1.ct_resp, &cso->sense_data, cto->ct_senselen); | |
| 1553 | cto->ct_scsi_status |= (FCP_SNSLEN_VALID << 8); | |
| 1554 | } | |
| 1555 | } else { | |
| 1556 | cto->ct_flags |= CT7_FLAG_MODE0; | |
| 1557 | if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { | |
| 1558 | cto->ct_flags |= CT7_DATA_IN; | |
| 1559 | } else { | |
| 1560 | cto->ct_flags |= CT7_DATA_OUT; | |
| 1561 | } | |
| 1562 | cto->rsp.m0.reloff = atp->bytes_xfered; | |
| 1563 | /* | |
| 1564 | * Don't overrun the limits placed on us | |
| 1565 | */ | |
| 1566 | if (atp->bytes_xfered + cso->dxfer_len > atp->orig_datalen) { | |
| 1567 | cso->dxfer_len = atp->orig_datalen - atp->bytes_xfered; | |
| 1568 | } | |
| 1569 | atp->last_xframt = cso->dxfer_len; | |
| 1570 | cto->rsp.m0.ct_xfrlen = cso->dxfer_len; | |
| 1571 | } | |
| 1572 | if (cto->ct_flags & CT7_SENDSTATUS) { | |
| 1573 | int lvl = (cso->scsi_status)? ISP_LOGTINFO : ISP_LOGTDEBUG0; | |
| 1574 | cto->ct_resid = atp->orig_datalen - (atp->bytes_xfered + cso->dxfer_len); | |
| 1575 | if (cto->ct_resid < 0) { | |
| 1576 | cto->ct_scsi_status |= (FCP_RESID_OVERFLOW << 8); | |
| 1577 | } else if (cto->ct_resid > 0) { | |
| 1578 | cto->ct_scsi_status |= (FCP_RESID_UNDERFLOW << 8); | |
| 1579 | } | |
| 1580 | atp->state = ATPD_STATE_LAST_CTIO; | |
| 1581 | ISP_PATH_PRT(isp, lvl, cso->ccb_h.path, "%s: CTIO7[%x] CDB0=%x scsi status %x flags %x resid %d xfrlen %u offset %u\n", __func__, cto->ct_rxid, | |
| 1582 | atp->cdb0, cto->ct_scsi_status, cto->ct_flags, cto->ct_resid, cso->dxfer_len, atp->bytes_xfered); | |
| 1583 | } else { | |
| 1584 | cto->ct_resid = 0; | |
| 1585 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, cso->ccb_h.path, "%s: CTIO7[%x] flags %x xfrlen %u offset %u\n", __func__, cto->ct_rxid, cto->ct_flags, | |
| 1586 | cso->dxfer_len, atp->bytes_xfered); | |
| 1587 | atp->state = ATPD_STATE_CTIO; | |
| 1588 | } | |
| 1589 | } else if (IS_FC(isp)) { | |
| 984263bc MD |
1590 | ct2_entry_t *cto = (ct2_entry_t *) local; |
| 1591 | ||
| 1592 | cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2; | |
| 1593 | cto->ct_header.rqs_entry_count = 1; | |
| 191d7ec1 SW |
1594 | cto->ct_header.rqs_seqno = 1; |
| 1595 | if (ISP_CAP_2KLOGIN(isp) == 0) { | |
| 1596 | ((ct2e_entry_t *)cto)->ct_iid = cso->init_id; | |
| 1597 | } else { | |
| 1598 | cto->ct_iid = cso->init_id; | |
| 1599 | if (ISP_CAP_SCCFW(isp) == 0) { | |
| 1600 | cto->ct_lun = ccb->ccb_h.target_lun; | |
| 1601 | } | |
| 984263bc MD |
1602 | } |
| 1603 | ||
| 984263bc MD |
1604 | |
| 1605 | cto->ct_rxid = cso->tag_id; | |
| 1606 | if (cso->dxfer_len == 0) { | |
| 191d7ec1 SW |
1607 | cto->ct_flags |= CT2_FLAG_MODE1 | CT2_NO_DATA | CT2_SENDSTATUS; |
| 1608 | cto->rsp.m1.ct_scsi_status = cso->scsi_status; | |
| 1609 | cto->ct_resid = atp->orig_datalen - atp->bytes_xfered; | |
| 1610 | if (cto->ct_resid < 0) { | |
| 1611 | cto->rsp.m1.ct_scsi_status |= CT2_DATA_OVER; | |
| 1612 | } else if (cto->ct_resid > 0) { | |
| 1613 | cto->rsp.m1.ct_scsi_status |= CT2_DATA_UNDER; | |
| 984263bc MD |
1614 | } |
| 1615 | if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) { | |
| 1616 | int m = min(cso->sense_len, MAXRESPLEN); | |
| 191d7ec1 | 1617 | memcpy(cto->rsp.m1.ct_resp, &cso->sense_data, m); |
| 984263bc MD |
1618 | cto->rsp.m1.ct_senselen = m; |
| 1619 | cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID; | |
| 191d7ec1 SW |
1620 | } else if (cso->scsi_status == SCSI_STATUS_CHECK_COND) { |
| 1621 | /* | |
| 1622 | * XXX: DEBUG | |
| 1623 | */ | |
| 1624 | xpt_print(ccb->ccb_h.path, "CHECK CONDITION being sent without associated SENSE DATA for CDB=0x%x\n", atp->cdb0); | |
| 984263bc MD |
1625 | } |
| 1626 | } else { | |
| 1627 | cto->ct_flags |= CT2_FLAG_MODE0; | |
| 1628 | if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { | |
| 1629 | cto->ct_flags |= CT2_DATA_IN; | |
| 1630 | } else { | |
| 1631 | cto->ct_flags |= CT2_DATA_OUT; | |
| 1632 | } | |
| 1633 | cto->ct_reloff = atp->bytes_xfered; | |
| 191d7ec1 SW |
1634 | cto->rsp.m0.ct_xfrlen = cso->dxfer_len; |
| 1635 | /* | |
| 1636 | * Don't overrun the limits placed on us | |
| 1637 | */ | |
| 1638 | if (atp->bytes_xfered + cso->dxfer_len > atp->orig_datalen) { | |
| 1639 | cso->dxfer_len = atp->orig_datalen - atp->bytes_xfered; | |
| 1640 | } | |
| 984263bc MD |
1641 | if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { |
| 1642 | cto->ct_flags |= CT2_SENDSTATUS; | |
| 1643 | cto->rsp.m0.ct_scsi_status = cso->scsi_status; | |
| 191d7ec1 | 1644 | cto->ct_resid = atp->orig_datalen - (atp->bytes_xfered + cso->dxfer_len); |
| 984263bc | 1645 | if (cto->ct_resid < 0) { |
| 191d7ec1 | 1646 | cto->rsp.m0.ct_scsi_status |= CT2_DATA_OVER; |
| 984263bc | 1647 | } else if (cto->ct_resid > 0) { |
| 191d7ec1 | 1648 | cto->rsp.m0.ct_scsi_status |= CT2_DATA_UNDER; |
| 984263bc MD |
1649 | } |
| 1650 | } else { | |
| 1651 | atp->last_xframt = cso->dxfer_len; | |
| 1652 | } | |
| 1653 | /* | |
| 1654 | * If we're sending data and status back together, | |
| 1655 | * we can't also send back sense data as well. | |
| 1656 | */ | |
| 1657 | ccb->ccb_h.flags &= ~CAM_SEND_SENSE; | |
| 1658 | } | |
| 1659 | ||
| 1660 | if (cto->ct_flags & CT2_SENDSTATUS) { | |
| 191d7ec1 | 1661 | int lvl = (cso->scsi_status)? ISP_LOGTINFO : ISP_LOGTDEBUG0; |
| 984263bc MD |
1662 | cto->ct_flags |= CT2_CCINCR; |
| 1663 | atp->state = ATPD_STATE_LAST_CTIO; | |
| 191d7ec1 SW |
1664 | ISP_PATH_PRT(isp, lvl, cso->ccb_h.path, "%s: CTIO2[%x] CDB0=%x scsi status %x flags %x resid %d xfrlen %u offset %u\n", __func__, cto->ct_rxid, |
| 1665 | atp->cdb0, cto->rsp.m0.ct_scsi_status, cto->ct_flags, cto->ct_resid, cso->dxfer_len, atp->bytes_xfered); | |
| 1666 | } else { | |
| 1667 | cto->ct_resid = 0; | |
| 984263bc | 1668 | atp->state = ATPD_STATE_CTIO; |
| 191d7ec1 SW |
1669 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: CTIO2[%x] flags %x xfrlen %u offset %u\n", __func__, cto->ct_rxid, cto->ct_flags, |
| 1670 | cso->dxfer_len, atp->bytes_xfered); | |
| 1671 | } | |
| 984263bc | 1672 | cto->ct_timeout = 10; |
| 984263bc MD |
1673 | } else { |
| 1674 | ct_entry_t *cto = (ct_entry_t *) local; | |
| 1675 | ||
| 1676 | cto->ct_header.rqs_entry_type = RQSTYPE_CTIO; | |
| 1677 | cto->ct_header.rqs_entry_count = 1; | |
| 191d7ec1 | 1678 | cto->ct_header.rqs_seqno = 1; |
| 984263bc MD |
1679 | cto->ct_iid = cso->init_id; |
| 1680 | cto->ct_iid |= XS_CHANNEL(ccb) << 7; | |
| 1681 | cto->ct_tgt = ccb->ccb_h.target_id; | |
| 1682 | cto->ct_lun = ccb->ccb_h.target_lun; | |
| 191d7ec1 | 1683 | cto->ct_fwhandle = cso->tag_id >> 16; |
| 984263bc | 1684 | if (AT_HAS_TAG(cso->tag_id)) { |
| 191d7ec1 | 1685 | cto->ct_tag_val = cso->tag_id; |
| 984263bc MD |
1686 | cto->ct_flags |= CT_TQAE; |
| 1687 | } | |
| 1688 | if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) { | |
| 1689 | cto->ct_flags |= CT_NODISC; | |
| 1690 | } | |
| 1691 | if (cso->dxfer_len == 0) { | |
| 1692 | cto->ct_flags |= CT_NO_DATA; | |
| 1693 | } else if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { | |
| 1694 | cto->ct_flags |= CT_DATA_IN; | |
| 1695 | } else { | |
| 1696 | cto->ct_flags |= CT_DATA_OUT; | |
| 1697 | } | |
| 1698 | if (ccb->ccb_h.flags & CAM_SEND_STATUS) { | |
| 1699 | cto->ct_flags |= CT_SENDSTATUS|CT_CCINCR; | |
| 1700 | cto->ct_scsi_status = cso->scsi_status; | |
| 1701 | cto->ct_resid = cso->resid; | |
| 191d7ec1 SW |
1702 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "%s: CTIO[%x] scsi status %x resid %d tag_id %x\n", __func__, |
| 1703 | cto->ct_fwhandle, cso->scsi_status, cso->resid, cso->tag_id); | |
| 984263bc MD |
1704 | } |
| 1705 | ccb->ccb_h.flags &= ~CAM_SEND_SENSE; | |
| 1706 | cto->ct_timeout = 10; | |
| 984263bc MD |
1707 | } |
| 1708 | ||
| 191d7ec1 SW |
1709 | if (isp_allocate_xs_tgt(isp, ccb, &handle)) { |
| 1710 | xpt_print(ccb->ccb_h.path, "No XFLIST pointers for %s\n", __func__); | |
| 1711 | ccb->ccb_h.status = CAM_REQUEUE_REQ; | |
| 1712 | goto out; | |
| 984263bc MD |
1713 | } |
| 1714 | ||
| 1715 | ||
| 1716 | /* | |
| 1717 | * Call the dma setup routines for this entry (and any subsequent | |
| 1718 | * CTIOs) if there's data to move, and then tell the f/w it's got | |
| 1719 | * new things to play with. As with isp_start's usage of DMA setup, | |
| 1720 | * any swizzling is done in the machine dependent layer. Because | |
| 1721 | * of this, we put the request onto the queue area first in native | |
| 1722 | * format. | |
| 1723 | */ | |
| 1724 | ||
| 191d7ec1 SW |
1725 | if (IS_24XX(isp)) { |
| 1726 | ct7_entry_t *cto = (ct7_entry_t *) local; | |
| 1727 | cto->ct_syshandle = handle; | |
| 1728 | } else if (IS_FC(isp)) { | |
| 1729 | ct2_entry_t *cto = (ct2_entry_t *) local; | |
| 1730 | cto->ct_syshandle = handle; | |
| 1731 | } else { | |
| 1732 | ct_entry_t *cto = (ct_entry_t *) local; | |
| 1733 | cto->ct_syshandle = handle; | |
| 1734 | } | |
| 984263bc | 1735 | |
| 191d7ec1 SW |
1736 | dmaresult = ISP_DMASETUP(isp, cso, (ispreq_t *) local); |
| 1737 | if (dmaresult == CMD_QUEUED) { | |
| 1738 | isp->isp_nactive++; | |
| 1739 | ccb->ccb_h.status |= CAM_SIM_QUEUED; | |
| 1740 | rls_lun_statep(isp, tptr); | |
| 1741 | return; | |
| 984263bc | 1742 | } |
| 191d7ec1 SW |
1743 | if (dmaresult == CMD_EAGAIN) { |
| 1744 | ccb->ccb_h.status = CAM_REQUEUE_REQ; | |
| 1745 | } else { | |
| 1746 | ccb->ccb_h.status = CAM_REQ_CMP_ERR; | |
| 1747 | } | |
| 1748 | isp_destroy_tgt_handle(isp, handle); | |
| 1749 | out: | |
| 1750 | rls_lun_statep(isp, tptr); | |
| 1751 | isp_free_pcmd(isp, ccb); | |
| 1752 | xpt_done(ccb); | |
| 984263bc MD |
1753 | } |
| 1754 | ||
| 1755 | static void | |
| 1756 | isp_refire_putback_atio(void *arg) | |
| 1757 | { | |
| 191d7ec1 SW |
1758 | union ccb *ccb = arg; |
| 1759 | ispsoftc_t *isp = XS_ISP(ccb); | |
| 1760 | ISP_LOCK(isp); | |
| 1761 | isp_target_putback_atio(ccb); | |
| 1762 | ISP_UNLOCK(isp); | |
| 984263bc MD |
1763 | } |
| 1764 | ||
| 1765 | static void | |
| 1766 | isp_target_putback_atio(union ccb *ccb) | |
| 1767 | { | |
| 191d7ec1 | 1768 | ispsoftc_t *isp; |
| 984263bc | 1769 | struct ccb_scsiio *cso; |
| 984263bc MD |
1770 | void *qe; |
| 1771 | ||
| 1772 | isp = XS_ISP(ccb); | |
| 1773 | ||
| 191d7ec1 SW |
1774 | qe = isp_getrqentry(isp); |
| 1775 | if (qe == NULL) { | |
| 1776 | xpt_print(ccb->ccb_h.path, rqo, __func__); | |
| 984263bc | 1777 | (void) timeout(isp_refire_putback_atio, ccb, 10); |
| 984263bc MD |
1778 | return; |
| 1779 | } | |
| 191d7ec1 | 1780 | memset(qe, 0, QENTRY_LEN); |
| 984263bc MD |
1781 | cso = &ccb->csio; |
| 1782 | if (IS_FC(isp)) { | |
| 1783 | at2_entry_t local, *at = &local; | |
| 191d7ec1 | 1784 | ISP_MEMZERO(at, sizeof (at2_entry_t)); |
| 984263bc MD |
1785 | at->at_header.rqs_entry_type = RQSTYPE_ATIO2; |
| 1786 | at->at_header.rqs_entry_count = 1; | |
| 191d7ec1 | 1787 | if (ISP_CAP_SCCFW(isp)) { |
| 984263bc MD |
1788 | at->at_scclun = (uint16_t) ccb->ccb_h.target_lun; |
| 1789 | } else { | |
| 1790 | at->at_lun = (uint8_t) ccb->ccb_h.target_lun; | |
| 1791 | } | |
| 1792 | at->at_status = CT_OK; | |
| 1793 | at->at_rxid = cso->tag_id; | |
| 1794 | at->at_iid = cso->ccb_h.target_id; | |
| 1795 | isp_put_atio2(isp, at, qe); | |
| 1796 | } else { | |
| 1797 | at_entry_t local, *at = &local; | |
| 191d7ec1 | 1798 | ISP_MEMZERO(at, sizeof (at_entry_t)); |
| 984263bc MD |
1799 | at->at_header.rqs_entry_type = RQSTYPE_ATIO; |
| 1800 | at->at_header.rqs_entry_count = 1; | |
| 1801 | at->at_iid = cso->init_id; | |
| 1802 | at->at_iid |= XS_CHANNEL(ccb) << 7; | |
| 1803 | at->at_tgt = cso->ccb_h.target_id; | |
| 1804 | at->at_lun = cso->ccb_h.target_lun; | |
| 1805 | at->at_status = CT_OK; | |
| 1806 | at->at_tag_val = AT_GET_TAG(cso->tag_id); | |
| 1807 | at->at_handle = AT_GET_HANDLE(cso->tag_id); | |
| 1808 | isp_put_atio(isp, at, qe); | |
| 1809 | } | |
| 191d7ec1 SW |
1810 | ISP_TDQE(isp, "isp_target_putback_atio", isp->isp_reqidx, qe); |
| 1811 | ISP_SYNC_REQUEST(isp); | |
| 984263bc MD |
1812 | isp_complete_ctio(ccb); |
| 1813 | } | |
| 1814 | ||
| 1815 | static void | |
| 1816 | isp_complete_ctio(union ccb *ccb) | |
| 1817 | { | |
| 1818 | if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { | |
| 1819 | ccb->ccb_h.status |= CAM_REQ_CMP; | |
| 1820 | } | |
| 1821 | ccb->ccb_h.status &= ~CAM_SIM_QUEUED; | |
| 191d7ec1 | 1822 | isp_free_pcmd(XS_ISP(ccb), ccb); |
| 984263bc MD |
1823 | xpt_done(ccb); |
| 1824 | } | |
| 1825 | ||
| 1826 | /* | |
| 1827 | * Handle ATIO stuff that the generic code can't. | |
| 1828 | * This means handling CDBs. | |
| 1829 | */ | |
| 1830 | ||
| 191d7ec1 SW |
1831 | static void |
| 1832 | isp_handle_platform_atio(ispsoftc_t *isp, at_entry_t *aep) | |
| 984263bc MD |
1833 | { |
| 1834 | tstate_t *tptr; | |
| 191d7ec1 | 1835 | int status, bus; |
| 984263bc | 1836 | struct ccb_accept_tio *atiop; |
| 191d7ec1 | 1837 | atio_private_data_t *atp; |
| 984263bc MD |
1838 | |
| 1839 | /* | |
| 1840 | * The firmware status (except for the QLTM_SVALID bit) | |
| 1841 | * indicates why this ATIO was sent to us. | |
| 1842 | * | |
| 191d7ec1 | 1843 | * If QLTM_SVALID is set, the firmware has recommended Sense Data. |
| 984263bc MD |
1844 | * |
| 1845 | * If the DISCONNECTS DISABLED bit is set in the flags field, | |
| 1846 | * we're still connected on the SCSI bus. | |
| 1847 | */ | |
| 1848 | status = aep->at_status; | |
| 1849 | if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) { | |
| 1850 | /* | |
| 1851 | * Bus Phase Sequence error. We should have sense data | |
| 1852 | * suggested by the f/w. I'm not sure quite yet what | |
| 1853 | * to do about this for CAM. | |
| 1854 | */ | |
| 1855 | isp_prt(isp, ISP_LOGWARN, "PHASE ERROR"); | |
| 1856 | isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); | |
| 191d7ec1 | 1857 | return; |
| 984263bc MD |
1858 | } |
| 1859 | if ((status & ~QLTM_SVALID) != AT_CDB) { | |
| 191d7ec1 | 1860 | isp_prt(isp, ISP_LOGWARN, "bad atio (0x%x) leaked to platform", status); |
| 984263bc | 1861 | isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); |
| 191d7ec1 | 1862 | return; |
| 984263bc MD |
1863 | } |
| 1864 | ||
| 1865 | bus = GET_BUS_VAL(aep->at_iid); | |
| 1866 | tptr = get_lun_statep(isp, bus, aep->at_lun); | |
| 1867 | if (tptr == NULL) { | |
| 1868 | tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD); | |
| 191d7ec1 SW |
1869 | if (tptr == NULL) { |
| 1870 | /* | |
| 1871 | * Because we can't autofeed sense data back with | |
| 1872 | * a command for parallel SCSI, we can't give back | |
| 1873 | * a CHECK CONDITION. We'll give back a BUSY status | |
| 1874 | * instead. This works out okay because the only | |
| 1875 | * time we should, in fact, get this, is in the | |
| 1876 | * case that somebody configured us without the | |
| 1877 | * blackhole driver, so they get what they deserve. | |
| 1878 | */ | |
| 1879 | isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); | |
| 1880 | return; | |
| 1881 | } | |
| 984263bc MD |
1882 | } |
| 1883 | ||
| 191d7ec1 | 1884 | atp = isp_get_atpd(isp, tptr, 0); |
| 984263bc | 1885 | atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); |
| 191d7ec1 | 1886 | if (atiop == NULL || atp == NULL) { |
| 984263bc MD |
1887 | /* |
| 1888 | * Because we can't autofeed sense data back with | |
| 1889 | * a command for parallel SCSI, we can't give back | |
| 1890 | * a CHECK CONDITION. We'll give back a QUEUE FULL status | |
| 1891 | * instead. This works out okay because the only time we | |
| 1892 | * should, in fact, get this, is in the case that we've | |
| 1893 | * run out of ATIOS. | |
| 1894 | */ | |
| 191d7ec1 SW |
1895 | xpt_print(tptr->owner, "no %s for lun %d from initiator %d\n", (atp == NULL && atiop == NULL)? "ATIOs *or* ATPS" : |
| 1896 | ((atp == NULL)? "ATPs" : "ATIOs"), aep->at_lun, aep->at_iid); | |
| 1897 | isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); | |
| 1898 | if (atp) { | |
| 1899 | isp_put_atpd(isp, tptr, atp); | |
| 1900 | } | |
| 984263bc | 1901 | rls_lun_statep(isp, tptr); |
| 191d7ec1 | 1902 | return; |
| 984263bc | 1903 | } |
| 191d7ec1 SW |
1904 | atp->tag = aep->at_tag_val; |
| 1905 | if (atp->tag == 0) { | |
| 1906 | atp->tag = ~0; | |
| 984263bc | 1907 | } |
| 191d7ec1 SW |
1908 | atp->state = ATPD_STATE_ATIO; |
| 1909 | SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); | |
| 1910 | tptr->atio_count--; | |
| 1911 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count); | |
| 1912 | atiop->ccb_h.target_id = aep->at_tgt; | |
| 1913 | atiop->ccb_h.target_lun = aep->at_lun; | |
| 984263bc MD |
1914 | if (aep->at_flags & AT_NODISC) { |
| 1915 | atiop->ccb_h.flags = CAM_DIS_DISCONNECT; | |
| 1916 | } else { | |
| 1917 | atiop->ccb_h.flags = 0; | |
| 1918 | } | |
| 1919 | ||
| 1920 | if (status & QLTM_SVALID) { | |
| 191d7ec1 | 1921 | size_t amt = ISP_MIN(QLTM_SENSELEN, sizeof (atiop->sense_data)); |
| 984263bc | 1922 | atiop->sense_len = amt; |
| 191d7ec1 | 1923 | ISP_MEMCPY(&atiop->sense_data, aep->at_sense, amt); |
| 984263bc MD |
1924 | } else { |
| 1925 | atiop->sense_len = 0; | |
| 1926 | } | |
| 1927 | ||
| 1928 | atiop->init_id = GET_IID_VAL(aep->at_iid); | |
| 1929 | atiop->cdb_len = aep->at_cdblen; | |
| 191d7ec1 | 1930 | ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen); |
| 984263bc MD |
1931 | atiop->ccb_h.status = CAM_CDB_RECVD; |
| 1932 | /* | |
| 1933 | * Construct a tag 'id' based upon tag value (which may be 0..255) | |
| 1934 | * and the handle (which we have to preserve). | |
| 1935 | */ | |
| 191d7ec1 | 1936 | atiop->tag_id = atp->tag; |
| 984263bc MD |
1937 | if (aep->at_flags & AT_TQAE) { |
| 1938 | atiop->tag_action = aep->at_tag_type; | |
| 1939 | atiop->ccb_h.status |= CAM_TAG_ACTION_VALID; | |
| 1940 | } | |
| 191d7ec1 SW |
1941 | atp->orig_datalen = 0; |
| 1942 | atp->bytes_xfered = 0; | |
| 1943 | atp->last_xframt = 0; | |
| 1944 | atp->lun = aep->at_lun; | |
| 1945 | atp->nphdl = aep->at_iid; | |
| 1946 | atp->portid = PORT_NONE; | |
| 1947 | atp->oxid = 0; | |
| 1948 | atp->cdb0 = atiop->cdb_io.cdb_bytes[0]; | |
| 1949 | atp->tattr = aep->at_tag_type; | |
| 1950 | atp->state = ATPD_STATE_CAM; | |
| 1951 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO[%x] CDB=0x%x lun %d\n", aep->at_tag_val, atp->cdb0, atp->lun); | |
| 984263bc | 1952 | rls_lun_statep(isp, tptr); |
| 984263bc MD |
1953 | } |
| 1954 | ||
| 191d7ec1 SW |
1955 | static void |
| 1956 | isp_handle_platform_atio2(ispsoftc_t *isp, at2_entry_t *aep) | |
| 984263bc MD |
1957 | { |
| 1958 | lun_id_t lun; | |
| 191d7ec1 | 1959 | fcportdb_t *lp; |
| 984263bc MD |
1960 | tstate_t *tptr; |
| 1961 | struct ccb_accept_tio *atiop; | |
| 191d7ec1 | 1962 | uint16_t nphdl; |
| 984263bc | 1963 | atio_private_data_t *atp; |
| 191d7ec1 | 1964 | inot_private_data_t *ntp; |
| 984263bc MD |
1965 | |
| 1966 | /* | |
| 1967 | * The firmware status (except for the QLTM_SVALID bit) | |
| 1968 | * indicates why this ATIO was sent to us. | |
| 1969 | * | |
| 191d7ec1 | 1970 | * If QLTM_SVALID is set, the firmware has recommended Sense Data. |
| 984263bc MD |
1971 | */ |
| 1972 | if ((aep->at_status & ~QLTM_SVALID) != AT_CDB) { | |
| 191d7ec1 | 1973 | isp_prt(isp, ISP_LOGWARN, "bogus atio (0x%x) leaked to platform", aep->at_status); |
| 984263bc | 1974 | isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); |
| 191d7ec1 | 1975 | return; |
| 984263bc MD |
1976 | } |
| 1977 | ||
| 191d7ec1 | 1978 | if (ISP_CAP_SCCFW(isp)) { |
| 984263bc MD |
1979 | lun = aep->at_scclun; |
| 1980 | } else { | |
| 1981 | lun = aep->at_lun; | |
| 1982 | } | |
| 191d7ec1 SW |
1983 | if (ISP_CAP_2KLOGIN(isp)) { |
| 1984 | nphdl = ((at2e_entry_t *)aep)->at_iid; | |
| 1985 | } else { | |
| 1986 | nphdl = aep->at_iid; | |
| 1987 | } | |
| 984263bc MD |
1988 | tptr = get_lun_statep(isp, 0, lun); |
| 1989 | if (tptr == NULL) { | |
| 984263bc | 1990 | tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD); |
| 191d7ec1 SW |
1991 | if (tptr == NULL) { |
| 1992 | isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] no state pointer for lun %d", aep->at_rxid, lun); | |
| 1993 | isp_endcmd(isp, aep, SCSI_STATUS_CHECK_COND | ECMD_SVALID | (0x5 << 12) | (0x25 << 16), 0); | |
| 1994 | return; | |
| 1995 | } | |
| 984263bc MD |
1996 | } |
| 1997 | ||
| 191d7ec1 SW |
1998 | /* |
| 1999 | * Start any commands pending resources first. | |
| 2000 | */ | |
| 2001 | if (tptr->restart_queue) { | |
| 2002 | inot_private_data_t *restart_queue = tptr->restart_queue; | |
| 2003 | tptr->restart_queue = NULL; | |
| 2004 | while (restart_queue) { | |
| 2005 | ntp = restart_queue; | |
| 2006 | restart_queue = ntp->rd.nt.nt_hba; | |
| 2007 | isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at2_entry_t *)ntp->rd.data)->at_rxid); | |
| 2008 | isp_handle_platform_atio2(isp, (at2_entry_t *) ntp->rd.data); | |
| 2009 | isp_put_ntpd(isp, tptr, ntp); | |
| 2010 | /* | |
| 2011 | * If a recursion caused the restart queue to start to fill again, | |
| 2012 | * stop and splice the new list on top of the old list and restore | |
| 2013 | * it and go to noresrc. | |
| 2014 | */ | |
| 2015 | if (tptr->restart_queue) { | |
| 2016 | ntp = tptr->restart_queue; | |
| 2017 | tptr->restart_queue = restart_queue; | |
| 2018 | while (restart_queue->rd.nt.nt_hba) { | |
| 2019 | restart_queue = restart_queue->rd.nt.nt_hba; | |
| 2020 | } | |
| 2021 | restart_queue->rd.nt.nt_hba = ntp; | |
| 2022 | goto noresrc; | |
| 2023 | } | |
| 2024 | } | |
| 984263bc MD |
2025 | } |
| 2026 | ||
| 984263bc | 2027 | atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); |
| 191d7ec1 SW |
2028 | if (atiop == NULL) { |
| 2029 | goto noresrc; | |
| 2030 | } | |
| 2031 | ||
| 2032 | atp = isp_get_atpd(isp, tptr, 0); | |
| 2033 | if (atp == NULL) { | |
| 2034 | goto noresrc; | |
| 984263bc | 2035 | } |
| 191d7ec1 SW |
2036 | |
| 2037 | atp->tag = aep->at_rxid; | |
| 984263bc MD |
2038 | atp->state = ATPD_STATE_ATIO; |
| 2039 | SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); | |
| 2040 | tptr->atio_count--; | |
| 191d7ec1 SW |
2041 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count); |
| 2042 | atiop->ccb_h.target_id = FCPARAM(isp, 0)->isp_loopid; | |
| 2043 | atiop->ccb_h.target_lun = lun; | |
| 984263bc | 2044 | |
| 984263bc MD |
2045 | /* |
| 2046 | * We don't get 'suggested' sense data as we do with SCSI cards. | |
| 2047 | */ | |
| 2048 | atiop->sense_len = 0; | |
| 191d7ec1 SW |
2049 | if (ISP_CAP_2KLOGIN(isp)) { |
| 2050 | /* | |
| 2051 | * NB: We could not possibly have 2K logins if we | |
| 2052 | * NB: also did not have SCC FW. | |
| 2053 | */ | |
| 2054 | atiop->init_id = ((at2e_entry_t *)aep)->at_iid; | |
| 2055 | } else { | |
| 2056 | atiop->init_id = aep->at_iid; | |
| 2057 | } | |
| 984263bc | 2058 | |
| 191d7ec1 SW |
2059 | /* |
| 2060 | * If we're not in the port database, add ourselves. | |
| 2061 | */ | |
| 2062 | if (!IS_2100(isp) && isp_find_pdb_by_loopid(isp, 0, atiop->init_id, &lp) == 0) { | |
| 2063 | uint64_t iid = | |
| 2064 | (((uint64_t) aep->at_wwpn[0]) << 48) | | |
| 2065 | (((uint64_t) aep->at_wwpn[1]) << 32) | | |
| 2066 | (((uint64_t) aep->at_wwpn[2]) << 16) | | |
| 2067 | (((uint64_t) aep->at_wwpn[3]) << 0); | |
| 2068 | /* | |
| 2069 | * However, make sure we delete ourselves if otherwise | |
| 2070 | * we were there but at a different loop id. | |
| 2071 | */ | |
| 2072 | if (isp_find_pdb_by_wwn(isp, 0, iid, &lp)) { | |
| 2073 | isp_del_wwn_entry(isp, 0, iid, lp->handle, lp->portid); | |
| 2074 | } | |
| 2075 | isp_add_wwn_entry(isp, 0, iid, atiop->init_id, PORT_ANY); | |
| 2076 | } | |
| 2077 | atiop->cdb_len = ATIO2_CDBLEN; | |
| 2078 | ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN); | |
| 2079 | atiop->ccb_h.status = CAM_CDB_RECVD; | |
| 2080 | atiop->tag_id = atp->tag; | |
| 2081 | switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) { | |
| 2082 | case ATIO2_TC_ATTR_SIMPLEQ: | |
| 2083 | atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; | |
| 2084 | atiop->tag_action = MSG_SIMPLE_Q_TAG; | |
| 984263bc | 2085 | break; |
| 191d7ec1 SW |
2086 | case ATIO2_TC_ATTR_HEADOFQ: |
| 2087 | atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; | |
| 984263bc MD |
2088 | atiop->tag_action = MSG_HEAD_OF_Q_TAG; |
| 2089 | break; | |
| 191d7ec1 SW |
2090 | case ATIO2_TC_ATTR_ORDERED: |
| 2091 | atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; | |
| 984263bc MD |
2092 | atiop->tag_action = MSG_ORDERED_Q_TAG; |
| 2093 | break; | |
| 191d7ec1 | 2094 | case ATIO2_TC_ATTR_ACAQ: /* ?? */ |
| 984263bc MD |
2095 | case ATIO2_TC_ATTR_UNTAGGED: |
| 2096 | default: | |
| 2097 | atiop->tag_action = 0; | |
| 2098 | break; | |
| 2099 | } | |
| 984263bc | 2100 | |
| 984263bc | 2101 | atp->orig_datalen = aep->at_datalen; |
| 984263bc | 2102 | atp->bytes_xfered = 0; |
| 191d7ec1 SW |
2103 | atp->last_xframt = 0; |
| 2104 | atp->lun = lun; | |
| 2105 | atp->nphdl = atiop->init_id; | |
| 2106 | atp->sid = PORT_ANY; | |
| 2107 | atp->oxid = aep->at_oxid; | |
| 2108 | atp->cdb0 = aep->at_cdb[0]; | |
| 2109 | atp->tattr = aep->at_taskflags & ATIO2_TC_ATTR_MASK; | |
| 984263bc | 2110 | atp->state = ATPD_STATE_CAM; |
| 191d7ec1 SW |
2111 | xpt_done((union ccb *)atiop); |
| 2112 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO2[%x] CDB=0x%x lun %d datalen %u\n", aep->at_rxid, atp->cdb0, lun, atp->orig_datalen); | |
| 2113 | rls_lun_statep(isp, tptr); | |
| 2114 | return; | |
| 2115 | noresrc: | |
| 2116 | ntp = isp_get_ntpd(isp, tptr); | |
| 2117 | if (ntp == NULL) { | |
| 2118 | rls_lun_statep(isp, tptr); | |
| 2119 | isp_endcmd(isp, aep, nphdl, 0, SCSI_STATUS_BUSY, 0); | |
| 2120 | return; | |
| 2121 | } | |
| 2122 | memcpy(ntp->rd.data, aep, QENTRY_LEN); | |
| 2123 | ntp->rd.nt.nt_hba = tptr->restart_queue; | |
| 2124 | tptr->restart_queue = ntp; | |
| 2125 | rls_lun_statep(isp, tptr); | |
| 2126 | } | |
| 2127 | ||
| 2128 | static void | |
| 2129 | isp_handle_platform_atio7(ispsoftc_t *isp, at7_entry_t *aep) | |
| 2130 | { | |
| 2131 | int cdbxlen; | |
| 2132 | uint16_t lun, chan, nphdl = NIL_HANDLE; | |
| 2133 | uint32_t did, sid; | |
| 2134 | uint64_t wwn = INI_NONE; | |
| 2135 | fcportdb_t *lp; | |
| 2136 | tstate_t *tptr; | |
| 2137 | struct ccb_accept_tio *atiop; | |
| 2138 | atio_private_data_t *atp = NULL; | |
| 2139 | inot_private_data_t *ntp; | |
| 2140 | ||
| 2141 | did = (aep->at_hdr.d_id[0] << 16) | (aep->at_hdr.d_id[1] << 8) | aep->at_hdr.d_id[2]; | |
| 2142 | sid = (aep->at_hdr.s_id[0] << 16) | (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2]; | |
| 2143 | lun = (aep->at_cmnd.fcp_cmnd_lun[0] << 8) | aep->at_cmnd.fcp_cmnd_lun[1]; | |
| 2144 | ||
| 2145 | /* | |
| 2146 | * Find the N-port handle, and Virtual Port Index for this command. | |
| 2147 | * | |
| 2148 | * If we can't, we're somewhat in trouble because we can't actually respond w/o that information. | |
| 2149 | * We also, as a matter of course, need to know the WWN of the initiator too. | |
| 2150 | */ | |
| 2151 | if (ISP_CAP_MULTI_ID(isp)) { | |
| 2152 | /* | |
| 2153 | * Find the right channel based upon D_ID | |
| 2154 | */ | |
| 2155 | isp_find_chan_by_did(isp, did, &chan); | |
| 2156 | ||
| 2157 | if (chan == ISP_NOCHAN) { | |
| 2158 | NANOTIME_T now; | |
| 2159 | ||
| 2160 | /* | |
| 2161 | * If we don't recognizer our own D_DID, terminate the exchange, unless we're within 2 seconds of startup | |
| 2162 | * It's a bit tricky here as we need to stash this command *somewhere*. | |
| 2163 | */ | |
| 2164 | GET_NANOTIME(&now); | |
| 2165 | if (NANOTIME_SUB(&isp->isp_init_time, &now) > 2000000000ULL) { | |
| 2166 | isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel- dropping", __func__, aep->at_rxid, did); | |
| 2167 | isp_endcmd(isp, aep, NIL_HANDLE, ISP_NOCHAN, ECMD_TERMINATE, 0); | |
| 2168 | return; | |
| 2169 | } | |
| 2170 | tptr = get_lun_statep(isp, 0, 0); | |
| 2171 | if (tptr == NULL) { | |
| 2172 | tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD); | |
| 2173 | if (tptr == NULL) { | |
| 2174 | isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel and no tptr- dropping", __func__, aep->at_rxid, did); | |
| 2175 | isp_endcmd(isp, aep, NIL_HANDLE, ISP_NOCHAN, ECMD_TERMINATE, 0); | |
| 2176 | return; | |
| 2177 | } | |
| 2178 | } | |
| 2179 | isp_prt(isp, ISP_LOGWARN, "%s: [RX_ID 0x%x] D_ID %x not found on any channel- deferring", __func__, aep->at_rxid, did); | |
| 2180 | goto noresrc; | |
| 2181 | } | |
| 2182 | isp_prt(isp, ISP_LOGTDEBUG0, "%s: [RX_ID 0x%x] D_ID 0x%06x found on Chan %d for S_ID 0x%06x", __func__, aep->at_rxid, did, chan, sid); | |
| 2183 | } else { | |
| 2184 | chan = 0; | |
| 2185 | } | |
| 2186 | ||
| 2187 | /* | |
| 2188 | * Find the PDB entry for this initiator | |
| 2189 | */ | |
| 2190 | if (isp_find_pdb_by_sid(isp, chan, sid, &lp) == 0) { | |
| 2191 | /* | |
| 2192 | * If we're not in the port database terminate the exchange. | |
| 2193 | */ | |
| 2194 | isp_prt(isp, ISP_LOGTINFO, "%s: [RX_ID 0x%x] D_ID 0x%06x found on Chan %d for S_ID 0x%06x wasn't in PDB already", | |
| 2195 | __func__, aep->at_rxid, did, chan, sid); | |
| 2196 | isp_endcmd(isp, aep, NIL_HANDLE, chan, ECMD_TERMINATE, 0); | |
| 2197 | return; | |
| 2198 | } | |
| 2199 | nphdl = lp->handle; | |
| 2200 | wwn = lp->port_wwn; | |
| 2201 | ||
| 2202 | /* | |
| 2203 | * Get the tstate pointer | |
| 2204 | */ | |
| 2205 | tptr = get_lun_statep(isp, chan, lun); | |
| 2206 | if (tptr == NULL) { | |
| 2207 | tptr = get_lun_statep(isp, chan, CAM_LUN_WILDCARD); | |
| 2208 | if (tptr == NULL) { | |
| 2209 | isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] no state pointer for lun %d or wildcard", aep->at_rxid, lun); | |
| 2210 | isp_endcmd(isp, aep, nphdl, chan, SCSI_STATUS_CHECK_COND | ECMD_SVALID | (0x5 << 12) | (0x25 << 16), 0); | |
| 2211 | return; | |
| 2212 | } | |
| 2213 | } | |
| 2214 | ||
| 2215 | /* | |
| 2216 | * Start any commands pending resources first. | |
| 2217 | */ | |
| 2218 | if (tptr->restart_queue) { | |
| 2219 | inot_private_data_t *restart_queue = tptr->restart_queue; | |
| 2220 | tptr->restart_queue = NULL; | |
| 2221 | while (restart_queue) { | |
| 2222 | ntp = restart_queue; | |
| 2223 | restart_queue = ntp->rd.nt.nt_hba; | |
| 2224 | isp_prt(isp, ISP_LOGTDEBUG0, "%s: restarting resrc deprived %x", __func__, ((at7_entry_t *)ntp->rd.data)->at_rxid); | |
| 2225 | isp_handle_platform_atio7(isp, (at7_entry_t *) ntp->rd.data); | |
| 2226 | isp_put_ntpd(isp, tptr, ntp); | |
| 2227 | /* | |
| 2228 | * If a recursion caused the restart queue to start to fill again, | |
| 2229 | * stop and splice the new list on top of the old list and restore | |
| 2230 | * it and go to noresrc. | |
| 2231 | */ | |
| 2232 | if (tptr->restart_queue) { | |
| 2233 | if (restart_queue) { | |
| 2234 | ntp = tptr->restart_queue; | |
| 2235 | tptr->restart_queue = restart_queue; | |
| 2236 | while (restart_queue->rd.nt.nt_hba) { | |
| 2237 | restart_queue = restart_queue->rd.nt.nt_hba; | |
| 2238 | } | |
| 2239 | restart_queue->rd.nt.nt_hba = ntp; | |
| 2240 | } | |
| 2241 | goto noresrc; | |
| 2242 | } | |
| 2243 | } | |
| 2244 | } | |
| 2245 | ||
| 2246 | /* | |
| 2247 | * If the f/w is out of resources, just send a BUSY status back. | |
| 2248 | */ | |
| 2249 | if (aep->at_rxid == AT7_NORESRC_RXID) { | |
| 2250 | rls_lun_statep(isp, tptr); | |
| 2251 | isp_endcmd(isp, aep, nphdl, chan, SCSI_BUSY, 0); | |
| 2252 | return; | |
| 2253 | } | |
| 2254 | ||
| 2255 | /* | |
| 2256 | * If we're out of resources, just send a BUSY status back. | |
| 2257 | */ | |
| 2258 | atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); | |
| 2259 | if (atiop == NULL) { | |
| 2260 | isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] out of atios", aep->at_rxid); | |
| 2261 | goto noresrc; | |
| 2262 | } | |
| 984263bc | 2263 | |
| 191d7ec1 SW |
2264 | atp = isp_get_atpd(isp, tptr, 0); |
| 2265 | if (atp == NULL) { | |
| 2266 | isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] out of atps", aep->at_rxid); | |
| 2267 | goto noresrc; | |
| 2268 | } | |
| 2269 | if (isp_get_atpd(isp, tptr, aep->at_rxid)) { | |
| 2270 | isp_prt(isp, ISP_LOGTDEBUG0, "[0x%x] tag wraparound in isp_handle_platforms_atio7 (N-Port Handle 0x%04x S_ID 0x%04x OX_ID 0x%04x)\n", | |
| 2271 | aep->at_rxid, nphdl, sid, aep->at_hdr.ox_id); | |
| 2272 | /* | |
| 2273 | * It's not a "no resource" condition- but we can treat it like one | |
| 2274 | */ | |
| 2275 | goto noresrc; | |
| 2276 | } | |
| 2277 | ||
| 2278 | atp->tag = aep->at_rxid; | |
| 2279 | atp->state = ATPD_STATE_ATIO; | |
| 2280 | SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); | |
| 2281 | tptr->atio_count--; | |
| 2282 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, atiop->ccb_h.path, "Take FREE ATIO count now %d\n", tptr->atio_count); | |
| 2283 | atiop->init_id = nphdl; | |
| 2284 | atiop->ccb_h.target_id = FCPARAM(isp, chan)->isp_loopid; | |
| 2285 | atiop->ccb_h.target_lun = lun; | |
| 2286 | atiop->sense_len = 0; | |
| 2287 | cdbxlen = aep->at_cmnd.fcp_cmnd_alen_datadir >> FCP_CMND_ADDTL_CDBLEN_SHIFT; | |
| 2288 | if (cdbxlen) { | |
| 2289 | isp_prt(isp, ISP_LOGWARN, "additional CDBLEN ignored"); | |
| 2290 | } | |
| 2291 | cdbxlen = sizeof (aep->at_cmnd.cdb_dl.sf.fcp_cmnd_cdb); | |
| 2292 | ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cmnd.cdb_dl.sf.fcp_cmnd_cdb, cdbxlen); | |
| 2293 | atiop->cdb_len = cdbxlen; | |
| 2294 | atiop->ccb_h.status = CAM_CDB_RECVD; | |
| 2295 | atiop->tag_id = atp->tag; | |
| 2296 | switch (aep->at_cmnd.fcp_cmnd_task_attribute & FCP_CMND_TASK_ATTR_MASK) { | |
| 2297 | case FCP_CMND_TASK_ATTR_SIMPLE: | |
| 2298 | atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; | |
| 2299 | atiop->tag_action = MSG_SIMPLE_Q_TAG; | |
| 2300 | break; | |
| 2301 | case FCP_CMND_TASK_ATTR_HEAD: | |
| 2302 | atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; | |
| 2303 | atiop->tag_action = MSG_HEAD_OF_Q_TAG; | |
| 2304 | break; | |
| 2305 | case FCP_CMND_TASK_ATTR_ORDERED: | |
| 2306 | atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; | |
| 2307 | atiop->tag_action = MSG_ORDERED_Q_TAG; | |
| 2308 | break; | |
| 2309 | default: | |
| 2310 | /* FALLTHROUGH */ | |
| 2311 | case FCP_CMND_TASK_ATTR_ACA: | |
| 2312 | case FCP_CMND_TASK_ATTR_UNTAGGED: | |
| 2313 | atiop->tag_action = 0; | |
| 2314 | break; | |
| 2315 | } | |
| 2316 | atp->orig_datalen = aep->at_cmnd.cdb_dl.sf.fcp_cmnd_dl; | |
| 2317 | atp->bytes_xfered = 0; | |
| 2318 | atp->last_xframt = 0; | |
| 2319 | atp->lun = lun; | |
| 2320 | atp->nphdl = nphdl; | |
| 2321 | atp->portid = sid; | |
| 2322 | atp->oxid = aep->at_hdr.ox_id; | |
| 2323 | atp->cdb0 = atiop->cdb_io.cdb_bytes[0]; | |
| 2324 | atp->tattr = aep->at_cmnd.fcp_cmnd_task_attribute & FCP_CMND_TASK_ATTR_MASK; | |
| 2325 | atp->state = ATPD_STATE_CAM; | |
| 2326 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "ATIO7[%x] CDB=0x%x lun %d datalen %u\n", aep->at_rxid, atp->cdb0, lun, atp->orig_datalen); | |
| 2327 | xpt_done((union ccb *)atiop); | |
| 2328 | rls_lun_statep(isp, tptr); | |
| 2329 | return; | |
| 2330 | noresrc: | |
| 2331 | if (atp) { | |
| 2332 | isp_put_atpd(isp, tptr, atp); | |
| 2333 | } | |
| 2334 | ntp = isp_get_ntpd(isp, tptr); | |
| 2335 | if (ntp == NULL) { | |
| 2336 | rls_lun_statep(isp, tptr); | |
| 2337 | isp_endcmd(isp, aep, nphdl, chan, SCSI_STATUS_BUSY, 0); | |
| 2338 | return; | |
| 2339 | } | |
| 2340 | memcpy(ntp->rd.data, aep, QENTRY_LEN); | |
| 2341 | ntp->rd.nt.nt_hba = tptr->restart_queue; | |
| 2342 | tptr->restart_queue = ntp; | |
| 984263bc | 2343 | rls_lun_statep(isp, tptr); |
| 984263bc MD |
2344 | } |
| 2345 | ||
| 191d7ec1 SW |
2346 | static void |
| 2347 | isp_handle_platform_ctio(ispsoftc_t *isp, void *arg) | |
| 984263bc MD |
2348 | { |
| 2349 | union ccb *ccb; | |
| 2350 | int sentstatus, ok, notify_cam, resid = 0; | |
| 191d7ec1 SW |
2351 | tstate_t *tptr = NULL; |
| 2352 | atio_private_data_t *atp = NULL; | |
| 2353 | int bus; | |
| 2354 | uint32_t tval, handle; | |
| 984263bc MD |
2355 | |
| 2356 | /* | |
| 191d7ec1 SW |
2357 | * CTIO handles are 16 bits. |
| 2358 | * CTIO2 and CTIO7 are 32 bits. | |
| 984263bc MD |
2359 | */ |
| 2360 | ||
| 191d7ec1 SW |
2361 | if (IS_SCSI(isp)) { |
| 2362 | handle = ((ct_entry_t *)arg)->ct_syshandle; | |
| 2363 | } else { | |
| 2364 | handle = ((ct2_entry_t *)arg)->ct_syshandle; | |
| 2365 | } | |
| 2366 | ccb = isp_find_xs_tgt(isp, handle); | |
| 2367 | if (ccb == NULL) { | |
| 2368 | isp_print_bytes(isp, "null ccb in isp_handle_platform_ctio", QENTRY_LEN, arg); | |
| 2369 | return; | |
| 2370 | } | |
| 2371 | isp_destroy_tgt_handle(isp, handle); | |
| 2372 | bus = XS_CHANNEL(ccb); | |
| 2373 | tptr = get_lun_statep(isp, bus, XS_LUN(ccb)); | |
| 2374 | if (tptr == NULL) { | |
| 2375 | tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD); | |
| 2376 | } | |
| 2377 | KASSERT((tptr != NULL), ("cannot get state pointer")); | |
| 2378 | if (isp->isp_nactive) { | |
| 2379 | isp->isp_nactive++; | |
| 2380 | } | |
| 2381 | if (IS_24XX(isp)) { | |
| 2382 | ct7_entry_t *ct = arg; | |
| 2383 | ||
| 2384 | atp = isp_get_atpd(isp, tptr, ct->ct_rxid); | |
| 2385 | if (atp == NULL) { | |
| 2386 | rls_lun_statep(isp, tptr); | |
| 2387 | isp_prt(isp, ISP_LOGERR, "%s: cannot find adjunct for %x after I/O", __func__, ct->ct_rxid); | |
| 2388 | return; | |
| 2389 | } | |
| 984263bc | 2390 | |
| 191d7ec1 SW |
2391 | sentstatus = ct->ct_flags & CT7_SENDSTATUS; |
| 2392 | ok = (ct->ct_nphdl == CT7_OK); | |
| 2393 | if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) { | |
| 2394 | ccb->ccb_h.status |= CAM_SENT_SENSE; | |
| 2395 | } | |
| 2396 | notify_cam = ct->ct_header.rqs_seqno & 0x1; | |
| 2397 | if ((ct->ct_flags & CT7_DATAMASK) != CT7_NO_DATA) { | |
| 2398 | resid = ct->ct_resid; | |
| 2399 | atp->bytes_xfered += (atp->last_xframt - resid); | |
| 2400 | atp->last_xframt = 0; | |
| 2401 | } | |
| 2402 | if (ct->ct_nphdl == CT_HBA_RESET) { | |
| 2403 | ok = 0; | |
| 2404 | notify_cam = 1; | |
| 2405 | sentstatus = 1; | |
| 2406 | ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; | |
| 2407 | } else if (!ok) { | |
| 2408 | ccb->ccb_h.status |= CAM_REQ_CMP_ERR; | |
| 2409 | } | |
| 2410 | tval = atp->tag; | |
| 2411 | isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN, "%s: CTIO7[%x] sts 0x%x flg 0x%x sns %d resid %d %s", __func__, | |
| 2412 | ct->ct_rxid, ct->ct_nphdl, ct->ct_flags, (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, resid, sentstatus? "FIN" : "MID"); | |
| 2413 | atp->state = ATPD_STATE_PDON; /* XXX: should really come after isp_complete_ctio */ | |
| 2414 | } else if (IS_FC(isp)) { | |
| 984263bc | 2415 | ct2_entry_t *ct = arg; |
| 191d7ec1 SW |
2416 | |
| 2417 | atp = isp_get_atpd(isp, tptr, ct->ct_rxid); | |
| 984263bc | 2418 | if (atp == NULL) { |
| 191d7ec1 SW |
2419 | rls_lun_statep(isp, tptr); |
| 2420 | isp_prt(isp, ISP_LOGERR, "%s: cannot find adjunct for %x after I/O", __func__, ct->ct_rxid); | |
| 2421 | return; | |
| 984263bc MD |
2422 | } |
| 2423 | sentstatus = ct->ct_flags & CT2_SENDSTATUS; | |
| 2424 | ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; | |
| 2425 | if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) { | |
| 2426 | ccb->ccb_h.status |= CAM_SENT_SENSE; | |
| 2427 | } | |
| 2428 | notify_cam = ct->ct_header.rqs_seqno & 0x1; | |
| 2429 | if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) { | |
| 2430 | resid = ct->ct_resid; | |
| 2431 | atp->bytes_xfered += (atp->last_xframt - resid); | |
| 2432 | atp->last_xframt = 0; | |
| 2433 | } | |
| 191d7ec1 SW |
2434 | if (ct->ct_status == CT_HBA_RESET) { |
| 2435 | ok = 0; | |
| 2436 | notify_cam = 1; | |
| 2437 | sentstatus = 1; | |
| 2438 | ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; | |
| 2439 | } else if (!ok) { | |
| 2440 | ccb->ccb_h.status |= CAM_REQ_CMP_ERR; | |
| 984263bc | 2441 | } |
| 191d7ec1 SW |
2442 | isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN, "%s: CTIO2[%x] sts 0x%x flg 0x%x sns %d resid %d %s", __func__, |
| 2443 | ct->ct_rxid, ct->ct_status, ct->ct_flags, (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, resid, sentstatus? "FIN" : "MID"); | |
| 2444 | tval = atp->tag; | |
| 2445 | atp->state = ATPD_STATE_PDON; /* XXX: should really come after isp_complete_ctio */ | |
| 984263bc MD |
2446 | } else { |
| 2447 | ct_entry_t *ct = arg; | |
| 2448 | sentstatus = ct->ct_flags & CT_SENDSTATUS; | |
| 2449 | ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; | |
| 2450 | /* | |
| 2451 | * We *ought* to be able to get back to the original ATIO | |
| 2452 | * here, but for some reason this gets lost. It's just as | |
| 2453 | * well because it's squirrelled away as part of periph | |
| 2454 | * private data. | |
| 2455 | * | |
| 2456 | * We can live without it as long as we continue to use | |
| 2457 | * the auto-replenish feature for CTIOs. | |
| 2458 | */ | |
| 2459 | notify_cam = ct->ct_header.rqs_seqno & 0x1; | |
| 191d7ec1 SW |
2460 | if (ct->ct_status == (CT_HBA_RESET & 0xff)) { |
| 2461 | ok = 0; | |
| 2462 | notify_cam = 1; | |
| 2463 | sentstatus = 1; | |
| 2464 | ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; | |
| 2465 | } else if (!ok) { | |
| 2466 | ccb->ccb_h.status |= CAM_REQ_CMP_ERR; | |
| 2467 | } else if (ct->ct_status & QLTM_SVALID) { | |
| 984263bc MD |
2468 | char *sp = (char *)ct; |
| 2469 | sp += CTIO_SENSE_OFFSET; | |
| 191d7ec1 SW |
2470 | ccb->csio.sense_len = min(sizeof (ccb->csio.sense_data), QLTM_SENSELEN); |
| 2471 | ISP_MEMCPY(&ccb->csio.sense_data, sp, ccb->csio.sense_len); | |
| 984263bc MD |
2472 | ccb->ccb_h.status |= CAM_AUTOSNS_VALID; |
| 2473 | } | |
| 2474 | if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) { | |
| 2475 | resid = ct->ct_resid; | |
| 2476 | } | |
| 191d7ec1 SW |
2477 | isp_prt(isp, ISP_LOGTDEBUG0, "%s: CTIO[%x] tag %x S_ID 0x%x lun %d sts %x flg %x resid %d %s", __func__, |
| 2478 | ct->ct_fwhandle, ct->ct_tag_val, ct->ct_iid, ct->ct_lun, ct->ct_status, ct->ct_flags, resid, sentstatus? "FIN" : "MID"); | |
| 984263bc MD |
2479 | tval = ct->ct_fwhandle; |
| 2480 | } | |
| 2481 | ccb->csio.resid += resid; | |
| 2482 | ||
| 2483 | /* | |
| 2484 | * We're here either because intermediate data transfers are done | |
| 2485 | * and/or the final status CTIO (which may have joined with a | |
| 2486 | * Data Transfer) is done. | |
| 2487 | * | |
| 2488 | * In any case, for this platform, the upper layers figure out | |
| 2489 | * what to do next, so all we do here is collect status and | |
| 2490 | * pass information along. Any DMA handles have already been | |
| 2491 | * freed. | |
| 2492 | */ | |
| 2493 | if (notify_cam == 0) { | |
| 2494 | isp_prt(isp, ISP_LOGTDEBUG0, " INTER CTIO[0x%x] done", tval); | |
| 191d7ec1 | 2495 | return; |
| 984263bc | 2496 | } |
| 191d7ec1 SW |
2497 | if (tptr) { |
| 2498 | rls_lun_statep(isp, tptr); | |
| 2499 | } | |
| 2500 | isp_prt(isp, ISP_LOGTDEBUG0, "%s CTIO[0x%x] done", (sentstatus)? " FINAL " : "MIDTERM ", tval); | |
| 984263bc | 2501 | |
| 191d7ec1 | 2502 | if (!ok && !IS_24XX(isp)) { |
| 984263bc MD |
2503 | isp_target_putback_atio(ccb); |
| 2504 | } else { | |
| 2505 | isp_complete_ctio(ccb); | |
| 984263bc | 2506 | } |
| 984263bc MD |
2507 | } |
| 2508 | ||
| 191d7ec1 SW |
2509 | static void |
| 2510 | isp_handle_platform_notify_scsi(ispsoftc_t *isp, in_entry_t *inot) | |
| 984263bc | 2511 | { |
| 191d7ec1 | 2512 | (void) isp_notify_ack(isp, inot); |
| 984263bc MD |
2513 | } |
| 2514 | ||
| 191d7ec1 SW |
2515 | static void |
| 2516 | isp_handle_platform_notify_fc(ispsoftc_t *isp, in_fcentry_t *inp) | |
| 984263bc | 2517 | { |
| 191d7ec1 | 2518 | int needack = 1; |
| 984263bc MD |
2519 | switch (inp->in_status) { |
| 2520 | case IN_PORT_LOGOUT: | |
| 191d7ec1 SW |
2521 | /* |
| 2522 | * XXX: Need to delete this initiator's WWN from the database | |
| 2523 | * XXX: Need to send this LOGOUT upstream | |
| 2524 | */ | |
| 2525 | isp_prt(isp, ISP_LOGWARN, "port logout of S_ID 0x%x", inp->in_iid); | |
| 984263bc MD |
2526 | break; |
| 2527 | case IN_PORT_CHANGED: | |
| 191d7ec1 | 2528 | isp_prt(isp, ISP_LOGWARN, "port changed for S_ID 0x%x", inp->in_iid); |
| 984263bc MD |
2529 | break; |
| 2530 | case IN_GLOBAL_LOGO: | |
| 191d7ec1 | 2531 | isp_del_all_wwn_entries(isp, 0); |
| 984263bc MD |
2532 | isp_prt(isp, ISP_LOGINFO, "all ports logged out"); |
| 2533 | break; | |
| 2534 | case IN_ABORT_TASK: | |
| 2535 | { | |
| 191d7ec1 SW |
2536 | tstate_t *tptr; |
| 2537 | uint16_t lun; | |
| 2538 | uint32_t loopid; | |
| 2539 | uint64_t wwn; | |
| 2540 | atio_private_data_t *atp; | |
| 2541 | fcportdb_t *lp; | |
| 2542 | struct ccb_immediate_notify *inot = NULL; | |
| 2543 | ||
| 2544 | if (ISP_CAP_SCCFW(isp)) { | |
| 2545 | lun = inp->in_scclun; | |
| 2546 | } else { | |
| 2547 | lun = inp->in_lun; | |
| 2548 | } | |
| 2549 | if (ISP_CAP_2KLOGIN(isp)) { | |
| 2550 | loopid = ((in_fcentry_e_t *)inp)->in_iid; | |
| 2551 | } else { | |
| 2552 | loopid = inp->in_iid; | |
| 2553 | } | |
| 2554 | if (isp_find_pdb_by_loopid(isp, 0, loopid, &lp)) { | |
| 2555 | wwn = lp->port_wwn; | |
| 2556 | } else { | |
| 2557 | wwn = INI_ANY; | |
| 2558 | } | |
| 2559 | tptr = get_lun_statep(isp, 0, lun); | |
| 2560 | if (tptr == NULL) { | |
| 2561 | tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD); | |
| 2562 | if (tptr == NULL) { | |
| 2563 | isp_prt(isp, ISP_LOGWARN, "ABORT TASK for lun %u- but no tstate", lun); | |
| 2564 | return; | |
| 2565 | } | |
| 2566 | } | |
| 2567 | atp = isp_get_atpd(isp, tptr, inp->in_seqid); | |
| 984263bc MD |
2568 | |
| 2569 | if (atp) { | |
| 191d7ec1 SW |
2570 | inot = (struct ccb_immediate_notify *) SLIST_FIRST(&tptr->inots); |
| 2571 | isp_prt(isp, ISP_LOGTDEBUG0, "ABORT TASK RX_ID %x WWN 0x%016llx state %d", inp->in_seqid, (unsigned long long) wwn, atp->state); | |
| 2572 | if (inot) { | |
| 2573 | tptr->inot_count--; | |
| 2574 | SLIST_REMOVE_HEAD(&tptr->inots, sim_links.sle); | |
| 2575 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: Take FREE INOT count now %d\n", __func__, tptr->inot_count); | |
| 2576 | } else { | |
| 2577 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, tptr->owner, "out of INOT structures\n"); | |
| 984263bc | 2578 | } |
| 984263bc | 2579 | } else { |
| 191d7ec1 | 2580 | ISP_PATH_PRT(isp, ISP_LOGWARN, tptr->owner, "abort task RX_ID %x from wwn 0x%016llx, state unknown\n", inp->in_seqid, wwn); |
| 984263bc MD |
2581 | } |
| 2582 | if (inot) { | |
| 191d7ec1 SW |
2583 | isp_notify_t tmp, *nt = &tmp; |
| 2584 | ISP_MEMZERO(nt, sizeof (isp_notify_t)); | |
| 2585 | nt->nt_hba = isp; | |
| 2586 | nt->nt_tgt = FCPARAM(isp, 0)->isp_wwpn; | |
| 2587 | nt->nt_wwn = wwn; | |
| 2588 | nt->nt_nphdl = loopid; | |
| 2589 | nt->nt_sid = PORT_ANY; | |
| 2590 | nt->nt_did = PORT_ANY; | |
| 2591 | nt->nt_lun = lun; | |
| 2592 | nt->nt_need_ack = 1; | |
| 2593 | nt->nt_channel = 0; | |
| 2594 | nt->nt_ncode = NT_ABORT_TASK; | |
| 2595 | nt->nt_lreserved = inot; | |
| 2596 | isp_handle_platform_target_tmf(isp, nt); | |
| 2597 | needack = 0; | |
| 984263bc | 2598 | } |
| 191d7ec1 | 2599 | rls_lun_statep(isp, tptr); |
| 984263bc MD |
2600 | break; |
| 2601 | } | |
| 2602 | default: | |
| 2603 | break; | |
| 2604 | } | |
| 191d7ec1 SW |
2605 | if (needack) { |
| 2606 | (void) isp_notify_ack(isp, inp); | |
| 2607 | } | |
| 984263bc | 2608 | } |
| 984263bc MD |
2609 | |
| 2610 | static void | |
| 191d7ec1 | 2611 | isp_handle_platform_notify_24xx(ispsoftc_t *isp, in_fcentry_24xx_t *inot) |
| 984263bc | 2612 | { |
| 191d7ec1 SW |
2613 | uint16_t nphdl; |
| 2614 | uint32_t portid; | |
| 2615 | fcportdb_t *lp; | |
| 2616 | uint8_t *ptr = NULL; | |
| 2617 | uint64_t wwn; | |
| 2618 | ||
| 2619 | nphdl = inot->in_nphdl; | |
| 2620 | if (nphdl != NIL_HANDLE) { | |
| 2621 | portid = inot->in_portid_hi << 16 | inot->in_portid_lo; | |
| 2622 | } else { | |
| 2623 | portid = PORT_ANY; | |
| 2624 | } | |
| 984263bc | 2625 | |
| 191d7ec1 SW |
2626 | switch (inot->in_status) { |
| 2627 | case IN24XX_ELS_RCVD: | |
| 2628 | { | |
| 2629 | char buf[16], *msg; | |
| 2630 | int chan = ISP_GET_VPIDX(isp, inot->in_vpidx); | |
| 984263bc | 2631 | |
| 191d7ec1 SW |
2632 | /* |
| 2633 | * Note that we're just getting notification that an ELS was received | |
| 2634 | * (possibly with some associated information sent upstream). This is | |
| 2635 | * *not* the same as being given the ELS frame to accept or reject. | |
| 2636 | */ | |
| 2637 | switch (inot->in_status_subcode) { | |
| 2638 | case LOGO: | |
| 2639 | msg = "LOGO"; | |
| 2640 | if (ISP_FW_NEWER_THAN(isp, 4, 0, 25)) { | |
| 2641 | ptr = (uint8_t *)inot; /* point to unswizzled entry! */ | |
| 2642 | wwn = (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF]) << 56) | | |
| 2643 | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+1]) << 48) | | |
| 2644 | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+2]) << 40) | | |
| 2645 | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+3]) << 32) | | |
| 2646 | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+4]) << 24) | | |
| 2647 | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+5]) << 16) | | |
| 2648 | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+6]) << 8) | | |
| 2649 | (((uint64_t) ptr[IN24XX_LOGO_WWPN_OFF+7])); | |
| 2650 | } else { | |
| 2651 | wwn = INI_ANY; | |
| 984263bc | 2652 | } |
| 191d7ec1 SW |
2653 | isp_del_wwn_entry(isp, chan, wwn, nphdl, portid); |
| 2654 | break; | |
| 2655 | case PRLO: | |
| 2656 | msg = "PRLO"; | |
| 2657 | break; | |
| 2658 | case PLOGI: | |
| 2659 | case PRLI: | |
| 2660 | /* | |
| 2661 | * Treat PRLI the same as PLOGI and make a database entry for it. | |
| 2662 | */ | |
| 2663 | if (inot->in_status_subcode == PLOGI) | |
| 2664 | msg = "PLOGI"; | |
| 2665 | else | |
| 2666 | msg = "PRLI"; | |
| 2667 | if (ISP_FW_NEWER_THAN(isp, 4, 0, 25)) { | |
| 2668 | ptr = (uint8_t *)inot; /* point to unswizzled entry! */ | |
| 2669 | wwn = (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF]) << 56) | | |
| 2670 | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+1]) << 48) | | |
| 2671 | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+2]) << 40) | | |
| 2672 | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+3]) << 32) | | |
| 2673 | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+4]) << 24) | | |
| 2674 | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+5]) << 16) | | |
| 2675 | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+6]) << 8) | | |
| 2676 | (((uint64_t) ptr[IN24XX_PLOGI_WWPN_OFF+7])); | |
| 2677 | } else { | |
| 2678 | wwn = INI_NONE; | |
| 2679 | } | |
| 2680 | isp_add_wwn_entry(isp, chan, wwn, nphdl, portid); | |
| 2681 | break; | |
| 2682 | case PDISC: | |
| 2683 | msg = "PDISC"; | |
| 2684 | break; | |
| 2685 | case ADISC: | |
| 2686 | msg = "ADISC"; | |
| 2687 | break; | |
| 2688 | default: | |
| 2689 | ISP_SNPRINTF(buf, sizeof (buf), "ELS 0x%x", inot->in_status_subcode); | |
| 2690 | msg = buf; | |
| 2691 | break; | |
| 2692 | } | |
| 2693 | if (inot->in_flags & IN24XX_FLAG_PUREX_IOCB) { | |
| 2694 | isp_prt(isp, ISP_LOGERR, "%s Chan %d ELS N-port handle %x PortID 0x%06x marked as needing a PUREX response", msg, chan, nphdl, portid); | |
| 2695 | break; | |
| 2696 | } | |
| 2697 | isp_prt(isp, ISP_LOGTDEBUG0, "%s Chan %d ELS N-port handle %x PortID 0x%06x RX_ID 0x%x OX_ID 0x%x", msg, chan, nphdl, portid, | |
| 2698 | inot->in_rxid, inot->in_oxid); | |
| 2699 | (void) isp_notify_ack(isp, inot); | |
| 2700 | break; | |
| 2701 | } | |
| 2702 | ||
| 2703 | case IN24XX_PORT_LOGOUT: | |
| 2704 | ptr = "PORT LOGOUT"; | |
| 2705 | if (isp_find_pdb_by_loopid(isp, ISP_GET_VPIDX(isp, inot->in_vpidx), nphdl, &lp)) { | |
| 2706 | isp_del_wwn_entry(isp, ISP_GET_VPIDX(isp, inot->in_vpidx), lp->port_wwn, nphdl, lp->portid); | |
| 2707 | } | |
| 2708 | /* FALLTHROUGH */ | |
| 2709 | case IN24XX_PORT_CHANGED: | |
| 2710 | if (ptr == NULL) { | |
| 2711 | ptr = "PORT CHANGED"; | |
| 2712 | } | |
| 2713 | /* FALLTHROUGH */ | |
| 2714 | case IN24XX_LIP_RESET: | |
| 2715 | if (ptr == NULL) { | |
| 2716 | ptr = "LIP RESET"; | |
| 984263bc | 2717 | } |
| 191d7ec1 SW |
2718 | isp_prt(isp, ISP_LOGINFO, "Chan %d %s (sub-status 0x%x) for N-port handle 0x%x", ISP_GET_VPIDX(isp, inot->in_vpidx), ptr, inot->in_status_subcode, nphdl); |
| 2719 | ||
| 2720 | /* | |
| 2721 | * All subcodes here are irrelevant. What is relevant | |
| 2722 | * is that we need to terminate all active commands from | |
| 2723 | * this initiator (known by N-port handle). | |
| 2724 | */ | |
| 2725 | /* XXX IMPLEMENT XXX */ | |
| 2726 | (void) isp_notify_ack(isp, inot); | |
| 984263bc | 2727 | break; |
| 191d7ec1 SW |
2728 | |
| 2729 | case IN24XX_LINK_RESET: | |
| 2730 | case IN24XX_LINK_FAILED: | |
| 2731 | case IN24XX_SRR_RCVD: | |
| 984263bc | 2732 | default: |
| 191d7ec1 | 2733 | (void) isp_notify_ack(isp, inot); |
| 984263bc MD |
2734 | break; |
| 2735 | } | |
| 2736 | } | |
| 2737 | ||
| 191d7ec1 SW |
2738 | static int |
| 2739 | isp_handle_platform_target_notify_ack(ispsoftc_t *isp, isp_notify_t *mp) | |
| 2740 | { | |
| 2741 | ||
| 2742 | if (isp->isp_state != ISP_RUNSTATE) { | |
| 2743 | isp_prt(isp, ISP_LOGTINFO, "Notify Code 0x%x (qevalid=%d) acked- h/w not ready (dropping)", mp->nt_ncode, mp->nt_lreserved != NULL); | |
| 2744 | return (0); | |
| 2745 | } | |
| 2746 | ||
| 2747 | /* | |
| 2748 | * This case is for a Task Management Function, which shows up as an ATIO7 entry. | |
| 2749 | */ | |
| 2750 | if (IS_24XX(isp) && mp->nt_lreserved && ((isphdr_t *)mp->nt_lreserved)->rqs_entry_type == RQSTYPE_ATIO) { | |
| 2751 | ct7_entry_t local, *cto = &local; | |
| 2752 | at7_entry_t *aep = (at7_entry_t *)mp->nt_lreserved; | |
| 2753 | fcportdb_t *lp; | |
| 2754 | uint32_t sid; | |
| 2755 | uint16_t nphdl; | |
| 2756 | ||
| 2757 | sid = (aep->at_hdr.s_id[0] << 16) | (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2]; | |
| 2758 | if (isp_find_pdb_by_sid(isp, mp->nt_channel, sid, &lp)) { | |
| 2759 | nphdl = lp->handle; | |
| 2760 | } else { | |
| 2761 | nphdl = NIL_HANDLE; | |
| 2762 | } | |
| 2763 | ISP_MEMZERO(&local, sizeof (local)); | |
| 2764 | cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7; | |
| 2765 | cto->ct_header.rqs_entry_count = 1; | |
| 2766 | cto->ct_nphdl = nphdl; | |
| 2767 | cto->ct_rxid = aep->at_rxid; | |
| 2768 | cto->ct_vpidx = mp->nt_channel; | |
| 2769 | cto->ct_iid_lo = sid; | |
| 2770 | cto->ct_iid_hi = sid >> 16; | |
| 2771 | cto->ct_oxid = aep->at_hdr.ox_id; | |
| 2772 | cto->ct_flags = CT7_SENDSTATUS|CT7_NOACK|CT7_NO_DATA|CT7_FLAG_MODE1; | |
| 2773 | cto->ct_flags |= (aep->at_ta_len >> 12) << CT7_TASK_ATTR_SHIFT; | |
| 2774 | return (isp_target_put_entry(isp, &local)); | |
| 2775 | } | |
| 2776 | ||
| 2777 | /* | |
| 2778 | * This case is for a responding to an ABTS frame | |
| 2779 | */ | |
| 2780 | if (IS_24XX(isp) && mp->nt_lreserved && ((isphdr_t *)mp->nt_lreserved)->rqs_entry_type == RQSTYPE_ABTS_RCVD) { | |
| 2781 | ||
| 2782 | /* | |
| 2783 | * Overload nt_need_ack here to mark whether we've terminated the associated command. | |
| 2784 | */ | |
| 2785 | if (mp->nt_need_ack) { | |
| 2786 | uint8_t storage[QENTRY_LEN]; | |
| 2787 | ct7_entry_t *cto = (ct7_entry_t *) storage; | |
| 2788 | abts_t *abts = (abts_t *)mp->nt_lreserved; | |
| 2789 | ||
| 2790 | ISP_MEMZERO(cto, sizeof (ct7_entry_t)); | |
| 2791 | isp_prt(isp, ISP_LOGTDEBUG0, "%s: [%x] terminating after ABTS received", __func__, abts->abts_rxid_task); | |
| 2792 | cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7; | |
| 2793 | cto->ct_header.rqs_entry_count = 1; | |
| 2794 | cto->ct_nphdl = mp->nt_nphdl; | |
| 2795 | cto->ct_rxid = abts->abts_rxid_task; | |
| 2796 | cto->ct_iid_lo = mp->nt_sid; | |
| 2797 | cto->ct_iid_hi = mp->nt_sid >> 16; | |
| 2798 | cto->ct_oxid = abts->abts_ox_id; | |
| 2799 | cto->ct_vpidx = mp->nt_channel; | |
| 2800 | cto->ct_flags = CT7_NOACK|CT7_TERMINATE; | |
| 2801 | if (isp_target_put_entry(isp, cto)) { | |
| 2802 | return (ENOMEM); | |
| 2803 | } | |
| 2804 | mp->nt_need_ack = 0; | |
| 2805 | } | |
| 2806 | if (isp_acknak_abts(isp, mp->nt_lreserved, 0) == ENOMEM) { | |
| 2807 | return (ENOMEM); | |
| 2808 | } else { | |
| 2809 | return (0); | |
| 2810 | } | |
| 2811 | } | |
| 2812 | ||
| 2813 | /* | |
| 2814 | * Handle logout cases here | |
| 2815 | */ | |
| 2816 | if (mp->nt_ncode == NT_GLOBAL_LOGOUT) { | |
| 2817 | isp_del_all_wwn_entries(isp, mp->nt_channel); | |
| 2818 | } | |
| 2819 | ||
| 2820 | if (mp->nt_ncode == NT_LOGOUT) { | |
| 2821 | if (!IS_2100(isp) && IS_FC(isp)) { | |
| 2822 | isp_del_wwn_entries(isp, mp); | |
| 2823 | } | |
| 2824 | } | |
| 2825 | ||
| 2826 | /* | |
| 2827 | * General purpose acknowledgement | |
| 2828 | */ | |
| 2829 | if (mp->nt_need_ack) { | |
| 2830 | isp_prt(isp, ISP_LOGTINFO, "Notify Code 0x%x (qevalid=%d) being acked", mp->nt_ncode, mp->nt_lreserved != NULL); | |
| 2831 | return (isp_notify_ack(isp, mp->nt_lreserved)); | |
| 2832 | } | |
| 2833 | return (0); | |
| 2834 | } | |
| 2835 | ||
| 2836 | /* | |
| 2837 | * Handle task management functions. | |
| 2838 | * | |
| 2839 | * We show up here with a notify structure filled out. | |
| 2840 | * | |
| 2841 | * The nt_lreserved tag points to the original queue entry | |
| 2842 | */ | |
| 2843 | static void | |
| 2844 | isp_handle_platform_target_tmf(ispsoftc_t *isp, isp_notify_t *notify) | |
| 2845 | { | |
| 2846 | tstate_t *tptr; | |
| 2847 | fcportdb_t *lp; | |
| 2848 | struct ccb_immediate_notify *inot; | |
| 2849 | inot_private_data_t *ntp = NULL; | |
| 2850 | lun_id_t lun; | |
| 2851 | ||
| 2852 | isp_prt(isp, ISP_LOGTDEBUG0, "%s: code 0x%x sid 0x%x tagval 0x%016llx chan %d lun 0x%x", __func__, notify->nt_ncode, | |
| 2853 | notify->nt_sid, (unsigned long long) notify->nt_tagval, notify->nt_channel, notify->nt_lun); | |
| 2854 | /* | |
| 2855 | * NB: This assignment is necessary because of tricky type conversion. | |
| 2856 | * XXX: This is tricky and I need to check this. If the lun isn't known | |
| 2857 | * XXX: for the task management function, it does not of necessity follow | |
| 2858 | * XXX: that it should go up stream to the wildcard listener. | |
| 2859 | */ | |
| 2860 | if (notify->nt_lun == LUN_ANY) { | |
| 2861 | lun = CAM_LUN_WILDCARD; | |
| 2862 | } else { | |
| 2863 | lun = notify->nt_lun; | |
| 2864 | } | |
| 2865 | tptr = get_lun_statep(isp, notify->nt_channel, lun); | |
| 2866 | if (tptr == NULL) { | |
| 2867 | tptr = get_lun_statep(isp, notify->nt_channel, CAM_LUN_WILDCARD); | |
| 2868 | if (tptr == NULL) { | |
| 2869 | isp_prt(isp, ISP_LOGWARN, "%s: no state pointer found for chan %d lun 0x%x", __func__, notify->nt_channel, lun); | |
| 2870 | goto bad; | |
| 2871 | } | |
| 2872 | } | |
| 2873 | inot = (struct ccb_immediate_notify *) SLIST_FIRST(&tptr->inots); | |
| 2874 | if (inot == NULL) { | |
| 2875 | isp_prt(isp, ISP_LOGWARN, "%s: out of immediate notify structures for chan %d lun 0x%x", __func__, notify->nt_channel, lun); | |
| 2876 | goto bad; | |
| 2877 | } | |
| 2878 | ||
| 2879 | if (isp_find_pdb_by_sid(isp, notify->nt_channel, notify->nt_sid, &lp) == 0) { | |
| 2880 | inot->initiator_id = CAM_TARGET_WILDCARD; | |
| 2881 | } else { | |
| 2882 | inot->initiator_id = lp->handle; | |
| 2883 | } | |
| 2884 | inot->seq_id = notify->nt_tagval; | |
| 2885 | inot->tag_id = notify->nt_tagval >> 32; | |
| 2886 | ||
| 2887 | switch (notify->nt_ncode) { | |
| 2888 | case NT_ABORT_TASK: | |
| 2889 | isp_target_mark_aborted_early(isp, tptr, inot->tag_id); | |
| 2890 | inot->arg = MSG_ABORT_TASK; | |
| 2891 | break; | |
| 2892 | case NT_ABORT_TASK_SET: | |
| 2893 | isp_target_mark_aborted_early(isp, tptr, TAG_ANY); | |
| 2894 | inot->arg = MSG_ABORT_TASK_SET; | |
| 2895 | break; | |
| 2896 | case NT_CLEAR_ACA: | |
| 2897 | inot->arg = MSG_CLEAR_ACA; | |
| 2898 | break; | |
| 2899 | case NT_CLEAR_TASK_SET: | |
| 2900 | inot->arg = MSG_CLEAR_TASK_SET; | |
| 2901 | break; | |
| 2902 | case NT_LUN_RESET: | |
| 2903 | inot->arg = MSG_LOGICAL_UNIT_RESET; | |
| 2904 | break; | |
| 2905 | case NT_TARGET_RESET: | |
| 2906 | inot->arg = MSG_TARGET_RESET; | |
| 2907 | break; | |
| 2908 | default: | |
| 2909 | isp_prt(isp, ISP_LOGWARN, "%s: unknown TMF code 0x%x for chan %d lun 0x%x", __func__, notify->nt_ncode, notify->nt_channel, lun); | |
| 2910 | goto bad; | |
| 2911 | } | |
| 2912 | ||
| 2913 | ntp = isp_get_ntpd(isp, tptr); | |
| 2914 | if (ntp == NULL) { | |
| 2915 | isp_prt(isp, ISP_LOGWARN, "%s: out of inotify private structures", __func__); | |
| 2916 | goto bad; | |
| 2917 | } | |
| 2918 | ISP_MEMCPY(&ntp->rd.nt, notify, sizeof (isp_notify_t)); | |
| 2919 | if (notify->nt_lreserved) { | |
| 2920 | ISP_MEMCPY(&ntp->rd.data, notify->nt_lreserved, QENTRY_LEN); | |
| 2921 | ntp->rd.nt.nt_lreserved = &ntp->rd.data; | |
| 2922 | } | |
| 2923 | ntp->rd.seq_id = notify->nt_tagval; | |
| 2924 | ntp->rd.tag_id = notify->nt_tagval >> 32; | |
| 2925 | ||
| 2926 | tptr->inot_count--; | |
| 2927 | SLIST_REMOVE_HEAD(&tptr->inots, sim_links.sle); | |
| 2928 | rls_lun_statep(isp, tptr); | |
| 2929 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: Take FREE INOT count now %d\n", __func__, tptr->inot_count); | |
| 2930 | inot->ccb_h.status = CAM_MESSAGE_RECV; | |
| 2931 | xpt_done((union ccb *)inot); | |
| 2932 | return; | |
| 2933 | bad: | |
| 2934 | if (tptr) { | |
| 2935 | rls_lun_statep(isp, tptr); | |
| 2936 | } | |
| 2937 | if (notify->nt_need_ack && notify->nt_lreserved) { | |
| 2938 | if (((isphdr_t *)notify->nt_lreserved)->rqs_entry_type == RQSTYPE_ABTS_RCVD) { | |
| 2939 | (void) isp_acknak_abts(isp, notify->nt_lreserved, ENOMEM); | |
| 2940 | } else { | |
| 2941 | (void) isp_notify_ack(isp, notify->nt_lreserved); | |
| 2942 | } | |
| 2943 | } | |
| 2944 | } | |
| 2945 | ||
| 2946 | /* | |
| 2947 | * Find the associated private data and mark it as dead so | |
| 2948 | * we don't try to work on it any further. | |
| 2949 | */ | |
| 2950 | static void | |
| 2951 | isp_target_mark_aborted(ispsoftc_t *isp, union ccb *ccb) | |
| 2952 | { | |
| 2953 | tstate_t *tptr; | |
| 2954 | atio_private_data_t *atp; | |
| 2955 | ||
| 2956 | tptr = get_lun_statep(isp, XS_CHANNEL(ccb), XS_LUN(ccb)); | |
| 2957 | if (tptr == NULL) { | |
| 2958 | tptr = get_lun_statep(isp, XS_CHANNEL(ccb), CAM_LUN_WILDCARD); | |
| 2959 | if (tptr == NULL) { | |
| 2960 | ccb->ccb_h.status = CAM_REQ_INVALID; | |
| 2961 | return; | |
| 2962 | } | |
| 2963 | } | |
| 2964 | ||
| 2965 | atp = isp_get_atpd(isp, tptr, ccb->atio.tag_id); | |
| 2966 | if (atp == NULL) { | |
| 2967 | ccb->ccb_h.status = CAM_REQ_INVALID; | |
| 2968 | return; | |
| 2969 | } | |
| 2970 | atp->dead = 1; | |
| 2971 | ccb->ccb_h.status = CAM_REQ_CMP; | |
| 2972 | } | |
| 2973 | ||
| 2974 | static void | |
| 2975 | isp_target_mark_aborted_early(ispsoftc_t *isp, tstate_t *tptr, uint32_t tag_id) | |
| 2976 | { | |
| 2977 | atio_private_data_t *atp; | |
| 2978 | inot_private_data_t *restart_queue = tptr->restart_queue; | |
| 2979 | ||
| 2980 | /* | |
| 2981 | * First, clean any commands pending restart | |
| 2982 | */ | |
| 2983 | tptr->restart_queue = NULL; | |
| 2984 | while (restart_queue) { | |
| 2985 | uint32_t this_tag_id; | |
| 2986 | inot_private_data_t *ntp = restart_queue; | |
| 2987 | ||
| 2988 | restart_queue = ntp->rd.nt.nt_hba; | |
| 2989 | ||
| 2990 | if (IS_24XX(isp)) { | |
| 2991 | this_tag_id = ((at7_entry_t *)ntp->rd.data)->at_rxid; | |
| 2992 | } else { | |
| 2993 | this_tag_id = ((at2_entry_t *)ntp->rd.data)->at_rxid; | |
| 2994 | } | |
| 2995 | if ((uint64_t)tag_id == TAG_ANY || tag_id == this_tag_id) { | |
| 2996 | isp_put_ntpd(isp, tptr, ntp); | |
| 2997 | } else { | |
| 2998 | ntp->rd.nt.nt_hba = tptr->restart_queue; | |
| 2999 | tptr->restart_queue = ntp; | |
| 3000 | } | |
| 3001 | } | |
| 3002 | ||
| 3003 | /* | |
| 3004 | * Now mark other ones dead as well. | |
| 3005 | */ | |
| 3006 | for (atp = tptr->atpool; atp < &tptr->atpool[ATPDPSIZE]; atp++) { | |
| 3007 | if ((uint64_t)tag_id == TAG_ANY || atp->tag == tag_id) { | |
| 3008 | atp->dead = 1; | |
| 3009 | } | |
| 3010 | } | |
| 3011 | } | |
| 3012 | ||
| 3013 | ||
| 3014 | #ifdef ISP_INTERNAL_TARGET | |
| 3015 | // #define ISP_FORCE_TIMEOUT 1 | |
| 3016 | // #define ISP_TEST_WWNS 1 | |
| 3017 | // #define ISP_TEST_SEPARATE_STATUS 1 | |
| 3018 | ||
| 3019 | #define ccb_data_offset ppriv_field0 | |
| 3020 | #define ccb_atio ppriv_ptr1 | |
| 3021 | #define ccb_inot ppriv_ptr1 | |
| 3022 | ||
| 3023 | #define MAX_ISP_TARG_TRANSFER (2 << 20) | |
| 3024 | #define NISP_TARG_CMDS 1024 | |
| 3025 | #define NISP_TARG_NOTIFIES 1024 | |
| 3026 | #define DISK_SHIFT 9 | |
| 3027 | #define JUNK_SIZE 256 | |
| 3028 | ||
| 3029 | #ifndef VERIFY_10 | |
| 3030 | #define VERIFY_10 0x2f | |
| 3031 | #endif | |
| 3032 | ||
| 3033 | TAILQ_HEAD(ccb_queue, ccb_hdr); | |
| 3034 | extern u_int vm_kmem_size; | |
| 3035 | static int ca; | |
| 3036 | static uint32_t disk_size; | |
| 3037 | static uint8_t *disk_data = NULL; | |
| 3038 | static uint8_t *junk_data; | |
| 3039 | static MALLOC_DEFINE(M_ISPTARG, "ISPTARG", "ISP TARGET data"); | |
| 3040 | struct isptarg_softc { | |
| 3041 | /* CCBs (CTIOs, ATIOs, INOTs) pending on the controller */ | |
| 3042 | struct ccb_queue work_queue; | |
| 3043 | struct ccb_queue rework_queue; | |
| 3044 | struct ccb_queue running_queue; | |
| 3045 | struct ccb_queue inot_queue; | |
| 3046 | struct cam_periph *periph; | |
| 3047 | struct cam_path *path; | |
| 3048 | ispsoftc_t *isp; | |
| 3049 | }; | |
| 3050 | static periph_ctor_t isptargctor; | |
| 3051 | static periph_dtor_t isptargdtor; | |
| 3052 | static periph_start_t isptargstart; | |
| 3053 | static periph_init_t isptarginit; | |
| 3054 | static void isptarg_done(struct cam_periph *, union ccb *); | |
| 3055 | static void isptargasync(void *, u_int32_t, struct cam_path *, void *); | |
| 3056 | ||
| 3057 | ||
| 3058 | static int isptarg_rwparm(uint8_t *, uint8_t *, uint64_t, uint32_t, uint8_t **, uint32_t *, int *); | |
| 3059 | ||
| 3060 | static struct periph_driver isptargdriver = | |
| 3061 | { | |
| 3062 | isptarginit, "isptarg", TAILQ_HEAD_INITIALIZER(isptargdriver.units), /* generation */ 0 | |
| 3063 | }; | |
| 3064 | ||
| 3065 | static void | |
| 3066 | isptarginit(void) | |
| 3067 | { | |
| 3068 | } | |
| 3069 | ||
| 3070 | static void | |
| 3071 | isptargnotify(ispsoftc_t *isp, union ccb *iccb, struct ccb_immediate_notify *inot) | |
| 3072 | { | |
| 3073 | struct ccb_notify_acknowledge *ack = &iccb->cna2; | |
| 3074 | ||
| 3075 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "%s: [0x%x] immediate notify for 0x%x from 0x%x status 0x%x arg 0x%x\n", __func__, | |
| 3076 | inot->tag_id, inot->initiator_id, inot->seq_id, inot->ccb_h.status, inot->arg); | |
| 3077 | ack->ccb_h.func_code = XPT_NOTIFY_ACKNOWLEDGE; | |
| 3078 | ack->ccb_h.flags = 0; | |
| 3079 | ack->ccb_h.retry_count = 0; | |
| 3080 | ack->ccb_h.cbfcnp = isptarg_done; | |
| 3081 | ack->ccb_h.timeout = 0; | |
| 3082 | ack->ccb_h.ccb_inot = inot; | |
| 3083 | ack->tag_id = inot->tag_id; | |
| 3084 | ack->seq_id = inot->seq_id; | |
| 3085 | ack->initiator_id = inot->initiator_id; | |
| 3086 | xpt_action(iccb); | |
| 3087 | } | |
| 3088 | ||
| 3089 | static void | |
| 3090 | isptargstart(struct cam_periph *periph, union ccb *iccb) | |
| 3091 | { | |
| 3092 | const uint8_t niliqd[SHORT_INQUIRY_LENGTH] = { | |
| 3093 | 0x7f, 0x0, 0x5, 0x2, 32, 0, 0, 0x32, | |
| 3094 | 'F', 'R', 'E', 'E', 'B', 'S', 'D', ' ', | |
| 3095 | 'S', 'C', 'S', 'I', ' ', 'N', 'U', 'L', | |
| 3096 | 'L', ' ', 'D', 'E', 'V', 'I', 'C', 'E', | |
| 3097 | '0', '0', '0', '1' | |
| 3098 | }; | |
| 3099 | const uint8_t iqd[SHORT_INQUIRY_LENGTH] = { | |
| 3100 | 0, 0x0, 0x5, 0x2, 32, 0, 0, 0x32, | |
| 3101 | 'F', 'R', 'E', 'E', 'B', 'S', 'D', ' ', | |
| 3102 | 'S', 'C', 'S', 'I', ' ', 'M', 'E', 'M', | |
| 3103 | 'O', 'R', 'Y', ' ', 'D', 'I', 'S', 'K', | |
| 3104 | '0', '0', '0', '1' | |
| 3105 | }; | |
| 3106 | int i, more = 0, last; | |
| 3107 | struct isptarg_softc *softc = periph->softc; | |
| 3108 | struct ccb_scsiio *csio; | |
| 3109 | lun_id_t return_lun; | |
| 3110 | struct ccb_accept_tio *atio; | |
| 3111 | uint8_t *cdb, *ptr, status; | |
| 3112 | uint8_t *data_ptr; | |
| 3113 | uint32_t data_len, flags; | |
| 3114 | struct ccb_hdr *ccbh; | |
| 3115 | ||
| 3116 | KKASSERT(lockstatus(periph->sim->mtx, curthread) != 0); | |
| 3117 | ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, iccb->ccb_h.path, "%s: function code 0x%x INOTQ=%c WORKQ=%c REWORKQ=%c\n", __func__, iccb->ccb_h.func_code, | |
| 3118 | TAILQ_FIRST(&softc->inot_queue)? 'y' : 'n', TAILQ_FIRST(&softc->work_queue)? 'y' : 'n', TAILQ_FIRST(&softc->rework_queue)? 'y' : 'n'); | |
| 3119 | /* | |
| 3120 | * Check for immediate notifies first | |
| 3121 | */ | |
| 3122 | ccbh = TAILQ_FIRST(&softc->inot_queue); | |
| 3123 | if (ccbh) { | |
| 3124 | TAILQ_REMOVE(&softc->inot_queue, ccbh, periph_links.tqe); | |
| 3125 | if (TAILQ_FIRST(&softc->inot_queue) || TAILQ_FIRST(&softc->work_queue) || TAILQ_FIRST(&softc->rework_queue)) { | |
| 3126 | xpt_schedule(periph, 1); | |
| 3127 | } | |
| 3128 | isptargnotify(softc->isp, iccb, (struct ccb_immediate_notify *)ccbh); | |
| 3129 | return; | |
| 3130 | } | |
| 3131 | ||
| 3132 | /* | |
| 3133 | * Check the rework (continuation) work queue first. | |
| 3134 | */ | |
| 3135 | ccbh = TAILQ_FIRST(&softc->rework_queue); | |
| 3136 | if (ccbh) { | |
| 3137 | atio = (struct ccb_accept_tio *)ccbh; | |
| 3138 | TAILQ_REMOVE(&softc->rework_queue, ccbh, periph_links.tqe); | |
| 3139 | more = TAILQ_FIRST(&softc->work_queue) || TAILQ_FIRST(&softc->rework_queue); | |
| 3140 | } else { | |
| 3141 | ccbh = TAILQ_FIRST(&softc->work_queue); | |
| 3142 | if (ccbh == NULL) { | |
| 3143 | ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, iccb->ccb_h.path, "%s: woken up but no work?\n", __func__); | |
| 3144 | xpt_release_ccb(iccb); | |
| 3145 | return; | |
| 3146 | } | |
| 3147 | atio = (struct ccb_accept_tio *)ccbh; | |
| 3148 | TAILQ_REMOVE(&softc->work_queue, ccbh, periph_links.tqe); | |
| 3149 | more = TAILQ_FIRST(&softc->work_queue) != NULL; | |
| 3150 | atio->ccb_h.ccb_data_offset = 0; | |
| 3151 | } | |
| 3152 | ||
| 3153 | if (atio->tag_id == 0xffffffff || atio->ccb_h.func_code != XPT_ACCEPT_TARGET_IO) { | |
| 3154 | panic("BAD ATIO"); | |
| 3155 | } | |
| 3156 | ||
| 3157 | data_ptr = NULL; | |
| 3158 | data_len = 0; | |
| 3159 | csio = &iccb->csio; | |
| 3160 | status = SCSI_STATUS_OK; | |
| 3161 | flags = CAM_SEND_STATUS; | |
| 3162 | memset(&atio->sense_data, 0, sizeof (atio->sense_data)); | |
| 3163 | cdb = atio->cdb_io.cdb_bytes; | |
| 3164 | ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, ccbh->path, "%s: [0x%x] processing ATIO from 0x%x CDB=0x%x data_offset=%u\n", __func__, atio->tag_id, atio->init_id, | |
| 3165 | cdb[0], atio->ccb_h.ccb_data_offset); | |
| 3166 | ||
| 3167 | return_lun = XS_LUN(atio); | |
| 3168 | if (return_lun != 0) { | |
| 3169 | xpt_print(atio->ccb_h.path, "[0x%x] Non-Zero Lun %d: cdb0=0x%x\n", atio->tag_id, return_lun, cdb[0]); | |
| 3170 | if (cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { | |
| 3171 | status = SCSI_STATUS_CHECK_COND; | |
| 3172 | atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_ILLEGAL_REQUEST; | |
| 3173 | atio->sense_data.add_sense_code = 0x25; | |
| 3174 | atio->sense_data.add_sense_code_qual = 0x0; | |
| 3175 | atio->sense_len = sizeof (atio->sense_data); | |
| 3176 | } | |
| 3177 | return_lun = CAM_LUN_WILDCARD; | |
| 3178 | } | |
| 3179 | ||
| 3180 | switch (cdb[0]) { | |
| 3181 | case REQUEST_SENSE: | |
| 3182 | flags |= CAM_DIR_IN; | |
| 3183 | data_len = sizeof (atio->sense_data); | |
| 3184 | junk_data[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_NO_SENSE; | |
| 3185 | memset(junk_data+1, 0, data_len-1); | |
| 3186 | if (data_len > cdb[4]) { | |
| 3187 | data_len = cdb[4]; | |
| 3188 | } | |
| 3189 | if (data_len) { | |
| 3190 | data_ptr = junk_data; | |
| 3191 | } | |
| 3192 | break; | |
| 3193 | case READ_6: | |
| 3194 | case READ_10: | |
| 3195 | case READ_12: | |
| 3196 | case READ_16: | |
| 3197 | if (isptarg_rwparm(cdb, disk_data, disk_size, atio->ccb_h.ccb_data_offset, &data_ptr, &data_len, &last)) { | |
| 3198 | status = SCSI_STATUS_CHECK_COND; | |
| 3199 | atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; | |
| 3200 | atio->sense_data.add_sense_code = 0x5; | |
| 3201 | atio->sense_data.add_sense_code_qual = 0x24; | |
| 3202 | atio->sense_len = sizeof (atio->sense_data); | |
| 3203 | } else { | |
| 3204 | #ifdef ISP_FORCE_TIMEOUT | |
| 3205 | { | |
| 3206 | static int foo; | |
| 3207 | if (foo++ == 500) { | |
| 3208 | if (more) { | |
| 3209 | xpt_schedule(periph, 1); | |
| 3210 | } | |
| 3211 | foo = 0; | |
| 3212 | return; | |
| 3213 | } | |
| 3214 | } | |
| 3215 | #endif | |
| 3216 | #ifdef ISP_TEST_SEPARATE_STATUS | |
| 3217 | if (last && data_len) { | |
| 3218 | last = 0; | |
| 3219 | } | |
| 3220 | #endif | |
| 3221 | if (last == 0) { | |
| 3222 | flags &= ~CAM_SEND_STATUS; | |
| 3223 | } | |
| 3224 | if (data_len) { | |
| 3225 | atio->ccb_h.ccb_data_offset += data_len; | |
| 3226 | flags |= CAM_DIR_IN; | |
| 3227 | } else { | |
| 3228 | flags |= CAM_DIR_NONE; | |
| 3229 | } | |
| 3230 | } | |
| 3231 | break; | |
| 3232 | case WRITE_6: | |
| 3233 | case WRITE_10: | |
| 3234 | case WRITE_12: | |
| 3235 | case WRITE_16: | |
| 3236 | if (isptarg_rwparm(cdb, disk_data, disk_size, atio->ccb_h.ccb_data_offset, &data_ptr, &data_len, &last)) { | |
| 3237 | status = SCSI_STATUS_CHECK_COND; | |
| 3238 | atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; | |
| 3239 | atio->sense_data.add_sense_code = 0x5; | |
| 3240 | atio->sense_data.add_sense_code_qual = 0x24; | |
| 3241 | atio->sense_len = sizeof (atio->sense_data); | |
| 3242 | } else { | |
| 3243 | #ifdef ISP_FORCE_TIMEOUT | |
| 3244 | { | |
| 3245 | static int foo; | |
| 3246 | if (foo++ == 500) { | |
| 3247 | if (more) { | |
| 3248 | xpt_schedule(periph, 1); | |
| 3249 | } | |
| 3250 | foo = 0; | |
| 3251 | return; | |
| 3252 | } | |
| 3253 | } | |
| 3254 | #endif | |
| 3255 | #ifdef ISP_TEST_SEPARATE_STATUS | |
| 3256 | if (last && data_len) { | |
| 3257 | last = 0; | |
| 3258 | } | |
| 3259 | #endif | |
| 3260 | if (last == 0) { | |
| 3261 | flags &= ~CAM_SEND_STATUS; | |
| 3262 | } | |
| 3263 | if (data_len) { | |
| 3264 | atio->ccb_h.ccb_data_offset += data_len; | |
| 3265 | flags |= CAM_DIR_OUT; | |
| 3266 | } else { | |
| 3267 | flags |= CAM_DIR_NONE; | |
| 3268 | } | |
| 3269 | } | |
| 3270 | break; | |
| 3271 | case INQUIRY: | |
| 3272 | flags |= CAM_DIR_IN; | |
| 3273 | if (cdb[1] || cdb[2] || cdb[3]) { | |
| 3274 | status = SCSI_STATUS_CHECK_COND; | |
| 3275 | atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; | |
| 3276 | atio->sense_data.add_sense_code = 0x5; | |
| 3277 | atio->sense_data.add_sense_code_qual = 0x20; | |
| 3278 | atio->sense_len = sizeof (atio->sense_data); | |
| 3279 | break; | |
| 3280 | } | |
| 3281 | data_len = sizeof (iqd); | |
| 3282 | if (data_len > cdb[4]) { | |
| 3283 | data_len = cdb[4]; | |
| 3284 | } | |
| 3285 | if (data_len) { | |
| 3286 | if (XS_LUN(iccb) != 0) { | |
| 3287 | memcpy(junk_data, niliqd, sizeof (iqd)); | |
| 3288 | } else { | |
| 3289 | memcpy(junk_data, iqd, sizeof (iqd)); | |
| 3290 | } | |
| 3291 | data_ptr = junk_data; | |
| 3292 | } | |
| 3293 | break; | |
| 3294 | case TEST_UNIT_READY: | |
| 3295 | flags |= CAM_DIR_NONE; | |
| 3296 | if (ca) { | |
| 3297 | ca = 0; | |
| 3298 | status = SCSI_STATUS_CHECK_COND; | |
| 3299 | atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; | |
| 3300 | atio->sense_data.add_sense_code = 0x28; | |
| 3301 | atio->sense_data.add_sense_code_qual = 0x0; | |
| 3302 | atio->sense_len = sizeof (atio->sense_data); | |
| 3303 | } | |
| 3304 | break; | |
| 3305 | case SYNCHRONIZE_CACHE: | |
| 3306 | case START_STOP: | |
| 3307 | case RESERVE: | |
| 3308 | case RELEASE: | |
| 3309 | case VERIFY_10: | |
| 3310 | flags |= CAM_DIR_NONE; | |
| 3311 | break; | |
| 3312 | ||
| 3313 | case READ_CAPACITY: | |
| 3314 | flags |= CAM_DIR_IN; | |
| 3315 | if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { | |
| 3316 | status = SCSI_STATUS_CHECK_COND; | |
| 3317 | atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; | |
| 3318 | atio->sense_data.add_sense_code = 0x5; | |
| 3319 | atio->sense_data.add_sense_code_qual = 0x24; | |
| 3320 | atio->sense_len = sizeof (atio->sense_data); | |
| 3321 | break; | |
| 3322 | } | |
| 3323 | if (cdb[8] & 0x1) { /* PMI */ | |
| 3324 | junk_data[0] = 0xff; | |
| 3325 | junk_data[1] = 0xff; | |
| 3326 | junk_data[2] = 0xff; | |
| 3327 | junk_data[3] = 0xff; | |
| 3328 | } else { | |
| 3329 | uint64_t last_blk = (disk_size >> DISK_SHIFT) - 1; | |
| 3330 | if (last_blk < 0xffffffffULL) { | |
| 3331 | junk_data[0] = (last_blk >> 24) & 0xff; | |
| 3332 | junk_data[1] = (last_blk >> 16) & 0xff; | |
| 3333 | junk_data[2] = (last_blk >> 8) & 0xff; | |
| 3334 | junk_data[3] = (last_blk) & 0xff; | |
| 3335 | } else { | |
| 3336 | junk_data[0] = 0xff; | |
| 3337 | junk_data[1] = 0xff; | |
| 3338 | junk_data[2] = 0xff; | |
| 3339 | junk_data[3] = 0xff; | |
| 3340 | } | |
| 3341 | } | |
| 3342 | junk_data[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; | |
| 3343 | junk_data[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; | |
| 3344 | junk_data[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; | |
| 3345 | junk_data[7] = ((1 << DISK_SHIFT)) & 0xff; | |
| 3346 | data_ptr = junk_data; | |
| 3347 | data_len = 8; | |
| 3348 | break; | |
| 3349 | case REPORT_LUNS: | |
| 3350 | flags |= CAM_DIR_IN; | |
| 3351 | memset(junk_data, 0, JUNK_SIZE); | |
| 3352 | junk_data[0] = (1 << 3) >> 24; | |
| 3353 | junk_data[1] = (1 << 3) >> 16; | |
| 3354 | junk_data[2] = (1 << 3) >> 8; | |
| 3355 | junk_data[3] = (1 << 3); | |
| 3356 | ptr = NULL; | |
| 3357 | for (i = 0; i < 1; i++) { | |
| 3358 | ptr = &junk_data[8 + (1 << 3)]; | |
| 3359 | if (i >= 256) { | |
| 3360 | ptr[0] = 0x40 | ((i >> 8) & 0x3f); | |
| 3361 | } | |
| 3362 | ptr[1] = i; | |
| 3363 | } | |
| 3364 | data_ptr = junk_data; | |
| 3365 | data_len = (ptr + 8) - junk_data; | |
| 3366 | break; | |
| 3367 | ||
| 3368 | default: | |
| 3369 | flags |= CAM_DIR_NONE; | |
| 3370 | status = SCSI_STATUS_CHECK_COND; | |
| 3371 | atio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR|SSD_KEY_UNIT_ATTENTION; | |
| 3372 | atio->sense_data.add_sense_code = 0x5; | |
| 3373 | atio->sense_data.add_sense_code_qual = 0x20; | |
| 3374 | atio->sense_len = sizeof (atio->sense_data); | |
| 3375 | break; | |
| 3376 | } | |
| 3377 | ||
| 3378 | /* | |
| 3379 | * If we are done with the transaction, tell the | |
| 3380 | * controller to send status and perform a CMD_CMPLT. | |
| 3381 | * If we have associated sense data, see if we can | |
| 3382 | * send that too. | |
| 3383 | */ | |
| 3384 | if (status == SCSI_STATUS_CHECK_COND) { | |
| 3385 | flags |= CAM_SEND_SENSE; | |
| 3386 | csio->sense_len = atio->sense_len; | |
| 3387 | csio->sense_data = atio->sense_data; | |
| 3388 | flags &= ~CAM_DIR_MASK; | |
| 3389 | data_len = 0; | |
| 3390 | data_ptr = NULL; | |
| 3391 | } | |
| 3392 | cam_fill_ctio(csio, 0, isptarg_done, flags, MSG_SIMPLE_Q_TAG, atio->tag_id, atio->init_id, status, data_ptr, data_len, 0); | |
| 3393 | iccb->ccb_h.target_id = atio->ccb_h.target_id; | |
| 3394 | iccb->ccb_h.target_lun = return_lun; | |
| 3395 | iccb->ccb_h.ccb_atio = atio; | |
| 3396 | xpt_action(iccb); | |
| 3397 | ||
| 3398 | if ((atio->ccb_h.status & CAM_DEV_QFRZN) != 0) { | |
| 3399 | cam_release_devq(periph->path, 0, 0, 0, 0); | |
| 3400 | atio->ccb_h.status &= ~CAM_DEV_QFRZN; | |
| 3401 | } | |
| 3402 | if (more) { | |
| 3403 | xpt_schedule(periph, 1); | |
| 3404 | } | |
| 3405 | } | |
| 3406 | ||
| 3407 | static cam_status | |
| 3408 | isptargctor(struct cam_periph *periph, void *arg) | |
| 3409 | { | |
| 3410 | struct isptarg_softc *softc; | |
| 3411 | ||
| 3412 | softc = (struct isptarg_softc *)arg; | |
| 3413 | periph->softc = softc; | |
| 3414 | softc->periph = periph; | |
| 3415 | softc->path = periph->path; | |
| 3416 | ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, periph->path, "%s called\n", __func__); | |
| 3417 | return (CAM_REQ_CMP); | |
| 3418 | } | |
| 3419 | ||
| 3420 | static void | |
| 3421 | isptargdtor(struct cam_periph *periph) | |
| 3422 | { | |
| 3423 | struct isptarg_softc *softc; | |
| 3424 | softc = (struct isptarg_softc *)periph->softc; | |
| 3425 | ISP_PATH_PRT(softc->isp, ISP_LOGTDEBUG0, periph->path, "%s called\n", __func__); | |
| 3426 | softc->periph = NULL; | |
| 3427 | softc->path = NULL; | |
| 3428 | periph->softc = NULL; | |
| 3429 | } | |
| 3430 | ||
| 3431 | static void | |
| 3432 | isptarg_done(struct cam_periph *periph, union ccb *ccb) | |
| 3433 | { | |
| 3434 | struct isptarg_softc *softc; | |
| 3435 | ispsoftc_t *isp; | |
| 3436 | struct ccb_accept_tio *atio; | |
| 3437 | struct ccb_immediate_notify *inot; | |
| 3438 | cam_status status; | |
| 3439 | ||
| 3440 | softc = (struct isptarg_softc *)periph->softc; | |
| 3441 | isp = softc->isp; | |
| 3442 | status = ccb->ccb_h.status & CAM_STATUS_MASK; | |
| 3443 | ||
| 3444 | switch (ccb->ccb_h.func_code) { | |
| 3445 | case XPT_ACCEPT_TARGET_IO: | |
| 3446 | atio = (struct ccb_accept_tio *) ccb; | |
| 3447 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] ATIO seen in %s\n", atio->tag_id, __func__); | |
| 3448 | TAILQ_INSERT_TAIL(&softc->work_queue, &ccb->ccb_h, periph_links.tqe); | |
| 3449 | xpt_schedule(periph, 1); | |
| 3450 | break; | |
| 3451 | case XPT_IMMEDIATE_NOTIFY: | |
| 3452 | inot = (struct ccb_immediate_notify *) ccb; | |
| 3453 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] INOT for 0x%x seen in %s\n", inot->tag_id, inot->seq_id, __func__); | |
| 3454 | TAILQ_INSERT_TAIL(&softc->inot_queue, &ccb->ccb_h, periph_links.tqe); | |
| 3455 | xpt_schedule(periph, 1); | |
| 3456 | break; | |
| 3457 | case XPT_CONT_TARGET_IO: | |
| 3458 | if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { | |
| 3459 | cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0); | |
| 3460 | ccb->ccb_h.status &= ~CAM_DEV_QFRZN; | |
| 3461 | } | |
| 3462 | atio = ccb->ccb_h.ccb_atio; | |
| 3463 | if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | |
| 3464 | cam_error_print(ccb, CAM_ESF_ALL, CAM_EPF_ALL); | |
| 3465 | xpt_action((union ccb *)atio); | |
| 3466 | } else if ((ccb->ccb_h.flags & CAM_SEND_STATUS) == 0) { | |
| 3467 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] MID CTIO seen in %s\n", atio->tag_id, __func__); | |
| 3468 | TAILQ_INSERT_TAIL(&softc->rework_queue, &atio->ccb_h, periph_links.tqe); | |
| 3469 | xpt_schedule(periph, 1); | |
| 3470 | } else { | |
| 3471 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, ccb->ccb_h.path, "[0x%x] FINAL CTIO seen in %s\n", atio->tag_id, __func__); | |
| 3472 | xpt_action((union ccb *)atio); | |
| 3473 | } | |
| 3474 | xpt_release_ccb(ccb); | |
| 3475 | break; | |
| 3476 | case XPT_NOTIFY_ACKNOWLEDGE: | |
| 3477 | if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { | |
| 3478 | cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0); | |
| 3479 | ccb->ccb_h.status &= ~CAM_DEV_QFRZN; | |
| 3480 | } | |
| 3481 | inot = ccb->ccb_h.ccb_inot; | |
| 3482 | ISP_PATH_PRT(isp, ISP_LOGTDEBUG0, inot->ccb_h.path, "[0x%x] recycle notify for tag 0x%x\n", inot->tag_id, inot->seq_id); | |
| 3483 | xpt_release_ccb(ccb); | |
| 3484 | xpt_action((union ccb *)inot); | |
| 3485 | break; | |
| 3486 | default: | |
| 3487 | xpt_print(ccb->ccb_h.path, "unexpected code 0x%x\n", ccb->ccb_h.func_code); | |
| 3488 | break; | |
| 3489 | } | |
| 3490 | } | |
| 3491 | ||
| 3492 | static void | |
| 3493 | isptargasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) | |
| 3494 | { | |
| 3495 | struct ac_contract *acp = arg; | |
| 3496 | struct ac_device_changed *fc = (struct ac_device_changed *) acp->contract_data; | |
| 3497 | ||
| 3498 | if (code != AC_CONTRACT) { | |
| 3499 | return; | |
| 3500 | } | |
| 3501 | xpt_print(path, "0x%016llx Port ID 0x%06x %s\n", (unsigned long long) fc->wwpn, fc->port, fc->arrived? "arrived" : "departed"); | |
| 3502 | } | |
| 3503 | ||
| 3504 | static void | |
| 3505 | isp_target_thread(ispsoftc_t *isp, int chan) | |
| 3506 | { | |
| 3507 | union ccb *ccb = NULL; | |
| 3508 | int i; | |
| 3509 | void *wchan; | |
| 3510 | cam_status status; | |
| 3511 | struct isptarg_softc *softc = NULL; | |
| 3512 | struct cam_periph *periph = NULL, *wperiph = NULL; | |
| 3513 | struct cam_path *path, *wpath; | |
| 3514 | struct cam_sim *sim; | |
| 3515 | ||
| 3516 | if (disk_data == NULL) { | |
| 3517 | disk_size = roundup2(vm_kmem_size >> 1, (1ULL << 20)); | |
| 3518 | if (disk_size < (50 << 20)) { | |
| 3519 | disk_size = 50 << 20; | |
| 3520 | } | |
| 3521 | disk_data = kmalloc(disk_size, M_ISPTARG, M_WAITOK | M_ZERO); | |
| 191d7ec1 SW |
3522 | isp_prt(isp, ISP_LOGINFO, "allocated a %ju MiB disk", (uintmax_t) (disk_size >> 20)); |
| 3523 | } | |
| 3524 | junk_data = kmalloc(JUNK_SIZE, M_ISPTARG, M_WAITOK | M_ZERO); | |
| 191d7ec1 SW |
3525 | |
| 3526 | ||
| 3527 | softc = kmalloc(sizeof (*softc), M_ISPTARG, M_WAITOK | M_ZERO); | |
| 191d7ec1 SW |
3528 | TAILQ_INIT(&softc->work_queue); |
| 3529 | TAILQ_INIT(&softc->rework_queue); | |
| 3530 | TAILQ_INIT(&softc->running_queue); | |
| 3531 | TAILQ_INIT(&softc->inot_queue); | |
| 3532 | softc->isp = isp; | |
| 3533 | ||
| 3534 | periphdriver_register(&isptargdriver); | |
| 3535 | ISP_GET_PC(isp, chan, sim, sim); | |
| 3536 | ISP_GET_PC(isp, chan, path, path); | |
| 3537 | status = xpt_create_path_unlocked(&wpath, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); | |
| 3538 | if (status != CAM_REQ_CMP) { | |
| 3539 | isp_prt(isp, ISP_LOGERR, "%s: could not allocate wildcard path", __func__); | |
| 3540 | return; | |
| 3541 | } | |
| 3542 | status = xpt_create_path_unlocked(&path, NULL, cam_sim_path(sim), 0, 0); | |
| 3543 | if (status != CAM_REQ_CMP) { | |
| 3544 | xpt_free_path(wpath); | |
| 3545 | isp_prt(isp, ISP_LOGERR, "%s: could not allocate path", __func__); | |
| 3546 | return; | |
| 3547 | } | |
| 3548 | ||
| 3549 | ccb = xpt_alloc_ccb(); | |
| 3550 | ||
| 3551 | ISP_LOCK(isp); | |
| 3552 | status = cam_periph_alloc(isptargctor, NULL, isptargdtor, isptargstart, "isptarg", CAM_PERIPH_BIO, wpath, NULL, 0, softc); | |
| 3553 | if (status != CAM_REQ_CMP) { | |
| 3554 | ISP_UNLOCK(isp); | |
| 3555 | isp_prt(isp, ISP_LOGERR, "%s: cam_periph_alloc for wildcard failed", __func__); | |
| 3556 | goto out; | |
| 3557 | } | |
| 3558 | wperiph = cam_periph_find(wpath, "isptarg"); | |
| 3559 | if (wperiph == NULL) { | |
| 3560 | ISP_UNLOCK(isp); | |
| 3561 | isp_prt(isp, ISP_LOGERR, "%s: wildcard periph already allocated but doesn't exist", __func__); | |
| 3562 | goto out; | |
| 3563 | } | |
| 3564 | ||
| 3565 | status = cam_periph_alloc(isptargctor, NULL, isptargdtor, isptargstart, "isptarg", CAM_PERIPH_BIO, path, NULL, 0, softc); | |
| 3566 | if (status != CAM_REQ_CMP) { | |
| 3567 | ISP_UNLOCK(isp); | |
| 3568 | isp_prt(isp, ISP_LOGERR, "%s: cam_periph_alloc failed", __func__); | |
| 3569 | goto out; | |
| 3570 | } | |
| 3571 | ||
| 3572 | periph = cam_periph_find(path, "isptarg"); | |
| 3573 | if (periph == NULL) { | |
| 3574 | ISP_UNLOCK(isp); | |
| 3575 | isp_prt(isp, ISP_LOGERR, "%s: periph already allocated but doesn't exist", __func__); | |
| 3576 | goto out; | |
| 3577 | } | |
| 3578 | ||
| 3579 | status = xpt_register_async(AC_CONTRACT, isptargasync, isp, wpath); | |
| 3580 | if (status != CAM_REQ_CMP) { | |
| 3581 | ISP_UNLOCK(isp); | |
| 3582 | isp_prt(isp, ISP_LOGERR, "%s: xpt_register_async failed", __func__); | |
| 3583 | goto out; | |
| 3584 | } | |
| 3585 | ||
| 3586 | ISP_UNLOCK(isp); | |
| 3587 | ||
| 3588 | ccb = xpt_alloc_ccb(); | |
| 3589 | ||
| 3590 | /* | |
| 3591 | * Make sure role is none. | |
| 3592 | */ | |
| 3593 | xpt_setup_ccb(&ccb->ccb_h, periph->path, 10); | |
| 3594 | ccb->ccb_h.func_code = XPT_SET_SIM_KNOB; | |
| 3595 | ccb->knob.xport_specific.fc.role = KNOB_ROLE_NONE; | |
| 3596 | #ifdef ISP_TEST_WWNS | |
| 3597 | ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE | KNOB_VALID_ADDRESS; | |
| 3598 | ccb->knob.xport_specific.fc.wwnn = 0x508004d000000000ULL | (device_get_unit(isp->isp_osinfo.dev) << 8) | (chan << 16); | |
| 3599 | ccb->knob.xport_specific.fc.wwpn = 0x508004d000000001ULL | (device_get_unit(isp->isp_osinfo.dev) << 8) | (chan << 16); | |
| 3600 | #else | |
| 3601 | ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE; | |
| 3602 | #endif | |
| 3603 | ||
| 3604 | ISP_LOCK(isp); | |
| 3605 | xpt_action(ccb); | |
| 3606 | ISP_UNLOCK(isp); | |
| 3607 | ||
| 3608 | /* | |
| 3609 | * Now enable luns | |
| 3610 | */ | |
| 3611 | xpt_setup_ccb(&ccb->ccb_h, periph->path, 10); | |
| 3612 | ccb->ccb_h.func_code = XPT_EN_LUN; | |
| 3613 | ccb->cel.enable = 1; | |
| 3614 | ISP_LOCK(isp); | |
| 3615 | xpt_action(ccb); | |
| 3616 | ISP_UNLOCK(isp); | |
| 3617 | if (ccb->ccb_h.status != CAM_REQ_CMP) { | |
| 3618 | xpt_free_ccb(ccb); | |
| 3619 | xpt_print(periph->path, "failed to enable lun (0x%x)\n", ccb->ccb_h.status); | |
| 3620 | goto out; | |
| 3621 | } | |
| 3622 | ||
| 3623 | xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 10); | |
| 3624 | ccb->ccb_h.func_code = XPT_EN_LUN; | |
| 3625 | ccb->cel.enable = 1; | |
| 3626 | ISP_LOCK(isp); | |
| 3627 | xpt_action(ccb); | |
| 3628 | ISP_UNLOCK(isp); | |
| 3629 | if (ccb->ccb_h.status != CAM_REQ_CMP) { | |
| 3630 | xpt_free_ccb(ccb); | |
| 3631 | xpt_print(wperiph->path, "failed to enable lun (0x%x)\n", ccb->ccb_h.status); | |
| 3632 | goto out; | |
| 3633 | } | |
| 3634 | xpt_free_ccb(ccb); | |
| 3635 | ||
| 3636 | /* | |
| 3637 | * Add resources | |
| 3638 | */ | |
| 3639 | ISP_GET_PC_ADDR(isp, chan, target_proc, wchan); | |
| 3640 | for (i = 0; i < 4; i++) { | |
| 3641 | ccb = kmalloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO); | |
| 3642 | xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 1); | |
| 3643 | ccb->ccb_h.func_code = XPT_ACCEPT_TARGET_IO; | |
| 3644 | ccb->ccb_h.cbfcnp = isptarg_done; | |
| 3645 | ISP_LOCK(isp); | |
| 3646 | xpt_action(ccb); | |
| 3647 | ISP_UNLOCK(isp); | |
| 3648 | } | |
| 3649 | for (i = 0; i < NISP_TARG_CMDS; i++) { | |
| 3650 | ccb = kmalloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO); | |
| 3651 | xpt_setup_ccb(&ccb->ccb_h, periph->path, 1); | |
| 3652 | ccb->ccb_h.func_code = XPT_ACCEPT_TARGET_IO; | |
| 3653 | ccb->ccb_h.cbfcnp = isptarg_done; | |
| 3654 | ISP_LOCK(isp); | |
| 3655 | xpt_action(ccb); | |
| 3656 | ISP_UNLOCK(isp); | |
| 3657 | } | |
| 3658 | for (i = 0; i < 4; i++) { | |
| 3659 | ccb = kmalloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO); | |
| 3660 | xpt_setup_ccb(&ccb->ccb_h, wperiph->path, 1); | |
| 3661 | ccb->ccb_h.func_code = XPT_IMMEDIATE_NOTIFY; | |
| 3662 | ccb->ccb_h.cbfcnp = isptarg_done; | |
| 3663 | ISP_LOCK(isp); | |
| 3664 | xpt_action(ccb); | |
| 3665 | ISP_UNLOCK(isp); | |
| 3666 | } | |
| 3667 | for (i = 0; i < NISP_TARG_NOTIFIES; i++) { | |
| 3668 | ccb = kmalloc(sizeof (*ccb), M_ISPTARG, M_WAITOK | M_ZERO); | |
| 3669 | xpt_setup_ccb(&ccb->ccb_h, periph->path, 1); | |
| 3670 | ccb->ccb_h.func_code = XPT_IMMEDIATE_NOTIFY; | |
| 3671 | ccb->ccb_h.cbfcnp = isptarg_done; | |
| 3672 | ISP_LOCK(isp); | |
| 3673 | xpt_action(ccb); | |
| 3674 | ISP_UNLOCK(isp); | |
| 3675 | } | |
| 3676 | ||
| 3677 | /* | |
| 3678 | * Now turn it all back on | |
| 3679 | */ | |
| 3680 | xpt_setup_ccb(&ccb->ccb_h, periph->path, 10); | |
| 3681 | ccb->ccb_h.func_code = XPT_SET_SIM_KNOB; | |
| 3682 | ccb->knob.xport_specific.fc.valid = KNOB_VALID_ROLE; | |
| 3683 | ccb->knob.xport_specific.fc.role = KNOB_ROLE_TARGET; | |
| 3684 | ISP_LOCK(isp); | |
| 3685 | xpt_action(ccb); | |
| 3686 | ISP_UNLOCK(isp); | |
| 3687 | ||
| 3688 | /* | |
| 3689 | * Okay, while things are still active, sleep... | |
| 3690 | */ | |
| 3691 | ISP_LOCK(isp); | |
| 3692 | for (;;) { | |
| 3693 | ISP_GET_PC(isp, chan, proc_active, i); | |
| 3694 | if (i == 0) { | |
| 3695 | break; | |
| 3696 | } | |
| 3697 | lksleep(wchan, &isp->isp_lock, PUSER, "tsnooze", 0); | |
| 3698 | } | |
| 3699 | ISP_UNLOCK(isp); | |
| 3700 | ||
| 3701 | out: | |
| 3702 | if (wperiph) { | |
| 3703 | cam_periph_invalidate(wperiph); | |
| 3704 | } | |
| 3705 | if (periph) { | |
| 3706 | cam_periph_invalidate(periph); | |
| 3707 | } | |
| 3708 | if (junk_data) { | |
| 3709 | kfree(junk_data, M_ISPTARG); | |
| 3710 | } | |
| 3711 | if (disk_data) { | |
| 3712 | kfree(disk_data, M_ISPTARG); | |
| 3713 | } | |
| 3714 | if (softc) { | |
| 3715 | kfree(softc, M_ISPTARG); | |
| 3716 | } | |
| 3717 | xpt_free_path(path); | |
| 3718 | xpt_free_path(wpath); | |
| 3719 | } | |
| 3720 | ||
| 3721 | static void | |
| 3722 | isp_target_thread_pi(void *arg) | |
| 3723 | { | |
| 3724 | struct isp_spi *pi = arg; | |
| 3725 | isp_target_thread(cam_sim_softc(pi->sim), cam_sim_bus(pi->sim)); | |
| 3726 | } | |
| 3727 | ||
| 3728 | static void | |
| 3729 | isp_target_thread_fc(void *arg) | |
| 3730 | { | |
| 3731 | struct isp_fc *fc = arg; | |
| 3732 | isp_target_thread(cam_sim_softc(fc->sim), cam_sim_bus(fc->sim)); | |
| 3733 | } | |
| 3734 | ||
| 3735 | static int | |
| 3736 | isptarg_rwparm(uint8_t *cdb, uint8_t *dp, uint64_t dl, uint32_t offset, uint8_t **kp, uint32_t *tl, int *lp) | |
| 3737 | { | |
| 3738 | uint32_t cnt, curcnt; | |
| 3739 | uint64_t lba; | |
| 3740 | ||
| 3741 | switch (cdb[0]) { | |
| 3742 | case WRITE_16: | |
| 3743 | case READ_16: | |