From: Markus Lidel - 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: Andrew Morton --- 25-akpm/drivers/message/i2o/driver.c | 26 ++++ 25-akpm/drivers/message/i2o/i2o_scsi.c | 179 +++++++++++++++------------------ 25-akpm/drivers/message/i2o/iop.c | 50 ++++++++- 25-akpm/include/linux/i2o.h | 29 +++++ 4 files changed, 185 insertions(+), 99 deletions(-) diff -puN drivers/message/i2o/driver.c~i2o-remove-on-demand-allocation-of-scsi_hosts-in-i2o_scsi drivers/message/i2o/driver.c --- 25/drivers/message/i2o/driver.c~i2o-remove-on-demand-allocation-of-scsi_hosts-in-i2o_scsi Thu Aug 19 16:23:55 2004 +++ 25-akpm/drivers/message/i2o/driver.c Thu Aug 19 16:23:55 2004 @@ -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_drive 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_drive */ 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); @@ -220,6 +228,23 @@ int i2o_driver_dispatch(struct i2o_contr } /** + * 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) * * Registers the I2O bus and allocate memory for the array of 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); diff -puN drivers/message/i2o/i2o_scsi.c~i2o-remove-on-demand-allocation-of-scsi_hosts-in-i2o_scsi drivers/message/i2o/i2o_scsi.c --- 25/drivers/message/i2o/i2o_scsi.c~i2o-remove-on-demand-allocation-of-scsi_hosts-in-i2o_scsi Thu Aug 19 16:23:55 2004 +++ 25-akpm/drivers/message/i2o/i2o_scsi.c Thu Aug 19 16:23:55 2004 @@ -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_ho * 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 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 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_con 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 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; - - spin_unlock_irq(host->host_lock); - - 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)) + 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; + + 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]); + + 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_scs .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); }; diff -puN drivers/message/i2o/iop.c~i2o-remove-on-demand-allocation-of-scsi_hosts-in-i2o_scsi drivers/message/i2o/iop.c --- 25/drivers/message/i2o/iop.c~i2o-remove-on-demand-allocation-of-scsi_hosts-in-i2o_scsi Thu Aug 19 16:23:55 2004 +++ 25-akpm/drivers/message/i2o/iop.c Thu Aug 19 16:23:55 2004 @@ -108,8 +108,8 @@ u32 i2o_msg_get_wait(struct i2o_controll #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_controll * * 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_contro /** * 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_con /** * 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_cont 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_controlle 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); diff -puN include/linux/i2o.h~i2o-remove-on-demand-allocation-of-scsi_hosts-in-i2o_scsi include/linux/i2o.h --- 25/include/linux/i2o.h~i2o-remove-on-demand-allocation-of-scsi_hosts-in-i2o_scsi Thu Aug 19 16:23:55 2004 +++ 25-akpm/include/linux/i2o.h Thu Aug 19 16:23:55 2004 @@ -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_i 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( 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_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 _