Commit 55aac6ef authored by Linus Torvalds's avatar Linus Torvalds

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
parents 2b369478 b22bc278
...@@ -3816,6 +3816,7 @@ static struct configfs_attribute *ibmvscsis_tpg_attrs[] = { ...@@ -3816,6 +3816,7 @@ static struct configfs_attribute *ibmvscsis_tpg_attrs[] = {
static const struct target_core_fabric_ops ibmvscsis_ops = { static const struct target_core_fabric_ops ibmvscsis_ops = {
.module = THIS_MODULE, .module = THIS_MODULE,
.name = "ibmvscsis", .name = "ibmvscsis",
.max_data_sg_nents = MAX_TXU / PAGE_SIZE,
.get_fabric_name = ibmvscsis_get_fabric_name, .get_fabric_name = ibmvscsis_get_fabric_name,
.tpg_get_wwn = ibmvscsis_get_fabric_wwn, .tpg_get_wwn = ibmvscsis_get_fabric_wwn,
.tpg_get_tag = ibmvscsis_get_tag, .tpg_get_tag = ibmvscsis_get_tag,
......
...@@ -352,7 +352,15 @@ int core_enable_device_list_for_node( ...@@ -352,7 +352,15 @@ int core_enable_device_list_for_node(
kfree(new); kfree(new);
return -EINVAL; return -EINVAL;
} }
BUG_ON(orig->se_lun_acl != NULL); if (orig->se_lun_acl != NULL) {
pr_warn_ratelimited("Detected existing explicit"
" se_lun_acl->se_lun_group reference for %s"
" mapped_lun: %llu, failing\n",
nacl->initiatorname, mapped_lun);
mutex_unlock(&nacl->lun_entry_mutex);
kfree(new);
return -EINVAL;
}
rcu_assign_pointer(new->se_lun, lun); rcu_assign_pointer(new->se_lun, lun);
rcu_assign_pointer(new->se_lun_acl, lun_acl); rcu_assign_pointer(new->se_lun_acl, lun_acl);
......
...@@ -451,6 +451,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, ...@@ -451,6 +451,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
int *post_ret) int *post_ret)
{ {
struct se_device *dev = cmd->se_dev; struct se_device *dev = cmd->se_dev;
sense_reason_t ret = TCM_NO_SENSE;
/* /*
* Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through * 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,9 +459,12 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
* sent to the backend driver. * sent to the backend driver.
*/ */
spin_lock_irq(&cmd->t_state_lock); spin_lock_irq(&cmd->t_state_lock);
if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) { if (cmd->transport_state & CMD_T_SENT) {
cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST; cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
*post_ret = 1; *post_ret = 1;
if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION)
ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
} }
spin_unlock_irq(&cmd->t_state_lock); spin_unlock_irq(&cmd->t_state_lock);
...@@ -470,7 +474,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, ...@@ -470,7 +474,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
*/ */
up(&dev->caw_sem); up(&dev->caw_sem);
return TCM_NO_SENSE; return ret;
} }
static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success, static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
......
...@@ -457,8 +457,20 @@ static void target_complete_nacl(struct kref *kref) ...@@ -457,8 +457,20 @@ static void target_complete_nacl(struct kref *kref)
{ {
struct se_node_acl *nacl = container_of(kref, struct se_node_acl *nacl = container_of(kref,
struct se_node_acl, acl_kref); struct se_node_acl, acl_kref);
struct se_portal_group *se_tpg = nacl->se_tpg;
complete(&nacl->acl_free_comp); if (!nacl->dynamic_stop) {
complete(&nacl->acl_free_comp);
return;
}
mutex_lock(&se_tpg->acl_node_mutex);
list_del(&nacl->acl_list);
mutex_unlock(&se_tpg->acl_node_mutex);
core_tpg_wait_for_nacl_pr_ref(nacl);
core_free_device_list_for_node(nacl, se_tpg);
kfree(nacl);
} }
void target_put_nacl(struct se_node_acl *nacl) void target_put_nacl(struct se_node_acl *nacl)
...@@ -499,12 +511,39 @@ EXPORT_SYMBOL(transport_deregister_session_configfs); ...@@ -499,12 +511,39 @@ EXPORT_SYMBOL(transport_deregister_session_configfs);
void transport_free_session(struct se_session *se_sess) void transport_free_session(struct se_session *se_sess)
{ {
struct se_node_acl *se_nacl = se_sess->se_node_acl; struct se_node_acl *se_nacl = se_sess->se_node_acl;
/* /*
* Drop the se_node_acl->nacl_kref obtained from within * Drop the se_node_acl->nacl_kref obtained from within
* core_tpg_get_initiator_node_acl(). * core_tpg_get_initiator_node_acl().
*/ */
if (se_nacl) { if (se_nacl) {
struct se_portal_group *se_tpg = se_nacl->se_tpg;
const struct target_core_fabric_ops *se_tfo = se_tpg->se_tpg_tfo;
unsigned long flags;
se_sess->se_node_acl = NULL; se_sess->se_node_acl = NULL;
/*
* Also determine if we need to drop the extra ->cmd_kref if
* it had been previously dynamically generated, and
* the endpoint is not caching dynamic ACLs.
*/
mutex_lock(&se_tpg->acl_node_mutex);
if (se_nacl->dynamic_node_acl &&
!se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags);
if (list_empty(&se_nacl->acl_sess_list))
se_nacl->dynamic_stop = true;
spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags);
if (se_nacl->dynamic_stop)
list_del(&se_nacl->acl_list);
}
mutex_unlock(&se_tpg->acl_node_mutex);
if (se_nacl->dynamic_stop)
target_put_nacl(se_nacl);
target_put_nacl(se_nacl); target_put_nacl(se_nacl);
} }
if (se_sess->sess_cmd_map) { if (se_sess->sess_cmd_map) {
...@@ -518,16 +557,12 @@ EXPORT_SYMBOL(transport_free_session); ...@@ -518,16 +557,12 @@ EXPORT_SYMBOL(transport_free_session);
void transport_deregister_session(struct se_session *se_sess) void transport_deregister_session(struct se_session *se_sess)
{ {
struct se_portal_group *se_tpg = se_sess->se_tpg; struct se_portal_group *se_tpg = se_sess->se_tpg;
const struct target_core_fabric_ops *se_tfo;
struct se_node_acl *se_nacl;
unsigned long flags; unsigned long flags;
bool drop_nacl = false;
if (!se_tpg) { if (!se_tpg) {
transport_free_session(se_sess); transport_free_session(se_sess);
return; return;
} }
se_tfo = se_tpg->se_tpg_tfo;
spin_lock_irqsave(&se_tpg->session_lock, flags); spin_lock_irqsave(&se_tpg->session_lock, flags);
list_del(&se_sess->sess_list); list_del(&se_sess->sess_list);
...@@ -535,33 +570,15 @@ void transport_deregister_session(struct se_session *se_sess) ...@@ -535,33 +570,15 @@ void transport_deregister_session(struct se_session *se_sess)
se_sess->fabric_sess_ptr = NULL; se_sess->fabric_sess_ptr = NULL;
spin_unlock_irqrestore(&se_tpg->session_lock, flags); spin_unlock_irqrestore(&se_tpg->session_lock, flags);
/*
* Determine if we need to do extra work for this initiator node's
* struct se_node_acl if it had been previously dynamically generated.
*/
se_nacl = se_sess->se_node_acl;
mutex_lock(&se_tpg->acl_node_mutex);
if (se_nacl && se_nacl->dynamic_node_acl) {
if (!se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
list_del(&se_nacl->acl_list);
drop_nacl = true;
}
}
mutex_unlock(&se_tpg->acl_node_mutex);
if (drop_nacl) {
core_tpg_wait_for_nacl_pr_ref(se_nacl);
core_free_device_list_for_node(se_nacl, se_tpg);
se_sess->se_node_acl = NULL;
kfree(se_nacl);
}
pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n", pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n",
se_tpg->se_tpg_tfo->get_fabric_name()); se_tpg->se_tpg_tfo->get_fabric_name());
/* /*
* If last kref is dropping now for an explicit NodeACL, awake sleeping * If last kref is dropping now for an explicit NodeACL, awake sleeping
* ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
* removal context from within transport_free_session() code. * removal context from within transport_free_session() code.
*
* For dynamic ACL, target_put_nacl() uses target_complete_nacl()
* to release all remaining generate_node_acl=1 created ACL resources.
*/ */
transport_free_session(se_sess); transport_free_session(se_sess);
...@@ -3110,7 +3127,6 @@ static void target_tmr_work(struct work_struct *work) ...@@ -3110,7 +3127,6 @@ static void target_tmr_work(struct work_struct *work)
spin_unlock_irqrestore(&cmd->t_state_lock, flags); spin_unlock_irqrestore(&cmd->t_state_lock, flags);
goto check_stop; goto check_stop;
} }
cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
spin_unlock_irqrestore(&cmd->t_state_lock, flags); spin_unlock_irqrestore(&cmd->t_state_lock, flags);
cmd->se_tfo->queue_tm_rsp(cmd); cmd->se_tfo->queue_tm_rsp(cmd);
...@@ -3123,11 +3139,25 @@ int transport_generic_handle_tmr( ...@@ -3123,11 +3139,25 @@ int transport_generic_handle_tmr(
struct se_cmd *cmd) struct se_cmd *cmd)
{ {
unsigned long flags; unsigned long flags;
bool aborted = false;
spin_lock_irqsave(&cmd->t_state_lock, flags); spin_lock_irqsave(&cmd->t_state_lock, flags);
cmd->transport_state |= CMD_T_ACTIVE; if (cmd->transport_state & CMD_T_ABORTED) {
aborted = true;
} else {
cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
cmd->transport_state |= CMD_T_ACTIVE;
}
spin_unlock_irqrestore(&cmd->t_state_lock, flags); spin_unlock_irqrestore(&cmd->t_state_lock, flags);
if (aborted) {
pr_warn_ratelimited("handle_tmr caught CMD_T_ABORTED TMR %d"
"ref_tag: %llu tag: %llu\n", cmd->se_tmr_req->function,
cmd->se_tmr_req->ref_task_tag, cmd->tag);
transport_cmd_check_stop_to_fabric(cmd);
return 0;
}
INIT_WORK(&cmd->work, target_tmr_work); INIT_WORK(&cmd->work, target_tmr_work);
queue_work(cmd->se_dev->tmr_wq, &cmd->work); queue_work(cmd->se_dev->tmr_wq, &cmd->work);
return 0; return 0;
......
...@@ -864,7 +864,7 @@ static void target_xcopy_do_work(struct work_struct *work) ...@@ -864,7 +864,7 @@ static void target_xcopy_do_work(struct work_struct *work)
" CHECK_CONDITION -> sending response\n", rc); " CHECK_CONDITION -> sending response\n", rc);
ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION; ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
} }
target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION); target_complete_cmd(ec_cmd, ec_cmd->scsi_status);
} }
sense_reason_t target_do_xcopy(struct se_cmd *se_cmd) sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
......
...@@ -538,6 +538,7 @@ struct se_node_acl { ...@@ -538,6 +538,7 @@ struct se_node_acl {
char initiatorname[TRANSPORT_IQN_LEN]; char initiatorname[TRANSPORT_IQN_LEN];
/* Used to signal demo mode created ACL, disabled by default */ /* Used to signal demo mode created ACL, disabled by default */
bool dynamic_node_acl; bool dynamic_node_acl;
bool dynamic_stop;
u32 queue_depth; u32 queue_depth;
u32 acl_index; u32 acl_index;
enum target_prot_type saved_prot_type; enum target_prot_type saved_prot_type;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment