patch-2.2.16 linux/drivers/i2o/i2o_block.c
Next file: linux/drivers/i2o/i2o_config.c
Previous file: linux/drivers/i2o/README.ioctl
Back to the patch index
Back to the overall index
- Lines: 1018
- Date:
Wed Jun 7 14:26:42 2000
- Orig file:
v2.2.15/linux/drivers/i2o/i2o_block.c
- Orig date:
Wed May 3 17:16:39 2000
diff -urN v2.2.15/linux/drivers/i2o/i2o_block.c linux/drivers/i2o/i2o_block.c
@@ -20,6 +20,8 @@
* Alan Cox: FC920 has an rmw bug. Dont or in the
* end marker.
* Removed queue walk, fixed for 64bitness.
+ * Boji T Kannanthanam:
+ * Support for dynamic device creation/deletion
* To do:
* Multiple majors
* Serial number scanning to find duplicates for FC multipathing
@@ -46,8 +48,12 @@
#include <linux/reboot.h>
#include <asm/uaccess.h>
+#include <asm/semaphore.h>
#include <asm/io.h>
#include <asm/atomic.h>
+#include <linux/smp_lock.h>
+#include <linux/wait.h>
+
#define MAJOR_NR I2O_MAJOR
@@ -55,9 +61,39 @@
#define MAX_I2OB 16
-#define MAX_I2OB_DEPTH 32
+#define MAX_I2OB_DEPTH 128
#define MAX_I2OB_RETRIES 4
+
+/*
+ * Events that this OSM is interested in
+ */
+#define I2OB_EVENT_MASK (I2O_EVT_IND_BSA_VOLUME_LOAD | \
+ I2O_EVT_IND_BSA_VOLUME_UNLOAD | \
+ I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ | \
+ I2O_EVT_IND_BSA_CAPACITY_CHANGE)
+
+
+/*
+ * I2O Block Error Codes - should be in a header file really...
+ */
+#define I2O_BSA_DSC_SUCCESS 0x0000
+#define I2O_BSA_DSC_MEDIA_ERROR 0x0001
+#define I2O_BSA_DSC_ACCESS_ERROR 0x0002
+#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003
+#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004
+#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005
+#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006
+#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007
+#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008
+#define I2O_BSA_DSC_BUS_FAILURE 0x0009
+#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A
+#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B
+#define I2O_BSA_DSC_DEVICE_RESET 0x000C
+#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D
+#define I2O_BSA_DSC_TIMEOUT 0x000E
+
+
/*
* Some of these can be made smaller later
*/
@@ -74,10 +110,12 @@
{
struct i2o_controller *controller;
struct i2o_device *i2odev;
+ int unit;
int tid;
int flags;
int refcnt;
struct request *head, *tail;
+ int max_segments;
int done_flag;
};
@@ -101,9 +139,20 @@
static struct i2ob_device i2ob_dev[MAX_I2OB<<4];
static int i2ob_devices = 0;
+static int i2ob_dev_count = 0;
static struct hd_struct i2ob[MAX_I2OB<<4];
static struct gendisk i2ob_gendisk; /* Declared later */
+/*
+ * Mutex and spin lock for event handling synchronization
+ * evt_msg contains the last event.
+ */
+DECLARE_MUTEX(i2ob_evt_sem);
+static spinlock_t i2ob_evt_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int evt_msg[MSG_FRAME_SIZE>>2];
+DECLARE_WAIT_QUEUE_HEAD(i2ob_evt_wait);
+
+
static atomic_t queue_depth; /* For flow control later on */
static struct i2ob_request i2ob_queue[MAX_I2OB_DEPTH+1];
static struct i2ob_request *i2ob_qhead;
@@ -118,20 +167,31 @@
static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int);
static void i2ob_end_request(struct request *);
static void i2ob_request(struct request * q);
+static int do_i2ob_revalidate(kdev_t, int);
+static int i2ob_query_device(struct i2ob_device *, int, int, void*, int);
+static int i2ob_evt(void *);
+
+static int evt_pid = 0;
+static int evt_running = 0;
+
+static void i2ob_new_device(struct i2o_controller *, struct i2o_device *);
+static void i2ob_del_device(struct i2o_controller *, struct i2o_device *);
+static void i2ob_reboot_event(void);
+
/*
* Dump messages.
*/
static void i2ob_dump_msg(struct i2ob_device *dev,u32 *msg,int size)
{
- int cnt;
+ int cnt;
- printk(KERN_INFO "\n\ni2o message:\n");
- for (cnt = 0; cnt<size; cnt++)
- {
- printk(KERN_INFO "m[%d]=%x\n",cnt,msg[cnt]);
- }
- printk(KERN_INFO "\n");
+ printk(KERN_INFO "\n\ni2o message:\n");
+ for (cnt = 0; cnt<size; cnt++)
+ {
+ printk(KERN_INFO "m[%d]=%x\n",cnt,msg[cnt]);
+ }
+ printk(KERN_INFO "\n");
}
/*
@@ -163,7 +223,7 @@
msg = c->mem_offset + m;
/*
- * Build the message based on the request.
+ * Build the message based on the request.
*/
writel(i2ob_context|(unit<<8), msg+8);
writel(ireq->num, msg+12);
@@ -283,6 +343,7 @@
u8 st;
u32 *m = (u32 *)msg;
u8 unit = (m[2]>>8)&0xF0; /* low 4 bits are partition */
+ struct i2ob_device *dev = &i2ob_dev[(unit&0xF0)];
if(m[0] & (1<<13))
{
@@ -313,43 +374,83 @@
i2o_post_message(c,virt_to_bus(m));
}
- else
+
+ if(msg->function == I2O_CMD_UTIL_EVT_REGISTER)
+ {
+ spin_lock(&i2ob_evt_lock);
+ memcpy(&evt_msg, m, msg->size);
+ spin_unlock(&i2ob_evt_lock);
+ wake_up_interruptible(&i2ob_evt_wait);
+ return;
+ }
+ if(!dev->i2odev)
{
- if(m[2]&0x40000000)
- {
- 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.
+ * This is HACK, but Intel Integrated RAID allows user
+ * to delete a volume that is claimed, locked, and in use
+ * by the OS. We have to check for a reply from a
+ * non-existent device and flag it as an error or the system
+ * goes kaput...
*/
+ printk(KERN_INFO "I2O Block: Data transfer to deleted device!\n");
+
+ ireq=&i2ob_queue[m[3]];
+ ireq->req->errors++;
+ printk(KERN_WARNING "I2O Block: Data transfer to deleted device!\n");
+ spin_lock_irqsave(&io_request_lock, flags);
+ i2ob_unhook_request(ireq);
+ i2ob_end_request(ireq->req);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return;
+ }
+ /*
+ * Lets see what is cooking. We stuffed the
+ * request in the context.
+ */
+
ireq=&i2ob_queue[m[3]];
st=m[4]>>24;
if(st!=0)
{
- printk(KERN_ERR "i2ob: error %08X\n", m[4]);
+ char *bsa_errors[] =
+ {
+ "Success",
+ "Media Error",
+ "Failure communicating to device",
+ "Device Failure",
+ "Device is not ready",
+ "Media not present",
+ "Media is locked by another user",
+ "Media has failed",
+ "Failure communicating to device",
+ "Device bus failure",
+ "Device is locked by another user",
+ "Device is write protected",
+ "Device has reset",
+ "Volume has changed, waiting for acknowledgement"
+ };
+ printk(KERN_ERR "\n/dev/%s error: %s", dev->i2odev->dev_name, bsa_errors[m[4]&0XFFFF]);
+
+ if(m[4]&0x00FF0000)
+ printk(" - DDM attempted %d retries", (m[4]>>16)&0x00FF);
+ printk("\n");
+
ireq->req->errors++;
+
if (ireq->req->errors < MAX_I2OB_RETRIES)
{
u32 retry_msg;
- struct i2ob_device *dev;
printk(KERN_ERR "i2ob: attempting retry %d for request %p\n",ireq->req->errors+1,ireq->req);
- /*
+ /*
* Get a message for this retry.
*/
- dev = &i2ob_dev[(unit&0xF0)];
- retry_msg = i2ob_get(dev);
+ retry_msg = i2ob_get(dev);
- /*
+ /*
* If we cannot get a message then
* forget the retry and fail the
* request. Note that since this is
@@ -359,20 +460,20 @@
* be space on the inbound message
* fifo so this won't happen often.
*/
- if(retry_msg!=0xFFFFFFFF)
+ if(retry_msg!=0xFFFFFFFF)
{
- /*
- * Decrement the queue depth since
- * this request has completed and
- * it will be incremented again when
- * i2ob_send is called below.
- */
- atomic_dec(&queue_depth);
-
- /*
- * Send the request again.
- */
- i2ob_send(retry_msg, dev,ireq,i2ob[unit].start_sect, (unit&0xF0));
+ /*
+ * Decrement the queue depth since
+ * this request has completed and
+ * it will be incremented again when
+ * i2ob_send is called below.
+ */
+ atomic_dec(&queue_depth);
+
+ /*
+ * Send the request again.
+ */
+ i2ob_send(retry_msg, dev,ireq,i2ob[unit].start_sect, (unit&0xF0));
/*
* Don't fall through.
*/
@@ -382,7 +483,6 @@
}
else
ireq->req->errors = 0;
- }
/*
* Dequeue the request. We use irqsave locks as one day we
@@ -402,9 +502,148 @@
spin_unlock_irqrestore(&io_request_lock, flags);
}
+
+/*
+ * Event handler. Needs to be a separate thread b/c we may have
+ * to do things like scan a partition table, or query parameters
+ * which cannot be done from an interrupt or from a bottom half.
+ */
+static int i2ob_evt(void *dummy)
+{
+ unsigned int evt;
+ unsigned int flags;
+ int unit;
+ int i;
+ struct fs_struct *fs;
+
+ lock_kernel();
+
+ exit_files(current);
+ exit_mm(current);
+ current->session = 1;
+ current->pgrp = 1;
+ /* Become as one with the init task */
+ exit_fs(current);
+ fs = init_task.fs;
+ current->fs = fs;
+ atomic_inc(&fs->count);
+
+ unlock_kernel();
+
+ strcpy(current->comm, "i2oblock");
+ evt_running = 1;
+
+ while(1)
+ {
+ interruptible_sleep_on(&i2ob_evt_wait);
+ if(signal_pending(current)) {
+ evt_running = 0;
+ return 0;
+ }
+
+ /*
+ * Keep another CPU/interrupt from overwriting the
+ * message while we're reading it
+ *
+ * We stuffed the unit in the TxContext and grab the event mask
+ * None of the BSA we care about events have EventData
+ */
+ spin_lock_irqsave(&i2ob_evt_lock, flags);
+ unit = evt_msg[3];
+ evt = evt_msg[4];
+ spin_unlock_irqrestore(&i2ob_evt_lock, flags);
+
+ switch(evt)
+ {
+ /*
+ * New volume loaded on same TID, so we just re-install.
+ * The TID/controller don't change as it is the same
+ * I2O device. It's just new media that we have to
+ * rescan.
+ */
+ case I2O_EVT_IND_BSA_VOLUME_LOAD:
+ {
+ i2ob_install_device(i2ob_dev[unit].i2odev->controller,
+ i2ob_dev[unit].i2odev, unit);
+ break;
+ }
+
+ /*
+ * No media, so set all parameters to 0 and set the media
+ * change flag. The I2O device is still valid, just doesn't
+ * have media, so we don't want to clear the controller or
+ * device pointer.
+ */
+ case I2O_EVT_IND_BSA_VOLUME_UNLOAD:
+ {
+ for(i = unit; i <= unit+15; i++)
+ {
+ i2ob_sizes[i] = 0;
+ i2ob_hardsizes[i] = 0;
+ i2ob_max_sectors[i] = 0;
+ i2ob[i].nr_sects = 0;
+ i2ob_gendisk.part[i].nr_sects = 0;
+ }
+ i2ob_media_change_flag[unit] = 1;
+ break;
+ }
+
+ case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ:
+ printk(KERN_WARNING "%s: Attempt to eject locked media\n",
+ i2ob_dev[unit].i2odev->dev_name);
+ break;
+
+ /*
+ * The capacity has changed and we are going to be
+ * updating the max_sectors and other information
+ * about this disk. We try a revalidate first. If
+ * the block device is in use, we don't want to
+ * do that as there may be I/Os bound for the disk
+ * at the moment. In that case we read the size
+ * from the device and update the information ourselves
+ * and the user can later force a partition table
+ * update through an ioctl.
+ */
+ case I2O_EVT_IND_BSA_CAPACITY_CHANGE:
+ {
+ u64 size;
+
+ if(do_i2ob_revalidate(MKDEV(MAJOR_NR, unit),0) != -EBUSY)
+ continue;
+
+ if(i2ob_query_device(&i2ob_dev[unit], 0x0004, 0, &size, 8) !=0 )
+ i2ob_query_device(&i2ob_dev[unit], 0x0000, 4, &size, 8);
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ i2ob_sizes[unit] = (int)(size>>10);
+ i2ob_gendisk.part[unit].nr_sects = size>>9;
+ i2ob[unit].nr_sects = (int)(size>>9);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ break;
+ }
+
+ /*
+ * An event we didn't ask for. Call the card manufacturer
+ * and tell them to fix their firmware :)
+ */
+ default:
+ printk(KERN_INFO "%s: Received event we didn't register for\n"
+ KERN_INFO " Call I2O card manufacturer\n",
+ i2ob_dev[unit].i2odev->dev_name);
+ break;
+ }
+ };
+
+ return 0;
+}
+
+
static struct i2o_handler i2o_block_handler =
{
i2o_block_reply,
+ i2ob_new_device,
+ i2ob_del_device,
+ i2ob_reboot_event,
"I2O Block OSM",
0,
I2O_CLASS_RANDOM_BLOCK_STORAGE
@@ -423,7 +662,7 @@
/*
* We cannot touch the request queue or the timer
- * flag without holding the io_request_lock.
+ * flag without holding the io_request_lock.
*/
spin_lock_irqsave(&io_request_lock,flags);
@@ -454,6 +693,7 @@
static void i2ob_request(struct request * q)
{
+ unsigned long flags;
struct request *req;
struct i2ob_request *ireq;
int unit;
@@ -480,6 +720,22 @@
req = CURRENT;
unit = MINOR(req->rq_dev);
dev = &i2ob_dev[(unit&0xF0)];
+
+ /*
+ * Check to see if the request is for a deleted/non-existent
+ * device. If so return.
+ */
+ if (dev->i2odev == NULL)
+ {
+ printk("I2O: ERROR I/O Request to a non existent device !\n");
+ req->errors = 1;
+ CURRENT = CURRENT->next;
+ req->sem = NULL;
+ i2ob_end_request(req);
+ }
+
+ else
+ {
/* Get a message */
m = i2ob_get(dev);
@@ -522,6 +778,7 @@
ireq->req = req;
i2ob_send(m, dev, ireq, i2ob[unit].start_sect, (unit&0xF0));
+ }
}
}
@@ -545,12 +802,12 @@
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 */
+ heads++; /* Else, increment number of heads */
temp = cylinders * heads; /* Compute divisor for sectors */
sectors = capacity / temp; /* Compute value for sectors per
- track */
+ track */
if (capacity % temp) { /* If no remainder, done! */
- sectors++; /* Else, increment number of sectors */
+ sectors++; /* Else, increment number of sectors */
temp = heads * sectors; /* Compute divisor for cylinders */
cylinders = capacity / temp;/* Compute number of cylinders */
}
@@ -695,6 +952,24 @@
if (minor >= (MAX_I2OB<<4))
return -ENODEV;
dev = &i2ob_dev[(minor&0xF0)];
+
+ /*
+ * This is to deal with the case of an application
+ * opening a device and then the device dissapears while
+ * it's in use, and then the application tries to release
+ * it. ex: Unmounting a deleted RAID volume at reboot.
+ * If we send messages, it will just cause FAILs since
+ * the TID no longer exists.
+ */
+ if(!dev->i2odev)
+ {
+ printk(KERN_INFO "I2O : ERROR No such Device\n");
+ return 0;
+ }
+
+ /* Sync the device so we don't get errors */
+ fsync_dev(inode->i_rdev);
+
if (dev->refcnt <= 0)
printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt);
dev->refcnt--;
@@ -727,7 +1002,7 @@
/*
* Now unclaim the device.
*/
- if (i2o_release_device(dev->i2odev, &i2o_block_handler, I2O_CLAIM_PRIMARY)<0)
+ if (i2o_release_device(dev->i2odev, &i2o_block_handler)<0)
printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n");
}
@@ -759,7 +1034,7 @@
int *query_done;
- if(i2o_claim_device(dev->i2odev, &i2o_block_handler, I2O_CLAIM_PRIMARY)<0)
+ if(i2o_claim_device(dev->i2odev, &i2o_block_handler)<0)
{
dev->refcnt--;
return -EBUSY;
@@ -819,11 +1094,16 @@
struct i2ob_device *dev=&i2ob_dev[unit];
int i;
+ /*
+ * For logging purposes...
+ */
+ printk(KERN_INFO "i2ob: Installing tid %d device at unit %d\n",
+ d->lct_data.tid, unit);
+
/*
* 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 )
{
@@ -835,23 +1115,38 @@
i2ob_query_device(dev, 0x0000, 6, &status, 4);
i2ob_sizes[unit] = (int)(size>>10);
i2ob_hardsizes[unit] = blocksize;
+ i2ob_gendisk.part[unit].nr_sects = size>>9;
+ i2ob[unit].nr_sects = (int)(size>>9);
- limit=4096; /* 8 deep scatter gather */
+ /* Set limit based on inbound frame size */
+ limit = (d->controller->status_block->inbound_frame_size - 8)/2;
+ limit = limit<<9;
+
+ /*
+ * Max number of Scatter-Gather Elements
+ */
+ i2ob_dev[unit].max_segments =
+ (d->controller->status_block->inbound_frame_size - 8)/2;
+
+ printk(KERN_INFO "Max Segments set to %d\n",
+ i2ob_dev[unit].max_segments);
+ printk(KERN_INFO "Byte limit is %d.\n", limit);
+
+ for(i=unit;i<=unit+15;i++)
+ {
+ i2ob_max_sectors[i]=MAX_SECTORS;
+ i2ob_dev[i].max_segments =
+ (d->controller->status_block->inbound_frame_size - 8)/2; }
- 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);
+ printk(KERN_INFO "%s: ", d->dev_name);
+
if(status&(1<<10))
- printk("RAID ");
+ printk("(RAID) ");
switch(type)
{
case 0: printk("Disk Storage");break;
@@ -864,10 +1159,10 @@
if(((flags & (1<<3)) && !(status & (1<<3))) ||
((flags & (1<<4)) && !(status & (1<<4))))
{
- printk(" Not loaded.\n");
- return 0;
+ printk(KERN_INFO " Not loaded.\n");
+ return 1;
}
- printk(" %dMb, %d byte sectors",
+ printk("- %dMb, %d byte sectors",
(int)(size>>20), blocksize);
if(status&(1<<0))
{
@@ -880,9 +1175,18 @@
printk(", %dKb cache", cachesize);
}
printk(".\n");
- printk("%s: Maximum sectors/read set to %d.\n",
+ printk(KERN_INFO "%s: Maximum sectors/read set to %d.\n",
d->dev_name, i2ob_max_sectors[unit]);
+
resetup_one_dev(&i2ob_gendisk, unit>>4);
+
+ /*
+ * Register for the events we're interested in and that the
+ * device actually supports.
+ */
+ i2o_event_register(c, d->lct_data.tid, i2ob_context, unit,
+ (I2OB_EVENT_MASK & d->lct_data.event_capabilities));
+
return 0;
}
@@ -902,12 +1206,20 @@
for(d=c->devices;d!=NULL;d=d->next)
{
- if(d->lct_data->class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE)
+ if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE)
continue;
- if(d->lct_data->user_tid != 0xFFF)
+ if(d->lct_data.user_tid != 0xFFF)
continue;
+ if(i2o_claim_device(d, &i2o_block_handler))
+ {
+ printk(KERN_WARNING "i2o_block: Controller %d, TID %d\n", c->unit,
+ d->lct_data.tid);
+ printk(KERN_WARNING "\tDevice refused claim! Skipping installation\n");
+ continue;
+ }
+
if(unit<MAX_I2OB<<4)
{
/*
@@ -917,42 +1229,186 @@
struct i2ob_device *dev=&i2ob_dev[unit];
dev->i2odev = d;
dev->controller = c;
- dev->tid = d->lct_data->tid;
+ dev->unit = c->unit;
+ dev->tid = d->lct_data.tid;
- /*
- * Insure the device can be claimed
- * before installing it.
- */
- if(i2o_claim_device(dev->i2odev, &i2o_block_handler, I2O_CLAIM_PRIMARY )==0)
- {
- printk(KERN_INFO "Claimed Dev %p 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(i2o_release_device(dev->i2odev, &i2o_block_handler, I2O_CLAIM_PRIMARY))
- printk(KERN_INFO "Could not unclaim Dev %p Tid %d\n",dev,dev->tid);
- }
- else
- printk(KERN_INFO "TID %d not claimed\n",dev->tid);
+ if(i2ob_install_device(c,d,unit))
+ printk(KERN_WARNING "Could not install I2O block device\n");
+ else
+ {
+ unit+=16;
+ i2ob_dev_count++;
+
+ /* We want to know when device goes away */
+ i2o_device_notify_on(d, &i2o_block_handler);
+ }
}
else
{
- if(!warned++)
- printk("i2o_block: too many device, registering only %d.\n", unit>>4);
+ if(!warned++)
+ printk(KERN_WARNING "i2o_block: too many device, registering only %d.\n", unit>>4);
}
+ i2o_release_device(d, &i2o_block_handler);
}
i2o_unlock_controller(c);
}
- i2ob_devices = unit;
}
/*
+ * New device notification handler. Called whenever a new
+ * I2O block storage device is added to the system.
+ *
+ * Should we spin lock around this to keep multiple devs from
+ * getting updated at the same time?
+ *
+ */
+
+void i2ob_new_device(struct i2o_controller *c, struct i2o_device *d)
+{
+ struct i2ob_device *dev;
+ int unit = 0;
+
+ printk(KERN_INFO "i2o_block: New device detected\n");
+ printk(KERN_INFO " Controller %d Tid %d\n",c->unit, d->lct_data.tid);
+
+ /* Check for available space */
+ if(i2ob_dev_count>=MAX_I2OB<<4)
+ {
+ printk(KERN_ERR "i2o_block: No more devices allowed!\n");
+ return;
+ }
+ for(unit = 0; unit < (MAX_I2OB<<4); unit += 16)
+ {
+ if(!i2ob_dev[unit].i2odev)
+ break;
+ }
+
+ /*
+ * Creating a RAID 5 volume takes a little while and the UTIL_CLAIM
+ * will fail if we don't give the card enough time to do it's magic,
+ * so we just sleep for a little while and let it do it's thing
+ */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(5*HZ);
+
+ if(i2o_claim_device(d, &i2o_block_handler))
+ {
+ printk(KERN_INFO
+ "i2o_block: Unable to claim device. Installation aborted\n");
+ return;
+ }
+
+ dev = &i2ob_dev[unit];
+ dev->i2odev = d;
+ dev->controller = c;
+ dev->tid = d->lct_data.tid;
+
+ if(i2ob_install_device(c,d,unit))
+ printk(KERN_ERR "i2o_block: Could not install new device\n");
+ else
+ {
+ i2ob_dev_count++;
+ i2o_device_notify_on(d, &i2o_block_handler);
+ }
+
+ i2o_release_device(d, &i2o_block_handler);
+
+ return;
+}
+
+/*
+ * Deleted device notification handler. Called when a device we
+ * are talking to has been deleted by the user or some other
+ * mysterious fource outside the kernel.
+ */
+void i2ob_del_device(struct i2o_controller *c, struct i2o_device *d)
+{
+ int unit = 0;
+ int i = 0;
+ int flags;
+
+ spin_lock_irqsave(&io_request_lock, flags);
+
+ /*
+ * Need to do this...we somtimes get two events from the IRTOS
+ * in a row and that causes lots of problems.
+ */
+
+ i2o_device_notify_off(d, &i2o_block_handler);
+
+ printk(KERN_INFO "I2O Block Device Deleted\n");
+
+ for(unit = 0; unit < MAX_I2OB<<4; unit += 16)
+ {
+ if(i2ob_dev[unit].i2odev == d)
+ {
+ printk(KERN_INFO " /dev/%s: Controller %d Tid %d\n",
+ d->dev_name, c->unit, d->lct_data.tid);
+ break;
+ }
+ }
+ if(unit >= MAX_I2OB<<4)
+ {
+ printk(KERN_ERR "i2ob_del_device called, but not in dev table!\n");
+ return;
+ }
+
+ for(i = unit; i <= unit+15; i++)
+ {
+ i2ob_dev[i].i2odev = NULL;
+ i2ob_sizes[i] = 0;
+ i2ob_hardsizes[i] = 0;
+ i2ob_max_sectors[i] = 0;
+ i2ob[i].nr_sects = 0;
+ i2ob_gendisk.part[i].nr_sects = 0;
+ }
+ spin_unlock_irqrestore(&io_request_lock, flags);
+
+ /*
+ * Sync the device...this will force all outstanding I/Os
+ * to attempt to complete, thus causing error messages.
+ * We have to do this as the user could immediatelly create
+ * a new volume that gets assigned the same minor number.
+ * If there are still outstanding writes to the device,
+ * that could cause data corruption on the new volume!
+ *
+ * The truth is that deleting a volume that you are currently
+ * accessing will do _bad things_ to your system. This
+ * handler will keep it from crashing, but must probably
+ * you'll have to do a 'reboot' to get the system running
+ * properly. Deleting disks you are using is dumb.
+ * Umount them first and all will be good!
+ *
+ * It's not this driver's job to protect the system from
+ * dumb user mistakes :)
+ */
+ if(i2ob_dev[unit].refcnt)
+ fsync_dev(MKDEV(MAJOR_NR,unit));
+
+ /*
+ * Decrease usage count for module
+ */
+ while(i2ob_dev[unit].refcnt--)
+ MOD_DEC_USE_COUNT;
+
+ i2ob_dev[unit].refcnt = 0;
+
+ i2ob_dev[i].tid = 0;
+
+ /*
+ * Do we need this?
+ * The media didn't really change...the device is just gone
+ */
+ i2ob_media_change_flag[unit] = 1;
+
+ i2ob_dev_count--;
+
+ return;
+}
+
+
+
+/*
* Have we seen a media change ?
*/
@@ -973,12 +1429,10 @@
return do_i2ob_revalidate(dev, 0);
}
-static int i2ob_reboot_event(struct notifier_block *n, unsigned long code, void *p)
+static void i2ob_reboot_event(void)
{
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)];
@@ -1008,16 +1462,8 @@
i2o_post_wait(dev->controller, msg, 20, 2);
}
}
- return NOTIFY_DONE;
}
-struct notifier_block i2ob_reboot_notifier =
-{
- i2ob_reboot_event,
- NULL,
- 0
-};
-
static struct file_operations i2ob_fops =
{
NULL, /* lseek - default */
@@ -1070,19 +1516,19 @@
{
int i;
- printk(KERN_INFO "I2O Block Storage OSM v0.07. (C) 1999 Red Hat Software.\n");
+ printk(KERN_INFO "I2O Block Storage OSM v0.9. (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);
+ printk(KERN_ERR "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);
+ printk(KERN_INFO "i2o_block: registered device at major %d\n", MAJOR_NR);
#endif
/*
@@ -1142,13 +1588,25 @@
}
i2ob_context = i2o_block_handler.context;
+ /*
+ * Initialize event handling thread
+ */
+ sema_init(&i2ob_evt_sem, 0);
+ evt_pid = kernel_thread(i2ob_evt, NULL, CLONE_SIGHAND);
+ if(evt_pid < 0)
+ {
+ printk(KERN_ERR
+ "i2o_block: Could not initialize event thread. Aborting\n");
+ i2o_remove_handler(&i2o_block_handler);
+ return 0;
+ }
+
+
/*
* Finally see what is actually plugged in to our controllers
*/
-
i2ob_probe();
- register_reboot_notifier(&i2ob_reboot_notifier);
return 0;
}
@@ -1161,9 +1619,22 @@
void cleanup_module(void)
{
struct gendisk **gdp;
+ int i;
- unregister_reboot_notifier(&i2ob_reboot_notifier);
-
+ /*
+ * Unregister for updates from any devices..otherwise we still
+ * get them and the core jumps to random memory :O
+ */
+ if(i2ob_dev_count) {
+ struct i2o_device *d;
+ for(i = 0; i < MAX_I2OB; i++)
+ if((d=i2ob_dev[i<<4].i2odev)) {
+ i2o_device_notify_off(d, &i2o_block_handler);
+ i2o_event_register(d->controller, d->lct_data.tid,
+ i2ob_context, i<<4, 0);
+ }
+ }
+
/*
* Flush the OSM
*/
@@ -1175,6 +1646,21 @@
*/
if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0)
printk("i2o_block: cleanup_module failed\n");
+
+ if(evt_running) {
+ i = kill_proc(evt_pid, SIGTERM, 1);
+ if(!i) {
+ int count = 5 * 100;
+ while(evt_running && --count) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+ }
+
+ if(!count)
+ printk(KERN_ERR "Giving up on i2oblock thread...\n");
+ }
+
+}
/*
* Why isnt register/unregister gendisk in the kernel ???
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)