2007-09-13 17:03:32

by Yang, Bo

[permalink] [raw]
Subject: [PATCH 1/4] scsi: megaraid_sas -- add hibernation support

The megaraid_sas driver doesn't support the hibernation, the suspend/resume
routine implemented to support the hibernation.

Signed-off-by: Bo Yang <[email protected]>

---
drivers/scsi/megaraid/megaraid_sas.c | 309 +++++++++++++++++++------
drivers/scsi/megaraid/megaraid_sas.h | 1
2 files changed, 240 insertions(+), 70 deletions(-)
diff -rupN linux-2.6.22_orig/drivers/scsi/megaraid/megaraid_sas.c linux-2.6.22_new/drivers/scsi/megaraid/megaraid_sas.c
--- linux-2.6.22_orig/drivers/scsi/megaraid/megaraid_sas.c 2007-07-01 22:25:57.000000000 -0400
+++ linux-2.6.22_new/drivers/scsi/megaraid/megaraid_sas.c 2007-07-31 23:57:11.000000000 -0400
@@ -1804,6 +1804,81 @@ static void megasas_complete_cmd_dpc(uns
}

/**
+ * megasas_issue_init_mfi - Initializes the FW
+ * @instance: Adapter soft state
+ *
+ * Issues the INIT MFI cmd
+ */
+static int
+megasas_issue_init_mfi(struct megasas_instance *instance)
+{
+ u32 context;
+
+ struct megasas_cmd *cmd;
+
+ struct megasas_init_frame *init_frame;
+ struct megasas_init_queue_info *initq_info;
+ dma_addr_t init_frame_h;
+ dma_addr_t initq_info_h;
+
+ /*
+ * Prepare a init frame. Note the init frame points to queue info
+ * structure. Each frame has SGL allocated after first 64 bytes. For
+ * this frame - since we don't need any SGL - we use SGL's space as
+ * queue info structure
+ *
+ * We will not get a NULL command below. We just created the pool.
+ */
+ cmd = megasas_get_cmd(instance);
+
+ init_frame = (struct megasas_init_frame *)cmd->frame;
+ initq_info = (struct megasas_init_queue_info *)
+ ((unsigned long)init_frame + 64);
+
+ init_frame_h = cmd->frame_phys_addr;
+ initq_info_h = init_frame_h + 64;
+
+ context = init_frame->context;
+ 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;
+
+ initq_info->producer_index_phys_addr_lo = instance->producer_h;
+ initq_info->consumer_index_phys_addr_lo = instance->consumer_h;
+
+ init_frame->cmd = MFI_CMD_INIT;
+ init_frame->cmd_status = 0xFF;
+ init_frame->queue_info_new_phys_addr_lo = initq_info_h;
+
+ init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info);
+
+ /*
+ * 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");
+ megasas_return_cmd(instance, cmd);
+ goto fail_fw_init;
+ }
+
+ megasas_return_cmd(instance, cmd);
+
+ return 0;
+
+fail_fw_init:
+ return -EINVAL;
+}
+
+/**
* megasas_init_mfi - Initializes the FW
* @instance: Adapter soft state
*
@@ -1816,15 +1891,7 @@ static int megasas_init_mfi(struct megas
u32 max_sectors_1;
u32 max_sectors_2;
struct megasas_register_set __iomem *reg_set;
-
- struct megasas_cmd *cmd;
struct megasas_ctrl_info *ctrl_info;
-
- struct megasas_init_frame *init_frame;
- struct megasas_init_queue_info *initq_info;
- dma_addr_t init_frame_h;
- dma_addr_t initq_info_h;
-
/*
* Map the message registers
*/
@@ -1901,52 +1968,8 @@ static int megasas_init_mfi(struct megas
goto fail_reply_queue;
}

- /*
- * Prepare a init frame. Note the init frame points to queue info
- * structure. Each frame has SGL allocated after first 64 bytes. For
- * this frame - since we don't need any SGL - we use SGL's space as
- * queue info structure
- *
- * We will not get a NULL command below. We just created the pool.
- */
- cmd = megasas_get_cmd(instance);
-
- init_frame = (struct megasas_init_frame *)cmd->frame;
- initq_info = (struct megasas_init_queue_info *)
- ((unsigned long)init_frame + 64);
-
- init_frame_h = cmd->frame_phys_addr;
- initq_info_h = init_frame_h + 64;
-
- memset(init_frame, 0, MEGAMFI_FRAME_SIZE);
- memset(initq_info, 0, sizeof(struct megasas_init_queue_info));
-
- initq_info->reply_queue_entries = instance->max_fw_cmds + 1;
- initq_info->reply_queue_start_phys_addr_lo = instance->reply_queue_h;
-
- initq_info->producer_index_phys_addr_lo = instance->producer_h;
- initq_info->consumer_index_phys_addr_lo = instance->consumer_h;
-
- init_frame->cmd = MFI_CMD_INIT;
- init_frame->cmd_status = 0xFF;
- init_frame->queue_info_new_phys_addr_lo = initq_info_h;
-
- init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info);
-
- /*
- * 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_DEBUG "megasas: Failed to init firmware\n");
+ if (megasas_issue_init_mfi(instance))
goto fail_fw_init;
- }
-
- megasas_return_cmd(instance, cmd);

ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);

@@ -1982,7 +2005,6 @@ static int megasas_init_mfi(struct megas
return 0;

fail_fw_init:
- megasas_return_cmd(instance, cmd);

pci_free_consistent(instance->pdev, reply_q_sz,
instance->reply_queue, instance->reply_queue_h);
@@ -2264,6 +2286,28 @@ static int megasas_io_attach(struct mega
return 0;
}

+static int
+megasas_set_dma_mask(struct pci_dev *pdev)
+{
+ /*
+ * All our contollers are capable of performing 64-bit DMA
+ */
+ if (IS_DMA64) {
+ if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) != 0) {
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0)
+ goto fail_set_dma_mask;
+ }
+ } else {
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0)
+ goto fail_set_dma_mask;
+ }
+ return 0;
+
+fail_set_dma_mask:
+ return 1;
+}
+
/**
* megasas_probe_one - PCI hotplug entry point
* @pdev: PCI device structure
@@ -2297,19 +2341,8 @@ megasas_probe_one(struct pci_dev *pdev,

pci_set_master(pdev);

- /*
- * All our contollers are capable of performing 64-bit DMA
- */
- if (IS_DMA64) {
- if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) != 0) {
-
- if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0)
- goto fail_set_dma_mask;
- }
- } else {
- if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0)
- goto fail_set_dma_mask;
- }
+ if (megasas_set_dma_mask(pdev))
+ goto fail_set_dma_mask;

host = scsi_host_alloc(&megasas_template,
sizeof(struct megasas_instance));
@@ -2491,8 +2524,10 @@ static void megasas_flush_cache(struct m
/**
* megasas_shutdown_controller - Instructs FW to shutdown the controller
* @instance: Adapter soft state
+ * @opcode: Shutdown/Hibernate
*/
-static void megasas_shutdown_controller(struct megasas_instance *instance)
+static void megasas_shutdown_controller(struct megasas_instance *instance,
+ u32 opcode)
{
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
@@ -2515,7 +2550,7 @@ static void megasas_shutdown_controller(
dcmd->flags = MFI_FRAME_DIR_NONE;
dcmd->timeout = 0;
dcmd->data_xfer_len = 0;
- dcmd->opcode = MR_DCMD_CTRL_SHUTDOWN;
+ dcmd->opcode = opcode;

megasas_issue_blocked_cmd(instance, cmd);

@@ -2525,6 +2560,138 @@ static void megasas_shutdown_controller(
}

/**
+ * megasas_suspend - driver suspend entry point
+ * @pdev: PCI device structure
+ * @state: PCI power state to suspend routine
+ */
+static int __devinit
+megasas_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct Scsi_Host *host;
+ struct megasas_instance *instance;
+
+ instance = pci_get_drvdata(pdev);
+ host = instance->host;
+
+ megasas_flush_cache(instance);
+ megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
+ tasklet_kill(&instance->isr_tasklet);
+
+ 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);
+
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+/**
+ * megasas_resume- driver resume entry point
+ * @pdev: PCI device structure
+ */
+static int __devinit
+megasas_resume(struct pci_dev *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);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+
+ /*
+ * PCI prepping: enable device set bus mastering and dma mask
+ */
+ rval = pci_enable_device(pdev);
+
+ if (rval) {
+ printk(KERN_ERR "megasas: Enable device failed\n");
+ return rval;
+ }
+
+ pci_set_master(pdev);
+
+ if (megasas_set_dma_mask(pdev))
+ goto fail_set_dma_mask;
+
+ /*
+ * Initialize MFI Firmware
+ */
+
+ *instance->producer = 0;
+ *instance->consumer = 0;
+
+ atomic_set(&instance->fw_outstanding, 0);
+
+ /*
+ * We expect the FW state to be READY
+ */
+ if (megasas_transition_to_ready(instance))
+ goto fail_ready_state;
+
+ if (megasas_issue_init_mfi(instance))
+ goto fail_init_mfi;
+
+ tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc,
+ (unsigned long)instance);
+
+ /*
+ * Register IRQ
+ */
+ if (request_irq(pdev->irq, megasas_isr, IRQF_SHARED,
+ "megasas", instance)) {
+ printk(KERN_ERR "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)
+ */
+ if (megasas_start_aen(instance))
+ printk(KERN_ERR "megasas: Start AEN failed\n");
+
+ return 0;
+
+fail_irq:
+fail_init_mfi:
+ if (instance->evt_detail)
+ pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
+ instance->evt_detail,
+ instance->evt_detail_h);
+
+ if (instance->producer)
+ pci_free_consistent(pdev, sizeof(u32), instance->producer,
+ instance->producer_h);
+ if (instance->consumer)
+ pci_free_consistent(pdev, sizeof(u32), instance->consumer,
+ instance->consumer_h);
+ scsi_host_put(host);
+
+fail_set_dma_mask:
+fail_ready_state:
+
+ pci_disable_device(pdev);
+
+ return -ENODEV;
+}
+
+/**
* megasas_detach_one - PCI hot"un"plug entry point
* @pdev: PCI device structure
*/
@@ -2539,7 +2706,7 @@ static void megasas_detach_one(struct pc

scsi_remove_host(instance->host);
megasas_flush_cache(instance);
- megasas_shutdown_controller(instance);
+ megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
tasklet_kill(&instance->isr_tasklet);

/*
@@ -2978,6 +3145,8 @@ static struct pci_driver megasas_pci_dri
.id_table = megasas_pci_table,
.probe = megasas_probe_one,
.remove = __devexit_p(megasas_detach_one),
+ .suspend = megasas_suspend,
+ .resume = megasas_resume,
.shutdown = megasas_shutdown,
};

diff -rupN linux-2.6.22_orig/drivers/scsi/megaraid/megaraid_sas.h linux-2.6.22_new/drivers/scsi/megaraid/megaraid_sas.h
--- linux-2.6.22_orig/drivers/scsi/megaraid/megaraid_sas.h 2007-07-01 22:25:57.000000000 -0400
+++ linux-2.6.22_new/drivers/scsi/megaraid/megaraid_sas.h 2007-07-01 22:25:57.000000000 -0400
@@ -117,6 +117,7 @@
#define MR_FLUSH_DISK_CACHE 0x02

#define MR_DCMD_CTRL_SHUTDOWN 0x01050000
+#define MR_DCMD_HIBERNATE_SHUTDOWN 0x01060000
#define MR_ENABLE_DRIVE_SPINDOWN 0x01

#define MR_DCMD_CTRL_EVENT_GET_INFO 0x01040100


2007-09-14 10:27:40

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 1/4] scsi: megaraid_sas -- add hibernation support

On Tue, 11 Sep 2007 12:21:24 +0000 linux-box <[email protected]> wrote:

> The megaraid_sas driver doesn't support the hibernation, the suspend/resume
> routine implemented to support the hibernation.
>
> Signed-off-by: Bo Yang <[email protected]>
>
> ---
> drivers/scsi/megaraid/megaraid_sas.c | 309 +++++++++++++++++++------
> drivers/scsi/megaraid/megaraid_sas.h | 1
> 2 files changed, 240 insertions(+), 70 deletions(-)
> diff -rupN linux-2.6.22_orig/drivers/scsi/megaraid/megaraid_sas.c linux-2.6.22_new/drivers/scsi/megaraid/megaraid_sas.c
> --- linux-2.6.22_orig/drivers/scsi/megaraid/megaraid_sas.c 2007-07-01 22:25:57.000000000 -0400
> +++ linux-2.6.22_new/drivers/scsi/megaraid/megaraid_sas.c 2007-07-31 23:57:11.000000000 -0400
> @@ -1804,6 +1804,81 @@ static void megasas_complete_cmd_dpc(uns
> }
>
> /**
> + * megasas_issue_init_mfi - Initializes the FW
> + * @instance: Adapter soft state
> + *
> + * Issues the INIT MFI cmd
> + */
> +static int
> +megasas_issue_init_mfi(struct megasas_instance *instance)
> +{
> + u32 context;
> +
> + struct megasas_cmd *cmd;
> +
> + struct megasas_init_frame *init_frame;
> + struct megasas_init_queue_info *initq_info;
> + dma_addr_t init_frame_h;
> + dma_addr_t initq_info_h;
> +
> + /*
> + * Prepare a init frame. Note the init frame points to queue info
> + * structure. Each frame has SGL allocated after first 64 bytes. For
> + * this frame - since we don't need any SGL - we use SGL's space as
> + * queue info structure
> + *
> + * We will not get a NULL command below. We just created the pool.
> + */
> + cmd = megasas_get_cmd(instance);
> +
> + init_frame = (struct megasas_init_frame *)cmd->frame;

This would be better as

init_frame = &cmd->frame.init;

please review the whole driver for this.

> + initq_info = (struct megasas_init_queue_info *)
> + ((unsigned long)init_frame + 64);

And here we could do

initq_info = (struct megasas_init_queue_info *)(init_frame + 1);

although that's not really a lot nicer.


2007-09-14 14:42:55

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 1/4] scsi: megaraid_sas -- add hibernation support

On Tue, 2007-09-11 at 12:21 +0000, linux-box wrote:
> The megaraid_sas driver doesn't support the hibernation, the suspend/resume
> routine implemented to support the hibernation.
>
> Signed-off-by: Bo Yang <[email protected]>

Just from a process point of view, your email from: is linux-box
<[email protected]>, but your signoff is Bo Yang <[email protected]>, I'm
assuming the signoff line is the real name you want me to use?

> @@ -2525,6 +2560,138 @@ static void megasas_shutdown_controller(
> }
>
> /**
> + * megasas_suspend - driver suspend entry point
> + * @pdev: PCI device structure
> + * @state: PCI power state to suspend routine
> + */
> +static int __devinit
> +megasas_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> + struct Scsi_Host *host;
> + struct megasas_instance *instance;
> +
> + instance = pci_get_drvdata(pdev);
> + host = instance->host;
> +
> + megasas_flush_cache(instance);
> + megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
> + tasklet_kill(&instance->isr_tasklet);
> +
> + pci_set_drvdata(instance->pdev, instance);
> + instance->instancet->disable_intr(instance->reg_set);
> + free_irq(instance->pdev->irq, instance);
> +
> + scsi_host_put(host);

This can't be right. You're not relinquishing the host here, since you
simply pick it out of the pci driver data on resume.

> +
> + pci_save_state(pdev);
> + pci_disable_device(pdev);
> +
> + pci_set_power_state(pdev, pci_choose_state(pdev, state));
> +
> + return 0;
> +}
> +
> +/**
> + * megasas_resume- driver resume entry point
> + * @pdev: PCI device structure
> + */
> +static int __devinit
> +megasas_resume(struct pci_dev *pdev)
> +{
> + int rval;
> + struct Scsi_Host *host;
> + struct megasas_instance *instance;
> +
> + instance = pci_get_drvdata(pdev);
> + host = instance->host;

As in here ... to get the correct refcounting, you'd have to bump up the
reference here. However, I don't think the put on suspend is correct.

> + pci_set_power_state(pdev, PCI_D0);
> + pci_enable_wake(pdev, PCI_D0, 0);
> + pci_restore_state(pdev);
> +
> + /*
> + * PCI prepping: enable device set bus mastering and dma mask
> + */
> + rval = pci_enable_device(pdev);
> +
> + if (rval) {
> + printk(KERN_ERR "megasas: Enable device failed\n");
> + return rval;
> + }
> +
> + pci_set_master(pdev);
> +
> + if (megasas_set_dma_mask(pdev))
> + goto fail_set_dma_mask;
> +
> + /*
> + * Initialize MFI Firmware
> + */
> +
> + *instance->producer = 0;
> + *instance->consumer = 0;
> +
> + atomic_set(&instance->fw_outstanding, 0);
> +
> + /*
> + * We expect the FW state to be READY
> + */
> + if (megasas_transition_to_ready(instance))
> + goto fail_ready_state;
> +
> + if (megasas_issue_init_mfi(instance))
> + goto fail_init_mfi;
> +
> + tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc,
> + (unsigned long)instance);
> +
> + /*
> + * Register IRQ
> + */
> + if (request_irq(pdev->irq, megasas_isr, IRQF_SHARED,
> + "megasas", instance)) {
> + printk(KERN_ERR "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);

This better be redundant ... you picked the instance out of the driver
data at the beginning of this routine ... it better still be there or
else something is seriously wrong.

> +
> + /*
> + * Initiate AEN (Asynchronous Event Notification)
> + */
> + if (megasas_start_aen(instance))
> + printk(KERN_ERR "megasas: Start AEN failed\n");
> +
> + return 0;


James