patch-2.3.6 linux/drivers/i2o/i2o_block.c
Next file: linux/drivers/i2o/i2o_config.c
Previous file: linux/drivers/i2o/README.lan
Back to the patch index
Back to the overall index
- Lines: 1072
- Date:
Wed Jun 2 14:40:22 1999
- Orig file:
v2.3.5/linux/drivers/i2o/i2o_block.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_block.c linux/drivers/i2o/i2o_block.c
@@ -0,0 +1,1071 @@
+/*
+ * I2O block device driver.
+ *
+ * (C) Copyright 1999 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This is an initial test release. Most of the good code was taken
+ * from the nbd driver by Pavel Machek, who in turn took some of it
+ * from loop.c. Isn't free software great for reusability 8)
+ *
+ * Fixes:
+ * Steve Ralston: Multiple device handling error fixes,
+ * Added a queue depth.
+ */
+
+#include <linux/major.h>
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
+#include <linux/i2o.h>
+#include <linux/blkdev.h>
+#include <linux/malloc.h>
+#include <linux/hdreg.h>
+
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+
+#define MAJOR_NR I2O_MAJOR
+
+#include <linux/blk.h>
+
+#define MAX_I2OB 16
+
+#define MAX_I2OB_DEPTH 4
+
+/*
+ * Some of these can be made smaller later
+ */
+
+static int i2ob_blksizes[MAX_I2OB<<4];
+static int i2ob_hardsizes[MAX_I2OB<<4];
+static int i2ob_sizes[MAX_I2OB<<4];
+static int i2ob_media_change_flag[MAX_I2OB];
+static u32 i2ob_max_sectors[MAX_I2OB<<4];
+
+static int i2ob_context;
+
+#ifdef __SMP__
+static spinlock_t i2ob_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+struct i2ob_device
+{
+ struct i2o_controller *controller;
+ struct i2o_device *i2odev;
+ int tid;
+ int flags;
+ int refcnt;
+ struct request *head, *tail;
+ int done_flag;
+};
+
+/*
+ * Each I2O disk is one of these.
+ */
+
+static struct i2ob_device i2ob_dev[MAX_I2OB<<4];
+static int i2ob_devices = 0;
+static struct hd_struct i2ob[MAX_I2OB<<4];
+static struct gendisk i2ob_gendisk; /* Declared later */
+
+static atomic_t queue_depth; /* For flow control later on */
+
+#define DEBUG( s )
+/* #define DEBUG( s ) printk( s )
+ */
+
+static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int);
+static void i2ob_end_request(struct request *);
+static void do_i2ob_request(void);
+
+/*
+ * Get a message
+ */
+
+static u32 i2ob_get(struct i2ob_device *dev)
+{
+ struct i2o_controller *c=dev->controller;
+ return I2O_POST_READ32(c);
+}
+
+/*
+ * Turn a Linux block request into an I2O block read/write.
+ */
+
+static int i2ob_send(u32 m, struct i2ob_device *dev, struct request *req, u32 base, int unit)
+{
+ struct i2o_controller *c = dev->controller;
+ int tid = dev->tid;
+ u32 *msg;
+ u32 *mptr;
+ u64 offset;
+ struct buffer_head *bh = req->bh;
+ static int old_qd = 2;
+ int count = req->nr_sectors<<9;
+
+ /*
+ * Build a message
+ */
+
+ msg = bus_to_virt(c->mem_offset + m);
+
+ msg[2] = i2ob_context|(unit<<8);
+ msg[3] = (u32)req; /* 64bit issue again here */
+ msg[5] = req->nr_sectors << 9;
+
+ /* This can be optimised later - just want to be sure its right for
+ starters */
+ offset = ((u64)(req->sector+base)) << 9;
+ msg[6] = offset & 0xFFFFFFFF;
+ msg[7] = (offset>>32);
+ mptr=msg+8;
+
+ if(req->cmd == READ)
+ {
+ msg[1] = I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid;
+ /* We don't yet do cache/readahead and other magic */
+ msg[4] = 1<<16;
+ while(bh!=NULL)
+ {
+ *mptr++ = 0x10000000|(bh->b_size);
+ *mptr++ = virt_to_bus(bh->b_data);
+ count -= bh->b_size;
+ bh = bh->b_reqnext;
+ }
+ }
+ else if(req->cmd == WRITE)
+ {
+ msg[1] = I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid;
+ msg[4] = 1<<16;
+ while(bh!=NULL)
+ {
+ *mptr++ = 0x14000000|(bh->b_size);
+ count -= bh->b_size;
+ *mptr++ = virt_to_bus(bh->b_data);
+ bh = bh->b_reqnext;
+ }
+ }
+ mptr[-2]|= 0xC0000000;
+ msg[0] = I2O_MESSAGE_SIZE(mptr-msg) | SGL_OFFSET_8;
+
+ if(req->current_nr_sectors > 8)
+ printk("Gathered sectors %ld.\n",
+ req->current_nr_sectors);
+
+ if(count != 0)
+ {
+ printk("Request count botched by %d.\n", count);
+ msg[5] -= count;
+ }
+
+// printk("Send for %p\n", req);
+
+ i2o_post_message(c,m);
+ atomic_inc(&queue_depth);
+ if(atomic_read(&queue_depth)>old_qd)
+ {
+ old_qd=atomic_read(&queue_depth);
+ printk("Depth now %d.\n", old_qd);
+ }
+ return 0;
+}
+
+/*
+ * Remove a request from the _locked_ request list. We update both the
+ * list chain and if this is the last item the tail pointer.
+ */
+
+static void i2ob_unhook_request(struct i2ob_device *dev, struct request *req)
+{
+ struct request **p = &dev->head;
+ struct request *nt = NULL;
+ static int crap = 0;
+
+ while(*p!=NULL)
+ {
+ if(*p==req)
+ {
+ if(dev->tail==req)
+ dev->tail = nt;
+ *p=req->next;
+ return;
+ }
+ nt=*p;
+ p=&(nt->next);
+ }
+ if(!crap++)
+ printk("i2o_block: request queue corrupt!\n");
+}
+
+/*
+ * Request completion handler
+ */
+
+static void i2ob_end_request(struct request *req)
+{
+ /*
+ * Loop until all of the buffers that are linked
+ * to this request have been marked updated and
+ * unlocked.
+ */
+ while (end_that_request_first( req, !req->errors, "i2o block" ));
+
+ /*
+ * It is now ok to complete the request.
+ */
+ end_that_request_last( req );
+}
+
+
+/*
+ * OSM reply handler. This gets all the message replies
+ */
+
+static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg)
+{
+ struct request *req;
+ u8 st;
+ u32 *m = (u32 *)msg;
+ u8 unit = (m[2]>>8)&0xF0; /* low 4 bits are partition */
+
+ if(m[0] & (1<<13))
+ {
+ printk("IOP fail.\n");
+ printk("From %d To %d Cmd %d.\n",
+ (m[1]>>12)&0xFFF,
+ m[1]&0xFFF,
+ m[1]>>24);
+ printk("Failure Code %d.\n", m[4]>>24);
+ if(m[4]&(1<<16))
+ printk("Format error.\n");
+ if(m[4]&(1<<17))
+ printk("Path error.\n");
+ if(m[4]&(1<<18))
+ printk("Path State.\n");
+ if(m[4]&(1<<18))
+ printk("Congestion.\n");
+
+ m=(u32 *)bus_to_virt(m[7]);
+ printk("Failing message is %p.\n", m);
+
+ /* We need to up the request failure count here and maybe
+ abort it */
+ req=(struct request *)m[3];
+ /* Now flush the message by making it a NOP */
+ m[0]&=0x00FFFFFF;
+ m[0]|=(I2O_CMD_UTIL_NOP)<<24;
+ i2o_post_message(c,virt_to_bus(m));
+
+ }
+ else
+ {
+ if(m[2]&0x80000000)
+ {
+ int * ptr = (int *)m[3];
+ if(m[4]>>24)
+ *ptr = -1;
+ else
+ *ptr = 1;
+ return;
+ }
+ /*
+ * Lets see what is cooking. We stuffed the
+ * request in the context.
+ */
+
+ req=(struct request *)m[3];
+ st=m[4]>>24;
+
+ if(st!=0)
+ {
+ printk(KERN_ERR "i2ob: error %08X\n", m[4]);
+ /*
+ * Now error out the request block
+ */
+ req->errors++;
+ }
+ }
+ /*
+ * Dequeue the request.
+ */
+
+ spin_lock(&io_request_lock);
+ spin_lock(&i2ob_lock);
+ i2ob_unhook_request(&i2ob_dev[unit], req);
+ i2ob_end_request(req);
+
+ /*
+ * We may be able to do more I/O
+ */
+
+ atomic_dec(&queue_depth);
+ do_i2ob_request();
+ spin_unlock(&i2ob_lock);
+ spin_unlock(&io_request_lock);
+}
+
+static struct i2o_handler i2o_block_handler =
+{
+ i2o_block_reply,
+ "I2O Block OSM",
+ 0
+};
+
+
+/*
+ * Flush all pending requests as errors. Must call with the queue
+ * locked.
+ */
+
+#if 0
+static void i2ob_clear_queue(struct i2ob_device *dev)
+{
+ struct request *req;
+
+ while (1) {
+ req = dev->tail;
+ if (!req)
+ return;
+ req->errors++;
+ i2ob_end_request(req);
+
+ if (dev->tail == dev->head)
+ dev->head = NULL;
+ dev->tail = dev->tail->next;
+ }
+}
+#endif
+
+/*
+ * The I2O block driver is listed as one of those that pulls the
+ * front entry off the queue before processing it. This is important
+ * to remember here. If we drop the io lock then CURRENT will change
+ * on us. We must unlink CURRENT in this routine before we return, if
+ * we use it.
+ */
+
+static void do_i2ob_request(void)
+{
+ struct request *req;
+ int unit;
+ struct i2ob_device *dev;
+ u32 m;
+
+ while (CURRENT) {
+ /*
+ * On an IRQ completion if there is an inactive
+ * request on the queue head it means it isnt yet
+ * ready to dispatch.
+ */
+ if(CURRENT->rq_status == RQ_INACTIVE)
+ return;
+
+ /*
+ * Queue depths probably belong with some kind of
+ * generic IOP commit control. Certainly its not right
+ * its global!
+ */
+ if(atomic_read(&queue_depth)>=MAX_I2OB_DEPTH)
+ break;
+
+ req = CURRENT;
+ unit = MINOR(req->rq_dev);
+ dev = &i2ob_dev[(unit&0xF0)];
+ /* Get a message */
+ m = i2ob_get(dev);
+ /* No messages -> punt
+ FIXME: if we have no messages, and there are no messages
+ we deadlock now. Need a timer/callback ?? */
+ if(m==0xFFFFFFFF)
+ {
+ printk("i2ob: no messages!\n");
+ break;
+ }
+ req->errors = 0;
+ CURRENT = CURRENT->next;
+ req->next = NULL;
+
+ if (dev->head == NULL) {
+ dev->head = req;
+ dev->tail = req;
+ } else {
+ dev->tail->next = req;
+ dev->tail = req;
+ }
+ i2ob_send(m, dev, req, i2ob[unit].start_sect, (unit&0xF0));
+ }
+}
+
+static void i2ob_request(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&i2ob_lock, flags);
+ do_i2ob_request();
+ spin_unlock_irqrestore(&i2ob_lock, flags);
+}
+
+/*
+ * SCSI-CAM for ioctl geometry mapping
+ * Duplicated with SCSI - this should be moved into somewhere common
+ * perhaps genhd ?
+ */
+
+static void i2o_block_biosparam(
+ unsigned long capacity,
+ unsigned short *cyls,
+ unsigned char *hds,
+ unsigned char *secs)
+{
+ unsigned long heads, sectors, cylinders, temp;
+
+ cylinders = 1024L; /* Set number of cylinders to max */
+ sectors = 62L; /* Maximize sectors per track */
+
+ temp = cylinders * sectors; /* Compute divisor for heads */
+ heads = capacity / temp; /* Compute value for number of heads */
+ if (capacity % temp) { /* If no remainder, done! */
+ heads++; /* Else, increment number of heads */
+ temp = cylinders * heads; /* Compute divisor for sectors */
+ sectors = capacity / temp; /* Compute value for sectors per
+ track */
+ if (capacity % temp) { /* If no remainder, done! */
+ sectors++; /* Else, increment number of sectors */
+ temp = heads * sectors; /* Compute divisor for cylinders */
+ cylinders = capacity / temp;/* Compute number of cylinders */
+ }
+ }
+ /* if something went wrong, then apparently we have to return
+ a geometry with more than 1024 cylinders */
+ if (cylinders == 0 || heads > 255 || sectors > 63 || cylinders >1023)
+ {
+ unsigned long temp_cyl;
+
+ heads = 64;
+ sectors = 32;
+ temp_cyl = capacity / (heads * sectors);
+ if (temp_cyl > 1024)
+ {
+ heads = 255;
+ sectors = 63;
+ }
+ cylinders = capacity / (heads * sectors);
+ }
+ *cyls = (unsigned int) cylinders; /* Stuff return values */
+ *secs = (unsigned int) sectors;
+ *hds = (unsigned int) heads;
+}
+
+/*
+ * Rescan the partition tables
+ */
+
+static int do_i2ob_revalidate(kdev_t dev, int maxu)
+{
+ int minor=MINOR(dev);
+ int i;
+
+ minor&=0xF0;
+
+ i2ob_dev[minor].refcnt++;
+ if(i2ob_dev[minor].refcnt>maxu+1)
+ {
+ i2ob_dev[minor].refcnt--;
+ return -EBUSY;
+ }
+
+ for( i = 15; i>=0 ; i--)
+ {
+ int m = minor+i;
+ kdev_t d = MKDEV(MAJOR_NR, m);
+ struct super_block *sb = get_super(d);
+
+ sync_dev(d);
+ if(sb)
+ invalidate_inodes(sb);
+ invalidate_buffers(d);
+ i2ob_gendisk.part[m].start_sect = 0;
+ i2ob_gendisk.part[m].nr_sects = 0;
+ }
+
+ /*
+ * Do a physical check and then reconfigure
+ */
+
+ i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev,
+ minor);
+ i2ob_dev[minor].refcnt--;
+ return 0;
+}
+
+/*
+ * Issue device specific ioctl calls.
+ */
+
+static int i2ob_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct i2ob_device *dev;
+ int minor;
+
+ /* Anyone capable of this syscall can do *real bad* things */
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (!inode)
+ return -EINVAL;
+ minor = MINOR(inode->i_rdev);
+ if (minor >= (MAX_I2OB<<4))
+ return -ENODEV;
+
+ dev = &i2ob_dev[minor];
+ switch (cmd) {
+ case BLKRASET:
+ if(!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ case BLKRAGET:
+ if (!arg) return -EINVAL;
+ return put_user(read_ahead[MAJOR(inode->i_rdev)],
+ (long *) arg);
+ case BLKGETSIZE:
+ return put_user(i2ob[minor].nr_sects, (long *) arg);
+
+ case BLKFLSBUF:
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case HDIO_GETGEO:
+ {
+ struct hd_geometry g;
+ int u=minor&0xF0;
+ i2o_block_biosparam(i2ob_sizes[u]<<1,
+ &g.cylinders, &g.heads, &g.sectors);
+ g.start = i2ob[minor].start_sect;
+ return copy_to_user((void *)arg,&g, sizeof(g))?-EFAULT:0;
+ }
+
+ case BLKRRPART:
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ return do_i2ob_revalidate(inode->i_rdev,1);
+
+ default:
+ return blk_ioctl(inode->i_rdev, cmd, arg);
+ }
+}
+
+/*
+ * Issue UTIL_CLAIM messages
+ */
+
+static int i2ob_claim_device(struct i2ob_device *dev, int onoff)
+{
+ return i2o_issue_claim(dev->controller, dev->tid, i2ob_context, onoff, &dev->done_flag);
+}
+
+/*
+ * Close the block device down
+ */
+
+static int i2ob_release(struct inode *inode, struct file *file)
+{
+ struct i2ob_device *dev;
+ int minor;
+
+ minor = MINOR(inode->i_rdev);
+ if (minor >= (MAX_I2OB<<4))
+ return -ENODEV;
+ sync_dev(inode->i_rdev);
+ dev = &i2ob_dev[(minor&0xF0)];
+ if (dev->refcnt <= 0)
+ printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt);
+ dev->refcnt--;
+ if(dev->refcnt==0)
+ {
+ /*
+ * Flush the onboard cache on unmount
+ */
+ u32 msg[5];
+ int *query_done = &dev->done_flag;
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = 60<<16;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+ /*
+ * Unlock the media
+ */
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = -1;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+
+ /*
+ * Now unclaim the device.
+ */
+ if (i2ob_claim_device(dev, 0)<0)
+ printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n");
+
+ }
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Open the block device.
+ */
+
+static int i2ob_open(struct inode *inode, struct file *file)
+{
+ int minor;
+ struct i2ob_device *dev;
+
+ if (!inode)
+ return -EINVAL;
+ minor = MINOR(inode->i_rdev);
+ if (minor >= MAX_I2OB<<4)
+ return -ENODEV;
+ dev=&i2ob_dev[(minor&0xF0)];
+
+ if(dev->refcnt++==0)
+ {
+ u32 msg[6];
+ int *query_done;
+
+
+ if(i2ob_claim_device(dev, 1)<0)
+ {
+ dev->refcnt--;
+ return -EBUSY;
+ }
+
+ query_done = &dev->done_flag;
+ /*
+ * Mount the media if needed. Note that we don't use
+ * the lock bit. Since we have to issue a lock if it
+ * refuses a mount (quite possible) then we might as
+ * well just send two messages out.
+ */
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = -1;
+ msg[5] = 0;
+ i2o_post_wait(dev->controller, dev->tid, msg, 24, query_done,2);
+ /*
+ * Lock the media
+ */
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = -1;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Issue a device query
+ */
+
+static int i2ob_query_device(struct i2ob_device *dev, int table,
+ int field, void *buf, int buflen)
+{
+ return i2o_query_scalar(dev->controller, dev->tid, i2ob_context,
+ table, field, buf, buflen, &dev->done_flag);
+}
+
+
+/*
+ * Install the I2O block device we found.
+ */
+
+static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, int unit)
+{
+ u64 size;
+ u32 blocksize;
+ u32 limit;
+ u8 type;
+ u32 flags, status;
+ struct i2ob_device *dev=&i2ob_dev[unit];
+ int i;
+
+ /*
+ * Ask for the current media data. If that isn't supported
+ * then we ask for the device capacity data
+ */
+
+ if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0
+ || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 )
+ {
+ i2ob_query_device(dev, 0x0000, 3, &blocksize, 4);
+ i2ob_query_device(dev, 0x0000, 4, &size, 8);
+ }
+
+ i2ob_query_device(dev, 0x0000, 5, &flags, 4);
+ i2ob_query_device(dev, 0x0000, 6, &status, 4);
+ i2ob_sizes[unit] = (int)(size>>10);
+ i2ob_hardsizes[unit] = blocksize;
+ i2ob_gendisk.part[unit].nr_sects = i2ob_sizes[unit];
+
+ /* Setting this higher than 1024 breaks the symbios for some reason */
+
+ limit=4096; /* 8 deep scatter gather */
+
+ printk("Byte limit is %d.\n", limit);
+
+ for(i=unit;i<=unit+15;i++)
+ i2ob_max_sectors[i]=(limit>>9);
+
+ i2ob[unit].nr_sects = (int)(size>>9);
+
+ i2ob_query_device(dev, 0x0000, 0, &type, 1);
+
+ sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4));
+
+ printk("%s: ", d->dev_name);
+ if(status&(1<<10))
+ printk("RAID ");
+ switch(type)
+ {
+ case 0: printk("Disk Storage");break;
+ case 4: printk("WORM");break;
+ case 5: printk("CD-ROM");break;
+ case 7: printk("Optical device");break;
+ default:
+ printk("Type %d", type);
+ }
+ if(((flags & (1<<3)) && !(status & (1<<3))) ||
+ ((flags & (1<<4)) && !(status & (1<<4))))
+ {
+ printk(" Not loaded.\n");
+ return 0;
+ }
+ printk(" %dMb, %d byte sectors",
+ (int)(size>>20), blocksize);
+ if(status&(1<<0))
+ {
+ u32 cachesize;
+ i2ob_query_device(dev, 0x0003, 0, &cachesize, 4);
+ cachesize>>=10;
+ if(cachesize>4095)
+ printk(", %dMb cache", cachesize>>10);
+ else
+ printk(", %dKb cache", cachesize);
+ }
+ printk(".\n");
+ printk("%s: Maximum sectors/read set to %d.\n",
+ d->dev_name, i2ob_max_sectors[unit]);
+ resetup_one_dev(&i2ob_gendisk, unit>>4);
+ return 0;
+}
+
+static void i2ob_probe(void)
+{
+ int i;
+ int unit = 0;
+ int warned = 0;
+
+ for(i=0; i< MAX_I2O_CONTROLLERS; i++)
+ {
+ struct i2o_controller *c=i2o_find_controller(i);
+ struct i2o_device *d;
+
+ if(c==NULL)
+ continue;
+
+ for(d=c->devices;d!=NULL;d=d->next)
+ {
+ if(d->class!=I2O_CLASS_RANDOM_BLOCK_STORAGE)
+ continue;
+
+ if(unit<MAX_I2OB<<4)
+ {
+ /*
+ * Get the device and fill in the
+ * Tid and controller.
+ */
+ struct i2ob_device *dev=&i2ob_dev[unit];
+ dev->i2odev = d;
+ dev->controller = c;
+ dev->tid = d->id;
+
+ /*
+ * Insure the device can be claimed
+ * before installing it.
+ */
+ if(i2ob_claim_device(dev, 1)==0)
+ {
+ printk(KERN_INFO "Claimed Dev %x Tid %d Unit %d\n",dev,dev->tid,unit);
+ i2ob_install_device(c,d,unit);
+ unit+=16;
+
+ /*
+ * Now that the device has been
+ * installed, unclaim it so that
+ * it can be claimed by either
+ * the block or scsi driver.
+ */
+ if (i2ob_claim_device(dev, 0)<0)
+ printk(KERN_INFO "Could not unclaim Dev %x Tid %d\n",dev,dev->tid);
+
+ }
+ else
+ printk(KERN_INFO "TID %d not claimed\n",dev->tid);
+ }
+ else
+ {
+ if(!warned++)
+ printk("i2o_block: too many controllers, registering only %d.\n", unit>>4);
+ }
+ }
+ }
+ i2ob_devices = unit;
+}
+
+/*
+ * Have we seen a media change ?
+ */
+
+static int i2ob_media_change(kdev_t dev)
+{
+ int i=MINOR(dev);
+ i>>=4;
+ if(i2ob_media_change_flag[i])
+ {
+ i2ob_media_change_flag[i]=0;
+ return 1;
+ }
+ return 0;
+}
+
+static int i2ob_revalidate(kdev_t dev)
+{
+ return do_i2ob_revalidate(dev, 0);
+}
+
+static int i2ob_reboot_event(struct notifier_block *n, unsigned long code, void *p)
+{
+ int i;
+
+ if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF)
+ return NOTIFY_DONE;
+ for(i=0;i<MAX_I2OB;i++)
+ {
+ struct i2ob_device *dev=&i2ob_dev[(i<<4)];
+
+ if(dev->refcnt!=0)
+ {
+ /*
+ * Flush the onboard cache on power down
+ * also unlock the media
+ */
+ u32 msg[5];
+ int *query_done = &dev->done_flag;
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = 60<<16;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+ /*
+ * Unlock the media
+ */
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = -1;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+struct notifier_block i2ob_reboot_notifier =
+{
+ i2ob_reboot_event,
+ NULL,
+ 0
+};
+
+static struct file_operations i2ob_fops =
+{
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ i2ob_ioctl, /* ioctl */
+ NULL, /* mmap */
+ i2ob_open, /* open */
+ NULL, /* flush */
+ i2ob_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ i2ob_media_change, /* Media Change */
+ i2ob_revalidate, /* Revalidate */
+ NULL /* File locks */
+};
+
+/*
+ * Partitioning
+ */
+
+static void i2ob_geninit(struct gendisk *gd)
+{
+}
+
+static struct gendisk i2ob_gendisk =
+{
+ MAJOR_NR,
+ "i2ohd",
+ 4,
+ 1<<4,
+ MAX_I2OB,
+ i2ob_geninit,
+ i2ob,
+ i2ob_sizes,
+ 0,
+ NULL,
+ NULL
+};
+
+/*
+ * And here should be modules and kernel interface
+ * (Just smiley confuses emacs :-)
+ */
+
+#ifdef MODULE
+#define i2ob_init init_module
+#endif
+
+int i2ob_init(void)
+{
+ int i;
+
+ printk("I2O block device OSM v0.06. (C) 1999 Red Hat Software.\n");
+
+ /*
+ * Register the block device interfaces
+ */
+
+ if (register_blkdev(MAJOR_NR, "i2o_block", &i2ob_fops)) {
+ printk("Unable to get major number %d for i2o_block\n",
+ MAJOR_NR);
+ return -EIO;
+ }
+#ifdef MODULE
+ printk("i2o_block: registered device at major %d\n", MAJOR_NR);
+#endif
+
+ /*
+ * Now fill in the boiler plate
+ */
+
+ blksize_size[MAJOR_NR] = i2ob_blksizes;
+ hardsect_size[MAJOR_NR] = i2ob_hardsizes;
+ blk_size[MAJOR_NR] = i2ob_sizes;
+ max_sectors[MAJOR_NR] = i2ob_max_sectors;
+
+ blk_dev[MAJOR_NR].request_fn = i2ob_request;
+ for (i = 0; i < MAX_I2OB << 4; i++) {
+ i2ob_dev[i].refcnt = 0;
+ i2ob_dev[i].flags = 0;
+ i2ob_dev[i].controller = NULL;
+ i2ob_dev[i].i2odev = NULL;
+ i2ob_dev[i].tid = 0;
+ i2ob_dev[i].head = NULL;
+ i2ob_dev[i].tail = NULL;
+ i2ob_blksizes[i] = 1024;
+ i2ob_max_sectors[i] = 2;
+ }
+
+ /*
+ * Register the OSM handler as we will need this to probe for
+ * drives, geometry and other goodies.
+ */
+
+ if(i2o_install_handler(&i2o_block_handler)<0)
+ {
+ unregister_blkdev(MAJOR_NR, "i2o_block");
+ printk(KERN_ERR "i2o_block: unable to register OSM.\n");
+ return -EINVAL;
+ }
+ i2ob_context = i2o_block_handler.context;
+
+ /*
+ * Finally see what is actually plugged in to our controllers
+ */
+
+ i2ob_probe();
+
+ register_reboot_notifier(&i2ob_reboot_notifier);
+ return 0;
+}
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O Block Device OSM");
+
+void cleanup_module(void)
+{
+ struct gendisk **gdp;
+
+ unregister_reboot_notifier(&i2ob_reboot_notifier);
+
+ /*
+ * Flush the OSM
+ */
+
+ i2o_remove_handler(&i2o_block_handler);
+
+ /*
+ * Return the block device
+ */
+ if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0)
+ printk("i2o_block: cleanup_module failed\n");
+ else
+ printk("i2o_block: module cleaned up.\n");
+
+ /*
+ * Why isnt register/unregister gendisk in the kernel ???
+ */
+
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == &i2ob_gendisk)
+ break;
+
+}
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)