aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJens Axboe <axboe@suse.de>2004-08-22 22:37:06 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-08-22 22:37:06 -0700
commitd2a8285687638fad1fb6b4b5e7e809330e21b1eb (patch)
tree8742fbe009a8523471e293eab190bf7f07f1dd59 /drivers
parent1e11a6c0c4d9a2cce94ac36e3eb6ab5c1c9eb912 (diff)
downloadhistory-d2a8285687638fad1fb6b4b5e7e809330e21b1eb.tar.gz
[PATCH] disk barriers: core
IDE disk barrier core. Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/ll_rw_blk.c144
1 files changed, 129 insertions, 15 deletions
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 6328becaf71936..33b424c92f8150 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -265,6 +265,45 @@ void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn)
EXPORT_SYMBOL(blk_queue_make_request);
/**
+ * blk_queue_ordered - does this queue support ordered writes
+ * @q: the request queue
+ * @flag: see below
+ *
+ * Description:
+ * For journalled file systems, doing ordered writes on a commit
+ * block instead of explicitly doing wait_on_buffer (which is bad
+ * for performance) can be a big win. Block drivers supporting this
+ * feature should call this function and indicate so.
+ *
+ **/
+void blk_queue_ordered(request_queue_t *q, int flag)
+{
+ if (flag)
+ set_bit(QUEUE_FLAG_ORDERED, &q->queue_flags);
+ else
+ clear_bit(QUEUE_FLAG_ORDERED, &q->queue_flags);
+}
+
+EXPORT_SYMBOL(blk_queue_ordered);
+
+/**
+ * blk_queue_issue_flush_fn - set function for issuing a flush
+ * @q: the request queue
+ * @iff: the function to be called issuing the flush
+ *
+ * Description:
+ * If a driver supports issuing a flush command, the support is notified
+ * to the block layer by defining it through this call.
+ *
+ **/
+void blk_queue_issue_flush_fn(request_queue_t *q, issue_flush_fn *iff)
+{
+ q->issue_flush_fn = iff;
+}
+
+EXPORT_SYMBOL(blk_queue_issue_flush_fn);
+
+/**
* blk_queue_bounce_limit - set bounce buffer limit for queue
* @q: the request queue for the device
* @dma_addr: bus address limit
@@ -1927,10 +1966,11 @@ int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk,
}
rq->flags |= REQ_NOMERGE;
- rq->waiting = &wait;
+ if (!rq->waiting)
+ rq->waiting = &wait;
elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
generic_unplug_device(q);
- wait_for_completion(&wait);
+ wait_for_completion(rq->waiting);
rq->waiting = NULL;
if (rq->errors)
@@ -1941,6 +1981,72 @@ int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk,
EXPORT_SYMBOL(blk_execute_rq);
+/**
+ * blkdev_issue_flush - queue a flush
+ * @bdev: blockdev to issue flush for
+ * @error_sector: error sector
+ *
+ * Description:
+ * Issue a flush for the block device in question. Caller can supply
+ * room for storing the error offset in case of a flush error, if they
+ * wish to. Caller must run wait_for_completion() on its own.
+ */
+int blkdev_issue_flush(struct block_device *bdev, sector_t *error_sector)
+{
+ request_queue_t *q;
+
+ if (bdev->bd_disk == NULL)
+ return -ENXIO;
+
+ q = bdev_get_queue(bdev);
+ if (!q)
+ return -ENXIO;
+ if (!q->issue_flush_fn)
+ return -EOPNOTSUPP;
+
+ return q->issue_flush_fn(q, bdev->bd_disk, error_sector);
+}
+
+EXPORT_SYMBOL(blkdev_issue_flush);
+
+/**
+ * blkdev_scsi_issue_flush_fn - issue flush for SCSI devices
+ * @q: device queue
+ * @disk: gendisk
+ * @error_sector: error offset
+ *
+ * Description:
+ * Devices understanding the SCSI command set, can use this function as
+ * a helper for issuing a cache flush. Note: driver is required to store
+ * the error offset (in case of error flushing) in ->sector of struct
+ * request.
+ */
+int blkdev_scsi_issue_flush_fn(request_queue_t *q, struct gendisk *disk,
+ sector_t *error_sector)
+{
+ struct request *rq = blk_get_request(q, WRITE, __GFP_WAIT);
+ int ret;
+
+ rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER;
+ rq->sector = 0;
+ memset(rq->cmd, 0, sizeof(rq->cmd));
+ rq->cmd[0] = 0x35;
+ rq->cmd_len = 12;
+ rq->data = NULL;
+ rq->data_len = 0;
+ rq->timeout = 60 * HZ;
+
+ ret = blk_execute_rq(q, disk, rq);
+
+ if (ret && error_sector)
+ *error_sector = rq->sector;
+
+ blk_put_request(rq);
+ return ret;
+}
+
+EXPORT_SYMBOL(blkdev_scsi_issue_flush_fn);
+
void drive_stat_acct(struct request *rq, int nr_sectors, int new_io)
{
int rw = rq_data_dir(rq);
@@ -2194,7 +2300,7 @@ EXPORT_SYMBOL(__blk_attempt_remerge);
static int __make_request(request_queue_t *q, struct bio *bio)
{
struct request *req, *freereq = NULL;
- int el_ret, rw, nr_sectors, cur_nr_sectors, barrier, ra;
+ int el_ret, rw, nr_sectors, cur_nr_sectors, barrier, err;
sector_t sector;
sector = bio->bi_sector;
@@ -2212,9 +2318,11 @@ static int __make_request(request_queue_t *q, struct bio *bio)
spin_lock_prefetch(q->queue_lock);
- barrier = test_bit(BIO_RW_BARRIER, &bio->bi_rw);
-
- ra = bio->bi_rw & (1 << BIO_RW_AHEAD);
+ barrier = bio_barrier(bio);
+ if (barrier && !(q->queue_flags & (1 << QUEUE_FLAG_ORDERED))) {
+ err = -EOPNOTSUPP;
+ goto end_io;
+ }
again:
spin_lock_irq(q->queue_lock);
@@ -2294,7 +2402,8 @@ get_rq:
/*
* READA bit set
*/
- if (ra)
+ err = -EWOULDBLOCK;
+ if (bio_rw_ahead(bio))
goto end_io;
freereq = get_request_wait(q, rw);
@@ -2305,10 +2414,9 @@ get_rq:
req->flags |= REQ_CMD;
/*
- * inherit FAILFAST from bio and don't stack up
- * retries for read ahead
+ * inherit FAILFAST from bio (for read-ahead, and explicit FAILFAST)
*/
- if (ra || test_bit(BIO_RW_FAILFAST, &bio->bi_rw))
+ if (bio_rw_ahead(bio) || bio_failfast(bio))
req->flags |= REQ_FAILFAST;
/*
@@ -2342,7 +2450,7 @@ out:
return 0;
end_io:
- bio_endio(bio, nr_sectors << 9, -EWOULDBLOCK);
+ bio_endio(bio, nr_sectors << 9, err);
return 0;
}
@@ -2649,10 +2757,17 @@ void blk_recalc_rq_sectors(struct request *rq, int nsect)
static int __end_that_request_first(struct request *req, int uptodate,
int nr_bytes)
{
- int total_bytes, bio_nbytes, error = 0, next_idx = 0;
+ int total_bytes, bio_nbytes, error, next_idx = 0;
struct bio *bio;
/*
+ * extend uptodate bool to allow < 0 value to be direct io error
+ */
+ error = 0;
+ if (end_io_error(uptodate))
+ error = !uptodate ? -EIO : uptodate;
+
+ /*
* for a REQ_BLOCK_PC request, we want to carry any eventual
* sense key with us all the way through
*/
@@ -2660,7 +2775,6 @@ static int __end_that_request_first(struct request *req, int uptodate,
req->errors = 0;
if (!uptodate) {
- error = -EIO;
if (blk_fs_request(req) && !(req->flags & REQ_QUIET))
printk("end_request: I/O error, dev %s, sector %llu\n",
req->rq_disk ? req->rq_disk->disk_name : "?",
@@ -2743,7 +2857,7 @@ static int __end_that_request_first(struct request *req, int uptodate,
/**
* end_that_request_first - end I/O on a request
* @req: the request being processed
- * @uptodate: 0 for I/O error
+ * @uptodate: 1 for success, 0 for I/O error, < 0 for specific error
* @nr_sectors: number of sectors to end I/O on
*
* Description:
@@ -2764,7 +2878,7 @@ EXPORT_SYMBOL(end_that_request_first);
/**
* end_that_request_chunk - end I/O on a request
* @req: the request being processed
- * @uptodate: 0 for I/O error
+ * @uptodate: 1 for success, 0 for I/O error, < 0 for specific error
* @nr_bytes: number of bytes to complete
*
* Description: