summaryrefslogtreecommitdiffstats
path: root/drivers/block/ll_rw_blk.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
commitdcec8a13bf565e47942a1751a9cec21bec5648fe (patch)
tree548b69625b18cc2e88c3e68d0923be546c9ebb03 /drivers/block/ll_rw_blk.c
parent2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (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.c73
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