From eb39d34004888afcc0a44d9c36383cd69fa3b3b9 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 26 Jul 2011 16:59:00 -0700 Subject: target: Change TCM_NON_EXISTENT_LUN response to ASC=LOGICAL UNIT NOT SUPPORTED This patch changes transport_send_check_condition_and_sense() for TCM_NON_EXISTENT_LUN emulation to use 0x25 (LOGICAL UNIT NOT SUPPORTED) instead of the original 0x20 (INVALID COMMAND OPERATION CODE). This is helpful to distinguish between TCM_UNSUPPORTED_SCSI_OPCODE ASC=0x20 exceptions. Signed-off-by: Nicholas A. Bellinger --- drivers/target/target_core_transport.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 89760329d5d0..cc5a339d4d5a 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -4726,6 +4726,13 @@ int transport_send_check_condition_and_sense( */ switch (reason) { case TCM_NON_EXISTENT_LUN: + /* CURRENT ERROR */ + buffer[offset] = 0x70; + /* ILLEGAL REQUEST */ + buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; + /* LOGICAL UNIT NOT SUPPORTED */ + buffer[offset+SPC_ASC_KEY_OFFSET] = 0x25; + break; case TCM_UNSUPPORTED_SCSI_OPCODE: case TCM_SECTOR_COUNT_TOO_MANY: /* CURRENT ERROR */ -- cgit v1.2.3 From 052605c6caa3e1edf8eee8fe5fe6d53f5721f39a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 26 Jul 2011 17:48:43 -0700 Subject: target: Make standard INQUIRY return 'not connected' for tpg_virt_lun0 This patch changes target_emulate_inquiry_std() to set the 'not connected' (0x35) bit in standard INQUIRY response data when we are processing a request to a virtual LUN=0 mapping from struct se_device *g_lun0_dev that have been setup for us in transport_lookup_cmd_lun(). This addresses an issue where qla2xxx FC clients need to be able to create demo-mode I_T FC Nexuses by default, but should not be exposing the default set of TPG LUNs to all FC clients. This includes adding an new optional target_core_fabric_ops->tpg_check_demo_mode_login_only() caller to allow demo_mode nexuses to skip the old default of bulding a demo-mode MappedLUNs list via core_tpg_add_node_to_devs(). (roland: Add missing tpg_check_demo_mode_login_only check in core_dev_add_lun) Reported-by: Roland Dreier Cc: Andrew Vasquez Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_cdb.c | 11 ++++++++--- drivers/target/target_core_device.c | 4 +++- drivers/target/target_core_tpg.c | 12 ++++++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 8ae09a1bdf74..d095408dbd5f 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -67,6 +67,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd) { struct se_lun *lun = cmd->se_lun; struct se_device *dev = cmd->se_dev; + struct se_portal_group *tpg = lun->lun_sep->sep_tpg; unsigned char *buf; /* @@ -81,9 +82,13 @@ target_emulate_inquiry_std(struct se_cmd *cmd) buf = transport_kmap_first_data_page(cmd); - buf[0] = dev->transport->get_device_type(dev); - if (buf[0] == TYPE_TAPE) - buf[1] = 0x80; + if (dev == tpg->tpg_virt_lun0.lun_se_dev) { + buf[0] = 0x3f; /* Not connected */ + } else { + buf[0] = dev->transport->get_device_type(dev); + if (buf[0] == TYPE_TAPE) + buf[1] = 0x80; + } buf[2] = dev->transport->get_device_rev(dev); /* diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index b38b6c993e65..ec3fbcda3e3c 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1346,7 +1346,9 @@ struct se_lun *core_dev_add_lun( struct se_node_acl *acl; spin_lock_bh(&tpg->acl_node_lock); list_for_each_entry(acl, &tpg->acl_node_list, acl_list) { - if (acl->dynamic_node_acl) { + if (acl->dynamic_node_acl && + (!tpg->se_tpg_tfo->tpg_check_demo_mode_login_only || + !tpg->se_tpg_tfo->tpg_check_demo_mode_login_only(tpg))) { spin_unlock_bh(&tpg->acl_node_lock); core_tpg_add_node_to_devs(acl, tpg); spin_lock_bh(&tpg->acl_node_lock); diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 4f1ba4c5ef11..718ccd1348b1 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -298,8 +298,16 @@ struct se_node_acl *core_tpg_check_initiator_node_acl( tpg->se_tpg_tfo->tpg_release_fabric_acl(tpg, acl); return NULL; } - - core_tpg_add_node_to_devs(acl, tpg); + /* + * Here we only create demo-mode MappedLUNs from the active + * TPG LUNs if the fabric is not explictly asking for + * tpg_check_demo_mode_login_only() == 1. + */ + if ((tpg->se_tpg_tfo->tpg_check_demo_mode_login_only != NULL) && + (tpg->se_tpg_tfo->tpg_check_demo_mode_login_only(tpg) == 1)) + do { ; } while (0); + else + core_tpg_add_node_to_devs(acl, tpg); spin_lock_bh(&tpg->acl_node_lock); list_add_tail(&acl->acl_list, &tpg->acl_node_list); -- cgit v1.2.3 From e1750ba20f0d850c38820190ccbf0f647723091a Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Mon, 1 Aug 2011 23:58:18 +0200 Subject: target: Use ERR_CAST inlined function Use ERR_CAST inlined function instead of ERR_PTR(PTR_ERR(...)) The semantic patch that makes this output is available in scripts/coccinelle/api/err_cast.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Thomas Meyer Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_configfs.c | 4 ++-- drivers/target/target_core_fabric_configfs.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index f095e65b1ccf..f1643dbf6a92 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -268,7 +268,7 @@ struct se_tpg_np *lio_target_call_addnptotpg( ISCSI_TCP); if (IS_ERR(tpg_np)) { iscsit_put_tpg(tpg); - return ERR_PTR(PTR_ERR(tpg_np)); + return ERR_CAST(tpg_np); } pr_debug("LIO_Target_ConfigFS: addnptotpg done!\n"); @@ -1285,7 +1285,7 @@ struct se_wwn *lio_target_call_coreaddtiqn( tiqn = iscsit_add_tiqn((unsigned char *)name); if (IS_ERR(tiqn)) - return ERR_PTR(PTR_ERR(tiqn)); + return ERR_CAST(tiqn); /* * Setup struct iscsi_wwn_stat_grps for se_wwn->fabric_stat_group. */ diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index f1654694f4ea..55bbe0847a6d 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -481,7 +481,7 @@ static struct config_group *target_fabric_make_nodeacl( se_nacl = tf->tf_ops.fabric_make_nodeacl(se_tpg, group, name); if (IS_ERR(se_nacl)) - return ERR_PTR(PTR_ERR(se_nacl)); + return ERR_CAST(se_nacl); nacl_cg = &se_nacl->acl_group; nacl_cg->default_groups = se_nacl->acl_default_groups; -- cgit v1.2.3 From 9be08c5804ae4ad96ec22d0b1e71e630803a85ea Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Tue, 2 Aug 2011 10:26:36 +0200 Subject: iscsi-target: Fix leak on failure in iscsi_copy_param_list() We leak memory if the allocations for 'new_param->name' or 'new_param->value' fail in iscsi_target_parameters.c::iscsi_copy_param_list() We also do a lot of variable assignments that are completely pointless if the allocations fail. So, let's move the allocations before the assignments and also make sure that we free whatever was allocated to one if the allocation fail. There's also some small CodingStyle fixups in there (curly braces on both branches of if statement, only one variable per line) since I was in the area anyway. And finally, error messages in the function are put on a single line for easy grep'abillity. Signed-off-by: Jesper Juhl Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_parameters.c | 43 ++++++++++---------------- 1 file changed, 16 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index 252e246cf51e..497b2e718a76 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -545,13 +545,13 @@ int iscsi_copy_param_list( struct iscsi_param_list *src_param_list, int leading) { - struct iscsi_param *new_param = NULL, *param = NULL; + struct iscsi_param *param = NULL; + struct iscsi_param *new_param = NULL; struct iscsi_param_list *param_list = NULL; param_list = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL); if (!param_list) { - pr_err("Unable to allocate memory for" - " struct iscsi_param_list.\n"); + pr_err("Unable to allocate memory for struct iscsi_param_list.\n"); goto err_out; } INIT_LIST_HEAD(¶m_list->param_list); @@ -567,8 +567,17 @@ int iscsi_copy_param_list( new_param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL); if (!new_param) { - pr_err("Unable to allocate memory for" - " struct iscsi_param.\n"); + pr_err("Unable to allocate memory for struct iscsi_param.\n"); + goto err_out; + } + + new_param->name = kstrdup(param->name, GFP_KERNEL); + new_param->value = kstrdup(param->value, GFP_KERNEL); + if (!new_param->value || !new_param->name) { + kfree(new_param->value); + kfree(new_param->name); + kfree(new_param); + pr_err("Unable to allocate memory for parameter name/value.\n"); goto err_out; } @@ -580,32 +589,12 @@ int iscsi_copy_param_list( new_param->use = param->use; new_param->type_range = param->type_range; - new_param->name = kzalloc(strlen(param->name) + 1, GFP_KERNEL); - if (!new_param->name) { - pr_err("Unable to allocate memory for" - " parameter name.\n"); - goto err_out; - } - - new_param->value = kzalloc(strlen(param->value) + 1, - GFP_KERNEL); - if (!new_param->value) { - pr_err("Unable to allocate memory for" - " parameter value.\n"); - goto err_out; - } - - memcpy(new_param->name, param->name, strlen(param->name)); - new_param->name[strlen(param->name)] = '\0'; - memcpy(new_param->value, param->value, strlen(param->value)); - new_param->value[strlen(param->value)] = '\0'; - list_add_tail(&new_param->p_list, ¶m_list->param_list); } - if (!list_empty(¶m_list->param_list)) + if (!list_empty(¶m_list->param_list)) { *dst_param_list = param_list; - else { + } else { pr_err("No parameters allocated.\n"); goto err_out; } -- cgit v1.2.3 From 6fc6148865c9a17cee33f251723f6a056f022ecd Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 2 Aug 2011 12:35:02 +0200 Subject: target: Convert target_core_rd.c to use use BUG_ON Use BUG_ON(x) rather than if(x) BUG(); The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier x; @@ -if (x) BUG(); +BUG_ON(x); @@ identifier x; @@ -if (!x) BUG(); +BUG_ON(!x); // Signed-off-by: Julia Lawall Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_rd.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 3dd81d24d9a9..e567e129c697 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -390,12 +390,10 @@ static int rd_MEMCPY_read(struct rd_request *req) length = req->rd_size; dst = sg_virt(&sg_d[i++]) + dst_offset; - if (!dst) - BUG(); + BUG_ON(!dst); src = sg_virt(&sg_s[j]) + src_offset; - if (!src) - BUG(); + BUG_ON(!src); dst_offset = 0; src_offset = length; @@ -415,8 +413,7 @@ static int rd_MEMCPY_read(struct rd_request *req) length = req->rd_size; dst = sg_virt(&sg_d[i]) + dst_offset; - if (!dst) - BUG(); + BUG_ON(!dst); if (sg_d[i].length == length) { i++; @@ -425,8 +422,7 @@ static int rd_MEMCPY_read(struct rd_request *req) dst_offset = length; src = sg_virt(&sg_s[j++]) + src_offset; - if (!src) - BUG(); + BUG_ON(!src); src_offset = 0; page_end = 1; @@ -510,12 +506,10 @@ static int rd_MEMCPY_write(struct rd_request *req) length = req->rd_size; src = sg_virt(&sg_s[i++]) + src_offset; - if (!src) - BUG(); + BUG_ON(!src); dst = sg_virt(&sg_d[j]) + dst_offset; - if (!dst) - BUG(); + BUG_ON(!dst); src_offset = 0; dst_offset = length; @@ -535,8 +529,7 @@ static int rd_MEMCPY_write(struct rd_request *req) length = req->rd_size; src = sg_virt(&sg_s[i]) + src_offset; - if (!src) - BUG(); + BUG_ON(!src); if (sg_s[i].length == length) { i++; @@ -545,8 +538,7 @@ static int rd_MEMCPY_write(struct rd_request *req) src_offset = length; dst = sg_virt(&sg_d[j++]) + dst_offset; - if (!dst) - BUG(); + BUG_ON(!dst); dst_offset = 0; page_end = 1; -- cgit v1.2.3 From c2337c709102b343bd917ae00c79b266fb15b871 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 8 Aug 2011 14:02:27 -0700 Subject: iscsi-target: remove duplicate return We returned on the line before already. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index c24fb10de60b..6a4ea29c2f36 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -2243,7 +2243,6 @@ static int iscsit_handle_snack( case 0: return iscsit_handle_recovery_datain_or_r2t(conn, buf, hdr->itt, hdr->ttt, hdr->begrun, hdr->runlength); - return 0; case ISCSI_FLAG_SNACK_TYPE_STATUS: return iscsit_handle_status_snack(conn, hdr->itt, hdr->ttt, hdr->begrun, hdr->runlength); -- cgit v1.2.3 From 387e96c05299ca7a0ade874f343f91f0b01086a0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 8 Aug 2011 14:06:44 -0700 Subject: iscsi-target: forever loop bug in iscsit_attach_ooo_cmdsn() This patch fixes a forever loop bug in iscsit_attach_ooo_cmdsn() while walking sess->sess_ooo_cmdsn_list when the received CmdSN is less than the tail of the list. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_erl1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index 980650792cf6..c4c68da3e500 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -834,7 +834,7 @@ static int iscsit_attach_ooo_cmdsn( */ list_for_each_entry(ooo_tmp, &sess->sess_ooo_cmdsn_list, ooo_list) { - while (ooo_tmp->cmdsn < ooo_cmdsn->cmdsn) + if (ooo_tmp->cmdsn < ooo_cmdsn->cmdsn) continue; list_add(&ooo_cmdsn->ooo_list, -- cgit v1.2.3 From 16ab8e60a0ebc22cfbe61d84e620457a15f3a0bc Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 8 Aug 2011 19:03:38 -0700 Subject: target: Fix write payload exception handling with ->new_cmd_map This patch fixes a bug for fabrics using tfo->new_cmd_map() that are expect transport_generic_request_failure() to be calling transport_send_check_condition_and_sense() for both READ and WRITE, instead of only for READ exceptions. This was originally observed with a failed WRITE_SAME_16 w/ unmap=0 using tcm_loop. Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index cc5a339d4d5a..fd7d4518b8ef 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2053,8 +2053,14 @@ static void transport_generic_request_failure( cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE; break; } - - if (!sc) + /* + * If a fabric does not define a cmd->se_tfo->new_cmd_map caller, + * make the call to transport_send_check_condition_and_sense() + * directly. Otherwise expect the fabric to make the call to + * transport_send_check_condition_and_sense() after handling + * possible unsoliticied write data payloads. + */ + if (!sc && !cmd->se_tfo->new_cmd_map) transport_new_cmd_failure(cmd); else { ret = transport_send_check_condition_and_sense(cmd, -- cgit v1.2.3 From 706d5860969b3b24d65d9a57bd3bb5e4a1419c08 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 28 Jul 2011 00:07:03 -0700 Subject: target: Add WRITE_SAME (10) parsing and refactor passthrough checks This patch adds initial WRITE_SAME (10) w/ UNMAP=1 support following updates in sbcr26 to allow UNMAP=1 for the non 16 + 32 byte CDB case. It also refactors current pSCSI passthrough passthrough checks into target_check_write_same_discard() ahead of UNMAP=0 w/ write payload support into target_core_iblock.c. This includes the support for handling WRITE_SAME in transport_emulate_control_cdb(), and converts target_emulate_write_same to accept num_blocks directly for WRITE_SAME, WRITE_SAME_16 and WRITE_SAME_32. Reported-by: Eric Seppanen Cc: Roland Dreier Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_cdb.c | 28 +++++---- drivers/target/target_core_transport.c | 102 ++++++++++++++++++--------------- 2 files changed, 73 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index d095408dbd5f..4c1d3a98e894 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -1090,24 +1090,17 @@ err: * Note this is not used for TCM/pSCSI passthrough */ static int -target_emulate_write_same(struct se_task *task, int write_same32) +target_emulate_write_same(struct se_task *task, u32 num_blocks) { struct se_cmd *cmd = task->task_se_cmd; struct se_device *dev = cmd->se_dev; sector_t range; sector_t lba = cmd->t_task_lba; - unsigned int num_blocks; int ret; /* - * Extract num_blocks from the WRITE_SAME_* CDB. Then use the explict - * range when non zero is supplied, otherwise calculate the remaining - * range based on ->get_blocks() - starting LBA. + * Use the explicit range when non zero is supplied, otherwise calculate + * the remaining range based on ->get_blocks() - starting LBA. */ - if (write_same32) - num_blocks = get_unaligned_be32(&cmd->t_task_cdb[28]); - else - num_blocks = get_unaligned_be32(&cmd->t_task_cdb[10]); - if (num_blocks != 0) range = num_blocks; else @@ -1170,13 +1163,23 @@ transport_emulate_control_cdb(struct se_task *task) } ret = target_emulate_unmap(task); break; + case WRITE_SAME: + if (!dev->transport->do_discard) { + pr_err("WRITE_SAME emulation not supported" + " for: %s\n", dev->transport->name); + return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; + } + ret = target_emulate_write_same(task, + get_unaligned_be16(&cmd->t_task_cdb[7])); + break; case WRITE_SAME_16: if (!dev->transport->do_discard) { pr_err("WRITE_SAME_16 emulation not supported" " for: %s\n", dev->transport->name); return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; } - ret = target_emulate_write_same(task, 0); + ret = target_emulate_write_same(task, + get_unaligned_be32(&cmd->t_task_cdb[10])); break; case VARIABLE_LENGTH_CMD: service_action = @@ -1189,7 +1192,8 @@ transport_emulate_control_cdb(struct se_task *task) dev->transport->name); return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; } - ret = target_emulate_write_same(task, 1); + ret = target_emulate_write_same(task, + get_unaligned_be32(&cmd->t_task_cdb[28])); break; default: pr_err("Unsupported VARIABLE_LENGTH_CMD SA:" diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index fd7d4518b8ef..eb8055aa6e61 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2861,6 +2861,38 @@ static int transport_cmd_get_valid_sectors(struct se_cmd *cmd) return sectors; } +static int target_check_write_same_discard(unsigned char *flags, struct se_device *dev) +{ + /* + * Determine if the received WRITE_SAME is used to for direct + * passthrough into Linux/SCSI with struct request via TCM/pSCSI + * or we are signaling the use of internal WRITE_SAME + UNMAP=1 + * emulation for -> Linux/BLOCK disbard with TCM/IBLOCK code. + */ + int passthrough = (dev->transport->transport_type == + TRANSPORT_PLUGIN_PHBA_PDEV); + + if (!passthrough) { + if ((flags[0] & 0x04) || (flags[0] & 0x02)) { + pr_err("WRITE_SAME PBDATA and LBDATA" + " bits not supported for Block Discard" + " Emulation\n"); + return -ENOSYS; + } + /* + * Currently for the emulated case we only accept + * tpws with the UNMAP=1 bit set. + */ + if (!(flags[0] & 0x08)) { + pr_err("WRITE_SAME w/o UNMAP bit not" + " supported for Block Discard Emulation\n"); + return -ENOSYS; + } + } + + return 0; +} + /* transport_generic_cmd_sequencer(): * * Generic Command Sequencer that should work for most DAS transport @@ -3081,27 +3113,9 @@ static int transport_generic_cmd_sequencer( cmd->t_task_lba = get_unaligned_be64(&cdb[12]); cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB; - /* - * Skip the remaining assignments for TCM/PSCSI passthrough - */ - if (passthrough) - break; - - if ((cdb[10] & 0x04) || (cdb[10] & 0x02)) { - pr_err("WRITE_SAME PBDATA and LBDATA" - " bits not supported for Block Discard" - " Emulation\n"); + if (target_check_write_same_discard(&cdb[10], dev) < 0) goto out_invalid_cdb_field; - } - /* - * Currently for the emulated case we only accept - * tpws with the UNMAP=1 bit set. - */ - if (!(cdb[10] & 0x08)) { - pr_err("WRITE_SAME w/o UNMAP bit not" - " supported for Block Discard Emulation\n"); - goto out_invalid_cdb_field; - } + break; default: pr_err("VARIABLE_LENGTH_CMD service action" @@ -3358,33 +3372,31 @@ static int transport_generic_cmd_sequencer( } cmd->t_task_lba = get_unaligned_be64(&cdb[2]); - passthrough = (dev->transport->transport_type == - TRANSPORT_PLUGIN_PHBA_PDEV); - /* - * Determine if the received WRITE_SAME_16 is used to for direct - * passthrough into Linux/SCSI with struct request via TCM/pSCSI - * or we are signaling the use of internal WRITE_SAME + UNMAP=1 - * emulation for -> Linux/BLOCK disbard with TCM/IBLOCK and - * TCM/FILEIO subsystem plugin backstores. - */ - if (!passthrough) { - if ((cdb[1] & 0x04) || (cdb[1] & 0x02)) { - pr_err("WRITE_SAME PBDATA and LBDATA" - " bits not supported for Block Discard" - " Emulation\n"); - goto out_invalid_cdb_field; - } - /* - * Currently for the emulated case we only accept - * tpws with the UNMAP=1 bit set. - */ - if (!(cdb[1] & 0x08)) { - pr_err("WRITE_SAME w/o UNMAP bit not " - " supported for Block Discard Emulation\n"); - goto out_invalid_cdb_field; - } + cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB; + + if (target_check_write_same_discard(&cdb[1], dev) < 0) + goto out_invalid_cdb_field; + break; + case WRITE_SAME: + sectors = transport_get_sectors_10(cdb, cmd, §or_ret); + if (sector_ret) + goto out_unsupported_cdb; + + if (sectors) + size = transport_get_size(sectors, cdb, cmd); + else { + pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not supported\n"); + goto out_invalid_cdb_field; } + + cmd->t_task_lba = get_unaligned_be32(&cdb[2]); cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB; + /* + * Follow sbcr26 with WRITE_SAME (10) and check for the existence + * of byte 1 bit 3 UNMAP instead of original reserved field + */ + if (target_check_write_same_discard(&cdb[1], dev) < 0) + goto out_invalid_cdb_field; break; case ALLOW_MEDIUM_REMOVAL: case GPCMD_CLOSE_TRACK: -- cgit v1.2.3 From 12850626e2717f866a94e6ced724e3efe5a0aab8 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 8 Aug 2011 19:08:23 -0700 Subject: target: Fix WRITE_SAME usage with transport_get_size For all flavours of WRITE_SAME, we only expect to handle a single block of data-out buffer payload, regardless of the number of logical blocks presented in the CDB. This patch changes all flavours of WRITE_SAME in transport_generic_cmd_sequencer() to pass '1' into transport_get_size() instead of the extracted 'sectors' to properly handle the default usage of sg_write_same without the --xferlen parameter. Reported-by: Eric Seppanen Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index eb8055aa6e61..d35c2cc779e9 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3103,7 +3103,7 @@ static int transport_generic_cmd_sequencer( goto out_unsupported_cdb; if (sectors) - size = transport_get_size(sectors, cdb, cmd); + size = transport_get_size(1, cdb, cmd); else { pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not" " supported\n"); @@ -3365,7 +3365,7 @@ static int transport_generic_cmd_sequencer( goto out_unsupported_cdb; if (sectors) - size = transport_get_size(sectors, cdb, cmd); + size = transport_get_size(1, cdb, cmd); else { pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not supported\n"); goto out_invalid_cdb_field; @@ -3383,7 +3383,7 @@ static int transport_generic_cmd_sequencer( goto out_unsupported_cdb; if (sectors) - size = transport_get_size(sectors, cdb, cmd); + size = transport_get_size(1, cdb, cmd); else { pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not supported\n"); goto out_invalid_cdb_field; -- cgit v1.2.3 From 72f4ba1e32a1e5da31dcf14ea4b8985ae88a8bdb Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 9 Aug 2011 22:53:02 -0700 Subject: target: Remove duplicate task completions in transport_emulate_control_cdb This patch removes a duplicate set of transport_complete_task() calls in target_emulate_unmap() and target_emulate_write_same() as the completion call is already done within transport_emulate_control_cdb() This patch also adds a check in transport_emulate_control_cdb() for the existing SCF_EMULATE_CDB_ASYNC flag currently used by SYNCHRONIZE_CACHE in order to handle IMMEDIATE processing. Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_cdb.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 4c1d3a98e894..40ad142f7cb3 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -1077,8 +1077,6 @@ target_emulate_unmap(struct se_task *task) size -= 16; } - task->task_scsi_status = GOOD; - transport_complete_task(task, 1); err: transport_kunmap_first_data_page(cmd); @@ -1115,8 +1113,6 @@ target_emulate_write_same(struct se_task *task, u32 num_blocks) return ret; } - task->task_scsi_status = GOOD; - transport_complete_task(task, 1); return 0; } @@ -1228,8 +1224,14 @@ transport_emulate_control_cdb(struct se_task *task) if (ret < 0) return ret; - task->task_scsi_status = GOOD; - transport_complete_task(task, 1); + /* + * Handle the successful completion here unless a caller + * has explictly requested an asychronous completion. + */ + if (!(cmd->se_cmd_flags & SCF_EMULATE_CDB_ASYNC)) { + task->task_scsi_status = GOOD; + transport_complete_task(task, 1); + } return PYX_TRANSPORT_SENT_TO_TRANSPORT; } -- cgit v1.2.3 From 7abbe7f3e4243e28a9169ee1b8d76f10a6f5d37c Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 10 Aug 2011 18:41:14 -0700 Subject: target: Fix SYNCHRONIZE_CACHE zero LBA + range breakage This patch fixes a SYNCHRONIZE_CACHE CDB handling bug with IBLOCK/FILEIO backends where transport_cmd_get_valid_sectors() was incorrectly rejecting a zero LBA + range CDB from being processed, and returning CHECK_CONDITION. This includes changing transport_cmd_get_valid_sectors() to return '0' on success and '-EINVAL' on failure (this makes more sense than sectors), and to only check transport_cmd_get_valid_sectors() when a non zero LBA + range SYNCHRONIZE_CACHE operation has been receieved for the non passthrough case. Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index d35c2cc779e9..d385c317a7a4 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2853,12 +2853,10 @@ static int transport_cmd_get_valid_sectors(struct se_cmd *cmd) " transport_dev_end_lba(): %llu\n", cmd->t_task_lba, sectors, transport_dev_end_lba(dev)); - pr_err(" We should return CHECK_CONDITION" - " but we don't yet\n"); - return 0; + return -EINVAL; } - return sectors; + return 0; } static int target_check_write_same_discard(unsigned char *flags, struct se_device *dev) @@ -3350,10 +3348,12 @@ static int transport_generic_cmd_sequencer( cmd->se_cmd_flags |= SCF_EMULATE_CDB_ASYNC; /* * Check to ensure that LBA + Range does not exceed past end of - * device. + * device for IBLOCK and FILEIO ->do_sync_cache() backend calls */ - if (!transport_cmd_get_valid_sectors(cmd)) - goto out_invalid_cdb_field; + if ((cmd->t_task_lba != 0) || (sectors != 0)) { + if (transport_cmd_get_valid_sectors(cmd) < 0) + goto out_invalid_cdb_field; + } break; case UNMAP: size = get_unaligned_be16(&cdb[7]); -- cgit v1.2.3 From 01cde4d54327884a0b61ce8666092f5703557d4b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 10 Aug 2011 00:59:58 -0700 Subject: target: Add missing DATA_SG_IO transport_cmd_get_valid_sectors check This patch adds the missing transport_cmd_get_valid_sectors() check for SCF_SCSI_DATA_SG_IO_CDB type payloads to ensure that a received LBA + range does not exeed past the end of associated backend struct se_device. This patch also fixes a bug in the failure path of transport_new_cmd_obj() where this check can fail, so change to use a signed 'rc' and return '-EINVAL' to signal proper transport_generic_request_failure() handling. Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index d385c317a7a4..ab61c5550852 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3891,9 +3891,7 @@ EXPORT_SYMBOL(transport_generic_map_mem_to_cmd); static int transport_new_cmd_obj(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; - u32 task_cdbs; - u32 rc; - int set_counts = 1; + int set_counts = 1, rc, task_cdbs; /* * Setup any BIDI READ tasks and memory from @@ -3911,7 +3909,7 @@ static int transport_new_cmd_obj(struct se_cmd *cmd) cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - return PYX_TRANSPORT_LU_COMM_FAILURE; + return -EINVAL; } atomic_inc(&cmd->t_fe_count); atomic_inc(&cmd->t_se_count); @@ -3930,7 +3928,7 @@ static int transport_new_cmd_obj(struct se_cmd *cmd) cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - return PYX_TRANSPORT_LU_COMM_FAILURE; + return -EINVAL; } if (set_counts) { @@ -4248,10 +4246,13 @@ static u32 transport_allocate_tasks( struct scatterlist *sgl, unsigned int sgl_nents) { - if (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) + if (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) { + if (transport_cmd_get_valid_sectors(cmd) < 0) + return -EINVAL; + return transport_allocate_data_tasks(cmd, lba, data_direction, sgl, sgl_nents); - else + } else return transport_allocate_control_task(cmd); } -- cgit v1.2.3 From 525a48a21da259d00d6ebc5b60563b5bcf022c26 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sat, 13 Aug 2011 02:11:38 -0700 Subject: target: Fix task count > 1 handling breakage and use max_sector page alignment This patch addresses recent breakage with multiple se_task (task_count > 1) operation following backend dev->se_sub_dev->se_dev_attrib.max_sectors in new transport_allocate_data_tasks() code. The initial bug here was a bogus task->task_sg_nents assignment in transport_allocate_data_tasks() based on the passed parameter, which now uses DIV_ROUND_UP(task_size, PAGE_SIZE) to determine the proper number of per task SGL entries for the (task_count > 1) case. This also means we now need to enforce a PAGE_SIZE aligned max_sector count value for this to work as expected without bringing back the pre v3.1 transport_map_mem_to_sg() logic to handle SGL offsets across multiple tasks. So this patch adds se_dev_align_max_sectors() to round down max_sectors as necessary to ensure this alignment via se_dev_set_default_attribs() and se_dev_align_max_sectors() and keeps it simple for (task_count > 1) operation. So far this bugfix has been tested with (task_count > 1) operation using iscsi-target and iblock backends. Reported-by: Chris Boot Cc: Kiran Patil Cc: Andy Grover Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 28 ++++++++++++++++++++++++++++ drivers/target/target_core_transport.c | 7 +++++-- 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index ec3fbcda3e3c..4b5237f500a5 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -839,6 +839,24 @@ int se_dev_check_shutdown(struct se_device *dev) return ret; } +u32 se_dev_align_max_sectors(u32 max_sectors, u32 block_size) +{ + u32 tmp, aligned_max_sectors; + /* + * Limit max_sectors to a PAGE_SIZE aligned value for modern + * transport_allocate_data_tasks() operation. + */ + tmp = rounddown((max_sectors * block_size), PAGE_SIZE); + aligned_max_sectors = (tmp / block_size); + if (max_sectors != aligned_max_sectors) { + printk(KERN_INFO "Rounding down aligned max_sectors from %u" + " to %u\n", max_sectors, aligned_max_sectors); + return aligned_max_sectors; + } + + return max_sectors; +} + void se_dev_set_default_attribs( struct se_device *dev, struct se_dev_limits *dev_limits) @@ -878,6 +896,11 @@ void se_dev_set_default_attribs( * max_sectors is based on subsystem plugin dependent requirements. */ dev->se_sub_dev->se_dev_attrib.hw_max_sectors = limits->max_hw_sectors; + /* + * Align max_sectors down to PAGE_SIZE to follow transport_allocate_data_tasks() + */ + limits->max_sectors = se_dev_align_max_sectors(limits->max_sectors, + limits->logical_block_size); dev->se_sub_dev->se_dev_attrib.max_sectors = limits->max_sectors; /* * Set optimal_sectors from max_sectors, which can be lowered via @@ -1242,6 +1265,11 @@ int se_dev_set_max_sectors(struct se_device *dev, u32 max_sectors) return -EINVAL; } } + /* + * Align max_sectors down to PAGE_SIZE to follow transport_allocate_data_tasks() + */ + max_sectors = se_dev_align_max_sectors(max_sectors, + dev->se_sub_dev->se_dev_attrib.block_size); dev->se_sub_dev->se_dev_attrib.max_sectors = max_sectors; pr_debug("dev[%p]: SE Device max_sectors changed to %u\n", diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ab61c5550852..efac6a9a7438 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -4126,7 +4126,11 @@ static int transport_allocate_data_tasks( /* Update new cdb with updated lba/sectors */ cmd->transport_split_cdb(task->task_lba, task->task_sectors, cdb); - + /* + * This now assumes that passed sg_ents are in PAGE_SIZE chunks + * in order to calculate the number per task SGL entries + */ + task->task_sg_nents = DIV_ROUND_UP(task->task_size, PAGE_SIZE); /* * Check if the fabric module driver is requesting that all * struct se_task->task_sg[] be chained together.. If so, @@ -4136,7 +4140,6 @@ static int transport_allocate_data_tasks( * It's so much easier and only a waste when task_count > 1. * That is extremely rare. */ - task->task_sg_nents = sgl_nents; if (cmd->se_tfo->task_sg_chaining) { task->task_sg_nents++; task->task_padded_sg = 1; -- cgit v1.2.3 From c3c74c7a33d837be391ab61aaae39bb21f16736a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sat, 13 Aug 2011 05:30:31 -0700 Subject: target: Fix task SGL chaining breakage with transport_allocate_data_tasks This patch fixes two bugs associated with transport_do_task_sg_chain() operation where transport_allocate_data_tasks() was incorrectly setting task_padded_sg for all tasks, and causing bogus task->task_sg_nents assignments + OOPsen with fabrics depending upon this code. The first bit here adds a task_sg_nents_padded check in transport_allocate_data_tasks() to include an extra SGL vector when necessary for tasks that expect to be linked using sg_chain(). The second change involves making transport_do_task_sg_chain() properly account for the extra SGL vector when task->task_padded_sg is set for the non trailing ->task_sg or single ->task_sg allocations. Note this patch also removes the BUG_ON(!task->task_padded_sg) check within transport_do_task_sg_chain() as we expect this to happen normally with the updated logic in transport_allocate_data_tasks(), along with being bogus for CONTROL_SG_IO_CDB type payloads. So far this bugfix has been tested with tcm_qla2xxx and iblock backends in (task_count > 1)( and (task_count == 1) operation. Reported-by: Kiran Patil Cc: Kiran Patil Cc: Andy Grover Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index efac6a9a7438..9cc49d1b5b1f 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -4044,8 +4044,6 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) if (!task->task_sg) continue; - BUG_ON(!task->task_padded_sg); - if (!sg_first) { sg_first = task->task_sg; chained_nents = task->task_sg_nents; @@ -4053,9 +4051,19 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) sg_chain(sg_prev, sg_prev_nents, task->task_sg); chained_nents += task->task_sg_nents; } + /* + * For the padded tasks, use the extra SGL vector allocated + * in transport_allocate_data_tasks() for the sg_prev_nents + * offset into sg_chain() above.. The last task of a + * multi-task list, or a single task will not have + * task->task_sg_padded set.. + */ + if (task->task_padded_sg) + sg_prev_nents = (task->task_sg_nents + 1); + else + sg_prev_nents = task->task_sg_nents; sg_prev = task->task_sg; - sg_prev_nents = task->task_sg_nents; } /* * Setup the starting pointer and total t_tasks_sg_linked_no including @@ -4107,7 +4115,7 @@ static int transport_allocate_data_tasks( cmd_sg = sgl; for (i = 0; i < task_count; i++) { - unsigned int task_size; + unsigned int task_size, task_sg_nents_padded; int count; task = transport_generic_get_task(cmd, data_direction); @@ -4135,24 +4143,24 @@ static int transport_allocate_data_tasks( * Check if the fabric module driver is requesting that all * struct se_task->task_sg[] be chained together.. If so, * then allocate an extra padding SG entry for linking and - * marking the end of the chained SGL. - * Possibly over-allocate task sgl size by using cmd sgl size. - * It's so much easier and only a waste when task_count > 1. - * That is extremely rare. + * marking the end of the chained SGL for every task except + * the last one for (task_count > 1) operation, or skipping + * the extra padding for the (task_count == 1) case. */ - if (cmd->se_tfo->task_sg_chaining) { - task->task_sg_nents++; + if (cmd->se_tfo->task_sg_chaining && (i < (task_count - 1))) { + task_sg_nents_padded = (task->task_sg_nents + 1); task->task_padded_sg = 1; - } + } else + task_sg_nents_padded = task->task_sg_nents; task->task_sg = kmalloc(sizeof(struct scatterlist) * - task->task_sg_nents, GFP_KERNEL); + task_sg_nents_padded, GFP_KERNEL); if (!task->task_sg) { cmd->se_dev->transport->free_task(task); return -ENOMEM; } - sg_init_table(task->task_sg, task->task_sg_nents); + sg_init_table(task->task_sg, task_sg_nents_padded); task_size = task->task_size; -- cgit v1.2.3 From 6626a0572657a0945a7b9ccf4a6d6ad1750f9adc Mon Sep 17 00:00:00 2001 From: Chris Boot Date: Sat, 13 Aug 2011 22:10:46 -0700 Subject: iscsi-target: Implement iSCSI target IPv6 address printing. The iSCSI target configfs code to print out an initiator's IPv6 address is not fully implemented. This patch uses snprintf() with the "%pI6c" format string to format the IPv6 address for display purposes. Signed-off-by: Chris Boot Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_login.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index bcaf82f47037..daad362a93ce 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -1013,19 +1013,9 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) ISCSI_LOGIN_STATUS_TARGET_ERROR); goto new_sess_out; } -#if 0 - if (!iscsi_ntop6((const unsigned char *) - &sock_in6.sin6_addr.in6_u, - (char *)&conn->ipv6_login_ip[0], - IPV6_ADDRESS_SPACE)) { - pr_err("iscsi_ntop6() failed\n"); - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, - ISCSI_LOGIN_STATUS_TARGET_ERROR); - goto new_sess_out; - } -#else - pr_debug("Skipping iscsi_ntop6()\n"); -#endif + snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c", + &sock_in6.sin6_addr.in6_u); + conn->login_port = ntohs(sock_in6.sin6_port); } else { memset(&sock_in, 0, sizeof(struct sockaddr_in)); -- cgit v1.2.3 From ba7736696341ad4253125055c0c85aa9f42959a0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 13 Aug 2011 22:35:00 -0700 Subject: iscsi-target: Fix iscsit_allocate_se_cmd_for_tmr failure path bugs This patch fixes two bugs in allocation failure handling in iscsit_allocate_se_cmd_for_tmr(): This first reported by DanC is a free-after call to transport_free_se_cmd(), this patch drops the transport_free_se_cmd() call all together, as iscsit_release_cmd() will release existing allocations as expected. The second is a bug where iscsi_cmd_t was being leaked on a cmd->tmr_req allocation failure, so make this jump to iscsit_release_cmd() as well. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_util.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index a1acb0167902..a0d23bc0fc98 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -243,7 +243,7 @@ struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr( if (!cmd->tmr_req) { pr_err("Unable to allocate memory for" " Task Management command!\n"); - return NULL; + goto out; } /* * TASK_REASSIGN for ERL=2 / connection stays inside of @@ -298,8 +298,6 @@ struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr( return cmd; out: iscsit_release_cmd(cmd); - if (se_cmd) - transport_free_se_cmd(se_cmd); return NULL; } -- cgit v1.2.3 From f15ea5780d08e4c96930c0d607d05e480ec588c8 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 12 Aug 2011 10:01:24 -0700 Subject: target: Print subpage too for unhandled MODE SENSE pages Make a log message more useful by printing both the page and subpage that an initiator is requesting. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_cdb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 40ad142f7cb3..89ae923c5da6 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -920,8 +920,8 @@ target_emulate_modesense(struct se_cmd *cmd, int ten) length += target_modesense_control(dev, &buf[offset+length]); break; default: - pr_err("Got Unknown Mode Page: 0x%02x\n", - cdb[2] & 0x3f); + pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n", + cdb[2] & 0x3f, cdb[3]); return PYX_TRANSPORT_UNKNOWN_MODE_PAGE; } offset += length; -- cgit v1.2.3 From 4e0f05297ff615a9a4e269da301ff77f660a3ab0 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 12 Aug 2011 10:16:52 -0700 Subject: tcm_fc: init/exit functions should not be protected by "#ifdef MODULE" There's no need for the #ifdef protection when building into the kernel, and in fact we need the module_init() for the initialization function to be called. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/tcm_fc/tfc_conf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index 8781d1e423df..520a8baae794 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -655,9 +655,7 @@ static void __exit ft_exit(void) synchronize_rcu(); } -#ifdef MODULE MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION); MODULE_LICENSE("GPL"); module_init(ft_init); module_exit(ft_exit); -#endif /* MODULE */ -- cgit v1.2.3 From e63a8e1933a2218cf801e46dd01bd8cca4a555ec Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 12 Aug 2011 16:01:02 -0700 Subject: target: Make locking in transport_deregister_session() IRQ safe At least the tcm_qla2xxx fabric driver calls into transport_deregister_session() while holding an IRQ-disabled spinlock, so the inner locking needs to use spin_lock_irqsave() instead of spin_lock_bh(). This fixes warnings seen with tcm_qla2xxx like: WARNING: at kernel/softirq.c:159 local_bh_enable_ip+0x98/0xb0() Call Trace: [] warn_slowpath_common+0x7f/0xc0 [] warn_slowpath_null+0x1a/0x20 [] local_bh_enable_ip+0x98/0xb0 [] _raw_spin_unlock_bh+0x14/0x20 [] transport_deregister_session+0x96/0x180 [target_core_mod] [] tcm_qla2xxx_free_session+0xd1/0x170 [tcm_qla2xxx] [] qla_tgt_sess_put+0xc3/0x140 [qla2xxx] [] qla_tgt_stop_phase1+0x8f/0x2c0 [qla2xxx] [] tcm_qla2xxx_tpg_store_enable+0x6e/0xd0 [tcm_qla2xxx] [] target_fabric_tpg_attr_store+0x39/0x40 [target_core_mod] [] configfs_write_file+0xbd/0x120 [configfs] [] vfs_write+0xc6/0x180 [] sys_write+0x51/0x90 [] system_call_fastpath+0x16/0x1b Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 9cc49d1b5b1f..8d0c58ea6316 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -389,17 +389,18 @@ void transport_deregister_session(struct se_session *se_sess) { struct se_portal_group *se_tpg = se_sess->se_tpg; struct se_node_acl *se_nacl; + unsigned long flags; if (!se_tpg) { transport_free_session(se_sess); return; } - spin_lock_bh(&se_tpg->session_lock); + spin_lock_irqsave(&se_tpg->session_lock, flags); list_del(&se_sess->sess_list); se_sess->se_tpg = NULL; se_sess->fabric_sess_ptr = NULL; - spin_unlock_bh(&se_tpg->session_lock); + spin_unlock_irqrestore(&se_tpg->session_lock, flags); /* * Determine if we need to do extra work for this initiator node's @@ -407,22 +408,22 @@ void transport_deregister_session(struct se_session *se_sess) */ se_nacl = se_sess->se_node_acl; if (se_nacl) { - spin_lock_bh(&se_tpg->acl_node_lock); + spin_lock_irqsave(&se_tpg->acl_node_lock, flags); if (se_nacl->dynamic_node_acl) { if (!se_tpg->se_tpg_tfo->tpg_check_demo_mode_cache( se_tpg)) { list_del(&se_nacl->acl_list); se_tpg->num_node_acls--; - spin_unlock_bh(&se_tpg->acl_node_lock); + spin_unlock_irqrestore(&se_tpg->acl_node_lock, flags); core_tpg_wait_for_nacl_pr_ref(se_nacl); core_free_device_list_for_node(se_nacl, se_tpg); se_tpg->se_tpg_tfo->tpg_release_fabric_acl(se_tpg, se_nacl); - spin_lock_bh(&se_tpg->acl_node_lock); + spin_lock_irqsave(&se_tpg->acl_node_lock, flags); } } - spin_unlock_bh(&se_tpg->acl_node_lock); + spin_unlock_irqrestore(&se_tpg->acl_node_lock, flags); } transport_free_session(se_sess); -- cgit v1.2.3 From 28638887f351d11867562322b7abaa014dd5528a Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 16 Aug 2011 09:40:01 -0700 Subject: target: Convert acl_node_lock to be IRQ-disabling With qla2xxx, acl_node_lock is taken inside qla2xxx's hardware_lock, which is taken in hardirq context. This means acl_node_lock must become an IRQ-disabling lock; in particular this fixes lockdep warnings along the lines of ====================================================== [ INFO: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected ] (&(&se_tpg->acl_node_lock)->rlock){+.....}, at: [] transport_deregister_session+0x92/0x140 [target_core_mod] and this task is already holding: (&(&ha->hardware_lock)->rlock){-.-...}, at: [] qla_tgt_stop_phase1+0x57/0x2c0 [qla2xxx] which would create a new lock dependency: (&(&ha->hardware_lock)->rlock){-.-...} -> (&(&se_tpg->acl_node_lock)->rlock){+.....} but this new dependency connects a HARDIRQ-irq-safe lock: (&(&ha->hardware_lock)->rlock){-.-...} to a HARDIRQ-irq-unsafe lock: (&(&se_tpg->acl_node_lock)->rlock){+.....} Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 16 ++++++------ drivers/target/target_core_pr.c | 8 +++--- drivers/target/target_core_tpg.c | 52 ++++++++++++++++++------------------- drivers/target/tcm_fc/tfc_conf.c | 4 +-- 4 files changed, 40 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 4b5237f500a5..ca6e4a4df134 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -472,9 +472,9 @@ void core_clear_lun_from_tpg(struct se_lun *lun, struct se_portal_group *tpg) struct se_dev_entry *deve; u32 i; - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); list_for_each_entry(nacl, &tpg->acl_node_list, acl_list) { - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); spin_lock_irq(&nacl->device_list_lock); for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) { @@ -491,9 +491,9 @@ void core_clear_lun_from_tpg(struct se_lun *lun, struct se_portal_group *tpg) } spin_unlock_irq(&nacl->device_list_lock); - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); } - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); } static struct se_port *core_alloc_port(struct se_device *dev) @@ -1372,17 +1372,17 @@ struct se_lun *core_dev_add_lun( */ if (tpg->se_tpg_tfo->tpg_check_demo_mode(tpg)) { struct se_node_acl *acl; - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); list_for_each_entry(acl, &tpg->acl_node_list, acl_list) { if (acl->dynamic_node_acl && (!tpg->se_tpg_tfo->tpg_check_demo_mode_login_only || !tpg->se_tpg_tfo->tpg_check_demo_mode_login_only(tpg))) { - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); core_tpg_add_node_to_devs(acl, tpg); - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); } } - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); } return lun_p; diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 1c1b849cd4fb..7fd3a161f7cc 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1598,14 +1598,14 @@ static int core_scsi3_decode_spec_i_port( * from the decoded fabric module specific TransportID * at *i_str. */ - spin_lock_bh(&tmp_tpg->acl_node_lock); + spin_lock_irq(&tmp_tpg->acl_node_lock); dest_node_acl = __core_tpg_get_initiator_node_acl( tmp_tpg, i_str); if (dest_node_acl) { atomic_inc(&dest_node_acl->acl_pr_ref_count); smp_mb__after_atomic_inc(); } - spin_unlock_bh(&tmp_tpg->acl_node_lock); + spin_unlock_irq(&tmp_tpg->acl_node_lock); if (!dest_node_acl) { core_scsi3_tpg_undepend_item(tmp_tpg); @@ -3496,14 +3496,14 @@ after_iport_check: /* * Locate the destination struct se_node_acl from the received Transport ID */ - spin_lock_bh(&dest_se_tpg->acl_node_lock); + spin_lock_irq(&dest_se_tpg->acl_node_lock); dest_node_acl = __core_tpg_get_initiator_node_acl(dest_se_tpg, initiator_str); if (dest_node_acl) { atomic_inc(&dest_node_acl->acl_pr_ref_count); smp_mb__after_atomic_inc(); } - spin_unlock_bh(&dest_se_tpg->acl_node_lock); + spin_unlock_irq(&dest_se_tpg->acl_node_lock); if (!dest_node_acl) { pr_err("Unable to locate %s dest_node_acl for" diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 718ccd1348b1..162b736c7342 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -137,15 +137,15 @@ struct se_node_acl *core_tpg_get_initiator_node_acl( { struct se_node_acl *acl; - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); list_for_each_entry(acl, &tpg->acl_node_list, acl_list) { if (!strcmp(acl->initiatorname, initiatorname) && !acl->dynamic_node_acl) { - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); return acl; } } - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); return NULL; } @@ -309,10 +309,10 @@ struct se_node_acl *core_tpg_check_initiator_node_acl( else core_tpg_add_node_to_devs(acl, tpg); - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); list_add_tail(&acl->acl_list, &tpg->acl_node_list); tpg->num_node_acls++; - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); pr_debug("%s_TPG[%u] - Added DYNAMIC ACL with TCQ Depth: %d for %s" " Initiator Node: %s\n", tpg->se_tpg_tfo->get_fabric_name(), @@ -362,7 +362,7 @@ struct se_node_acl *core_tpg_add_initiator_node_acl( { struct se_node_acl *acl = NULL; - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); if (acl) { if (acl->dynamic_node_acl) { @@ -370,7 +370,7 @@ struct se_node_acl *core_tpg_add_initiator_node_acl( pr_debug("%s_TPG[%u] - Replacing dynamic ACL" " for %s\n", tpg->se_tpg_tfo->get_fabric_name(), tpg->se_tpg_tfo->tpg_get_tag(tpg), initiatorname); - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); /* * Release the locally allocated struct se_node_acl * because * core_tpg_add_initiator_node_acl() returned @@ -386,10 +386,10 @@ struct se_node_acl *core_tpg_add_initiator_node_acl( " Node %s already exists for TPG %u, ignoring" " request.\n", tpg->se_tpg_tfo->get_fabric_name(), initiatorname, tpg->se_tpg_tfo->tpg_get_tag(tpg)); - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); return ERR_PTR(-EEXIST); } - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); if (!se_nacl) { pr_err("struct se_node_acl pointer is NULL\n"); @@ -426,10 +426,10 @@ struct se_node_acl *core_tpg_add_initiator_node_acl( return ERR_PTR(-EINVAL); } - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); list_add_tail(&acl->acl_list, &tpg->acl_node_list); tpg->num_node_acls++; - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); done: pr_debug("%s_TPG[%hu] - Added ACL with TCQ Depth: %d for %s" @@ -453,14 +453,14 @@ int core_tpg_del_initiator_node_acl( struct se_session *sess, *sess_tmp; int dynamic_acl = 0; - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); if (acl->dynamic_node_acl) { acl->dynamic_node_acl = 0; dynamic_acl = 1; } list_del(&acl->acl_list); tpg->num_node_acls--; - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); spin_lock_bh(&tpg->session_lock); list_for_each_entry_safe(sess, sess_tmp, @@ -511,21 +511,21 @@ int core_tpg_set_initiator_node_queue_depth( struct se_node_acl *acl; int dynamic_acl = 0; - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); if (!acl) { pr_err("Access Control List entry for %s Initiator" " Node %s does not exists for TPG %hu, ignoring" " request.\n", tpg->se_tpg_tfo->get_fabric_name(), initiatorname, tpg->se_tpg_tfo->tpg_get_tag(tpg)); - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); return -ENODEV; } if (acl->dynamic_node_acl) { acl->dynamic_node_acl = 0; dynamic_acl = 1; } - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); spin_lock_bh(&tpg->session_lock); list_for_each_entry(sess, &tpg->tpg_sess_list, sess_list) { @@ -541,10 +541,10 @@ int core_tpg_set_initiator_node_queue_depth( tpg->se_tpg_tfo->get_fabric_name(), initiatorname); spin_unlock_bh(&tpg->session_lock); - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); if (dynamic_acl) acl->dynamic_node_acl = 1; - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); return -EEXIST; } /* @@ -579,10 +579,10 @@ int core_tpg_set_initiator_node_queue_depth( if (init_sess) tpg->se_tpg_tfo->close_session(init_sess); - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); if (dynamic_acl) acl->dynamic_node_acl = 1; - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); return -EINVAL; } spin_unlock_bh(&tpg->session_lock); @@ -598,10 +598,10 @@ int core_tpg_set_initiator_node_queue_depth( initiatorname, tpg->se_tpg_tfo->get_fabric_name(), tpg->se_tpg_tfo->tpg_get_tag(tpg)); - spin_lock_bh(&tpg->acl_node_lock); + spin_lock_irq(&tpg->acl_node_lock); if (dynamic_acl) acl->dynamic_node_acl = 1; - spin_unlock_bh(&tpg->acl_node_lock); + spin_unlock_irq(&tpg->acl_node_lock); return 0; } @@ -725,20 +725,20 @@ int core_tpg_deregister(struct se_portal_group *se_tpg) * not been released because of TFO->tpg_check_demo_mode_cache() == 1 * in transport_deregister_session(). */ - spin_lock_bh(&se_tpg->acl_node_lock); + spin_lock_irq(&se_tpg->acl_node_lock); list_for_each_entry_safe(nacl, nacl_tmp, &se_tpg->acl_node_list, acl_list) { list_del(&nacl->acl_list); se_tpg->num_node_acls--; - spin_unlock_bh(&se_tpg->acl_node_lock); + spin_unlock_irq(&se_tpg->acl_node_lock); core_tpg_wait_for_nacl_pr_ref(nacl); core_free_device_list_for_node(nacl, se_tpg); se_tpg->se_tpg_tfo->tpg_release_fabric_acl(se_tpg, nacl); - spin_lock_bh(&se_tpg->acl_node_lock); + spin_lock_irq(&se_tpg->acl_node_lock); } - spin_unlock_bh(&se_tpg->acl_node_lock); + spin_unlock_irq(&se_tpg->acl_node_lock); if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) core_tpg_release_virtual_lun0(se_tpg); diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index 520a8baae794..b15879d43e22 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -256,7 +256,7 @@ struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata) struct se_portal_group *se_tpg = &tpg->se_tpg; struct se_node_acl *se_acl; - spin_lock_bh(&se_tpg->acl_node_lock); + spin_lock_irq(&se_tpg->acl_node_lock); list_for_each_entry(se_acl, &se_tpg->acl_node_list, acl_list) { acl = container_of(se_acl, struct ft_node_acl, se_node_acl); pr_debug("acl %p port_name %llx\n", @@ -270,7 +270,7 @@ struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata) break; } } - spin_unlock_bh(&se_tpg->acl_node_lock); + spin_unlock_irq(&se_tpg->acl_node_lock); return found; } -- cgit v1.2.3