Skip to content

Commit

Permalink
hd: fix locking
Browse files Browse the repository at this point in the history
hd dance around local irq and HD_IRQ enable without achieving much.
It ends up transferring data from irq handler with both local irq and
HD_IRQ disabled.  The only place it actually does something is while
transferring the first block of a request which it does with HD_IRQ
disabled but local irq enabled.

Unfortunately, the dancing is horribly broken from locking POV.  IRQ
and timeout handlers access block queue without grabbing the queue
lock and running the driver in SMP configuration crashes the whole
machine pretty quickly.

Remove meaningless irq enable/disable dancing and add proper locking
in issue, irq and timeout paths.

Signed-off-by: Tejun Heo <tj@kernel.org>
  • Loading branch information
htejun authored and Jens Axboe committed Apr 28, 2009
1 parent 0d9f346 commit 0191944
Showing 1 changed file with 7 additions and 10 deletions.
17 changes: 7 additions & 10 deletions drivers/block/hd.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,6 @@ static void write_intr(void)
if (i > 0) {
SET_HANDLER(&write_intr);
outsw(HD_DATA, req->buffer, 256);
local_irq_enable();
} else {
#if (HD_DELAY > 0)
last_req = read_timer();
Expand Down Expand Up @@ -541,8 +540,7 @@ static void hd_times_out(unsigned long dummy)
if (!CURRENT)
return;

disable_irq(HD_IRQ);
local_irq_enable();
spin_lock_irq(hd_queue->queue_lock);
reset = 1;
name = CURRENT->rq_disk->disk_name;
printk("%s: timeout\n", name);
Expand All @@ -552,9 +550,8 @@ static void hd_times_out(unsigned long dummy)
#endif
end_request(CURRENT, 0);
}
local_irq_disable();
hd_request();
enable_irq(HD_IRQ);
spin_unlock_irq(hd_queue->queue_lock);
}

static int do_special_op(struct hd_i_struct *disk, struct request *req)
Expand Down Expand Up @@ -592,7 +589,6 @@ static void hd_request(void)
return;
repeat:
del_timer(&device_timer);
local_irq_enable();

req = CURRENT;
if (!req) {
Expand All @@ -601,7 +597,6 @@ static void hd_request(void)
}

if (reset) {
local_irq_disable();
reset_hd();
return;
}
Expand Down Expand Up @@ -660,9 +655,7 @@ static void hd_request(void)

static void do_hd_request(struct request_queue *q)
{
disable_irq(HD_IRQ);
hd_request();
enable_irq(HD_IRQ);
}

static int hd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
Expand All @@ -684,12 +677,16 @@ static irqreturn_t hd_interrupt(int irq, void *dev_id)
{
void (*handler)(void) = do_hd;

spin_lock(hd_queue->queue_lock);

do_hd = NULL;
del_timer(&device_timer);
if (!handler)
handler = unexpected_hd_interrupt;
handler();
local_irq_enable();

spin_unlock(hd_queue->queue_lock);

return IRQ_HANDLED;
}

Expand Down

0 comments on commit 0191944

Please sign in to comment.