diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
commit | dcec8a13bf565e47942a1751a9cec21bec5648fe (patch) | |
tree | 548b69625b18cc2e88c3e68d0923be546c9ebb03 /drivers/block/ll_rw_blk.c | |
parent | 2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff) |
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash.
o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'drivers/block/ll_rw_blk.c')
-rw-r--r-- | drivers/block/ll_rw_blk.c | 73 |
1 files changed, 52 insertions, 21 deletions
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 9f0db3b23..ac8d5bc12 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -38,6 +38,15 @@ DECLARE_TASK_QUEUE(tq_disk); /* * Protect the request list against multiple users.. + * + * With this spinlock the Linux block IO subsystem is 100% SMP threaded + * from the IRQ event side, and almost 100% SMP threaded from the syscall + * side (we still have protect against block device array operations, and + * the do_request() side is casually still unsafe. The kernel lock protects + * this part currently.). + * + * there is a fair chance that things will work just OK if these functions + * are called with no global kernel lock held ... */ spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED; @@ -106,6 +115,11 @@ static inline int get_max_sectors(kdev_t dev) return max_sectors[MAJOR(dev)][MINOR(dev)]; } +/* + * Is called with the request spinlock aquired. + * NOTE: the device-specific queue() functions + * have to be atomic! + */ static inline struct request **get_queue(kdev_t dev) { int major = MAJOR(dev); @@ -122,19 +136,22 @@ static inline struct request **get_queue(kdev_t dev) void unplug_device(void * data) { struct blk_dev_struct * dev = (struct blk_dev_struct *) data; + int queue_new_request=0; unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock,flags); if (dev->current_request == &dev->plug) { struct request * next = dev->plug.next; dev->current_request = next; if (next || dev->queue) { dev->plug.next = NULL; - (dev->request_fn)(); + queue_new_request = 1; } } - restore_flags(flags); + if (queue_new_request) + (dev->request_fn)(); + + spin_unlock_irqrestore(&io_request_lock,flags); } /* @@ -143,6 +160,7 @@ void unplug_device(void * data) * on the list. * * This is called with interrupts off and no requests on the queue. + * (and with the request spinlock aquired) */ static inline void plug_device(struct blk_dev_struct * dev) { @@ -154,8 +172,8 @@ static inline void plug_device(struct blk_dev_struct * dev) /* * look for a free request in the first N entries. - * NOTE: interrupts must be disabled on the way in, and will still - * be disabled on the way out. + * NOTE: interrupts must be disabled on the way in (on SMP the request queue + * spinlock has to be aquired), and will still be disabled on the way out. */ static inline struct request * get_request(int n, kdev_t dev) { @@ -191,13 +209,14 @@ static struct request * __get_request_wait(int n, kdev_t dev) { register struct request *req; struct wait_queue wait = { current, NULL }; + unsigned long flags; add_wait_queue(&wait_for_request, &wait); for (;;) { current->state = TASK_UNINTERRUPTIBLE; - cli(); + spin_lock_irqsave(&io_request_lock,flags); req = get_request(n, dev); - sti(); + spin_unlock_irqrestore(&io_request_lock,flags); if (req) break; run_task_queue(&tq_disk); @@ -211,10 +230,11 @@ static struct request * __get_request_wait(int n, kdev_t dev) static inline struct request * get_request_wait(int n, kdev_t dev) { register struct request *req; + unsigned long flags; - cli(); + spin_lock_irqsave(&io_request_lock,flags); req = get_request(n, dev); - sti(); + spin_unlock_irqrestore(&io_request_lock,flags); if (req) return req; return __get_request_wait(n, dev); @@ -261,8 +281,9 @@ static inline void drive_stat_acct(int cmd, unsigned long nr_sectors, /* * add-request adds a request to the linked list. - * It disables interrupts so that it can muck with the - * request-lists in peace. + * It disables interrupts (aquires the request spinlock) so that it can muck + * with the request-lists in peace. Thus it should be called with no spinlocks + * held. * * By this point, req->cmd is always either READ/WRITE, never READA/WRITEA, * which is important for drive_stat_acct() above. @@ -272,6 +293,8 @@ void add_request(struct blk_dev_struct * dev, struct request * req) { struct request * tmp, **current_request; short disk_index; + unsigned long flags; + int queue_new_request = 0; switch (MAJOR(req->rq_dev)) { case SCSI_DISK_MAJOR: @@ -292,16 +315,20 @@ void add_request(struct blk_dev_struct * dev, struct request * req) } req->next = NULL; + + /* + * We use the goto to reduce locking complexity + */ + spin_lock_irqsave(&io_request_lock,flags); current_request = get_queue(req->rq_dev); - cli(); + if (req->bh) mark_buffer_clean(req->bh); if (!(tmp = *current_request)) { *current_request = req; if (dev->current_request != &dev->plug) - (dev->request_fn)(); - sti(); - return; + queue_new_request = 1; + goto out; } for ( ; tmp->next ; tmp = tmp->next) { if ((IN_ORDER(tmp,req) || @@ -314,11 +341,16 @@ void add_request(struct blk_dev_struct * dev, struct request * req) /* for SCSI devices, call request_fn unconditionally */ if (scsi_blk_major(MAJOR(req->rq_dev))) + queue_new_request = 1; +out: + if (queue_new_request) (dev->request_fn)(); - - sti(); + spin_unlock_irqrestore(&io_request_lock,flags); } +/* + * Has to be called with the request spinlock aquired + */ static inline void attempt_merge (struct request *req, int max_sectors) { struct request *next = req->next; @@ -428,6 +460,8 @@ void make_request(int major,int rw, struct buffer_head * bh) case FLOPPY_MAJOR: case IDE2_MAJOR: case IDE3_MAJOR: + case IDE4_MAJOR: + case IDE5_MAJOR: case ACSI_MAJOR: /* * The scsi disk and cdrom drivers completely remove the request @@ -716,9 +750,6 @@ __initfunc(int blk_dev_init(void)) #ifdef CONFIG_BLK_DEV_LOOP loop_init(); #endif -#ifdef CONFIG_CDROM /* this must precede all CD-ROM drivers */ - cdrom_init(); -#endif CONFIG_CDROM #ifdef CONFIG_ISP16_CDI isp16_init(); #endif CONFIG_ISP16_CDI |