aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ieee1394/sbp2.c
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2006-04-01 14:11:41 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-04-02 15:58:09 -0400
commit24c7cd0630f76f0eb081d539c53893d9f15787e8 (patch)
treea77af55cf9dd3b40f38d1822817babe1226849af /drivers/ieee1394/sbp2.c
parentb043b673dc8a73daa233d5d92cf70b32e7351314 (diff)
[PATCH] sbp2: fix spinlock recursion
sbp2util_mark_command_completed takes a lock which was already taken by sbp2scsi_complete_all_commands. This is a regression in Linux 2.6.15. Reported by Kristian Harms at https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=187394 [ More complete commentary, as response to questions by Andrew: ] > This changes the call environment for all implementations of > ->Current_done(). Are they all safe to call under this lock? Short answer: Yes, trust me. ;-) Long answer: The done() callbacks are passed on to sbp2 from the SCSI stack along with each SCSI command via the queuecommand hook. The done() callback is safe to call in atomic context. So does Documentation/scsi/scsi_mid_low_api.txt say, and many if not all SCSI low-level handlers rely on this fact. So whatever this callback does, it is "self-contained" and it won't conflict with sbp2's internal ORB list handling. In particular, it won't race with the sbp2_command_orb_lock. Moreover, sbp2 already calls the done() handler with sbp2_command_orb_lock taken in sbp2scsi_complete_all_commands(). I admit this is ultimately no proof of correctness, especially since this portion of code introduced the spinlock recursion in the first place and we didn't realize it since this code's submission before 2.6.15 until now. (I have learned a lesson from this.) I stress-tested my patch on x86 uniprocessor with a preemptible SMP kernel (alas I have no SMP machine yet) and made sure that all code paths which involve the sbp2_command_orb_lock were gone through multiple times. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/ieee1394/sbp2.c')
-rw-r--r--drivers/ieee1394/sbp2.c32
1 files changed, 15 insertions, 17 deletions
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
index 2c765ca5aa50..f4206604db03 100644
--- a/drivers/ieee1394/sbp2.c
+++ b/drivers/ieee1394/sbp2.c
@@ -496,22 +496,17 @@ static struct sbp2_command_info *sbp2util_find_command_for_orb(
496/* 496/*
497 * This function finds the sbp2_command for a given outstanding SCpnt. 497 * This function finds the sbp2_command for a given outstanding SCpnt.
498 * Only looks at the inuse list. 498 * Only looks at the inuse list.
499 * Must be called with scsi_id->sbp2_command_orb_lock held.
499 */ 500 */
500static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(struct scsi_id_instance_data *scsi_id, void *SCpnt) 501static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(
502 struct scsi_id_instance_data *scsi_id, void *SCpnt)
501{ 503{
502 struct sbp2_command_info *command; 504 struct sbp2_command_info *command;
503 unsigned long flags;
504 505
505 spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); 506 if (!list_empty(&scsi_id->sbp2_command_orb_inuse))
506 if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) { 507 list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list)
507 list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list) { 508 if (command->Current_SCpnt == SCpnt)
508 if (command->Current_SCpnt == SCpnt) {
509 spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
510 return command; 509 return command;
511 }
512 }
513 }
514 spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
515 return NULL; 510 return NULL;
516} 511}
517 512
@@ -580,17 +575,15 @@ static void sbp2util_free_command_dma(struct sbp2_command_info *command)
580 575
581/* 576/*
582 * This function moves a command to the completed orb list. 577 * This function moves a command to the completed orb list.
578 * Must be called with scsi_id->sbp2_command_orb_lock held.
583 */ 579 */
584static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_id, 580static void sbp2util_mark_command_completed(
585 struct sbp2_command_info *command) 581 struct scsi_id_instance_data *scsi_id,
582 struct sbp2_command_info *command)
586{ 583{
587 unsigned long flags;
588
589 spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
590 list_del(&command->list); 584 list_del(&command->list);
591 sbp2util_free_command_dma(command); 585 sbp2util_free_command_dma(command);
592 list_add_tail(&command->list, &scsi_id->sbp2_command_orb_completed); 586 list_add_tail(&command->list, &scsi_id->sbp2_command_orb_completed);
593 spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
594} 587}
595 588
596/* 589/*
@@ -2148,7 +2141,9 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
2148 * Matched status with command, now grab scsi command pointers and check status 2141 * Matched status with command, now grab scsi command pointers and check status
2149 */ 2142 */
2150 SCpnt = command->Current_SCpnt; 2143 SCpnt = command->Current_SCpnt;
2144 spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
2151 sbp2util_mark_command_completed(scsi_id, command); 2145 sbp2util_mark_command_completed(scsi_id, command);
2146 spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
2152 2147
2153 if (SCpnt) { 2148 if (SCpnt) {
2154 2149
@@ -2484,6 +2479,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
2484 (struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0]; 2479 (struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0];
2485 struct sbp2scsi_host_info *hi = scsi_id->hi; 2480 struct sbp2scsi_host_info *hi = scsi_id->hi;
2486 struct sbp2_command_info *command; 2481 struct sbp2_command_info *command;
2482 unsigned long flags;
2487 2483
2488 SBP2_ERR("aborting sbp2 command"); 2484 SBP2_ERR("aborting sbp2 command");
2489 scsi_print_command(SCpnt); 2485 scsi_print_command(SCpnt);
@@ -2494,6 +2490,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
2494 * Right now, just return any matching command structures 2490 * Right now, just return any matching command structures
2495 * to the free pool. 2491 * to the free pool.
2496 */ 2492 */
2493 spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
2497 command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt); 2494 command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt);
2498 if (command) { 2495 if (command) {
2499 SBP2_DEBUG("Found command to abort"); 2496 SBP2_DEBUG("Found command to abort");
@@ -2511,6 +2508,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
2511 command->Current_done(command->Current_SCpnt); 2508 command->Current_done(command->Current_SCpnt);
2512 } 2509 }
2513 } 2510 }
2511 spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
2514 2512
2515 /* 2513 /*
2516 * Initiate a fetch agent reset. 2514 * Initiate a fetch agent reset.