Index: linux-2.6.25-source/drivers/block/cobd.c
===================================================================
--- /dev/null
+++ linux-2.6.25-source/drivers/block/cobd.c
@@ -0,0 +1,574 @@
+/*
+ *  Copyright (C) 2003 Dan Aloni <da-x@colinux.org>
+ *
+ *  Cooperative Linux Block Device implementation
+ */
+
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/cooperative_internal.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
+#include <linux/ctype.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+#include <asm/types.h>
+
+static int const hardsect_size = 512;
+static int const hardsect_size_shift = 9;
+static int const cobd_max = CO_MODULE_MAX_COBD;
+static spinlock_t cobd_lock = SPIN_LOCK_UNLOCKED;
+
+struct cobd_device {
+	int unit;
+	int refcount;
+	struct block_device *device;
+};
+
+static struct gendisk **cobd_disks;
+static struct cobd_device cobd_devs[CO_MODULE_MAX_COBD];
+
+static int cobd_request(struct cobd_device *cobd, co_block_request_type_t type, co_block_request_t *out_request)
+{
+	co_block_request_t *request;
+	unsigned long flags;
+	long rc;
+
+	co_passage_page_assert_valid();
+
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_BLOCK;
+	co_passage_page->params[1] = cobd->unit;
+	request = (co_block_request_t *)&co_passage_page->params[2];
+	request->type = type;
+	request->rc = -1;
+	co_switch_wrapper();
+	rc = request->rc;
+	*out_request = *request;
+	co_passage_page_release(flags);
+
+	return rc;
+}
+
+static int cobd_stat(struct cobd_device *cobd, co_block_request_t *out_request)
+{
+	return cobd_request(cobd, CO_BLOCK_STAT, out_request);
+}
+
+static int cobd_get_alias(struct cobd_device *cobd, co_block_request_t *out_request)
+{
+	return cobd_request(cobd, CO_BLOCK_GET_ALIAS, out_request);
+}
+
+static int cobd_ioctl(struct inode * inode, struct file * file,
+		      unsigned int cmd, unsigned long arg)
+{
+	return -ENOTTY; /* unknown command */
+}
+
+static int cobd_open(struct inode *inode, struct file *file)
+{
+	struct cobd_device *cobd = (struct cobd_device *)(inode->i_bdev->bd_disk->private_data);
+	co_block_request_t *co_request;
+	co_block_request_t stat_request;
+	unsigned long flags;
+	int result;
+
+	if (cobd->device  &&  cobd->device != inode->i_bdev)
+		return -EBUSY;
+
+	if (cobd->refcount == 0) {
+		if (cobd_stat(cobd, &stat_request)) {
+			return -ENODEV;
+		}
+	}
+
+	result = 0;
+
+	co_passage_page_assert_valid();
+
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_BLOCK;
+	co_passage_page->params[1] = cobd->unit;
+	co_request = (co_block_request_t *)&co_passage_page->params[2];
+	co_request->type = CO_BLOCK_OPEN;
+	co_switch_wrapper();
+	if (co_request->rc)
+		result = -EIO;
+	else
+		cobd->refcount++;
+	co_passage_page_release(flags);
+
+	if (result)
+		return result;
+
+	if (cobd->refcount == 1) {
+		set_capacity(inode->i_bdev->bd_disk, stat_request.disk_size >> hardsect_size_shift);
+		cobd->device = inode->i_bdev;
+	}
+
+	return 0;
+}
+
+static int cobd_release(struct inode *inode, struct file *file)
+{
+	struct cobd_device *cobd = (struct cobd_device *)(inode->i_bdev->bd_disk->private_data);
+	co_block_request_t *co_request;
+	unsigned long flags;
+	int ret = 0;
+
+	co_passage_page_assert_valid();
+
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_BLOCK;
+	co_passage_page->params[1] = cobd->unit;
+	co_request = (co_block_request_t *)&co_passage_page->params[2];
+	co_request->type = CO_BLOCK_CLOSE;
+	co_switch_wrapper();
+	if (co_request->rc)
+		ret = -EIO;
+	cobd->refcount--;
+	co_passage_page_release(flags);
+
+	if (cobd->refcount == 0)
+		cobd->device = NULL;
+
+	return ret;
+}
+
+/*
+ * Handle an I/O request.
+ */
+static int cobd_transfer(struct cobd_device *cobd, struct request *req, int *async)
+{
+	co_block_request_t *co_request;
+	unsigned long flags;
+	int ret;
+
+	co_passage_page_assert_valid();
+
+	co_passage_page_acquire(&flags);
+	co_passage_page->operation = CO_OPERATION_DEVICE;
+	co_passage_page->params[0] = CO_DEVICE_BLOCK;
+	co_passage_page->params[1] = cobd->unit;
+	co_request = (co_block_request_t *)&co_passage_page->params[2];
+	co_request->type = (rq_data_dir(req) == READ) ? CO_BLOCK_READ : CO_BLOCK_WRITE;
+	co_request->irq_request = req;
+	co_request->offset = ((unsigned long long)(req->sector)) << hardsect_size_shift;
+	co_request->size = req->current_nr_sectors << hardsect_size_shift;
+	co_request->address = req->buffer;
+	co_request->rc = 0;
+	co_request->async = 0;
+	co_switch_wrapper();
+	*async = co_request->async;
+	ret = co_request->rc;
+
+	co_passage_page_release(flags);
+	return ret;
+}
+
+static void do_cobd_request(request_queue_t *q)
+{
+        struct request *req;
+	struct cobd_device *cobd;
+
+        while ((req = elv_next_request(q)) != NULL) {
+		int ret;
+		int async;
+
+		if (!blk_fs_request(req)) {
+			end_request(req, 0);
+			continue;
+		}
+		cobd = (struct cobd_device *)(req->rq_disk->private_data);
+
+		ret = cobd_transfer(cobd, req, &async);
+
+		/*
+		 * OK:   ret ==  0 --> uptodate = 1
+		 * FAIL: ret == -1 --> uptodate = 0
+		 */
+		if (ret == CO_BLOCK_REQUEST_RETCODE_OK) {
+			if (async)
+				break; /* wait for interrupt */
+			end_request(req, 1);
+		} else {
+			end_request(req, 0);
+		}
+        }
+}
+
+static irqreturn_t cobd_interrupt(int irq, void *dev_id)
+{
+	co_message_node_t *input;
+
+	while (co_get_message(&input, CO_DEVICE_BLOCK)) {
+		co_linux_message_t *message;
+		co_block_intr_t *intr;
+		struct request *req;
+
+		message = (co_linux_message_t *)&input->msg.data;
+		if (message->unit >= CO_MODULE_MAX_COBD) {
+			printk("cobd interrupt: buggy unit reception: %x\n", message->unit);
+			goto goto_next_message;
+		}
+
+		BUG_ON(message->size != sizeof(co_block_intr_t));
+		intr = (co_block_intr_t *)message->data;
+		req = intr->irq_request;
+		BUG_ON(!req);
+
+		spin_lock(&cobd_lock);
+		end_request(req, intr->uptodate);
+		do_cobd_request(req->q);
+		spin_unlock(&cobd_lock);
+
+goto_next_message:
+		co_free_message(input);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct block_device_operations cobd_fops = {
+	.owner   = THIS_MODULE,
+	.open    = cobd_open,
+	.release = cobd_release,
+	.ioctl   = cobd_ioctl,
+};
+
+static int __init cobd_drives_init(void)
+{
+	int result, i;
+
+	if (request_irq(BLOCKDEV_IRQ, &cobd_interrupt, 0, "cobd", NULL)) {
+		printk("cobd: unable to get IRQ%d\n", BLOCKDEV_IRQ);
+		return -EBUSY;
+	}
+
+	if (register_blkdev(COLINUX_MAJOR, "cobd")) {
+		printk(KERN_WARNING "Unable to get major number %d for cobd device\n", COLINUX_MAJOR);
+		result = -EIO;
+		goto fail_irq;
+	}
+
+	result = -ENOMEM; /* for the possible errors */
+
+	cobd_disks = kmalloc(cobd_max * sizeof(struct gendisk *), GFP_KERNEL);
+	if (!cobd_disks)
+		goto fail_malloc;
+
+	for (i=0; i < cobd_max; i++) {
+		cobd_disks[i] = alloc_disk(1);
+		if (!cobd_disks[i])
+			goto fail_malloc3;
+	}
+
+	for (i=0; i < cobd_max; i++) {
+		struct cobd_device *cobd = &cobd_devs[i];
+		struct gendisk *disk = cobd_disks[i];
+
+		disk->queue = blk_init_queue(do_cobd_request, &cobd_lock);
+		if (!disk->queue)
+			goto fail_malloc4;
+
+		blk_queue_hardsect_size(disk->queue, hardsect_size);
+
+		cobd->unit = i;
+		disk->major = COLINUX_MAJOR;
+		disk->first_minor = i;
+		disk->fops = &cobd_fops;
+		sprintf(disk->disk_name, "cobd%d", i);
+		disk->private_data = cobd;
+	}
+
+	for (i=0; i < cobd_max; i++)
+		add_disk(cobd_disks[i]);
+
+	printk(KERN_INFO "cobd: loaded (max %d devices)\n", cobd_max);
+	return 0;
+
+/* error path */
+fail_malloc4:
+	while (i--)
+		blk_cleanup_queue(cobd_disks[i]->queue);
+	i = cobd_max;
+
+fail_malloc3:
+	while (i--)
+		if (cobd_disks[i] != NULL)
+			put_disk(cobd_disks[i]);
+
+	kfree(cobd_disks);
+
+fail_malloc:
+	if (unregister_blkdev(COLINUX_MAJOR, "cobd"))
+		printk(KERN_WARNING "cobd: cannot unregister blkdev\n");
+
+fail_irq:
+	free_irq(BLOCKDEV_IRQ, NULL);
+	return result;
+}
+
+struct cobd_alias_major {
+	const char *name;
+	int registered;
+	int number;
+};
+
+struct cobd_alias {
+	const char *name;
+	struct cobd_alias_major *major;
+	int minor_start;
+	int minor_count;
+	struct gendisk **gendisk;
+};
+
+struct cobd_alias_major cobd_aliases_major_ide0 = {
+	.name = "ide0",
+	.number = IDE0_MAJOR,
+};
+
+struct cobd_alias_major cobd_aliases_major_ide1 = {
+	.name = "ide1",
+	.number = IDE1_MAJOR,
+};
+
+struct cobd_alias_major cobd_aliases_major_ide2 = {
+	.name = "ide2",
+	.number = IDE2_MAJOR,
+};
+
+struct cobd_alias_major cobd_aliases_major_ide3 = {
+	.name = "ide3",
+	.number = IDE3_MAJOR,
+};
+
+struct cobd_alias_major cobd_aliases_major_sd = {
+	.name = "sd",
+	.number = SCSI_DISK0_MAJOR,
+};
+
+struct cobd_alias cobd_aliases[] = {
+	{"hda", &cobd_aliases_major_ide0, 0x00, 21, },
+	{"hdb", &cobd_aliases_major_ide0, 0x40, 21, },
+	{"hdc", &cobd_aliases_major_ide1, 0x00, 21, },
+	{"hdd", &cobd_aliases_major_ide1, 0x40, 21, },
+	{"hde", &cobd_aliases_major_ide2, 0x00, 21, },
+	{"hdf", &cobd_aliases_major_ide2, 0x40, 21, },
+	{"hdg", &cobd_aliases_major_ide3, 0x00, 21, },
+	{"hdh", &cobd_aliases_major_ide3, 0x40, 21, },
+	{"sda", &cobd_aliases_major_sd, 0x00, 0x10, },
+	{"sdb", &cobd_aliases_major_sd, 0x10, 0x10, },
+	{"sdc", &cobd_aliases_major_sd, 0x20, 0x10, },
+	{"sdd", &cobd_aliases_major_sd, 0x30, 0x10, },
+	{"sde", &cobd_aliases_major_sd, 0x40, 0x10, },
+	{"sdf", &cobd_aliases_major_sd, 0x50, 0x10, },
+	{"sdg", &cobd_aliases_major_sd, 0x60, 0x10, },
+	{"sdh", &cobd_aliases_major_sd, 0x70, 0x10, },
+	{"sdi", &cobd_aliases_major_sd, 0x80, 0x10, },
+	{"sdj", &cobd_aliases_major_sd, 0x90, 0x10, },
+	{"sdk", &cobd_aliases_major_sd, 0xa0, 0x10, },
+	{"sdl", &cobd_aliases_major_sd, 0xb0, 0x10, },
+	{"sdm", &cobd_aliases_major_sd, 0xc0, 0x10, },
+	{"sdn", &cobd_aliases_major_sd, 0xd0, 0x10, },
+	{"sdo", &cobd_aliases_major_sd, 0xe0, 0x10, },
+	{"sdp", &cobd_aliases_major_sd, 0xf0, 0x10, },
+	{NULL, },
+};
+
+static int __init skip_atoi(const char **s)
+{
+	/* lib/spprintf.h */
+
+        int i=0;
+
+        while (isdigit(**s))
+                i = i*10 + *((*s)++) - '0';
+
+        return i;
+}
+
+static int __init cobd_spawn_alias(struct cobd_alias *alias,
+				   const char *alias_name_requested,
+				   int cobd_unit)
+{
+	const char *index_str_start = &alias_name_requested[strlen(alias->name)];
+	const char *index_str_end = index_str_start;
+	struct cobd_device *cobd;
+	struct gendisk *disk;
+
+	int index = skip_atoi(&index_str_end);
+
+	if (!((index >= 0) && (index <= alias->minor_count))) {
+		printk(KERN_WARNING "index out of bounds for alias %s (1 - %d)\n",
+		       alias_name_requested, alias->minor_count);
+		return -1;
+	}
+
+	if (alias->gendisk == NULL) {
+		static struct gendisk **gendisks;
+
+		gendisks = kzalloc(alias->minor_count * sizeof(struct gendisk *), GFP_KERNEL);
+		if (!gendisks) {
+			printk(KERN_WARNING "cannot allocate gendisk array for %s\n", alias->name);
+			return -ENOMEM;
+		}
+
+		if (!alias->major->registered) {
+			if (register_blkdev(alias->major->number, alias->major->name)) {
+				printk(KERN_WARNING "unable to get major number %d for cobd alias device %s\n",
+				       alias->major->number, alias_name_requested);
+				kfree(gendisks);
+				return -EIO;
+			}
+
+			alias->major->registered = 1;
+		}
+
+		alias->gendisk = gendisks;
+	}
+
+	if (alias->gendisk[index] != NULL) {
+		printk(KERN_WARNING "alias %s already used\n", alias_name_requested);
+		return -1;
+	}
+
+	disk = alloc_disk(1);
+	if (!disk) {
+		printk(KERN_WARNING "cannot allocate disk for alias %s\n", alias_name_requested);
+		return -1;
+	}
+
+	disk->queue = blk_init_queue(do_cobd_request, &cobd_lock);
+	if (!disk->queue) {
+		printk(KERN_WARNING "cannot allocate init queue for alias %s\n", alias_name_requested);
+		put_disk(disk);
+		return -1;
+	}
+
+	cobd = &cobd_devs[cobd_unit];
+	blk_queue_hardsect_size(disk->queue, hardsect_size);
+	disk->major = alias->major->number;
+	disk->first_minor = alias->minor_start + index;
+	disk->fops = &cobd_fops;
+	if (index)
+		sprintf(disk->disk_name, "%s%d", alias->name, index);
+	else
+		sprintf(disk->disk_name, "%s", alias->name);
+	disk->private_data = cobd;
+	add_disk(disk);
+	alias->gendisk[index] = disk;
+
+	printk("cobd alias cobd%d -> %s created\n", cobd_unit, alias_name_requested);
+
+	return 0;
+}
+
+static void __init cobd_aliases_init(void)
+{
+	int unit;
+	co_block_request_t request;
+
+	for (unit=0; unit < cobd_max; unit++) {
+		struct cobd_alias *alias = cobd_aliases;
+		int result = cobd_get_alias(&cobd_devs[unit], &request);
+		if (result)
+			continue;
+
+		printk("alias for cobd%d is %s\n", unit, request.alias);
+
+		while (alias->name) {
+			const char *match = (strstr(request.alias, alias->name));
+			if (match == request.alias) {
+				cobd_spawn_alias(alias, request.alias, unit);
+				break;
+			}
+			alias++;
+		}
+
+		if (alias->name == NULL)
+			printk("alias %s is unknown (see cobd_aliases in cobd.c)\n", request.alias);
+	}
+}
+
+static void cobd_drives_exit(void)
+{
+	int i;
+
+	for (i = 0; i < cobd_max; i++) {
+		blk_cleanup_queue(cobd_disks[i]->queue);
+		del_gendisk(cobd_disks[i]);
+		put_disk(cobd_disks[i]);
+	}
+
+	if (unregister_blkdev(COLINUX_MAJOR, "cobd"))
+		printk(KERN_WARNING "cobd: cannot unregister blkdev\n");
+
+	free_irq(BLOCKDEV_IRQ, NULL);
+	kfree(cobd_disks);
+}
+
+static void cobd_aliases_exit(void)
+{
+	struct cobd_alias *alias = &cobd_aliases[0];
+	while (alias->name != NULL) {
+		int index;
+		if (alias->gendisk == NULL) {
+			alias++;
+			continue;
+		}
+
+		for (index=0; index < alias->minor_count; index++) {
+			struct gendisk *disk = alias->gendisk[index];
+			if (!disk)
+				return;
+
+			blk_cleanup_queue(disk->queue);
+			del_gendisk(disk);
+			put_disk(disk);
+		}
+
+		if (!alias->major->registered) {
+			unregister_blkdev(alias->major->number, alias->major->name);
+			alias->major->registered = 0;
+		}
+		kfree(alias->gendisk);
+
+		alias++;
+	}
+}
+
+static int __init cobd_init(void)
+{
+	int result = cobd_drives_init();
+	if (result)
+		return result;
+
+	cobd_aliases_init();
+
+	return result;
+}
+
+static void cobd_exit(void)
+{
+	cobd_aliases_exit();
+	cobd_drives_exit();
+}
+
+module_init(cobd_init);
+module_exit(cobd_exit);
+
+
