aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-02-09 16:22:54 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-02-09 16:22:54 -0500
commit55aac6ef53e114c28170ee3f79065cfa8ca9cf3f (patch)
tree7eac44b0f280f8faea96c9ff09d2f650f04e3d46
parent2b369478e1856e3809f439495567474725931585 (diff)
parentb22bc27868e8c11fe3f00937a341b44f80b50364 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending
Pull SCSI target fixes from Nicholas Bellinger: "This target series for v4.10 contains fixes which address a few long-standing bugs that DATERA's QA + automation teams have uncovered while putting v4.1.y target code into production usage. We've been running the top three in our nightly automated regression runs for the last two months, and the COMPARE_AND_WRITE fix Mr. Gary Guo has been manually verifying against a four node ESX cluster this past week. Note all of them have CC' stable tags. Summary: - Fix a bug with ESX EXTENDED_COPY + SAM_STAT_RESERVATION_CONFLICT status, where target_core_xcopy.c logic was incorrectly returning SAM_STAT_CHECK_CONDITION for all non SAM_STAT_GOOD cases (Nixon Vincent) - Fix a TMR LUN_RESET hung task bug while other in-flight TMRs are being aborted, before the new one had been dispatched into tmr_wq (Rob Millner) - Fix a long standing double free OOPs, where a dynamically generated 'demo-mode' NodeACL has multiple sessions associated with it, and the /sys/kernel/config/target/$FABRIC/$WWN/ subsequently disables demo-mode, but never converts the dynamic ACL into a explicit ACL (Rob Millner) - Fix a long standing reference leak with ESX VAAI COMPARE_AND_WRITE when the second phase WRITE COMMIT command fails, resulting in CHECK_CONDITION response never being sent and se_cmd->cmd_kref never reaching zero (Gary Guo) Beyond these items on v4.1.y we've reproduced, fixed, and run through our regression test suite using iscsi-target exports, there are two additional outstanding list items: - Remove a >= v4.2 RCU conversion BUG_ON that would trigger when dynamic node NodeACLs where being converted to explicit NodeACLs. The patch drops the BUG_ON to follow how pre RCU conversion worked for this special case (Benjamin Estrabaud) - Add ibmvscsis target_core_fabric_ops->max_data_sg_nent assignment to match what IBM's Virtual SCSI hypervisor is already enforcing at transport layer. (Bryant Ly + Steven Royer)" * git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending: ibmvscsis: Add SGL limit target: Fix COMPARE_AND_WRITE ref leak for non GOOD status target: Fix multi-session dynamic se_node_acl double free OOPs target: Fix early transport_generic_handle_tmr abort scenario target: Use correct SCSI status during EXTENDED_COPY exception target: Don't BUG_ON during NodeACL dynamic -> explicit conversion
-rw-r--r--drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c1
-rw-r--r--drivers/target/target_core_device.c10
-rw-r--r--drivers/target/target_core_sbc.c8
-rw-r--r--drivers/target/target_core_transport.c86
-rw-r--r--drivers/target/target_core_xcopy.c2
-rw-r--r--include/target/target_core_base.h1
6 files changed, 76 insertions, 32 deletions
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
index 99b747cedbeb..0f807798c624 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
@@ -3816,6 +3816,7 @@ static struct configfs_attribute *ibmvscsis_tpg_attrs[] = {
3816static const struct target_core_fabric_ops ibmvscsis_ops = { 3816static const struct target_core_fabric_ops ibmvscsis_ops = {
3817 .module = THIS_MODULE, 3817 .module = THIS_MODULE,
3818 .name = "ibmvscsis", 3818 .name = "ibmvscsis",
3819 .max_data_sg_nents = MAX_TXU / PAGE_SIZE,
3819 .get_fabric_name = ibmvscsis_get_fabric_name, 3820 .get_fabric_name = ibmvscsis_get_fabric_name,
3820 .tpg_get_wwn = ibmvscsis_get_fabric_wwn, 3821 .tpg_get_wwn = ibmvscsis_get_fabric_wwn,
3821 .tpg_get_tag = ibmvscsis_get_tag, 3822 .tpg_get_tag = ibmvscsis_get_tag,
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index 1ebd13ef7bd3..26929c44d703 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -352,7 +352,15 @@ int core_enable_device_list_for_node(
352 kfree(new); 352 kfree(new);
353 return -EINVAL; 353 return -EINVAL;
354 } 354 }
355 BUG_ON(orig->se_lun_acl != NULL); 355 if (orig->se_lun_acl != NULL) {
356 pr_warn_ratelimited("Detected existing explicit"
357 " se_lun_acl->se_lun_group reference for %s"
358 " mapped_lun: %llu, failing\n",
359 nacl->initiatorname, mapped_lun);
360 mutex_unlock(&nacl->lun_entry_mutex);
361 kfree(new);
362 return -EINVAL;
363 }
356 364
357 rcu_assign_pointer(new->se_lun, lun); 365 rcu_assign_pointer(new->se_lun, lun);
358 rcu_assign_pointer(new->se_lun_acl, lun_acl); 366 rcu_assign_pointer(new->se_lun_acl, lun_acl);
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 4879e70e2eef..df7b6e95c019 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -451,6 +451,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
451 int *post_ret) 451 int *post_ret)
452{ 452{
453 struct se_device *dev = cmd->se_dev; 453 struct se_device *dev = cmd->se_dev;
454 sense_reason_t ret = TCM_NO_SENSE;
454 455
455 /* 456 /*
456 * Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through 457 * Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through
@@ -458,9 +459,12 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
458 * sent to the backend driver. 459 * sent to the backend driver.
459 */ 460 */
460 spin_lock_irq(&cmd->t_state_lock); 461 spin_lock_irq(&cmd->t_state_lock);
461 if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) { 462 if (cmd->transport_state & CMD_T_SENT) {
462 cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST; 463 cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
463 *post_ret = 1; 464 *post_ret = 1;
465
466 if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION)
467 ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
464 } 468 }
465 spin_unlock_irq(&cmd->t_state_lock); 469 spin_unlock_irq(&cmd->t_state_lock);
466 470
@@ -470,7 +474,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
470 */ 474 */
471 up(&dev->caw_sem); 475 up(&dev->caw_sem);
472 476
473 return TCM_NO_SENSE; 477 return ret;
474} 478}
475 479
476static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success, 480static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 1cadc9eefa21..437591bc7c08 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -457,8 +457,20 @@ static void target_complete_nacl(struct kref *kref)
457{ 457{
458 struct se_node_acl *nacl = container_of(kref, 458 struct se_node_acl *nacl = container_of(kref,
459 struct se_node_acl, acl_kref); 459 struct se_node_acl, acl_kref);
460 struct se_portal_group *se_tpg = nacl->se_tpg;
460 461
461 complete(&nacl->acl_free_comp); 462 if (!nacl->dynamic_stop) {
463 complete(&nacl->acl_free_comp);
464 return;
465 }
466
467 mutex_lock(&se_tpg->acl_node_mutex);
468 list_del(&nacl->acl_list);
469 mutex_unlock(&se_tpg->acl_node_mutex);
470
471 core_tpg_wait_for_nacl_pr_ref(nacl);
472 core_free_device_list_for_node(nacl, se_tpg);
473 kfree(nacl);
462} 474}
463 475
464void target_put_nacl(struct se_node_acl *nacl) 476void target_put_nacl(struct se_node_acl *nacl)
@@ -499,12 +511,39 @@ EXPORT_SYMBOL(transport_deregister_session_configfs);
499void transport_free_session(struct se_session *se_sess) 511void transport_free_session(struct se_session *se_sess)
500{ 512{
501 struct se_node_acl *se_nacl = se_sess->se_node_acl; 513 struct se_node_acl *se_nacl = se_sess->se_node_acl;
514
502 /* 515 /*
503 * Drop the se_node_acl->nacl_kref obtained from within 516 * Drop the se_node_acl->nacl_kref obtained from within
504 * core_tpg_get_initiator_node_acl(). 517 * core_tpg_get_initiator_node_acl().
505 */ 518 */
506 if (se_nacl) { 519 if (se_nacl) {
520 struct se_portal_group *se_tpg = se_nacl->se_tpg;
521 const struct target_core_fabric_ops *se_tfo = se_tpg->se_tpg_tfo;
522 unsigned long flags;
523
507 se_sess->se_node_acl = NULL; 524 se_sess->se_node_acl = NULL;
525
526 /*
527 * Also determine if we need to drop the extra ->cmd_kref if
528 * it had been previously dynamically generated, and
529 * the endpoint is not caching dynamic ACLs.
530 */
531 mutex_lock(&se_tpg->acl_node_mutex);
532 if (se_nacl->dynamic_node_acl &&
533 !se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
534 spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags);
535 if (list_empty(&se_nacl->acl_sess_list))
536 se_nacl->dynamic_stop = true;
537 spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags);
538
539 if (se_nacl->dynamic_stop)
540 list_del(&se_nacl->acl_list);
541 }
542 mutex_unlock(&se_tpg->acl_node_mutex);
543
544 if (se_nacl->dynamic_stop)
545 target_put_nacl(se_nacl);
546
508 target_put_nacl(se_nacl); 547 target_put_nacl(se_nacl);
509 } 548 }
510 if (se_sess->sess_cmd_map) { 549 if (se_sess->sess_cmd_map) {
@@ -518,16 +557,12 @@ EXPORT_SYMBOL(transport_free_session);
518void transport_deregister_session(struct se_session *se_sess) 557void transport_deregister_session(struct se_session *se_sess)
519{ 558{
520 struct se_portal_group *se_tpg = se_sess->se_tpg; 559 struct se_portal_group *se_tpg = se_sess->se_tpg;
521 const struct target_core_fabric_ops *se_tfo;
522 struct se_node_acl *se_nacl;
523 unsigned long flags; 560 unsigned long flags;
524 bool drop_nacl = false;
525 561
526 if (!se_tpg) { 562 if (!se_tpg) {
527 transport_free_session(se_sess); 563 transport_free_session(se_sess);
528 return; 564 return;
529 } 565 }
530 se_tfo = se_tpg->se_tpg_tfo;
531 566
532 spin_lock_irqsave(&se_tpg->session_lock, flags); 567 spin_lock_irqsave(&se_tpg->session_lock, flags);
533 list_del(&se_sess->sess_list); 568 list_del(&se_sess->sess_list);
@@ -535,33 +570,15 @@ void transport_deregister_session(struct se_session *se_sess)
535 se_sess->fabric_sess_ptr = NULL; 570 se_sess->fabric_sess_ptr = NULL;
536 spin_unlock_irqrestore(&se_tpg->session_lock, flags); 571 spin_unlock_irqrestore(&se_tpg->session_lock, flags);
537 572
538 /*
539 * Determine if we need to do extra work for this initiator node's
540 * struct se_node_acl if it had been previously dynamically generated.
541 */
542 se_nacl = se_sess->se_node_acl;
543
544 mutex_lock(&se_tpg->acl_node_mutex);
545 if (se_nacl && se_nacl->dynamic_node_acl) {
546 if (!se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
547 list_del(&se_nacl->acl_list);
548 drop_nacl = true;
549 }
550 }
551 mutex_unlock(&se_tpg->acl_node_mutex);
552
553 if (drop_nacl) {
554 core_tpg_wait_for_nacl_pr_ref(se_nacl);
555 core_free_device_list_for_node(se_nacl, se_tpg);
556 se_sess->se_node_acl = NULL;
557 kfree(se_nacl);
558 }
559 pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n", 573 pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n",
560 se_tpg->se_tpg_tfo->get_fabric_name()); 574 se_tpg->se_tpg_tfo->get_fabric_name());
561 /* 575 /*
562 * If last kref is dropping now for an explicit NodeACL, awake sleeping 576 * If last kref is dropping now for an explicit NodeACL, awake sleeping
563 * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group 577 * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
564 * removal context from within transport_free_session() code. 578 * removal context from within transport_free_session() code.
579 *
580 * For dynamic ACL, target_put_nacl() uses target_complete_nacl()
581 * to release all remaining generate_node_acl=1 created ACL resources.
565 */ 582 */
566 583
567 transport_free_session(se_sess); 584 transport_free_session(se_sess);
@@ -3110,7 +3127,6 @@ static void target_tmr_work(struct work_struct *work)
3110 spin_unlock_irqrestore(&cmd->t_state_lock, flags); 3127 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
3111 goto check_stop; 3128 goto check_stop;
3112 } 3129 }
3113 cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
3114 spin_unlock_irqrestore(&cmd->t_state_lock, flags); 3130 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
3115 3131
3116 cmd->se_tfo->queue_tm_rsp(cmd); 3132 cmd->se_tfo->queue_tm_rsp(cmd);
@@ -3123,11 +3139,25 @@ int transport_generic_handle_tmr(
3123 struct se_cmd *cmd) 3139 struct se_cmd *cmd)
3124{ 3140{
3125 unsigned long flags; 3141 unsigned long flags;
3142 bool aborted = false;
3126 3143
3127 spin_lock_irqsave(&cmd->t_state_lock, flags); 3144 spin_lock_irqsave(&cmd->t_state_lock, flags);
3128 cmd->transport_state |= CMD_T_ACTIVE; 3145 if (cmd->transport_state & CMD_T_ABORTED) {
3146 aborted = true;
3147 } else {
3148 cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
3149 cmd->transport_state |= CMD_T_ACTIVE;
3150 }
3129 spin_unlock_irqrestore(&cmd->t_state_lock, flags); 3151 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
3130 3152
3153 if (aborted) {
3154 pr_warn_ratelimited("handle_tmr caught CMD_T_ABORTED TMR %d"
3155 "ref_tag: %llu tag: %llu\n", cmd->se_tmr_req->function,
3156 cmd->se_tmr_req->ref_task_tag, cmd->tag);
3157 transport_cmd_check_stop_to_fabric(cmd);
3158 return 0;
3159 }
3160
3131 INIT_WORK(&cmd->work, target_tmr_work); 3161 INIT_WORK(&cmd->work, target_tmr_work);
3132 queue_work(cmd->se_dev->tmr_wq, &cmd->work); 3162 queue_work(cmd->se_dev->tmr_wq, &cmd->work);
3133 return 0; 3163 return 0;
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
index d828b3b5000b..cac5a20a4de0 100644
--- a/drivers/target/target_core_xcopy.c
+++ b/drivers/target/target_core_xcopy.c
@@ -864,7 +864,7 @@ out:
864 " CHECK_CONDITION -> sending response\n", rc); 864 " CHECK_CONDITION -> sending response\n", rc);
865 ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION; 865 ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
866 } 866 }
867 target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION); 867 target_complete_cmd(ec_cmd, ec_cmd->scsi_status);
868} 868}
869 869
870sense_reason_t target_do_xcopy(struct se_cmd *se_cmd) 870sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 43edf82e54ff..da854fb4530f 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -538,6 +538,7 @@ struct se_node_acl {
538 char initiatorname[TRANSPORT_IQN_LEN]; 538 char initiatorname[TRANSPORT_IQN_LEN];
539 /* Used to signal demo mode created ACL, disabled by default */ 539 /* Used to signal demo mode created ACL, disabled by default */
540 bool dynamic_node_acl; 540 bool dynamic_node_acl;
541 bool dynamic_stop;
541 u32 queue_depth; 542 u32 queue_depth;
542 u32 acl_index; 543 u32 acl_index;
543 enum target_prot_type saved_prot_type; 544 enum target_prot_type saved_prot_type;