summaryrefslogtreecommitdiffstats
path: root/drivers/target
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2016-01-12 00:31:09 -0500
committerNicholas Bellinger <nab@linux-iscsi.org>2016-02-03 17:09:07 -0500
commitfebe562c20dfa8f33bee7d419c6b517986a5aa33 (patch)
tree8fab8d2713f877b4464739b302bda4b21c12436b /drivers/target
parenta07100e00ac42a4474530ce17b4978c9e06bde55 (diff)
target: Fix LUN_RESET active I/O handling for ACK_KREF
This patch fixes a NULL pointer se_cmd->cmd_kref < 0 refcount bug during TMR LUN_RESET with active se_cmd I/O, that can be triggered during se_cmd descriptor shutdown + release via core_tmr_drain_state_list() code. To address this bug, add common __target_check_io_state() helper for ABORT_TASK + LUN_RESET w/ CMD_T_COMPLETE checking, and set CMD_T_ABORTED + obtain ->cmd_kref for both cases ahead of last target_put_sess_cmd() after TFO->aborted_task() -> transport_cmd_finish_abort() callback has completed. It also introduces SCF_ACK_KREF to determine when transport_cmd_finish_abort() needs to drop the second extra reference, ahead of calling target_put_sess_cmd() for the final kref_put(&se_cmd->cmd_kref). It also updates transport_cmd_check_stop() to avoid holding se_cmd->t_state_lock while dropping se_cmd device state via target_remove_from_state_list(), now that core_tmr_drain_state_list() is holding the se_device lock while checking se_cmd state from within TMR logic. Finally, move transport_put_cmd() release of SGL + TMR + extended CDB memory into target_free_cmd_mem() in order to avoid potential resource leaks in TMR ABORT_TASK + LUN_RESET code-paths. Also update target_release_cmd_kref() accordingly. Reviewed-by: Quinn Tran <quinn.tran@qlogic.com> Cc: Himanshu Madhani <himanshu.madhani@qlogic.com> Cc: Sagi Grimberg <sagig@mellanox.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Hannes Reinecke <hare@suse.de> Cc: Andy Grover <agrover@redhat.com> Cc: Mike Christie <mchristi@redhat.com> Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/target_core_tmr.c69
-rw-r--r--drivers/target/target_core_transport.c67
2 files changed, 75 insertions, 61 deletions
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index fcdcb117c60d..fb3decc28589 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -107,6 +107,34 @@ static int target_check_cdb_and_preempt(struct list_head *list,
107 return 1; 107 return 1;
108} 108}
109 109
110static bool __target_check_io_state(struct se_cmd *se_cmd)
111{
112 struct se_session *sess = se_cmd->se_sess;
113
114 assert_spin_locked(&sess->sess_cmd_lock);
115 WARN_ON_ONCE(!irqs_disabled());
116 /*
117 * If command already reached CMD_T_COMPLETE state within
118 * target_complete_cmd(), this se_cmd has been passed to
119 * fabric driver and will not be aborted.
120 *
121 * Otherwise, obtain a local se_cmd->cmd_kref now for TMR
122 * ABORT_TASK + LUN_RESET for CMD_T_ABORTED processing as
123 * long as se_cmd->cmd_kref is still active unless zero.
124 */
125 spin_lock(&se_cmd->t_state_lock);
126 if (se_cmd->transport_state & CMD_T_COMPLETE) {
127 pr_debug("Attempted to abort io tag: %llu already complete,"
128 " skipping\n", se_cmd->tag);
129 spin_unlock(&se_cmd->t_state_lock);
130 return false;
131 }
132 se_cmd->transport_state |= CMD_T_ABORTED;
133 spin_unlock(&se_cmd->t_state_lock);
134
135 return kref_get_unless_zero(&se_cmd->cmd_kref);
136}
137
110void core_tmr_abort_task( 138void core_tmr_abort_task(
111 struct se_device *dev, 139 struct se_device *dev,
112 struct se_tmr_req *tmr, 140 struct se_tmr_req *tmr,
@@ -130,34 +158,22 @@ void core_tmr_abort_task(
130 if (tmr->ref_task_tag != ref_tag) 158 if (tmr->ref_task_tag != ref_tag)
131 continue; 159 continue;
132 160
133 if (!kref_get_unless_zero(&se_cmd->cmd_kref))
134 continue;
135
136 printk("ABORT_TASK: Found referenced %s task_tag: %llu\n", 161 printk("ABORT_TASK: Found referenced %s task_tag: %llu\n",
137 se_cmd->se_tfo->get_fabric_name(), ref_tag); 162 se_cmd->se_tfo->get_fabric_name(), ref_tag);
138 163
139 spin_lock(&se_cmd->t_state_lock); 164 if (!__target_check_io_state(se_cmd)) {
140 if (se_cmd->transport_state & CMD_T_COMPLETE) {
141 printk("ABORT_TASK: ref_tag: %llu already complete,"
142 " skipping\n", ref_tag);
143 spin_unlock(&se_cmd->t_state_lock);
144 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); 165 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
145
146 target_put_sess_cmd(se_cmd); 166 target_put_sess_cmd(se_cmd);
147
148 goto out; 167 goto out;
149 } 168 }
150 se_cmd->transport_state |= CMD_T_ABORTED;
151 spin_unlock(&se_cmd->t_state_lock);
152
153 list_del_init(&se_cmd->se_cmd_list); 169 list_del_init(&se_cmd->se_cmd_list);
154 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); 170 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
155 171
156 cancel_work_sync(&se_cmd->work); 172 cancel_work_sync(&se_cmd->work);
157 transport_wait_for_tasks(se_cmd); 173 transport_wait_for_tasks(se_cmd);
158 174
159 target_put_sess_cmd(se_cmd);
160 transport_cmd_finish_abort(se_cmd, true); 175 transport_cmd_finish_abort(se_cmd, true);
176 target_put_sess_cmd(se_cmd);
161 177
162 printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for" 178 printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for"
163 " ref_tag: %llu\n", ref_tag); 179 " ref_tag: %llu\n", ref_tag);
@@ -242,8 +258,10 @@ static void core_tmr_drain_state_list(
242 struct list_head *preempt_and_abort_list) 258 struct list_head *preempt_and_abort_list)
243{ 259{
244 LIST_HEAD(drain_task_list); 260 LIST_HEAD(drain_task_list);
261 struct se_session *sess;
245 struct se_cmd *cmd, *next; 262 struct se_cmd *cmd, *next;
246 unsigned long flags; 263 unsigned long flags;
264 int rc;
247 265
248 /* 266 /*
249 * Complete outstanding commands with TASK_ABORTED SAM status. 267 * Complete outstanding commands with TASK_ABORTED SAM status.
@@ -282,6 +300,16 @@ static void core_tmr_drain_state_list(
282 if (prout_cmd == cmd) 300 if (prout_cmd == cmd)
283 continue; 301 continue;
284 302
303 sess = cmd->se_sess;
304 if (WARN_ON_ONCE(!sess))
305 continue;
306
307 spin_lock(&sess->sess_cmd_lock);
308 rc = __target_check_io_state(cmd);
309 spin_unlock(&sess->sess_cmd_lock);
310 if (!rc)
311 continue;
312
285 list_move_tail(&cmd->state_list, &drain_task_list); 313 list_move_tail(&cmd->state_list, &drain_task_list);
286 cmd->state_active = false; 314 cmd->state_active = false;
287 } 315 }
@@ -289,7 +317,7 @@ static void core_tmr_drain_state_list(
289 317
290 while (!list_empty(&drain_task_list)) { 318 while (!list_empty(&drain_task_list)) {
291 cmd = list_entry(drain_task_list.next, struct se_cmd, state_list); 319 cmd = list_entry(drain_task_list.next, struct se_cmd, state_list);
292 list_del(&cmd->state_list); 320 list_del_init(&cmd->state_list);
293 321
294 pr_debug("LUN_RESET: %s cmd: %p" 322 pr_debug("LUN_RESET: %s cmd: %p"
295 " ITT/CmdSN: 0x%08llx/0x%08x, i_state: %d, t_state: %d" 323 " ITT/CmdSN: 0x%08llx/0x%08x, i_state: %d, t_state: %d"
@@ -313,16 +341,11 @@ static void core_tmr_drain_state_list(
313 * loop above, but we do it down here given that 341 * loop above, but we do it down here given that
314 * cancel_work_sync may block. 342 * cancel_work_sync may block.
315 */ 343 */
316 if (cmd->t_state == TRANSPORT_COMPLETE) 344 cancel_work_sync(&cmd->work);
317 cancel_work_sync(&cmd->work); 345 transport_wait_for_tasks(cmd);
318
319 spin_lock_irqsave(&cmd->t_state_lock, flags);
320 target_stop_cmd(cmd, &flags);
321
322 cmd->transport_state |= CMD_T_ABORTED;
323 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
324 346
325 core_tmr_handle_tas_abort(tmr_nacl, cmd, tas); 347 core_tmr_handle_tas_abort(tmr_nacl, cmd, tas);
348 target_put_sess_cmd(cmd);
326 } 349 }
327} 350}
328 351
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 9f3608e10f25..af52f8bd8954 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -534,9 +534,6 @@ void transport_deregister_session(struct se_session *se_sess)
534} 534}
535EXPORT_SYMBOL(transport_deregister_session); 535EXPORT_SYMBOL(transport_deregister_session);
536 536
537/*
538 * Called with cmd->t_state_lock held.
539 */
540static void target_remove_from_state_list(struct se_cmd *cmd) 537static void target_remove_from_state_list(struct se_cmd *cmd)
541{ 538{
542 struct se_device *dev = cmd->se_dev; 539 struct se_device *dev = cmd->se_dev;
@@ -561,10 +558,6 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
561{ 558{
562 unsigned long flags; 559 unsigned long flags;
563 560
564 spin_lock_irqsave(&cmd->t_state_lock, flags);
565 if (write_pending)
566 cmd->t_state = TRANSPORT_WRITE_PENDING;
567
568 if (remove_from_lists) { 561 if (remove_from_lists) {
569 target_remove_from_state_list(cmd); 562 target_remove_from_state_list(cmd);
570 563
@@ -574,6 +567,10 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
574 cmd->se_lun = NULL; 567 cmd->se_lun = NULL;
575 } 568 }
576 569
570 spin_lock_irqsave(&cmd->t_state_lock, flags);
571 if (write_pending)
572 cmd->t_state = TRANSPORT_WRITE_PENDING;
573
577 /* 574 /*
578 * Determine if frontend context caller is requesting the stopping of 575 * Determine if frontend context caller is requesting the stopping of
579 * this command for frontend exceptions. 576 * this command for frontend exceptions.
@@ -627,6 +624,8 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd)
627 624
628void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) 625void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
629{ 626{
627 bool ack_kref = (cmd->se_cmd_flags & SCF_ACK_KREF);
628
630 if (cmd->se_cmd_flags & SCF_SE_LUN_CMD) 629 if (cmd->se_cmd_flags & SCF_SE_LUN_CMD)
631 transport_lun_remove_cmd(cmd); 630 transport_lun_remove_cmd(cmd);
632 /* 631 /*
@@ -638,7 +637,7 @@ void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
638 637
639 if (transport_cmd_check_stop_to_fabric(cmd)) 638 if (transport_cmd_check_stop_to_fabric(cmd))
640 return; 639 return;
641 if (remove) 640 if (remove && ack_kref)
642 transport_put_cmd(cmd); 641 transport_put_cmd(cmd);
643} 642}
644 643
@@ -706,7 +705,7 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
706 * Check for case where an explicit ABORT_TASK has been received 705 * Check for case where an explicit ABORT_TASK has been received
707 * and transport_wait_for_tasks() will be waiting for completion.. 706 * and transport_wait_for_tasks() will be waiting for completion..
708 */ 707 */
709 if (cmd->transport_state & CMD_T_ABORTED && 708 if (cmd->transport_state & CMD_T_ABORTED ||
710 cmd->transport_state & CMD_T_STOP) { 709 cmd->transport_state & CMD_T_STOP) {
711 spin_unlock_irqrestore(&cmd->t_state_lock, flags); 710 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
712 complete_all(&cmd->t_transport_stop_comp); 711 complete_all(&cmd->t_transport_stop_comp);
@@ -2222,20 +2221,14 @@ static inline void transport_free_pages(struct se_cmd *cmd)
2222} 2221}
2223 2222
2224/** 2223/**
2225 * transport_release_cmd - free a command 2224 * transport_put_cmd - release a reference to a command
2226 * @cmd: command to free 2225 * @cmd: command to release
2227 * 2226 *
2228 * This routine unconditionally frees a command, and reference counting 2227 * This routine releases our reference to the command and frees it if possible.
2229 * or list removal must be done in the caller.
2230 */ 2228 */
2231static int transport_release_cmd(struct se_cmd *cmd) 2229static int transport_put_cmd(struct se_cmd *cmd)
2232{ 2230{
2233 BUG_ON(!cmd->se_tfo); 2231 BUG_ON(!cmd->se_tfo);
2234
2235 if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
2236 core_tmr_release_req(cmd->se_tmr_req);
2237 if (cmd->t_task_cdb != cmd->__t_task_cdb)
2238 kfree(cmd->t_task_cdb);
2239 /* 2232 /*
2240 * If this cmd has been setup with target_get_sess_cmd(), drop 2233 * If this cmd has been setup with target_get_sess_cmd(), drop
2241 * the kref and call ->release_cmd() in kref callback. 2234 * the kref and call ->release_cmd() in kref callback.
@@ -2243,18 +2236,6 @@ static int transport_release_cmd(struct se_cmd *cmd)
2243 return target_put_sess_cmd(cmd); 2236 return target_put_sess_cmd(cmd);
2244} 2237}
2245 2238
2246/**
2247 * transport_put_cmd - release a reference to a command
2248 * @cmd: command to release
2249 *
2250 * This routine releases our reference to the command and frees it if possible.
2251 */
2252static int transport_put_cmd(struct se_cmd *cmd)
2253{
2254 transport_free_pages(cmd);
2255 return transport_release_cmd(cmd);
2256}
2257
2258void *transport_kmap_data_sg(struct se_cmd *cmd) 2239void *transport_kmap_data_sg(struct se_cmd *cmd)
2259{ 2240{
2260 struct scatterlist *sg = cmd->t_data_sg; 2241 struct scatterlist *sg = cmd->t_data_sg;
@@ -2452,14 +2433,13 @@ static void transport_write_pending_qf(struct se_cmd *cmd)
2452 2433
2453int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) 2434int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
2454{ 2435{
2455 unsigned long flags;
2456 int ret = 0; 2436 int ret = 0;
2457 2437
2458 if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) { 2438 if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) {
2459 if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) 2439 if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
2460 transport_wait_for_tasks(cmd); 2440 transport_wait_for_tasks(cmd);
2461 2441
2462 ret = transport_release_cmd(cmd); 2442 ret = transport_put_cmd(cmd);
2463 } else { 2443 } else {
2464 if (wait_for_tasks) 2444 if (wait_for_tasks)
2465 transport_wait_for_tasks(cmd); 2445 transport_wait_for_tasks(cmd);
@@ -2468,11 +2448,8 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
2468 * has already added se_cmd to state_list, but fabric has 2448 * has already added se_cmd to state_list, but fabric has
2469 * failed command before I/O submission. 2449 * failed command before I/O submission.
2470 */ 2450 */
2471 if (cmd->state_active) { 2451 if (cmd->state_active)
2472 spin_lock_irqsave(&cmd->t_state_lock, flags);
2473 target_remove_from_state_list(cmd); 2452 target_remove_from_state_list(cmd);
2474 spin_unlock_irqrestore(&cmd->t_state_lock, flags);
2475 }
2476 2453
2477 if (cmd->se_lun) 2454 if (cmd->se_lun)
2478 transport_lun_remove_cmd(cmd); 2455 transport_lun_remove_cmd(cmd);
@@ -2517,6 +2494,16 @@ out:
2517} 2494}
2518EXPORT_SYMBOL(target_get_sess_cmd); 2495EXPORT_SYMBOL(target_get_sess_cmd);
2519 2496
2497static void target_free_cmd_mem(struct se_cmd *cmd)
2498{
2499 transport_free_pages(cmd);
2500
2501 if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
2502 core_tmr_release_req(cmd->se_tmr_req);
2503 if (cmd->t_task_cdb != cmd->__t_task_cdb)
2504 kfree(cmd->t_task_cdb);
2505}
2506
2520static void target_release_cmd_kref(struct kref *kref) 2507static void target_release_cmd_kref(struct kref *kref)
2521{ 2508{
2522 struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref); 2509 struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
@@ -2526,17 +2513,20 @@ static void target_release_cmd_kref(struct kref *kref)
2526 spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); 2513 spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
2527 if (list_empty(&se_cmd->se_cmd_list)) { 2514 if (list_empty(&se_cmd->se_cmd_list)) {
2528 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); 2515 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
2516 target_free_cmd_mem(se_cmd);
2529 se_cmd->se_tfo->release_cmd(se_cmd); 2517 se_cmd->se_tfo->release_cmd(se_cmd);
2530 return; 2518 return;
2531 } 2519 }
2532 if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) { 2520 if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
2533 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); 2521 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
2522 target_free_cmd_mem(se_cmd);
2534 complete(&se_cmd->cmd_wait_comp); 2523 complete(&se_cmd->cmd_wait_comp);
2535 return; 2524 return;
2536 } 2525 }
2537 list_del(&se_cmd->se_cmd_list); 2526 list_del(&se_cmd->se_cmd_list);
2538 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); 2527 spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
2539 2528
2529 target_free_cmd_mem(se_cmd);
2540 se_cmd->se_tfo->release_cmd(se_cmd); 2530 se_cmd->se_tfo->release_cmd(se_cmd);
2541} 2531}
2542 2532
@@ -2548,6 +2538,7 @@ int target_put_sess_cmd(struct se_cmd *se_cmd)
2548 struct se_session *se_sess = se_cmd->se_sess; 2538 struct se_session *se_sess = se_cmd->se_sess;
2549 2539
2550 if (!se_sess) { 2540 if (!se_sess) {
2541 target_free_cmd_mem(se_cmd);
2551 se_cmd->se_tfo->release_cmd(se_cmd); 2542 se_cmd->se_tfo->release_cmd(se_cmd);
2552 return 1; 2543 return 1;
2553 } 2544 }