colinux64/patch/cobd-core.diff
2025-02-13 19:44:47 -07:00

580 lines
14 KiB
Diff
Executable File

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);
+
+