*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2003-2005 LSI Corporation.
+ * Copyright (c) 2003-2005 LSI Logic Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 2 of the License, or (at your option) any later version.
*
* FILE : megaraid_sas.c
- * Version : v00.00.03.15-RH1
+ * Version : v00.00.03.21
*
* Authors:
* (email-id : megaraidlinux@lsi.com)
#include "megaraid_sas.h"
/*
- * Module parameters
+ * Modules parameters
*/
/*
- * poll_mode_io:1- schedule command completion from q cmd
+ * Fast driver load option, skip scanning for physical devices during load.
+ * This would result in physical devices being skipped during driver load
+ * time. These can be later added though, using /proc/scsi/scsi
+ */
+static unsigned int fast_load = 0;
+module_param_named(fast_load, fast_load, int, 0);
+MODULE_PARM_DESC(fast_load,
+ "megasas: Faster loading of the driver, skips physical devices! \
+ (default=0)");
+
+/*
+ * Number of sectors per IO command
+ * Will be set in megasas_init_mfi if user does not provide
+ */
+static unsigned int max_sectors = 0;
+module_param_named(max_sectors, max_sectors, int, 0);
+MODULE_PARM_DESC(max_sectors,
+ "Maximum number of sectors per IO command");
+
+/*
+ * Number of cmds per logical unit
+ */
+static unsigned int cmd_per_lun = MEGASAS_DEFAULT_CMD_PER_LUN;
+module_param_named(cmd_per_lun, cmd_per_lun, int, 0);
+MODULE_PARM_DESC(cmd_per_lun,
+ "Maximum number of commands per logical unit (default=128)");
+
+/*
+ * poll_mode_io:1- schedule complete completion from q cmd
*/
static unsigned int poll_mode_io;
module_param_named(poll_mode_io, poll_mode_io, int, 0);
MODULE_LICENSE("GPL");
MODULE_VERSION(MEGASAS_VERSION);
MODULE_AUTHOR("megaraidlinux@lsi.com");
-MODULE_DESCRIPTION("LSI MegaRAID SAS Driver");
+MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");
/*
* PCI ID table for all supported controllers
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064R)},
/* xscale IOP */
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078R)},
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078DE)},
/* ppc IOP */
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)},
/* xscale IOP, vega */
*/
writel(status, ®s->outbound_intr_status);
+ /* Dummy readl to force pci flush */
+ readl(®s->outbound_intr_status);
+
return 0;
}
}
/**
- * megasas_disable_intr_ppc - Disable interrupt
+ * megasas_disable_intr_ppc - Disables interrupt
* @regs: MFI register set
*/
static inline void
*/
writel(status, ®s->outbound_doorbell_clear);
+ /* Dummy readl to force pci flush */
+ readl(®s->outbound_doorbell_clear);
+
return 0;
}
/**
/**
* megasas_get_frame_count - Computes the number of frames
* @sge_count : number of sg elements
+ * @frame_type : type of frame- io or pthru frame
*
* Returns the number of frames required for numnber of sge's (sge_count)
*/
-static u32 megasas_get_frame_count(u8 sge_count)
+static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
{
int num_cnt;
int sge_bytes;
sizeof(struct megasas_sge32);
/*
- * Main frame can contain 2 SGEs for 64-bit SGLs and
- * 3 SGEs for 32-bit SGLs
- */
- if (IS_DMA64)
- num_cnt = sge_count - 2;
- else
- num_cnt = sge_count - 3;
+ * Main frame can contain 2 SGEs for 64-bit SGLs and
+ * 3 SGEs for 32-bit SGLs for ldio &
+ * 1 SGEs for 64-bit SGLs and
+ * 2 SGEs for 32-bit SGLs for pthru frame
+ */
+ if (unlikely(frame_type == PTHRU_FRAME)) {
+ if (IS_DMA64)
+ num_cnt = sge_count - 1;
+ else
+ num_cnt = sge_count - 2;
+ } else {
+ if (IS_DMA64)
+ num_cnt = sge_count - 2;
+ else
+ num_cnt = sge_count - 3;
+ }
if(num_cnt>0){
sge_bytes = sge_sz * num_cnt;
* Compute the total number of frames this command consumes. FW uses
* this number to pull sufficient number of frames from host memory.
*/
- cmd->frame_count = megasas_get_frame_count(pthru->sge_count);
+ cmd->frame_count = megasas_get_frame_count(pthru->sge_count,
+ PTHRU_FRAME);
return cmd->frame_count;
}
* Compute the total number of frames this command consumes. FW uses
* this number to pull sufficient number of frames from host memory.
*/
- cmd->frame_count = megasas_get_frame_count(ldio->sge_count);
+ cmd->frame_count = megasas_get_frame_count(ldio->sge_count, IO_FRAME);
return cmd->frame_count;
}
*/
scmd->result = DID_OK << 16;
goto out_done;
- default:
- break;
}
cmd = megasas_get_cmd(instance);
u32 consumer;
u32 context;
struct megasas_cmd *cmd;
- struct megasas_instance *instance =
- (struct megasas_instance *)instance_addr;
+ struct megasas_instance *instance = (struct megasas_instance *)instance_addr;
unsigned long flags;
/* If we have already declared adapter dead, donot complete cmds */
*instance->consumer = producer;
spin_unlock_irqrestore(&instance->completion_lock, flags);
-
- /*
- * Check if we can restore can_queue
- */
- if (instance->flag & MEGASAS_FW_BUSY
- && time_after(jiffies, instance->last_time + 5 * HZ)
- && atomic_read(&instance->fw_outstanding) < 17) {
-
- spin_lock_irqsave(instance->host->host_lock, flags);
- instance->flag &= ~MEGASAS_FW_BUSY;
- instance->host->can_queue =
- instance->max_fw_cmds - MEGASAS_INT_CMDS;
-
- spin_unlock_irqrestore(instance->host->host_lock, flags);
- }
-
}
/**
printk(KERN_NOTICE "megasas: [%2d]waiting for %d "
"commands to complete\n",i,outstanding);
/*
- * Call cmd completion routine. Cmd to be
+ * Call cmd completion routine. Cmd to be
* be completed directly without depending on isr.
*/
megasas_complete_cmd_dpc((unsigned long)instance);
return ret_val;
}
-/**
+ /**
* megasas_reset_timer - quiesce the adapter if required
* @scmd: scsi cmnd
*
megasas_return_cmd(instance, cmd);
}
+static struct megasas_instance *megasas_lookup_instance(u16 host_no)
+{
+ int i;
+
+ for (i = 0; i < megasas_mgmt_info.max_index; i++) {
+
+ if ((megasas_mgmt_info.instance[i]) &&
+ (megasas_mgmt_info.instance[i]->host->host_no == host_no))
+ return megasas_mgmt_info.instance[i];
+ }
+
+ return NULL;
+}
+
+static int megasas_slave_alloc(struct scsi_device *sdev) {
+ struct megasas_instance *instance ;
+ int tmp_fastload = fast_load;
+ instance = megasas_lookup_instance(sdev->host->host_no);
+
+ if (tmp_fastload && sdev->channel < MEGASAS_MAX_PD_CHANNELS) {
+ if ((sdev->id == MEGASAS_MAX_DEV_PER_CHANNEL -1) &&
+ (sdev->channel == MEGASAS_MAX_PD_CHANNELS - 1)) {
+ /* If fast load option was set and scan for last device is
+ * over, reset the fast_load flag so that during a possible
+ * next scan, devices can be made available
+ */
+ fast_load = 0;
+ }
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
/*
* Scsi host template for megaraid_sas driver
*/
static struct scsi_host_template megasas_template = {
.module = THIS_MODULE,
- .name = "LSI SAS based MegaRAID driver",
+ .name = "LSI Logic SAS based MegaRAID driver",
.proc_name = "megaraid_sas",
.slave_configure = megasas_slave_configure,
+ .slave_alloc = megasas_slave_alloc,
.queuecommand = megasas_queue_command,
.eh_device_reset_handler = megasas_reset_device,
.eh_bus_reset_handler = megasas_reset_bus_host,
.eh_host_reset_handler = megasas_reset_bus_host,
- .proc_info = (void *) RH_EXTENDED_MAGIC,
.eh_timed_out = megasas_reset_timer,
.bios_param = megasas_bios_param,
.use_clustering = ENABLE_CLUSTERING,
{
int exception = 0;
struct megasas_header *hdr = &cmd->frame->hdr;
+ unsigned long flags;
if (cmd->scmd)
cmd->scmd->SCp.ptr = NULL;
hdr->cmd);
break;
}
+
+ /*
+ * Check if we can restore can_queue
+ */
+ if (instance->flag & MEGASAS_FW_BUSY
+ && time_after(jiffies, instance->last_time + 5 * HZ)
+ && atomic_read(&instance->fw_outstanding) < 17) {
+
+ spin_lock_irqsave(instance->host->host_lock, flags);
+ instance->flag &= ~MEGASAS_FW_BUSY;
+ instance->host->can_queue =
+ instance->max_fw_cmds - MEGASAS_INT_CMDS;
+
+ spin_unlock_irqrestore(instance->host->host_lock, flags);
+ }
+
}
/**
/**
* megasas_isr - isr entry point
*/
-static irqreturn_t megasas_isr(int irq, void *devp, struct pt_regs *regs)
+static irqreturn_t megasas_isr(int irq, void *devp)
{
return megasas_deplete_reply_queue((struct megasas_instance *)devp,
DID_OK);
memset(init_frame, 0, MEGAMFI_FRAME_SIZE);
memset(initq_info, 0, sizeof(struct megasas_init_queue_info));
init_frame->context = context;
-
+
initq_info->reply_queue_entries = instance->max_fw_cmds + 1;
initq_info->reply_queue_start_phys_addr_lo = instance->reply_queue_h;
init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info);
/*
- * disable the intr before firing the init frame to FW
- */
+ * disable the intr before firing the init frame to FW
+ */
instance->instancet->disable_intr(instance->reg_set);
-
+
/*
* Issue the init frame in polled mode
*/
if (megasas_issue_polled(instance, cmd)) {
- printk(KERN_ERR "megasas: Failed to init firmware\n");
+ printk(KERN_DEBUG "megasas: Failed to init firmware\n");
megasas_return_cmd(instance, cmd);
goto fail_fw_init;
}
return 0;
-fail_fw_init:
- return -EINVAL;
+ fail_fw_init:
+ return -EINVAL;
}
/**
timer->data = (unsigned long)instance;
timer->function = fn;
add_timer(timer);
-}
+ }
/**
* megasas_io_completion_timer - Timer fn
* @instance_addr: Address of adapter soft state
*
- * Schedules tasklet for cmd completion
+ * Schedules tasklet for cmd completion
* if poll_mode_io is set
*/
static void
megasas_io_completion_timer(unsigned long instance_addr)
{
- struct megasas_instance *instance =
+ struct megasas_instance *instance =
(struct megasas_instance *)instance_addr;
if (atomic_read(&instance->fw_outstanding))
*/
instance->base_addr = pci_resource_start(instance->pdev, 0);
- if (pci_request_regions(instance->pdev, "megasas: LSI")) {
+ if (pci_request_regions(instance->pdev, "megasas: LSI Logic")) {
printk(KERN_DEBUG "megasas: IO memory region busy!\n");
return -EBUSY;
}
switch(instance->pdev->device)
{
case PCI_DEVICE_ID_LSI_SAS1078R:
+ case PCI_DEVICE_ID_LSI_SAS1078DE:
instance->instancet = &megasas_instance_template_ppc;
break;
case PCI_DEVICE_ID_LSI_SAS1064R:
printk(KERN_DEBUG "megasas: Out of DMA mem for reply queue\n");
goto fail_reply_queue;
}
-
+
if (megasas_issue_init_mfi(instance))
goto fail_fw_init;
tmp_sectors = (max_sectors_1 < max_sectors_2)
? max_sectors_1 : max_sectors_2;
}
-
+
instance->max_sectors_per_req = instance->max_num_sge *
- PAGE_SIZE / 512;
+ PAGE_SIZE / 512;
if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors))
instance->max_sectors_per_req = tmp_sectors;
kfree(ctrl_info);
/*
- * Setup tasklet for cmd completion
- */
+ * Setup tasklet for cmd completion
+ */
tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc,
(unsigned long)instance);
class_locale.word);
}
+static ssize_t
+sysfs_max_sectors_read(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct Scsi_Host *host = class_to_shost(container_of(kobj,
+ struct class_device, kobj));
+ struct megasas_instance *instance =
+ (struct megasas_instance *)host->hostdata;
+
+ count = sprintf(buf,"%u\n", instance->max_sectors_per_req);
+
+ return count+1;
+}
+
+static struct bin_attribute sysfs_max_sectors_attr = {
+ .attr = {
+ .name = "max_sectors",
+ .mode = S_IRUSR|S_IRGRP|S_IROTH,
+ .owner = THIS_MODULE,
+ },
+ .size = 7,
+ .read = sysfs_max_sectors_read,
+};
+
/**
* megasas_io_attach - Attaches this driver to SCSI mid-layer
* @instance: Adapter soft state
static int megasas_io_attach(struct megasas_instance *instance)
{
struct Scsi_Host *host = instance->host;
+ int error;
/*
* Export parameters required by SCSI mid-layer
host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS;
host->this_id = instance->init_id;
host->sg_tablesize = instance->max_num_sge;
+
+ /*
+ * Check if the module parameter value for max_sectors can be used
+ */
+ if (max_sectors && max_sectors < instance->max_sectors_per_req)
+ instance->max_sectors_per_req = max_sectors;
+ else {
+ if (max_sectors)
+ printk(KERN_INFO "megasas: max_sectors should be > 0 and"
+ "<= %d\n",instance->max_sectors_per_req);
+ }
+
host->max_sectors = instance->max_sectors_per_req;
- host->cmd_per_lun = MEGASAS_DEFAULT_CMD_PER_LUN;
+
+ /*
+ * Check if the module parameter value for cmd_per_lun can be used
+ */
+ instance->cmd_per_lun = MEGASAS_DEFAULT_CMD_PER_LUN;
+ if (cmd_per_lun && cmd_per_lun <= MEGASAS_DEFAULT_CMD_PER_LUN)
+ instance->cmd_per_lun = cmd_per_lun;
+ else
+ printk(KERN_INFO "megasas: cmd_per_lun should be > 0 and"
+ "<= %d\n",MEGASAS_DEFAULT_CMD_PER_LUN);
+
+ host->cmd_per_lun = instance->cmd_per_lun;
+
+ printk(KERN_DEBUG "megasas: max_sectors : 0x%x, cmd_per_lun : 0x%x\n",
+ instance->max_sectors_per_req, instance->cmd_per_lun);
+
host->max_channel = MEGASAS_MAX_CHANNELS - 1;
host->max_id = MEGASAS_MAX_DEV_PER_CHANNEL;
host->max_lun = MEGASAS_MAX_LUN;
return -ENODEV;
}
+ /*
+ * Create sysfs entries for module paramaters
+ */
+ error = sysfs_create_bin_file(&instance->host->shost_classdev.kobj,
+ &sysfs_max_sectors_attr);
+ if (error) {
+ printk(KERN_INFO "megasas: Error in creating the sysfs entry"
+ " max_sectors.\n");
+ goto out_remove_host;
+ }
+
/*
* Trigger SCSI to scan our drives
*/
scsi_scan_host(host);
return 0;
+
+out_remove_host:
+ scsi_remove_host(host);
+ return error;
}
static int
/**
* megasas_suspend - driver suspend entry point
* @pdev: PCI device structure
- * @state: state
+ * @state:
*/
static int __devinit
megasas_suspend(struct pci_dev *pdev, pm_message_t state)
megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
tasklet_kill(&instance->isr_tasklet);
- pci_set_drvdata(instance->pdev, instance);
+ pci_set_drvdata(instance->pdev, instance);
instance->instancet->disable_intr(instance->reg_set);
free_irq(instance->pdev->irq, instance);
+ scsi_host_put(host);
+
pci_save_state(pdev);
pci_disable_device(pdev);
int rval;
struct Scsi_Host *host;
struct megasas_instance *instance;
-
+
instance = pci_get_drvdata(pdev);
host = instance->host;
pci_set_power_state(pdev, PCI_D0);
*instance->producer = 0;
*instance->consumer = 0;
- atomic_set(&instance->fw_outstanding, 0);
+ atomic_set(&instance->fw_outstanding,0);
/*
* We expect the FW state to be READY
goto fail_init_mfi;
tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc,
- (unsigned long)instance);
+ (unsigned long)instance);
/*
* Register IRQ
*/
if (request_irq(pdev->irq, megasas_isr, IRQF_SHARED,
- "megasas", instance)) {
+ "megasas",instance)) {
printk(KERN_DEBUG "megasas: Failed to register IRQ\n");
goto fail_irq;
}
instance->instancet->enable_intr(instance->reg_set);
+ /*
+ * Store instance in PCI softstate
+ */
+ pci_set_drvdata(pdev, instance);
+
/*
* Initiate AEN (Asynchronous Event Notification)
*/
megasas_start_timer(instance, &instance->io_completion_timer,
megasas_io_completion_timer,
MEGASAS_COMPLETION_TIMER_INTERVAL);
-
- return 0;
-
-fail_irq:
-fail_init_mfi:
+
+ return 0;
+
+ fail_irq:
+ fail_init_mfi:
if (instance->evt_detail)
pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
pci_free_consistent(pdev, sizeof(u32), instance->consumer,
instance->consumer_h);
scsi_host_put(host);
-fail_set_dma_mask:
-fail_ready_state:
+ fail_set_dma_mask:
+ fail_ready_state:
pci_disable_device(pdev);
return -ENODEV;
if (poll_mode_io)
del_timer_sync(&instance->io_completion_timer);
+ sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_max_sectors_attr);
scsi_remove_host(instance->host);
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
{
struct megasas_instance *instance = pci_get_drvdata(pdev);
megasas_flush_cache(instance);
+ megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
}
/**
void *sense = NULL;
dma_addr_t sense_handle;
u32 *sense_ptr;
+ unsigned long *sense_buff;
memset(kbuff_arr, 0, sizeof(kbuff_arr));
*/
if (ioc->sense_len) {
/*
- * sense_ptr points to the location that has the user
+ * sense_buff points to the location that has the user
* sense buffer address
*/
+ sense_buff = (unsigned long *) ((unsigned long)ioc->frame.raw +
+ ioc->sense_off);
sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw +
- ioc->sense_off);
-
+ ioc->sense_off);
+#if defined(__ia64__)
+ if (copy_to_user((void __user *)((unsigned long)(*sense_buff)),
+#else
if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)),
- sense, ioc->sense_len)) {
- error = -EFAULT;
- goto out;
- }
+#endif
+ sense, ioc->sense_len)) {
+ printk(KERN_ERR "megasas: Failed to copy out to user"
+ "sense data\n");
+ error = -EFAULT;
+ goto out;
+ }
}
/*
return error;
}
-static struct megasas_instance *megasas_lookup_instance(u16 host_no)
-{
- int i;
-
- for (i = 0; i < megasas_mgmt_info.max_index; i++) {
-
- if ((megasas_mgmt_info.instance[i]) &&
- (megasas_mgmt_info.instance[i]->host->host_no == host_no))
- return megasas_mgmt_info.instance[i];
- }
-
- return NULL;
-}
-
static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
{
struct megasas_iocpacket __user *user_ioc =
static ssize_t
megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf)
{
- return sprintf(buf, "%u\n", megasas_dbg_lvl);
+ return sprintf(buf,"%u\n",megasas_dbg_lvl);
}
static ssize_t
static ssize_t
megasas_sysfs_show_poll_mode_io(struct device_driver *dd, char *buf)
{
- return sprintf(buf, "%u\n", poll_mode_io);
+ return sprintf(buf,"%u\n",poll_mode_io);
}
static ssize_t
-megasas_sysfs_set_poll_mode_io(struct device_driver *dd, const char *buf,
- size_t count)
+megasas_sysfs_set_poll_mode_io(struct device_driver *dd, const char *buf, size_t count)
{
int retval = count;
int tmp = poll_mode_io;
int i;
struct megasas_instance *instance;
- if (sscanf(buf, "%u", &poll_mode_io) < 1) {
+ if(sscanf(buf,"%u",&poll_mode_io) < 1){
printk(KERN_ERR "megasas: could not set poll_mode_io\n");
retval = -EINVAL;
}
for (i = 0; i < megasas_mgmt_info.max_index; i++) {
instance = megasas_mgmt_info.instance[i];
if (instance) {
- megasas_start_timer(instance,
- &instance->io_completion_timer,
+ megasas_start_timer(instance, &instance->io_completion_timer,
megasas_io_completion_timer,
MEGASAS_COMPLETION_TIMER_INTERVAL);
}
}
- } else {
+ }
+ else {
/*
* Delete timers for all adapters
*/
return retval;
}
-static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUGO,
- megasas_sysfs_show_poll_mode_io,
- megasas_sysfs_set_poll_mode_io);
+static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUGO, megasas_sysfs_show_poll_mode_io,
+ megasas_sysfs_set_poll_mode_io);
/**
* megasas_init - Driver load entry point
goto err_dcf_poll_mode_io;
return rval;
+
err_dcf_poll_mode_io:
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_dbg_lvl);
static void __exit megasas_exit(void)
{
driver_remove_file(&megasas_pci_driver.driver,
- &driver_attr_poll_mode_io);
- driver_remove_file(&megasas_pci_driver.driver,
- &driver_attr_dbg_lvl);
+ &driver_attr_poll_mode_io);
driver_remove_file(&megasas_pci_driver.driver,
- &driver_attr_release_date);
+ &driver_attr_dbg_lvl);
driver_remove_file(&megasas_pci_driver.driver,
- &driver_attr_version);
+ &driver_attr_release_date);
+ driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
pci_unregister_driver(&megasas_pci_driver);
unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl");