r1692
dslinux_amadeus at dslinux.in-berlin.de
dslinux_amadeus at dslinux.in-berlin.de
Fri Mar 16 22:44:22 CET 2007
Author: amadeus
Date: 2007-03-16 22:44:17 +0100 (Fri, 16 Mar 2007)
New Revision: 1692
Log:
Add speed to the DLDI driver.
Modified: trunk/linux-2.6.x/drivers/block/dldi_c.c
===================================================================
--- trunk/linux-2.6.x/drivers/block/dldi_c.c 2007-03-09 20:14:39 UTC (rev 1691)
+++ trunk/linux-2.6.x/drivers/block/dldi_c.c 2007-03-16 21:44:17 UTC (rev 1692)
@@ -21,6 +21,7 @@
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
+#include <linux/workqueue.h>
MODULE_LICENSE("GPL");
@@ -56,11 +57,18 @@
*/
#define dldi_MINORS 5
+#define SECTOR_SIZE 512
+
+#define REQ_SIZE 64
+
/*
* The internal representation of our device.
*/
struct dldi_dev {
int size; /* Device size in bytes */
+ spinlock_t lock; /* For mutual exclusion */
+ struct work_struct dldi_work; /* For deferred start */
+ int running;
struct request_queue *queue; /* The device request queue */
struct gendisk *gd; /* The gendisk structure */
};
@@ -90,40 +98,114 @@
}
/*
- * Transfer a single BIO.
+ * Delayed request function. This function is a complex one because
+ * I want to merge requests here: I have found that for FAT file
+ * systems, I will get 512 bytes per request, and this is faaar to slow.
*/
-static int dldi_xfer_bio(struct dldi_dev *dev, struct bio *bio)
+static void dldi_do_request(void *arg)
{
- int i;
- struct bio_vec *bvec;
- sector_t sector = bio->bi_sector;
+ struct dldi_dev *dev = (struct dldi_dev *)arg;
+ request_queue_t *q = dev->queue;
+ struct request *req;
+ struct request *next;
+ struct request *requests[REQ_SIZE];
+ int index;
- /* Do each segment independently. */
- bio_for_each_segment(bvec, bio, i) {
- char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
- dldi_transfer(dev, sector, bio_cur_sectors(bio),
- buffer, bio_data_dir(bio) == WRITE);
- sector += bio_cur_sectors(bio);
- __bio_kunmap_atomic(bio, KM_USER0);
+ long dir;
+ sector_t sector;
+ int nr_sec;
+ void *buffer;
+
+ spin_lock(&dev->lock);
+ dev->running = 0;
+
+ // printk (KERN_NOTICE "do_request\n");
+
+ next = NULL;
+
+ /* search the first fs request */
+again: while (1) {
+ if (next) {
+ req = next;
+ next = NULL;
+ } else {
+ req = elv_next_request(q);
+ if (!req)
+ /* no request found */
+ break;
+ blkdev_dequeue_request(req);
+ }
+ if (blk_fs_request(req))
+ /* fs request found */
+ break;
+ /* skip non-fs requests */
+ end_request(req, 0);
}
- return 0; /* Always "succeed" */
+ if (req) {
+ /* setup r/w parameters for first request */
+ dir = rq_data_dir(req);
+ sector = req->sector;
+ nr_sec = req->current_nr_sectors;
+ buffer = req->buffer;
+ /* store first request for later signaling */
+ memset(requests, 0, sizeof(requests));
+ index = 0;
+ requests[0] = req;
+ /* walk through the requests and try to merge */
+ index++;
+ while (index < REQ_SIZE) {
+ req = elv_next_request(q);
+ if (!req)
+ /* end of requests */
+ break;
+ blkdev_dequeue_request(req);
+ requests[index++] = req;
+ if (!blk_fs_request(req))
+ /* skip non-fs requests */
+ continue;
+ /* test for possible merge */
+ if ((dir == rq_data_dir(req))
+ &&((sector + nr_sec) == req->sector)
+ &&((buffer + nr_sec*SECTOR_SIZE) == req->buffer)) {
+ /* do the merge */
+ nr_sec += req->current_nr_sectors;
+ } else {
+ /* no merge possible */
+ next = req;
+ requests[--index] = NULL;
+ break;
+ }
+ }
+ /* do the transfer */
+ dldi_transfer(dev, sector, nr_sec, buffer, dir);
+ /* send completion to all requests */
+ for (index = 0; index < REQ_SIZE; index++) {
+ req = requests[index];
+ if (!req)
+ break;
+ end_request(req, blk_fs_request(req));
+ }
+ /* try the next request */
+ goto again;
+ }
+ spin_unlock(&dev->lock);
}
/*
- * The direct make request version.
+ * Request function. Defer action to have the chance to group requests together.
*/
-static int dldi_make_request(request_queue_t *q, struct bio *bio)
+static void dldi_request(request_queue_t *q)
{
- struct dldi_dev *dev = q->queuedata;
- int status;
+ struct dldi_dev *dev = Devices;
+
+ if (!dev->running) {
+ dev->running = 1;
+ schedule_work(&dev->dldi_work);
+ }
+}
- status = dldi_xfer_bio(dev, bio);
- bio_endio(bio, bio->bi_size, status);
- return 0;
-}
-
/*
* Open and close.
*/
@@ -194,18 +276,25 @@
* Get some memory.
*/
memset (dev, 0, sizeof (struct dldi_dev));
- dev->size = nsectors*512;
-
+ dev->size = nsectors*SECTOR_SIZE;
+ spin_lock_init(&dev->lock);
+ INIT_WORK(&dev->dldi_work, dldi_do_request, dev);
+ dev->running = 0;
+
/*
* The I/O queue, depending on whether we are using our own
* make_request function or not.
*/
- dev->queue = blk_alloc_queue(GFP_KERNEL);
+ dev->queue = blk_init_queue(dldi_request, &dev->lock);
if (dev->queue == NULL)
return;
- blk_queue_make_request(dev->queue, dldi_make_request);
- blk_queue_hardsect_size(dev->queue, 512);
+ blk_queue_hardsect_size(dev->queue, SECTOR_SIZE);
+ blk_queue_max_phys_segments(dev->queue, 1);
+ blk_queue_max_sectors(dev->queue, 128);
+ blk_queue_max_hw_segments(dev->queue, 1);
+ blk_queue_max_segment_size(dev->queue, SECTOR_SIZE * 128);
+ blk_queue_dma_alignment(dev->queue, 3);
dev->queue->queuedata = dev;
/*
* And the gendisk structure.
@@ -272,6 +361,8 @@
int ret;
struct dldi_dev *dev = Devices;
+ cancel_delayed_work(&dev->dldi_work);
+
/* Shutdown the interface */
ret = _io_dldi.fn_shutdown();
if (ret == 0) {
More information about the dslinux-commit
mailing list