Commit b590b47b authored by Andrew Morton's avatar Andrew Morton Committed by James Bottomley

[PATCH] I2O: remove on-demand allocation of Scsi_Host's in

From: Markus Lidel <Markus.Lidel@shadowconnect.com>

- New notification system for i2o_driver's which get now a notification if a
  I2O controller is added or removed.

- SCSI-OSM now uses notifications to create the Scsi_Host to the
  corresponding I2O controller.

- Use __scsi_add_device to preset hostdata.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent ff8da0ac
......@@ -72,6 +72,7 @@ struct bus_type i2o_bus_type = {
*/
int i2o_driver_register(struct i2o_driver *drv)
{
struct i2o_controller *c;
int i;
int rc = 0;
unsigned long flags;
......@@ -109,6 +110,9 @@ int i2o_driver_register(struct i2o_driver *drv)
pr_debug("driver %s gets context id %d\n", drv->name, drv->context);
list_for_each_entry(c, &i2o_controllers, list)
i2o_driver_notify(drv, I2O_DRIVER_NOTIFY_CONTROLLER_ADD, c);
rc = driver_register(&drv->driver);
if (rc)
destroy_workqueue(drv->event_queue);
......@@ -125,12 +129,16 @@ int i2o_driver_register(struct i2o_driver *drv)
*/
void i2o_driver_unregister(struct i2o_driver *drv)
{
struct i2o_controller *c;
unsigned long flags;
pr_debug("unregister driver %s\n", drv->name);
driver_unregister(&drv->driver);
list_for_each_entry(c, &i2o_controllers, list)
i2o_driver_notify(drv, I2O_DRIVER_NOTIFY_CONTROLLER_REMOVE, c);
spin_lock_irqsave(&i2o_drivers_lock, flags);
i2o_drivers[drv->context] = NULL;
spin_unlock_irqrestore(&i2o_drivers_lock, flags);
......@@ -219,6 +227,23 @@ int i2o_driver_dispatch(struct i2o_controller *c, u32 m,
return -EIO;
}
/**
* i2o_driver_notify_all - Send notification to all I2O drivers
*
* Send notifications to all registered drivers.
*/
void i2o_driver_notify_all(enum i2o_driver_notify evt, void *data) {
int i;
struct i2o_driver *drv;
for(i = 0; i < I2O_MAX_DRIVERS; i ++) {
drv = i2o_drivers[i];
if(drv)
i2o_driver_notify(drv, evt, data);
}
}
/**
* i2o_driver_init - initialize I2O drivers (OSMs)
*
......@@ -267,3 +292,4 @@ void __exit i2o_driver_exit(void)
EXPORT_SYMBOL(i2o_driver_register);
EXPORT_SYMBOL(i2o_driver_unregister);
EXPORT_SYMBOL(i2o_driver_notify_all);
......@@ -67,13 +67,12 @@
#define VERSION_STRING "Version 0.1.2"
static struct i2o_driver i2o_scsi_driver;
static int i2o_scsi_max_id = 16;
static int i2o_scsi_max_lun = 8;
static LIST_HEAD(i2o_scsi_hosts);
struct i2o_scsi_host {
struct list_head list; /* node in in i2o_scsi_hosts */
struct Scsi_Host *scsi_host; /* pointer to the SCSI host */
struct i2o_controller *iop; /* pointer to the I2O controller */
struct i2o_device *channel[0]; /* channel->i2o_dev mapping table */
......@@ -81,19 +80,6 @@ struct i2o_scsi_host {
static struct scsi_host_template i2o_scsi_host_template;
/*
* This is only needed, because we can only set the hostdata after the device is
* added to the scsi core. So we need this little workaround.
*/
static DECLARE_MUTEX(i2o_scsi_probe_lock);
static struct i2o_device *i2o_scsi_probe_dev = NULL;
static int i2o_scsi_slave_alloc(struct scsi_device *sdp)
{
sdp->hostdata = i2o_scsi_probe_dev;
return 0;
};
#define I2O_SCSI_CAN_QUEUE 4
/* SCSI OSM class handling definition */
......@@ -169,37 +155,11 @@ static struct i2o_scsi_host *i2o_scsi_host_alloc(struct i2o_controller *c)
* is returned, otherwise the I2O controller is added to the SCSI
* core.
*
* Returns pointer to the I2O SCSI host on success or negative error code
* on failure.
* Returns pointer to the I2O SCSI host on success or NULL on failure.
*/
static struct i2o_scsi_host *i2o_scsi_get_host(struct i2o_controller *c)
{
struct i2o_scsi_host *i2o_shost;
int rc;
/* skip if already registered as I2O SCSI host */
list_for_each_entry(i2o_shost, &i2o_scsi_hosts, list)
if (i2o_shost->iop == c)
return i2o_shost;
i2o_shost = i2o_scsi_host_alloc(c);
if (IS_ERR(i2o_shost)) {
printk(KERN_ERR "scsi-osm: Could not initialize SCSI host\n");
return i2o_shost;
}
rc = scsi_add_host(i2o_shost->scsi_host, &c->device);
if (rc) {
printk(KERN_ERR "scsi-osm: Could not add SCSI host\n");
scsi_host_put(i2o_shost->scsi_host);
return ERR_PTR(rc);
}
list_add(&i2o_shost->list, &i2o_scsi_hosts);
pr_debug("new I2O SCSI host added\n");
return i2o_shost;
return c->driver_data[i2o_scsi_driver.context];
};
/**
......@@ -252,8 +212,8 @@ static int i2o_scsi_probe(struct device *dev)
int i;
i2o_shost = i2o_scsi_get_host(c);
if (IS_ERR(i2o_shost))
return PTR_ERR(i2o_shost);
if (!i2o_shost)
return -EFAULT;
scsi_host = i2o_shost->scsi_host;
......@@ -292,11 +252,8 @@ static int i2o_scsi_probe(struct device *dev)
return -EFAULT;
}
down_interruptible(&i2o_scsi_probe_lock);
i2o_scsi_probe_dev = i2o_dev;
scsi_dev = scsi_add_device(i2o_shost->scsi_host, channel, id, lun);
i2o_scsi_probe_dev = NULL;
up(&i2o_scsi_probe_lock);
scsi_dev =
__scsi_add_device(i2o_shost->scsi_host, channel, id, lun, i2o_dev);
if (!scsi_dev) {
printk(KERN_WARNING "scsi-osm: can not add SCSI device "
......@@ -536,13 +493,67 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m,
cmd->request_bufflen, cmd->sc_data_direction);
return 1;
}
};
/**
* i2o_scsi_notify - Retrieve notifications of controller added or removed
* @notify: the notification event which occurs
* @data: pointer to additional data
*
* If a I2O controller is added, we catch the notification to add a
* corresponding Scsi_Host. On removal also remove the Scsi_Host.
*/
void i2o_scsi_notify(enum i2o_driver_notify notify, void *data)
{
struct i2o_controller *c = data;
struct i2o_scsi_host *i2o_shost;
int rc;
switch (notify) {
case I2O_DRIVER_NOTIFY_CONTROLLER_ADD:
i2o_shost = i2o_scsi_host_alloc(c);
if (IS_ERR(i2o_shost)) {
printk(KERN_ERR "scsi-osm: Could not initialize"
" SCSI host\n");
return;
}
rc = scsi_add_host(i2o_shost->scsi_host, &c->device);
if (rc) {
printk(KERN_ERR "scsi-osm: Could not add SCSI "
"host\n");
scsi_host_put(i2o_shost->scsi_host);
return;
}
c->driver_data[i2o_scsi_driver.context] = i2o_shost;
pr_debug("new I2O SCSI host added\n");
break;
case I2O_DRIVER_NOTIFY_CONTROLLER_REMOVE:
i2o_shost = i2o_scsi_get_host(c);
if (!i2o_shost)
return;
c->driver_data[i2o_scsi_driver.context] = NULL;
scsi_remove_host(i2o_shost->scsi_host);
scsi_host_put(i2o_shost->scsi_host);
pr_debug("I2O SCSI host removed\n");
break;
default:
break;
}
};
/* SCSI OSM driver struct */
static struct i2o_driver i2o_scsi_driver = {
.name = "scsi-osm",
.reply = i2o_scsi_reply,
.classes = i2o_scsi_class_id,
.notify = i2o_scsi_notify,
.driver = {
.probe = i2o_scsi_probe,
.remove = i2o_scsi_remove,
......@@ -736,54 +747,47 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt,
return 0;
};
#if 0
FIXME
/**
* i2o_scsi_abort - abort a running command
* i2o_scsi_abort - abort a running command
* @SCpnt: command to abort
*
* Ask the I2O controller to abort a command. This is an asynchrnous
* process and our callback handler will see the command complete
* with an aborted message if it succeeds.
* process and our callback handler will see the command complete with an
* aborted message if it succeeds.
*
* Locks: no locks are held or needed
* Returns 0 if the command is successfully aborted or negative error code
* on failure.
*/
int i2o_scsi_abort(struct scsi_cmnd *SCpnt)
{
struct i2o_device *i2o_dev;
struct i2o_controller *c;
struct Scsi_Host *host;
struct i2o_scsi_host *hostdata;
u32 msg[5];
struct i2o_message *msg;
u32 m;
int tid;
int status = FAILED;
printk(KERN_WARNING "i2o_scsi: Aborting command block.\n");
host = SCpnt->device->host;
hostdata = (struct i2o_scsi_host *)host->hostdata;
tid = hostdata->task[SCpnt->device->id][SCpnt->device->lun];
if (tid == -1) {
printk(KERN_ERR "i2o_scsi: Impossible command to abort!\n");
return status;
}
c = hostdata->controller;
i2o_dev = SCpnt->device->hostdata;
c = i2o_dev->iop;
tid = i2o_dev->lct_data.tid;
m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
if (m == I2O_QUEUE_EMPTY)
return SCSI_MLQUEUE_HOST_BUSY;
spin_unlock_irq(host->host_lock);
writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
writel(I2O_CMD_SCSI_ABORT << 24 | HOST_TID << 12 | tid,
&msg->u.head[1]);
writel(i2o_cntxt_list_get_ptr(c, SCpnt), &msg->body[0]);
msg[0] = FIVE_WORD_MSG_SIZE;
msg[1] = I2O_CMD_SCSI_ABORT << 24 | HOST_TID << 12 | tid;
msg[2] = scsi_context;
msg[3] = 0;
msg[4] = i2o_context_list_remove(SCpnt, c);
if (i2o_post_wait(c, msg, sizeof(msg), 240))
if (i2o_msg_post_wait(c, m, I2O_TIMEOUT_SCSI_SCB_ABORT))
status = SUCCESS;
spin_lock_irq(host->host_lock);
return status;
}
#endif
/**
* i2o_scsi_bios_param - Invent disk geometry
* @sdev: scsi device
......@@ -817,15 +821,12 @@ static struct scsi_host_template i2o_scsi_host_template = {
.name = "I2O SCSI Peripheral OSM",
.info = i2o_scsi_info,
.queuecommand = i2o_scsi_queuecommand,
/*
.eh_abort_handler = i2o_scsi_abort,
*/
.eh_abort_handler = i2o_scsi_abort,
.bios_param = i2o_scsi_bios_param,
.can_queue = I2O_SCSI_CAN_QUEUE,
.sg_tablesize = 8,
.cmd_per_lun = 6,
.use_clustering = ENABLE_CLUSTERING,
.slave_alloc = i2o_scsi_slave_alloc,
};
/*
......@@ -867,14 +868,6 @@ static int __init i2o_scsi_init(void)
*/
static void __exit i2o_scsi_exit(void)
{
struct i2o_scsi_host *i2o_shost, *tmp;
/* Remove I2O SCSI hosts */
list_for_each_entry_safe(i2o_shost, tmp, &i2o_scsi_hosts, list) {
scsi_remove_host(i2o_shost->scsi_host);
scsi_host_put(i2o_shost->scsi_host);
}
/* Unregister I2O SCSI OSM from I2O core */
i2o_driver_unregister(&i2o_scsi_driver);
};
......
......@@ -108,8 +108,8 @@ u32 i2o_msg_get_wait(struct i2o_controller *c, struct i2o_message **msg,
#if BITS_PER_LONG == 64
/**
* i2o_cntxt_list_add - Append a pointer to context list and return a id
* @ptr: pointer to add to the context list
* @c: controller to which the context list belong
* @ptr: pointer to add to the context list
*
* Because the context field in I2O is only 32-bit large, on 64-bit the
* pointer is to large to fit in the context field. The i2o_cntxt_list
......@@ -117,7 +117,7 @@ u32 i2o_msg_get_wait(struct i2o_controller *c, struct i2o_message **msg,
*
* Returns context id > 0 on success or 0 on failure.
*/
u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr)
u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr)
{
struct i2o_context_list_element *entry;
unsigned long flags;
......@@ -154,15 +154,15 @@ u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr)
/**
* i2o_cntxt_list_remove - Remove a pointer from the context list
* @ptr: pointer which should be removed from the context list
* @c: controller to which the context list belong
* @ptr: pointer which should be removed from the context list
*
* Removes a previously added pointer from the context list and returns
* the matching context id.
*
* Returns context id on succes or 0 on failure.
*/
u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr)
u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr)
{
struct i2o_context_list_element *entry;
u32 context = 0;
......@@ -189,9 +189,11 @@ u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr)
/**
* i2o_cntxt_list_get - Get a pointer from the context list and remove it
* @context: context id to which the pointer belong
* @c: controller to which the context list belong
* returns pointer to the matching context id
* @context: context id to which the pointer belong
*
* Returns pointer to the matching context id on success or NULL on
* failure.
*/
void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
{
......@@ -216,6 +218,37 @@ void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
return ptr;
};
/**
* i2o_cntxt_list_get_ptr - Get a context id from the context list
* @c: controller to which the context list belong
* @ptr: pointer to which the context id should be fetched
*
* Returns context id which matches to the pointer on succes or 0 on
* failure.
*/
u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr)
{
struct i2o_context_list_element *entry;
u32 context = 0;
unsigned long flags;
spin_lock_irqsave(&c->context_list_lock, flags);
list_for_each_entry(entry, &c->context_list, list)
if (entry->ptr == ptr) {
context = entry->context;
break;
}
spin_unlock_irqrestore(&c->context_list_lock, flags);
if (!context)
printk(KERN_WARNING "i2o: Could not find nonexistent ptr "
"%p\n", ptr);
pr_debug("get context id from context list %p -> %d\n", ptr, context);
return context;
};
#endif
/**
......@@ -782,6 +815,8 @@ void i2o_iop_remove(struct i2o_controller *c)
pr_debug("Deleting controller %s\n", c->name);
i2o_driver_notify_all(I2O_DRIVER_NOTIFY_CONTROLLER_REMOVE, c);
list_del(&c->list);
list_for_each_entry_safe(dev, tmp, &c->devices, list)
......@@ -1098,6 +1133,8 @@ int i2o_iop_add(struct i2o_controller *c)
list_add(&c->list, &i2o_controllers);
i2o_driver_notify_all(I2O_DRIVER_NOTIFY_CONTROLLER_ADD, c);
printk(KERN_INFO "%s: Controller added\n", c->name);
return 0;
......@@ -1209,6 +1246,7 @@ MODULE_LICENSE("GPL");
EXPORT_SYMBOL(i2o_cntxt_list_add);
EXPORT_SYMBOL(i2o_cntxt_list_get);
EXPORT_SYMBOL(i2o_cntxt_list_remove);
EXPORT_SYMBOL(i2o_cntxt_list_get_ptr);
#endif
EXPORT_SYMBOL(i2o_msg_get_wait);
EXPORT_SYMBOL(i2o_msg_nop);
......
......@@ -34,6 +34,10 @@
/* message queue empty */
#define I2O_QUEUE_EMPTY 0xffffffff
enum i2o_driver_notify {
I2O_DRIVER_NOTIFY_CONTROLLER_ADD = 0,
I2O_DRIVER_NOTIFY_CONTROLLER_REMOVE = 1,
};
/*
* Message structures
......@@ -113,6 +117,9 @@ struct i2o_driver {
struct device_driver driver;
/* notification of changes */
void (*notify)(enum i2o_driver_notify, void *);
struct semaphore lock;
};
......@@ -201,6 +208,8 @@ struct i2o_controller
#endif
spinlock_t lock; /* lock for controller
configuration */
void *driver_data[I2O_MAX_DRIVERS]; /* storage for drivers */
};
/*
......@@ -275,6 +284,7 @@ extern struct i2o_controller *i2o_find_iop(int);
extern u32 i2o_cntxt_list_add(struct i2o_controller *, void *);
extern void *i2o_cntxt_list_get(struct i2o_controller *, u32);
extern u32 i2o_cntxt_list_remove(struct i2o_controller *, void *);
extern u32 i2o_cntxt_list_get_ptr(struct i2o_controller *, void *);
static inline u32 i2o_ptr_low(void *ptr)
{
......@@ -301,6 +311,11 @@ static inline u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr)
return (u32)ptr;
};
static inline u32 i2o_cntxt_list_get_ptr(struct i2o_controller *c, void *ptr)
{
return (u32)ptr;
};
static inline u32 i2o_ptr_low(void *ptr)
{
return (u32)ptr;
......@@ -316,6 +331,19 @@ static inline u32 i2o_ptr_high(void *ptr)
extern int i2o_driver_register(struct i2o_driver *);
extern void i2o_driver_unregister(struct i2o_driver *);
/**
* i2o_driver_notify - Send notification to a single I2O drivers
*
* Send notifications to a single registered driver.
*/
static inline void i2o_driver_notify(struct i2o_driver *drv,
enum i2o_driver_notify notify, void *data) {
if(drv->notify)
drv->notify(notify, data);
}
extern void i2o_driver_notify_all(enum i2o_driver_notify, void *);
/* I2O device functions */
extern int i2o_device_claim(struct i2o_device *);
extern int i2o_device_claim_release(struct i2o_device *);
......@@ -890,6 +918,7 @@ extern void i2o_debug_state(struct i2o_controller *c);
#define I2O_TIMEOUT_RESET 30
#define I2O_TIMEOUT_STATUS_GET 5
#define I2O_TIMEOUT_LCT_GET 20
#define I2O_TIMEOUT_SCSI_SCB_ABORT 240
/* retries */
#define I2O_HRT_GET_TRIES 3
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment