diff options
Diffstat (limited to 'drivers/target/loopback/tcm_loop.c')
| -rw-r--r-- | drivers/target/loopback/tcm_loop.c | 242 |
1 files changed, 201 insertions, 41 deletions
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 0f6d69dabca1..1b41e6776152 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c | |||
| @@ -135,6 +135,21 @@ static int tcm_loop_change_queue_depth( | |||
| 135 | return sdev->queue_depth; | 135 | return sdev->queue_depth; |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag) | ||
| 139 | { | ||
| 140 | if (sdev->tagged_supported) { | ||
| 141 | scsi_set_tag_type(sdev, tag); | ||
| 142 | |||
| 143 | if (tag) | ||
| 144 | scsi_activate_tcq(sdev, sdev->queue_depth); | ||
| 145 | else | ||
| 146 | scsi_deactivate_tcq(sdev, sdev->queue_depth); | ||
| 147 | } else | ||
| 148 | tag = 0; | ||
| 149 | |||
| 150 | return tag; | ||
| 151 | } | ||
| 152 | |||
| 138 | /* | 153 | /* |
| 139 | * Locate the SAM Task Attr from struct scsi_cmnd * | 154 | * Locate the SAM Task Attr from struct scsi_cmnd * |
| 140 | */ | 155 | */ |
| @@ -178,7 +193,10 @@ static void tcm_loop_submission_work(struct work_struct *work) | |||
| 178 | set_host_byte(sc, DID_NO_CONNECT); | 193 | set_host_byte(sc, DID_NO_CONNECT); |
| 179 | goto out_done; | 194 | goto out_done; |
| 180 | } | 195 | } |
| 181 | 196 | if (tl_tpg->tl_transport_status == TCM_TRANSPORT_OFFLINE) { | |
| 197 | set_host_byte(sc, DID_TRANSPORT_DISRUPTED); | ||
| 198 | goto out_done; | ||
| 199 | } | ||
| 182 | tl_nexus = tl_hba->tl_nexus; | 200 | tl_nexus = tl_hba->tl_nexus; |
| 183 | if (!tl_nexus) { | 201 | if (!tl_nexus) { |
| 184 | scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus" | 202 | scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus" |
| @@ -233,6 +251,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) | |||
| 233 | } | 251 | } |
| 234 | 252 | ||
| 235 | tl_cmd->sc = sc; | 253 | tl_cmd->sc = sc; |
| 254 | tl_cmd->sc_cmd_tag = sc->tag; | ||
| 236 | INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); | 255 | INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); |
| 237 | queue_work(tcm_loop_workqueue, &tl_cmd->work); | 256 | queue_work(tcm_loop_workqueue, &tl_cmd->work); |
| 238 | return 0; | 257 | return 0; |
| @@ -242,41 +261,21 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) | |||
| 242 | * Called from SCSI EH process context to issue a LUN_RESET TMR | 261 | * Called from SCSI EH process context to issue a LUN_RESET TMR |
| 243 | * to struct scsi_device | 262 | * to struct scsi_device |
| 244 | */ | 263 | */ |
| 245 | static int tcm_loop_device_reset(struct scsi_cmnd *sc) | 264 | static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg, |
| 265 | struct tcm_loop_nexus *tl_nexus, | ||
| 266 | int lun, int task, enum tcm_tmreq_table tmr) | ||
| 246 | { | 267 | { |
| 247 | struct se_cmd *se_cmd = NULL; | 268 | struct se_cmd *se_cmd = NULL; |
| 248 | struct se_portal_group *se_tpg; | ||
| 249 | struct se_session *se_sess; | 269 | struct se_session *se_sess; |
| 270 | struct se_portal_group *se_tpg; | ||
| 250 | struct tcm_loop_cmd *tl_cmd = NULL; | 271 | struct tcm_loop_cmd *tl_cmd = NULL; |
| 251 | struct tcm_loop_hba *tl_hba; | ||
| 252 | struct tcm_loop_nexus *tl_nexus; | ||
| 253 | struct tcm_loop_tmr *tl_tmr = NULL; | 272 | struct tcm_loop_tmr *tl_tmr = NULL; |
| 254 | struct tcm_loop_tpg *tl_tpg; | 273 | int ret = TMR_FUNCTION_FAILED, rc; |
| 255 | int ret = FAILED, rc; | ||
| 256 | /* | ||
| 257 | * Locate the tcm_loop_hba_t pointer | ||
| 258 | */ | ||
| 259 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | ||
| 260 | /* | ||
| 261 | * Locate the tl_nexus and se_sess pointers | ||
| 262 | */ | ||
| 263 | tl_nexus = tl_hba->tl_nexus; | ||
| 264 | if (!tl_nexus) { | ||
| 265 | pr_err("Unable to perform device reset without" | ||
| 266 | " active I_T Nexus\n"); | ||
| 267 | return FAILED; | ||
| 268 | } | ||
| 269 | se_sess = tl_nexus->se_sess; | ||
| 270 | /* | ||
| 271 | * Locate the tl_tpg and se_tpg pointers from TargetID in sc->device->id | ||
| 272 | */ | ||
| 273 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | ||
| 274 | se_tpg = &tl_tpg->tl_se_tpg; | ||
| 275 | 274 | ||
| 276 | tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL); | 275 | tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL); |
| 277 | if (!tl_cmd) { | 276 | if (!tl_cmd) { |
| 278 | pr_err("Unable to allocate memory for tl_cmd\n"); | 277 | pr_err("Unable to allocate memory for tl_cmd\n"); |
| 279 | return FAILED; | 278 | return ret; |
| 280 | } | 279 | } |
| 281 | 280 | ||
| 282 | tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL); | 281 | tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL); |
| @@ -287,6 +286,8 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) | |||
| 287 | init_waitqueue_head(&tl_tmr->tl_tmr_wait); | 286 | init_waitqueue_head(&tl_tmr->tl_tmr_wait); |
| 288 | 287 | ||
| 289 | se_cmd = &tl_cmd->tl_se_cmd; | 288 | se_cmd = &tl_cmd->tl_se_cmd; |
| 289 | se_tpg = &tl_tpg->tl_se_tpg; | ||
| 290 | se_sess = tl_nexus->se_sess; | ||
| 290 | /* | 291 | /* |
| 291 | * Initialize struct se_cmd descriptor from target_core_mod infrastructure | 292 | * Initialize struct se_cmd descriptor from target_core_mod infrastructure |
| 292 | */ | 293 | */ |
| @@ -294,17 +295,23 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) | |||
| 294 | DMA_NONE, MSG_SIMPLE_TAG, | 295 | DMA_NONE, MSG_SIMPLE_TAG, |
| 295 | &tl_cmd->tl_sense_buf[0]); | 296 | &tl_cmd->tl_sense_buf[0]); |
| 296 | 297 | ||
| 297 | rc = core_tmr_alloc_req(se_cmd, tl_tmr, TMR_LUN_RESET, GFP_KERNEL); | 298 | rc = core_tmr_alloc_req(se_cmd, tl_tmr, tmr, GFP_KERNEL); |
| 298 | if (rc < 0) | 299 | if (rc < 0) |
| 299 | goto release; | 300 | goto release; |
| 301 | |||
| 302 | if (tmr == TMR_ABORT_TASK) | ||
| 303 | se_cmd->se_tmr_req->ref_task_tag = task; | ||
| 304 | |||
| 300 | /* | 305 | /* |
| 301 | * Locate the underlying TCM struct se_lun from sc->device->lun | 306 | * Locate the underlying TCM struct se_lun |
| 302 | */ | 307 | */ |
| 303 | if (transport_lookup_tmr_lun(se_cmd, sc->device->lun) < 0) | 308 | if (transport_lookup_tmr_lun(se_cmd, lun) < 0) { |
| 309 | ret = TMR_LUN_DOES_NOT_EXIST; | ||
| 304 | goto release; | 310 | goto release; |
| 311 | } | ||
| 305 | /* | 312 | /* |
| 306 | * Queue the TMR to TCM Core and sleep waiting for tcm_loop_queue_tm_rsp() | 313 | * Queue the TMR to TCM Core and sleep waiting for |
| 307 | * to wake us up. | 314 | * tcm_loop_queue_tm_rsp() to wake us up. |
| 308 | */ | 315 | */ |
| 309 | transport_generic_handle_tmr(se_cmd); | 316 | transport_generic_handle_tmr(se_cmd); |
| 310 | wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete)); | 317 | wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete)); |
| @@ -312,8 +319,7 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) | |||
| 312 | * The TMR LUN_RESET has completed, check the response status and | 319 | * The TMR LUN_RESET has completed, check the response status and |
| 313 | * then release allocations. | 320 | * then release allocations. |
| 314 | */ | 321 | */ |
| 315 | ret = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ? | 322 | ret = se_cmd->se_tmr_req->response; |
| 316 | SUCCESS : FAILED; | ||
| 317 | release: | 323 | release: |
| 318 | if (se_cmd) | 324 | if (se_cmd) |
| 319 | transport_generic_free_cmd(se_cmd, 1); | 325 | transport_generic_free_cmd(se_cmd, 1); |
| @@ -323,6 +329,94 @@ release: | |||
| 323 | return ret; | 329 | return ret; |
| 324 | } | 330 | } |
| 325 | 331 | ||
| 332 | static int tcm_loop_abort_task(struct scsi_cmnd *sc) | ||
| 333 | { | ||
| 334 | struct tcm_loop_hba *tl_hba; | ||
| 335 | struct tcm_loop_nexus *tl_nexus; | ||
| 336 | struct tcm_loop_tpg *tl_tpg; | ||
| 337 | int ret = FAILED; | ||
| 338 | |||
| 339 | /* | ||
| 340 | * Locate the tcm_loop_hba_t pointer | ||
| 341 | */ | ||
| 342 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | ||
| 343 | /* | ||
| 344 | * Locate the tl_nexus and se_sess pointers | ||
| 345 | */ | ||
| 346 | tl_nexus = tl_hba->tl_nexus; | ||
| 347 | if (!tl_nexus) { | ||
| 348 | pr_err("Unable to perform device reset without" | ||
| 349 | " active I_T Nexus\n"); | ||
| 350 | return FAILED; | ||
| 351 | } | ||
| 352 | |||
| 353 | /* | ||
| 354 | * Locate the tl_tpg pointer from TargetID in sc->device->id | ||
| 355 | */ | ||
| 356 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | ||
| 357 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, | ||
| 358 | sc->tag, TMR_ABORT_TASK); | ||
| 359 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; | ||
| 360 | } | ||
| 361 | |||
| 362 | /* | ||
| 363 | * Called from SCSI EH process context to issue a LUN_RESET TMR | ||
| 364 | * to struct scsi_device | ||
| 365 | */ | ||
| 366 | static int tcm_loop_device_reset(struct scsi_cmnd *sc) | ||
| 367 | { | ||
| 368 | struct tcm_loop_hba *tl_hba; | ||
| 369 | struct tcm_loop_nexus *tl_nexus; | ||
| 370 | struct tcm_loop_tpg *tl_tpg; | ||
| 371 | int ret = FAILED; | ||
| 372 | |||
| 373 | /* | ||
| 374 | * Locate the tcm_loop_hba_t pointer | ||
| 375 | */ | ||
| 376 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | ||
| 377 | /* | ||
| 378 | * Locate the tl_nexus and se_sess pointers | ||
| 379 | */ | ||
| 380 | tl_nexus = tl_hba->tl_nexus; | ||
| 381 | if (!tl_nexus) { | ||
| 382 | pr_err("Unable to perform device reset without" | ||
| 383 | " active I_T Nexus\n"); | ||
| 384 | return FAILED; | ||
| 385 | } | ||
| 386 | /* | ||
| 387 | * Locate the tl_tpg pointer from TargetID in sc->device->id | ||
| 388 | */ | ||
| 389 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | ||
| 390 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, | ||
| 391 | 0, TMR_LUN_RESET); | ||
| 392 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; | ||
| 393 | } | ||
| 394 | |||
| 395 | static int tcm_loop_target_reset(struct scsi_cmnd *sc) | ||
| 396 | { | ||
| 397 | struct tcm_loop_hba *tl_hba; | ||
| 398 | struct tcm_loop_tpg *tl_tpg; | ||
| 399 | |||
| 400 | /* | ||
| 401 | * Locate the tcm_loop_hba_t pointer | ||
| 402 | */ | ||
| 403 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | ||
| 404 | if (!tl_hba) { | ||
| 405 | pr_err("Unable to perform device reset without" | ||
| 406 | " active I_T Nexus\n"); | ||
| 407 | return FAILED; | ||
| 408 | } | ||
| 409 | /* | ||
| 410 | * Locate the tl_tpg pointer from TargetID in sc->device->id | ||
| 411 | */ | ||
| 412 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | ||
| 413 | if (tl_tpg) { | ||
| 414 | tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; | ||
| 415 | return SUCCESS; | ||
| 416 | } | ||
| 417 | return FAILED; | ||
| 418 | } | ||
| 419 | |||
| 326 | static int tcm_loop_slave_alloc(struct scsi_device *sd) | 420 | static int tcm_loop_slave_alloc(struct scsi_device *sd) |
| 327 | { | 421 | { |
| 328 | set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags); | 422 | set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags); |
| @@ -331,6 +425,15 @@ static int tcm_loop_slave_alloc(struct scsi_device *sd) | |||
| 331 | 425 | ||
| 332 | static int tcm_loop_slave_configure(struct scsi_device *sd) | 426 | static int tcm_loop_slave_configure(struct scsi_device *sd) |
| 333 | { | 427 | { |
| 428 | if (sd->tagged_supported) { | ||
| 429 | scsi_activate_tcq(sd, sd->queue_depth); | ||
| 430 | scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG, | ||
| 431 | sd->host->cmd_per_lun); | ||
| 432 | } else { | ||
| 433 | scsi_adjust_queue_depth(sd, 0, | ||
| 434 | sd->host->cmd_per_lun); | ||
| 435 | } | ||
| 436 | |||
| 334 | return 0; | 437 | return 0; |
| 335 | } | 438 | } |
| 336 | 439 | ||
| @@ -340,7 +443,10 @@ static struct scsi_host_template tcm_loop_driver_template = { | |||
| 340 | .name = "TCM_Loopback", | 443 | .name = "TCM_Loopback", |
| 341 | .queuecommand = tcm_loop_queuecommand, | 444 | .queuecommand = tcm_loop_queuecommand, |
| 342 | .change_queue_depth = tcm_loop_change_queue_depth, | 445 | .change_queue_depth = tcm_loop_change_queue_depth, |
| 446 | .change_queue_type = tcm_loop_change_queue_type, | ||
| 447 | .eh_abort_handler = tcm_loop_abort_task, | ||
| 343 | .eh_device_reset_handler = tcm_loop_device_reset, | 448 | .eh_device_reset_handler = tcm_loop_device_reset, |
| 449 | .eh_target_reset_handler = tcm_loop_target_reset, | ||
| 344 | .can_queue = 1024, | 450 | .can_queue = 1024, |
| 345 | .this_id = -1, | 451 | .this_id = -1, |
| 346 | .sg_tablesize = 256, | 452 | .sg_tablesize = 256, |
| @@ -699,7 +805,10 @@ static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl) | |||
| 699 | 805 | ||
| 700 | static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd) | 806 | static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd) |
| 701 | { | 807 | { |
| 702 | return 1; | 808 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
| 809 | struct tcm_loop_cmd, tl_se_cmd); | ||
| 810 | |||
| 811 | return tl_cmd->sc_cmd_tag; | ||
| 703 | } | 812 | } |
| 704 | 813 | ||
| 705 | static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd) | 814 | static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd) |
| @@ -932,7 +1041,10 @@ static int tcm_loop_drop_nexus( | |||
| 932 | struct tcm_loop_nexus *tl_nexus; | 1041 | struct tcm_loop_nexus *tl_nexus; |
| 933 | struct tcm_loop_hba *tl_hba = tpg->tl_hba; | 1042 | struct tcm_loop_hba *tl_hba = tpg->tl_hba; |
| 934 | 1043 | ||
| 935 | tl_nexus = tpg->tl_hba->tl_nexus; | 1044 | if (!tl_hba) |
| 1045 | return -ENODEV; | ||
| 1046 | |||
| 1047 | tl_nexus = tl_hba->tl_nexus; | ||
| 936 | if (!tl_nexus) | 1048 | if (!tl_nexus) |
| 937 | return -ENODEV; | 1049 | return -ENODEV; |
| 938 | 1050 | ||
| @@ -1061,8 +1173,56 @@ check_newline: | |||
| 1061 | 1173 | ||
| 1062 | TF_TPG_BASE_ATTR(tcm_loop, nexus, S_IRUGO | S_IWUSR); | 1174 | TF_TPG_BASE_ATTR(tcm_loop, nexus, S_IRUGO | S_IWUSR); |
| 1063 | 1175 | ||
| 1176 | static ssize_t tcm_loop_tpg_show_transport_status( | ||
| 1177 | struct se_portal_group *se_tpg, | ||
| 1178 | char *page) | ||
| 1179 | { | ||
| 1180 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, | ||
| 1181 | struct tcm_loop_tpg, tl_se_tpg); | ||
| 1182 | const char *status = NULL; | ||
| 1183 | ssize_t ret = -EINVAL; | ||
| 1184 | |||
| 1185 | switch (tl_tpg->tl_transport_status) { | ||
| 1186 | case TCM_TRANSPORT_ONLINE: | ||
| 1187 | status = "online"; | ||
| 1188 | break; | ||
| 1189 | case TCM_TRANSPORT_OFFLINE: | ||
| 1190 | status = "offline"; | ||
| 1191 | break; | ||
| 1192 | default: | ||
| 1193 | break; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | if (status) | ||
| 1197 | ret = snprintf(page, PAGE_SIZE, "%s\n", status); | ||
| 1198 | |||
| 1199 | return ret; | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | static ssize_t tcm_loop_tpg_store_transport_status( | ||
| 1203 | struct se_portal_group *se_tpg, | ||
| 1204 | const char *page, | ||
| 1205 | size_t count) | ||
| 1206 | { | ||
| 1207 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, | ||
| 1208 | struct tcm_loop_tpg, tl_se_tpg); | ||
| 1209 | |||
| 1210 | if (!strncmp(page, "online", 6)) { | ||
| 1211 | tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; | ||
| 1212 | return count; | ||
| 1213 | } | ||
| 1214 | if (!strncmp(page, "offline", 7)) { | ||
| 1215 | tl_tpg->tl_transport_status = TCM_TRANSPORT_OFFLINE; | ||
| 1216 | return count; | ||
| 1217 | } | ||
| 1218 | return -EINVAL; | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | TF_TPG_BASE_ATTR(tcm_loop, transport_status, S_IRUGO | S_IWUSR); | ||
| 1222 | |||
| 1064 | static struct configfs_attribute *tcm_loop_tpg_attrs[] = { | 1223 | static struct configfs_attribute *tcm_loop_tpg_attrs[] = { |
| 1065 | &tcm_loop_tpg_nexus.attr, | 1224 | &tcm_loop_tpg_nexus.attr, |
| 1225 | &tcm_loop_tpg_transport_status.attr, | ||
| 1066 | NULL, | 1226 | NULL, |
| 1067 | }; | 1227 | }; |
| 1068 | 1228 | ||
| @@ -1334,11 +1494,11 @@ static int tcm_loop_register_configfs(void) | |||
| 1334 | /* | 1494 | /* |
| 1335 | * Setup default attribute lists for various fabric->tf_cit_tmpl | 1495 | * Setup default attribute lists for various fabric->tf_cit_tmpl |
| 1336 | */ | 1496 | */ |
| 1337 | TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; | 1497 | fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; |
| 1338 | TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; | 1498 | fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; |
| 1339 | TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL; | 1499 | fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; |
| 1340 | TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL; | 1500 | fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; |
| 1341 | TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL; | 1501 | fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; |
| 1342 | /* | 1502 | /* |
| 1343 | * Once fabric->tf_ops has been setup, now register the fabric for | 1503 | * Once fabric->tf_ops has been setup, now register the fabric for |
| 1344 | * use within TCM | 1504 | * use within TCM |
