Add Poll_wait mechanism to SAS-2 MegaRAID SAS Linux driver. Driver will wakeup poll after the driver get event from MegaRAID SAS FW. Driver also reregisters for Event with the FW when it receives AEN. Signed-off-by Bo Yang<bo.yang@xxxxxxx> --- drivers/scsi/megaraid/megaraid_sas.c | 136 ++++++++++++++++++++++++++++++++++- drivers/scsi/megaraid/megaraid_sas.h | 18 ++++ 2 files changed, 153 insertions(+), 1 deletion(-) diff -rupN linux-2.6.28_orig/drivers/scsi/megaraid/megaraid_sas.c linux-2.6.28_new/drivers/scsi/megaraid/megaraid_sas.c --- linux-2.6.28_orig/drivers/scsi/megaraid/megaraid_sas.c 2009-05-04 20:10:20.000000000 -0400 +++ linux-2.6.28_new/drivers/scsi/megaraid/megaraid_sas.c 2009-05-04 20:11:52.000000000 -0400 @@ -40,6 +40,7 @@ #include <linux/compat.h> #include <linux/blkdev.h> #include <linux/mutex.h> +#include <linux/poll.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -89,6 +90,9 @@ static struct megasas_mgmt_info megasas_ static struct fasync_struct *megasas_async_queue; static DEFINE_MUTEX(megasas_async_queue_mutex); +static int megasas_poll_wait_aen; +static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait); + static u32 megasas_dbg_lvl; static void @@ -1277,6 +1281,8 @@ megasas_bios_param(struct scsi_device *s return 0; } +static void megasas_aen_polling(struct megasas_instance *instance); + /** * megasas_service_aen - Processes an event notification * @instance: Adapter soft state @@ -1295,13 +1301,20 @@ megasas_service_aen(struct megasas_insta /* * Don't signal app if it is just an aborted previously registered aen */ - if (!cmd->abort_aen) + if ((!cmd->abort_aen) && (instance->unload == 0)) { + megasas_poll_wait_aen = 1; + wake_up(&megasas_poll_wait); kill_fasync(&megasas_async_queue, SIGIO, POLL_IN); + } else cmd->abort_aen = 0; instance->aen_cmd = NULL; megasas_return_cmd(instance, cmd); + + if ((!cmd->abort_aen) && (instance->unload == 0)) { + megasas_aen_polling(instance); + } } /* @@ -2583,6 +2596,8 @@ megasas_probe_one(struct pci_dev *pdev, *instance->producer = 0; *instance->consumer = 0; + megasas_poll_wait_aen = 0; + instance->hotplug_wk = NULL; instance->evt_detail = pci_alloc_consistent(pdev, sizeof(struct @@ -2621,6 +2636,7 @@ megasas_probe_one(struct pci_dev *pdev, megasas_dbg_lvl = 0; instance->flag = 0; + instance->unload = 0; instance->last_time = 0; /* @@ -2924,6 +2940,7 @@ static void __devexit megasas_detach_one struct megasas_instance *instance; instance = pci_get_drvdata(pdev); + instance->unload = 1; host = instance->host; if (poll_mode_io) @@ -2932,6 +2949,15 @@ static void __devexit megasas_detach_one scsi_remove_host(instance->host); megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); + + /* + * cancel the delayed work if this work still in queue + */ + if (instance->hotplug_wk != NULL) { + cancel_delayed_work( + (struct delayed_work *)instance->hotplug_wk); + flush_scheduled_work(); + } tasklet_kill(&instance->isr_tasklet); /* @@ -3027,6 +3053,19 @@ static int megasas_mgmt_fasync(int fd, s } /** + * megasas_mgmt_poll - char node "poll" entry point + * */ +static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait) +{ + unsigned int mask = 0; + poll_wait(file, &megasas_poll_wait, wait); + if (megasas_poll_wait_aen) { + mask |= (POLLIN | POLLRDNORM); + } + return mask; +} + +/** * megasas_mgmt_fw_ioctl - Issues management ioctls to FW * @instance: Adapter soft state * @argp: User's ioctl packet @@ -3348,6 +3387,7 @@ static const struct file_operations mega .open = megasas_mgmt_open, .fasync = megasas_mgmt_fasync, .unlocked_ioctl = megasas_mgmt_ioctl, + .poll = megasas_mgmt_poll, #ifdef CONFIG_COMPAT .compat_ioctl = megasas_mgmt_compat_ioctl, #endif @@ -3462,6 +3502,100 @@ out: return retval; } +static void +megasas_scsi_scan_host(struct work_struct *work) +{ + struct megasas_aen_event *ev = container_of(work, + struct megasas_aen_event, hotplug_work); + struct megasas_instance *instance = ev->instance; + + instance->hotplug_wk = NULL; + if (!instance) { + printk(KERN_ERR "%s: invalid instance!\n", __FUNCTION__); + kfree(ev); + return; + } + + printk(KERN_INFO "%s[%d]: scanning ...\n", + __FUNCTION__, instance->host->host_no); + scsi_scan_host(instance->host); + kfree(ev); +} + +static void +megasas_aen_polling(struct megasas_instance *instance) +{ + union megasas_evt_class_locale class_locale; + int doscan = 0; + u32 seq_num; + int error; + + if (instance->evt_detail) { + printk(KERN_INFO "%s[%d]: event code 0x%04x\n", __FUNCTION__, + instance->host->host_no, instance->evt_detail->code); + + switch (instance->evt_detail->code) { + + case MR_EVT_LD_CREATED: + case MR_EVT_PD_INSERTED: + case MR_EVT_LD_DELETED: + case MR_EVT_LD_OFFLINE: + case MR_EVT_CFG_CLEARED: + case MR_EVT_PD_REMOVED: + case MR_EVT_FOREIGN_CFG_IMPORTED: + case MR_EVT_LD_STATE_CHANGE: + case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED: + doscan = 1; + break; + default: + doscan = 0; + break; + } + } else { + printk(KERN_ERR "%s[%d]: invalid evt_detail!\n", + __FUNCTION__, instance->host->host_no); + return; + } + + if (doscan) { + struct megasas_aen_event *ev; + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) { + printk(KERN_ERR "%s: out of memory\n", __FUNCTION__); + } else { + ev->instance = instance; + instance->hotplug_wk = &ev->hotplug_work; + INIT_WORK(&ev->hotplug_work, megasas_scsi_scan_host); + schedule_delayed_work( + (struct delayed_work *)&ev->hotplug_work, 0); + } + } + + seq_num = instance->evt_detail->seq_num + 1; + + /** + * Register AEN with FW for latest sequence number plus 1 + **/ + + class_locale.members.reserved = 0; + class_locale.members.locale = MR_EVT_LOCALE_ALL; + class_locale.members.class = MR_EVT_CLASS_DEBUG; + + if ( instance->aen_cmd != NULL ) { + return ; + } + + mutex_lock(&instance->aen_mutex); + error = megasas_register_aen(instance, seq_num, + class_locale.word); + mutex_unlock(&instance->aen_mutex); + + if (error) + printk(KERN_ERR "%s[%d]: register aen failed error %x\n", + __FUNCTION__, instance->host->host_no, error); +} + + static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUGO, megasas_sysfs_show_poll_mode_io, megasas_sysfs_set_poll_mode_io); diff -rupN linux-2.6.28_orig/drivers/scsi/megaraid/megaraid_sas.h linux-2.6.28_new/drivers/scsi/megaraid/megaraid_sas.h --- linux-2.6.28_orig/drivers/scsi/megaraid/megaraid_sas.h 2009-05-04 20:10:21.000000000 -0400 +++ linux-2.6.28_new/drivers/scsi/megaraid/megaraid_sas.h 2009-05-04 20:00:31.000000000 -0400 @@ -132,6 +132,17 @@ #define MR_DCMD_CLUSTER_RESET_ALL 0x08010100 #define MR_DCMD_CLUSTER_RESET_LD 0x08010200 +#define MR_EVT_CFG_CLEARED 0x0004 + +#define MR_EVT_LD_STATE_CHANGE 0x0051 +#define MR_EVT_PD_INSERTED 0x005b +#define MR_EVT_PD_REMOVED 0x0070 +#define MR_EVT_LD_CREATED 0x008a +#define MR_EVT_LD_DELETED 0x008b +#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db +#define MR_EVT_LD_OFFLINE 0x00fc +#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152 + /* * MFI command completion codes */ @@ -1072,6 +1083,11 @@ struct megasas_evt_detail { u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *); }; +struct megasas_aen_event { + struct work_struct hotplug_work; + struct megasas_instance *instance; +}; + struct megasas_instance { u32 *producer; @@ -1118,8 +1134,10 @@ struct megasas_instance { struct megasas_instance_template *instancet; struct tasklet_struct isr_tasklet; + struct work_struct *hotplug_wk; u8 flag; + u8 unload; unsigned long last_time; struct timer_list io_completion_timer; -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html