Index: sys/cam/cam_ccb.h =================================================================== --- sys/cam/cam_ccb.h +++ sys/cam/cam_ccb.h @@ -163,6 +163,9 @@ /* Path statistics (error counts, etc.) */ XPT_GDEV_STATS = 0x0c, /* Device statistics (error counts, etc.) */ + XPT_DEFERRED_CALLBACK = 0x0d | XPT_FC_QUEUED | XPT_FC_USER_CCB + | XPT_FC_XPT_ONLY, + /* Deferred callback */ XPT_DEV_ADVINFO = 0x0e, /* Get/Set Device advanced information */ XPT_ASYNC = 0x0f | XPT_FC_QUEUED | XPT_FC_USER_CCB @@ -1336,6 +1339,18 @@ void *async_arg_ptr; }; +/* + * CCB for sending deferred calls to a specific async callback that needs to be + * deferred to the async thread. + */ +struct ccb_deferred_callback { + struct ccb_hdr ccb_h; + ac_callback_t *deferred_callback; + uint32_t code; /* an async code */ + off_t arg_size; + void *arg_ptr; +}; + /* * Union of all CCB types for kernel space allocation. This union should * never be used for manipulating CCBs - its only use is for the allocation @@ -1376,6 +1391,7 @@ struct ccb_ataio ataio; struct ccb_dev_advinfo cdai; struct ccb_async casync; + struct ccb_deferred_callback cdc; struct ccb_nvmeio nvmeio; struct ccb_mmcio mmcio; }; Index: sys/cam/cam_xpt.c =================================================================== --- sys/cam/cam_xpt.c +++ sys/cam/cam_xpt.c @@ -3122,6 +3122,7 @@ start_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(start_ccb); break; + case XPT_DEFERRED_CALLBACK: case XPT_ASYNC: /* * Queue the async operation so it can be run from a sleepable @@ -4346,6 +4347,96 @@ } } +/* Note: periph will always be NULL */ +static void +xpt_dc_process(struct cam_periph *periph, union ccb *ccb) +{ + struct cam_eb *bus; + struct cam_path *path; + void *arg; + u_int32_t code; + + path = ccb->ccb_h.path; + code = ccb->cdc.code; + arg = ccb->cdc.arg_ptr; + CAM_DEBUG(path, CAM_DEBUG_TRACE | CAM_DEBUG_INFO, + ("xpt_deffered_callback(%s)\n", xpt_async_string(code))); + bus = path->bus; + + ccb->cdc.defferred_callback(periph, code, path, arg)l + + if (path->device != NULL && path->device->lun_id != CAM_LUN_WILDCARD) + xpt_release_devq(path, 1, TRUE); + else + xpt_release_simq(path->bus->sim, TRUE); + if (ccb->cdc.arg_size > 0) + free(arg, M_CAMXPT); + xpt_free_path(path); + xpt_free_ccb(ccb); +} + +/* + * Defer a single callback. The deferred callback is similar to an async + * callback, except it's is a call to a specific function instead of a + * dispatched call based on the target device. We pass in path information, but + * we clone the path in case this is used by code that destroys the path (like + * camperiphfree). We wind up calling this function with a first argument of + * NULL to indicate no async context, and callbacks that use this must tolerate + * that for the code they pass in here. We also assume that the periph in the + * path is 'unstable' so we NULL that out. The callback must be tolerant of that +a * as well. + */ +void +xpt_deferred_callback(ac_callback_t *deferred_callback, uint32_t code, + struct cam_path *path, void *arg, size_t arglen) +{ + union ccb *ccb; + struct ccb_deferred_callback *cdc; + int size; + + ccb = xpt_alloc_ccb_nowait(); + if (ccb == NULL) { + xpt_print(path, "Can't allocate CCB to defer %s\n", + xpt_async_string(code)); + return; + } + + if (xpt_clone_path(&ccb->ccb_h.path, path) != 0) { + xpt_print(path, "Can't allocate path to defer %s\n", + xpt_async_string(code)); + xpt_free_ccb(ccb); + return; + } + cdc = &ccb->cdc; + cdc->ccb_h.path->periph = NULL; + cdc->ccb_h.func_code = XPT_DEFERRED_CALLBACK; + cdc->ccb_h.cbfcnp = xpt_dc_process; + cdc->ccb_h.flags |= CAM_UNLOCKED; + cdc->deferred_callback = deferred_callback; + cdc->code = code; + cdc->arg_size = 0; + if (arglen > 0 && arg != NULL) { + cdc->arg_ptr = malloc(arglen, M_CAMXPT, M_NOWAIT); + if (cdc->arg_ptr == NULL) { + xpt_print(path, "Can't allocate argument to defer %s\n", + xpt_async_string(code)); + xpt_free_path(ccb->ccb_h.path); + xpt_free_ccb(ccb); + return; + } + memcpy(cdc->arg_ptr, arg, arglen); + cdc->arg_size = arglen; + } + /* + * Freeze processing of other events while we process this out of band + */ + if (path->device != NULL && path->device->lun_id != CAM_LUN_WILDCARD) + xpt_freeze_devq(path, 1); + else + xpt_freeze_simq(path->bus->sim, 1); + xpt_action(ccb); +} + void xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg) {