Patch 3 of 4:
Signed-off by: Sreenivas Bagalkote <[email protected]>
diff -Naurp linux-2.6.12-rc4-mm1.c/drivers/scsi/megaraid/megaraid_sas.c
linux-2.6.12-rc4-mm1.d/drivers/scsi/megaraid/megaraid_sas.c
--- linux-2.6.12-rc4-mm1.c/drivers/scsi/megaraid/megaraid_sas.c 1969-12-31
19:00:00.000000000 -0500
+++ linux-2.6.12-rc4-mm1.d/drivers/scsi/megaraid/megaraid_sas.c 2005-05-15
22:04:04.579228792 -0400
@@ -0,0 +1,3398 @@
+/*
+ *
+ * Linux MegaRAID driver for SAS based RAID controllers
+ *
+ * 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
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_sas.c
+ * Version : v00.00.01.02
+ *
+ * Authors:
+ * Sreenivas Bagalkote <[email protected]>
+ *
+ * List of supported controllers
+ *
+ * OEM Product Name VID DID SSVID SSID
+ * --- ------------ --- --- ---- ----
+ */
+
+#include "megaraid_sas.h"
+
+MODULE_LICENSE ("GPL");
+MODULE_VERSION (MEGASAS_VERSION);
+MODULE_AUTHOR ("[email protected]");
+MODULE_DESCRIPTION ("LSI Logic MegaRAID SAS Driver");
+
+/*
+ * PCI ID table for all supported controllers
+ */
+static struct pci_device_id megasas_pci_table_g[] = {
+
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_LSI_1064,
+ PCI_ANY_ID,
+ PCI_ANY_ID,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_DELL_PERC5,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYSTEM_DELL_PERC5E,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_DELL_PERC5,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYSTEM_DELL_PERC5I,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_DELL_PERC5,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYSTEM_DELL_PERC5I_INTEGRATED,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_DELL_SAS5,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYSTEM_DELL_SAS5I,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_DELL_SAS5,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYSTEM_DELL_SAS5I_INTEGRATED,
+ },
+ { 0 } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, megasas_pci_table_g);
+
+static int megasas_mgmt_majorno;
+static struct megasas_mgmt_info megasas_mgmt_info;
+static struct fasync_struct* megasas_async_queue;
+static DECLARE_MUTEX (megasas_async_queue_mutex);
+
+/**
+ * megasas_get_cmd - Get a command from the free pool
+ * @instance: Adapter soft state
+ *
+ * Returns a free command from the pool
+ */
+static inline struct megasas_cmd*
+megasas_get_cmd( struct megasas_instance* instance )
+{
+ unsigned long flags;
+ struct megasas_cmd* cmd;
+
+ spin_lock_irqsave(&instance->cmd_pool_lock, flags);
+
+ if ( list_empty(&instance->cmd_pool)) {
+ spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
+ return NULL;
+ }
+
+ cmd = list_entry((&instance->cmd_pool)->next, struct megasas_cmd,
list);
+
+ list_del_init( &cmd->list );
+
+ spin_unlock_irqrestore( &instance->cmd_pool_lock, flags );
+
+ return cmd;
+}
+
+/**
+ * megasas_return_cmd - Return a cmd to free command pool
+ * @instance: Adapter soft state
+ * @cmd: Command packet to be returned to free command pool
+ */
+static inline void
+megasas_return_cmd( struct megasas_instance* instance, struct megasas_cmd*
cmd )
+{
+ unsigned long flags;
+
+ spin_lock_irqsave( &instance->cmd_pool_lock, flags );
+
+ list_add( &cmd->list, &instance->cmd_pool );
+
+ spin_unlock_irqrestore( &instance->cmd_pool_lock, flags );
+}
+
+
+/**
+ * megasas_issue_polled - Issues a polling command
+ * @instance: Adapter soft state
+ * @cmd: Command packet to be issued
+ *
+ * For polling, MFI requires the cmd_status to be set to 0xFF before
posting.
+ */
+static int
+megasas_issue_polled(struct megasas_instance* instance, struct megasas_cmd*
cmd)
+{
+ int i;
+ u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000;
+
+ struct megasas_header* frame_hdr = (struct
megasas_header*)cmd->frame;
+
+ frame_hdr->cmd_status = 0xFF;
+ frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
+
+ /*
+ * Issue the frame using inbound queue port
+ */
+ WR_IN_QPORT( (cmd->frame_phys_addr >> 3), instance->reg_set );
+
+ /*
+ * Wait for cmd_status to change
+ */
+ for( i=0; i < msecs && (frame_hdr->cmd_status == 0xff); i++ ) {
+ rmb();
+ msleep(1);
+ }
+
+ if (frame_hdr->cmd_status == 0xff)
+ return -ETIME;
+
+ return 0;
+}
+
+/**
+ * megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds
+ * @instance: Adapter soft state
+ * @cmd: Command to be issued
+ *
+ * This function waits on an event for the command to be returned from ISR.
+ * Used to issue ioctl commands.
+ */
+static int
+megasas_issue_blocked_cmd( struct megasas_instance* instance,
+ struct megasas_cmd* cmd )
+{
+ u32 msg_frame;
+
+ cmd->cmd_status = ENODATA;
+ msg_frame = (cmd->frame_phys_addr >> 3);
+
+ WR_IN_QPORT( msg_frame, instance->reg_set );
+
+ wait_event( instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA));
+
+ return 0;
+}
+
+/**
+ * megasas_issue_blocked_abort_cmd - Aborts previously issued cmd
+ * @instance: Adapter soft state
+ * @cmd_to_abort: Previously issued cmd to be aborted
+ *
+ * MFI firmware can abort previously issued AEN comamnd (automatic event
+ * notification). The megasas_issue_blocked_abort_cmd() issues such abort
+ * cmd and blocks till it is completed.
+ */
+static int
+megasas_issue_blocked_abort_cmd( struct megasas_instance* instance,
+ struct megasas_cmd* cmd_to_abort )
+{
+ struct megasas_cmd* cmd;
+ struct megasas_abort_frame* abort_fr;
+
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd)
+ return -1;
+
+ abort_fr = &cmd->frame->abort;
+
+ /*
+ * Prepare and issue the abort frame
+ */
+ abort_fr->cmd = MFI_CMD_ABORT;
+ abort_fr->cmd_status = 0xFF;
+ abort_fr->flags = 0;
+ abort_fr->abort_context = cmd_to_abort->index;
+ abort_fr->abort_mfi_phys_addr_lo =
cmd_to_abort->frame_phys_addr;
+ abort_fr->abort_mfi_phys_addr_hi = 0;
+
+ WR_IN_QPORT( (cmd->frame_phys_addr >> 3), instance->reg_set );
+
+ /*
+ * Wait for this cmd to complete
+ */
+ cmd->sync_cmd = 1;
+ wait_event( instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF));
+
+ megasas_return_cmd( instance, cmd );
+
+ return 0;
+}
+
+/**
+ * megasas_make_sgl32 - Prepares 32-bit SGL
+ * @instance: Adapter soft state
+ * @scp: SCSI command from the mid-layer
+ * @mfi_sgl: SGL to be filled in
+ *
+ * If successful, this function returns the number of SG elements.
Otherwise,
+ * it returnes -1.
+ */
+static inline int
+megasas_make_sgl32( struct megasas_instance* instance, struct scsi_cmnd*
scp,
+ union megasas_sgl* mfi_sgl )
+{
+ int i;
+ int sge_count;
+ struct scatterlist* os_sgl;
+ struct page* page;
+ unsigned long offset;
+
+ /*
+ * Return 0 if there is no data transfer
+ */
+ if (!scp->request_buffer || !scp->request_bufflen)
+ return 0;
+
+ if (!scp->use_sg) {
+ page = virt_to_page( scp->request_buffer );
+ offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK);
+
+ mfi_sgl->sge32[0].phys_addr =
pci_map_page(instance->pdev,
+ page, offset,
+
scp->request_bufflen,
+
scp->sc_data_direction);
+ mfi_sgl->sge32[0].length = scp->request_bufflen;
+
+ return 1;
+ }
+
+ os_sgl = (struct scatterlist*) scp->request_buffer;
+ sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg,
+ scp->sc_data_direction );
+
+ for( i = 0; i < sge_count; i++, os_sgl++ ) {
+ mfi_sgl->sge32[i].length = sg_dma_len( os_sgl );
+ mfi_sgl->sge32[i].phys_addr = sg_dma_address( os_sgl );
+ }
+
+ return sge_count;
+}
+
+/**
+ * megasas_make_sgl64 - Prepares 64-bit SGL
+ * @instance: Adapter soft state
+ * @scp: SCSI command from the mid-layer
+ * @mfi_sgl: SGL to be filled in
+ *
+ * If successful, this function returns the number of SG elements.
Otherwise,
+ * it returnes -1.
+ */
+static inline int
+megasas_make_sgl64( struct megasas_instance* instance, struct scsi_cmnd*
scp,
+ union megasas_sgl* mfi_sgl )
+{
+ int i;
+ int sge_count;
+ struct scatterlist* os_sgl;
+ struct page* page;
+ unsigned long offset;
+
+ /*
+ * Return 0 if there is no data transfer
+ */
+ if (!scp->request_buffer || !scp->request_bufflen)
+ return 0;
+
+ if (!scp->use_sg) {
+ page = virt_to_page( scp->request_buffer );
+ offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK);
+
+ mfi_sgl->sge64[0].phys_addr =
pci_map_page(instance->pdev,
+ page, offset,
+
scp->request_bufflen,
+
scp->sc_data_direction);
+
+ mfi_sgl->sge64[0].length = scp->request_bufflen;
+
+ return 1;
+ }
+
+ os_sgl = (struct scatterlist*) scp->request_buffer;
+ sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg,
+ scp->sc_data_direction );
+
+ for( i = 0; i < sge_count; i++, os_sgl++ ) {
+ mfi_sgl->sge64[i].length = sg_dma_len( os_sgl );
+ mfi_sgl->sge64[i].phys_addr = sg_dma_address( os_sgl );
+ }
+
+ return sge_count;
+}
+
+/**
+ * megasas_build_dcdb - Prepares a direct cdb (DCDB) command
+ * @instance: Adapter soft state
+ * @scp: SCSI command
+ * @cmd: Command to be prepared in
+ *
+ * This function prepares CDB commands. These are typcially pass-through
+ * commands to the devices.
+ */
+static inline int
+megasas_build_dcdb( struct megasas_instance* instance, struct scsi_cmnd*
scp,
+ struct megasas_cmd* cmd )
+{
+ u32 sge_sz;
+ int sge_bytes;
+ u32 is_logical;
+ u32 device_id;
+ u16 flags = 0;
+ struct megasas_pthru_frame* pthru;
+
+ is_logical = MEGASAS_IS_LOGICAL(scp);
+ device_id = MEGASAS_DEV_INDEX(instance, scp);
+ pthru = (struct megasas_pthru_frame*) cmd->frame;
+
+ if (scp->sc_data_direction == PCI_DMA_TODEVICE )
+ flags = MFI_FRAME_DIR_WRITE;
+ else if( scp->sc_data_direction == PCI_DMA_FROMDEVICE )
+ flags = MFI_FRAME_DIR_READ;
+ else if( scp->sc_data_direction == PCI_DMA_NONE )
+ flags = MFI_FRAME_DIR_NONE;
+
+ /*
+ * Prepare the DCDB frame
+ */
+ pthru->cmd = (is_logical) ? MFI_CMD_LD_SCSI_IO :
+ MFI_CMD_PD_SCSI_IO;
+ pthru->cmd_status = 0x0;
+ pthru->scsi_status = 0x0;
+ pthru->target_id = device_id;
+ pthru->lun = SCP2LUN(scp);
+ pthru->cdb_len = scp->cmd_len;
+ pthru->timeout = 0;
+ pthru->flags = flags;
+ pthru->data_xfer_len = scp->request_bufflen;
+
+ memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
+
+ /*
+ * Construct SGL
+ */
+ sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
+ sizeof(struct megasas_sge32);
+
+ if (IS_DMA64) {
+ pthru->flags |= MFI_FRAME_SGL64;
+ pthru->sge_count = megasas_make_sgl64( instance, scp,
+ &pthru->sgl
);
+ }
+ else
+ pthru->sge_count = megasas_make_sgl32( instance, scp,
+ &pthru->sgl
);
+
+ /*
+ * Sense info specific
+ */
+ pthru->sense_len = SCSI_SENSE_BUFFERSIZE;
+ pthru->sense_buf_phys_addr_hi = 0;
+ pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr;
+
+ sge_bytes = sge_sz * pthru->sge_count;
+
+ /*
+ * TBD: Comment the code below
+ */
+ cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) +
+ ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) +
1;
+
+ if (cmd->frame_count > 7)
+ cmd->frame_count = 8;
+
+ return cmd->frame_count;
+}
+
+/**
+ * megasas_build_ldio - Prepares IOs to logical devices
+ * @instance: Adapter soft state
+ * @scp: SCSI command
+ * @cmd: Command to to be prepared
+ *
+ * Frames (and accompanying SGLs) for regular SCSI IOs use this function.
+ */
+static inline int
+megasas_build_ldio( struct megasas_instance* instance, struct scsi_cmnd*
scp,
+ struct megasas_cmd* cmd )
+{
+ u32 sge_sz;
+ int sge_bytes;
+ u32 device_id;
+ u8 sc = scp->cmnd[0];
+ u16 flags = 0;
+
+ struct megasas_io_frame* ldio;
+
+ device_id = MEGASAS_DEV_INDEX(instance, scp);
+ ldio = (struct megasas_io_frame*) cmd->frame;
+
+ if (scp->sc_data_direction == PCI_DMA_TODEVICE )
+ flags = MFI_FRAME_DIR_WRITE;
+ else if( scp->sc_data_direction == PCI_DMA_FROMDEVICE )
+ flags = MFI_FRAME_DIR_READ;
+
+ /*
+ * Preare the Logical IO frame: 2nd bit is zero for all read cmds
+ */
+ ldio->cmd = (sc &
0x02)?MFI_CMD_LD_WRITE:MFI_CMD_LD_READ;
+ ldio->cmd_status = 0x0;
+ ldio->scsi_status = 0x0;
+ ldio->target_id = device_id;
+ ldio->timeout = 0;
+ ldio->reserved_0 = 0;
+ ldio->pad_0 = 0;
+ ldio->flags = flags;
+ ldio->start_lba_hi = 0;
+ ldio->access_byte = (scp->cmd_len != 6) ? scp->cmnd[1] : 0;
+
+ /*
+ * 6-byte READ(0x08) or WRITE(0x0A) cdb
+ */
+ if (scp->cmd_len == 6) {
+ ldio->lba_count = (u32)scp->cmnd[4];
+ ldio->start_lba_lo = ((u32)scp->cmnd[1] << 16)|
+ ((u32)scp->cmnd[2] << 8) |
+ (u32)scp->cmnd[3];
+
+ ldio->start_lba_lo &= 0x1FFFFF;
+ }
+
+ /*
+ * 10-byte READ(0x28) or WRITE(0x2A) cdb
+ */
+ else if (scp->cmd_len == 10) {
+ ldio->lba_count = (u32)scp->cmnd[8] |
+ ((u32)scp->cmnd[7] << 8);
+ ldio->start_lba_lo = ((u32)scp->cmnd[2] << 24)|
+ ((u32)scp->cmnd[3] << 16)|
+ ((u32)scp->cmnd[4] << 8)|
+ (u32)scp->cmnd[5];
+ }
+
+ /*
+ * 12-byte READ(0xA8) or WRITE(0xAA) cdb
+ */
+ else if (scp->cmd_len == 12) {
+ ldio->lba_count = ((u32)scp->cmnd[6] << 24)|
+ ((u32)scp->cmnd[7] << 16)|
+ ((u32)scp->cmnd[8] << 8) |
+ (u32)scp->cmnd[9];
+
+ ldio->start_lba_lo = ((u32)scp->cmnd[2] << 24)|
+ ((u32)scp->cmnd[3] << 16)|
+ ((u32)scp->cmnd[4] << 8) |
+ (u32)scp->cmnd[5];
+ }
+
+ /*
+ * 16-byte READ(0x88) or WRITE(0x8A) cdb
+ */
+ else if (scp->cmd_len == 16) {
+ ldio->lba_count = ((u32)scp->cmnd[10] << 24)|
+ ((u32)scp->cmnd[11] << 16)|
+ ((u32)scp->cmnd[12] << 8) |
+ (u32)scp->cmnd[13];
+
+ ldio->start_lba_lo = ((u32)scp->cmnd[6] << 24)|
+ ((u32)scp->cmnd[7] << 16)|
+ ((u32)scp->cmnd[8] << 8) |
+ (u32)scp->cmnd[9];
+
+ ldio->start_lba_hi = ((u32)scp->cmnd[2] << 24)|
+ ((u32)scp->cmnd[3] << 16)|
+ ((u32)scp->cmnd[4] << 8) |
+ (u32)scp->cmnd[5];
+
+ }
+
+ /*
+ * Construct SGL
+ */
+ sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
+ sizeof(struct megasas_sge32);
+
+ if (IS_DMA64) {
+ ldio->flags |= MFI_FRAME_SGL64;
+ ldio->sge_count = megasas_make_sgl64( instance, scp,
+ &ldio->sgl
);
+ }
+ else
+ ldio->sge_count = megasas_make_sgl32( instance, scp,
+ &ldio->sgl
);
+
+ /*
+ * Sense info specific
+ */
+ ldio->sense_len = SCSI_SENSE_BUFFERSIZE;
+ ldio->sense_buf_phys_addr_hi = 0;
+ ldio->sense_buf_phys_addr_lo = cmd->sense_phys_addr;
+
+ sge_bytes = sge_sz * ldio->sge_count;
+
+ cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) +
+ ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) +
1;
+
+ if (cmd->frame_count > 7)
+ cmd->frame_count = 8;
+
+ return cmd->frame_count;
+}
+
+/**
+ * megasas_build_cmd - Prepares a command packet
+ * @instance: Adapter soft state
+ * @scp: SCSI command
+ * @frame_count: [OUT] Number of frames used to prepare this command
+ */
+static inline struct megasas_cmd*
+megasas_build_cmd( struct megasas_instance* instance, struct scsi_cmnd*
scp,
+ int* frame_count )
+{
+ u32 logical_cmd;
+ struct megasas_cmd* cmd;
+
+ /*
+ * Find out if this is logical or physical drive command.
+ */
+ logical_cmd = MEGASAS_IS_LOGICAL(scp);
+
+ /*
+ * Logical drive command
+ */
+ if (logical_cmd) {
+
+ if (SCP2TARGET(scp) >= MEGASAS_MAX_LD) {
+ scp->result = DID_BAD_TARGET << 16;
+ return NULL;
+ }
+
+ switch(scp->cmnd[0]) {
+
+ case READ_10:
+ case WRITE_10:
+ case READ_12:
+ case WRITE_12:
+ case READ_6:
+ case WRITE_6:
+ case READ_16:
+ case WRITE_16:
+ /*
+ * Fail for LUN > 0
+ */
+ if (SCP2LUN(scp)) {
+ scp->result = DID_BAD_TARGET << 16;
+ return NULL;
+ }
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ scp->result = DID_IMM_RETRY << 16;
+ return NULL;
+ }
+
+ *frame_count = megasas_build_ldio(instance, scp,
cmd);
+
+ if (! (*frame_count) ) {
+ megasas_return_cmd( instance, cmd );
+ return NULL;
+ }
+
+ return cmd;
+
+ default:
+ /*
+ * Fail for LUN > 0
+ */
+ if (SCP2LUN(scp)) {
+ scp->result = DID_BAD_TARGET << 16;
+ return NULL;
+ }
+
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd) {
+ scp->result = DID_IMM_RETRY << 16;
+ return NULL;
+ }
+
+ *frame_count = megasas_build_dcdb(instance, scp,
cmd);
+
+ if (! (*frame_count) ) {
+ megasas_return_cmd( instance, cmd );
+ return NULL;
+ }
+
+ return cmd;
+ }
+ }
+ else {
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd) {
+ scp->result = DID_IMM_RETRY << 16;
+ return NULL;
+ }
+
+ *frame_count = megasas_build_dcdb(instance, scp, cmd);
+
+ if (! (*frame_count) ) {
+ megasas_return_cmd( instance, cmd );
+ return NULL;
+ }
+
+ return cmd;
+ }
+
+ return NULL;
+}
+
+/**
+ * megasas_queue_command - Queue entry point
+ * @scmd: SCSI command to be queued
+ * @done: Callback entry point
+ */
+static int
+megasas_queue_command( struct scsi_cmnd* scmd, void (*done)(struct
scsi_cmnd*) )
+{
+ u32 frame_count;
+ struct megasas_cmd* cmd;
+ struct megasas_instance* instance;
+ u32 msg_frame;
+
+ instance = SCP2ADAPTER(scmd);
+ scmd->scsi_done = done;
+ scmd->result = 0;
+
+ cmd = megasas_build_cmd( instance, scmd, &frame_count );
+
+ if (!cmd) {
+ done(scmd);
+ return 0;
+ }
+
+ cmd->scmd = scmd;
+
+ /*
+ * Issue the command to the FW
+ */
+ msg_frame = (cmd->frame_phys_addr >> 3) | (cmd->frame_count - 1);
+
+ instance->fw_outstanding++;
+ WR_IN_QPORT( msg_frame, instance->reg_set );
+
+ return 0;
+}
+
+/**
+ * megasas_abort_handler - Abort entry point
+ * @scmd: SCSI command to be aborted
+ */
+static int
+megasas_abort_handler( struct scsi_cmnd* scmd )
+{
+ printk( KERN_NOTICE "megasas: ABORT -%ld cmd=%x <c=%d t=%d l=%d>\n",
+ scmd->serial_number, scmd->cmnd[0], SCP2CHANNEL(scmd),
+ SCP2TARGET(scmd), SCP2LUN(scmd));
+
+ return FAILED;
+}
+
+/**
+ * megasas_wait_for_outstanding - Wait for all outstanding cmds
+ * @instance: Adapter soft state
+ *
+ * This function waits for upto MEGASAS_RESET_WAIT_TIME seconds for FW to
+ * complete all its outstanding commands. Returns error if one or more IOs
+ * are pending after this time period. It also marks the controller dead.
+ */
+static int
+megasas_wait_for_outstanding( struct megasas_instance* instance )
+{
+ int i;
+ u32 wait_time = MEGASAS_RESET_WAIT_TIME;
+
+ for( i = 0; i < wait_time; i++ ) {
+
+ if (!instance->fw_outstanding)
+ break;
+
+ if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
+ printk( KERN_NOTICE "megasas: [%2d]waiting for %d \
+ commands to complete\n", i, instance->fw_outstanding
);
+ }
+
+ msleep(1000);
+ }
+
+
+ if (instance->fw_outstanding) {
+ instance->hw_crit_error = 1;
+ return FAILED;
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * megasas_generic_reset - Generic reset routine
+ * @scmd: Mid-layer SCSI command
+ *
+ * This routine implements a generic reset handler for device, bus and host
+ * reset requests. Device, bus and host specific reset handlers can use
this
+ * function after they do their specific tasks.
+ */
+static int
+megasas_generic_reset( struct scsi_cmnd* scmd )
+{
+ int ret_val;
+ struct megasas_instance* instance = SCP2ADAPTER(scmd);
+
+ spin_unlock( instance->host_lock );
+
+ printk( KERN_NOTICE "megasas: RESET -%ld cmd=%x <c=%d t=%d l=%d>\n",
+ scmd->serial_number, scmd->cmnd[0], SCP2CHANNEL(scmd),
+ SCP2TARGET(scmd), SCP2LUN(scmd));
+
+ if (instance->hw_crit_error) {
+ printk( KERN_ERR "megasas: cannot recover from previous
reset \
+ failures\n"
);
+ spin_lock( instance->host_lock );
+ return FAILED;
+ }
+
+ ret_val = megasas_wait_for_outstanding( instance );
+
+ if (ret_val == SUCCESS)
+ printk( KERN_NOTICE "megasas: reset successful \n" );
+ else
+ printk( KERN_ERR "megasas: failed to do reset\n");
+
+ spin_lock( instance->host_lock );
+
+ return ret_val;
+}
+
+/**
+ * megasas_reset_device - Device reset handler entry point
+ *
+ * Issues CLUSTER_RESET_LD (FW direct cmd) before calling generic reset fn.
+ */
+static int
+megasas_reset_device( struct scsi_cmnd* scmd )
+{
+ struct megasas_cmd* cmd;
+ struct megasas_dcmd_frame* dcmd;
+ struct megasas_instance* instance = SCP2ADAPTER(scmd);
+
+ cmd = megasas_get_cmd( instance );
+
+ if (cmd) {
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0x0;
+ dcmd->sge_count = 0;
+ dcmd->flags = MFI_FRAME_DIR_NONE;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = 0;
+ dcmd->opcode = MR_DCMD_CLUSTER_RESET_LD;
+ dcmd->mbox[0] = MEGASAS_DEV_INDEX(instance, scmd);
+
+ megasas_issue_blocked_cmd( instance, cmd );
+
+ megasas_return_cmd( instance, cmd );
+ }
+
+ return megasas_generic_reset( scmd );
+}
+
+/**
+ * megasas_reset_bus_host - Bus & host reset handler entry point
+ *
+ * Issues CLUSTER_RESET_ALL (FW direct cmd) before calling generic reset
fn.
+ */
+static int
+megasas_reset_bus_host( struct scsi_cmnd* scmd )
+{
+ struct megasas_cmd* cmd;
+ struct megasas_dcmd_frame* dcmd;
+ struct megasas_instance* instance = SCP2ADAPTER(scmd);
+
+ cmd = megasas_get_cmd( instance );
+
+ if (cmd) {
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0x0;
+ dcmd->sge_count = 0;
+ dcmd->flags = MFI_FRAME_DIR_NONE;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = 0;
+ dcmd->opcode = MR_DCMD_CLUSTER_RESET_ALL;
+
+ megasas_issue_blocked_cmd( instance, cmd );
+
+ megasas_return_cmd( instance, cmd );
+ }
+
+ return megasas_generic_reset( scmd );
+}
+
+/**
+ * megasas_service_aen - Processes an event notification
+ * @instance: Adapter soft state
+ * @cmd: AEN command completed by the ISR
+ *
+ * For AEN, driver sends a command down to FW that is held by the FW till
an
+ * event occurs. When an event of interest occurs, FW completes the command
+ * that it was previously holding.
+ *
+ * This routines sends SIGIO signal to processes that have registered with
the
+ * driver for AEN.
+ */
+static void
+megasas_service_aen(struct megasas_instance* instance, struct megasas_cmd*
cmd)
+{
+ /*
+ * Don't signal app if it is just an aborted previously registered
aen
+ */
+ if (!cmd->abort_aen)
+ kill_fasync( &megasas_async_queue, SIGIO, POLL_IN );
+ else
+ cmd->abort_aen = 0;
+
+ instance->aen_cmd = NULL;
+ megasas_return_cmd( instance, cmd );
+}
+
+/**
+ * megasas_sysfs_show_app_hndl - Exports adapter handle via sysfs
+ *
+ * User space applications _don't_ address the controllers using zero based
+ * indices. Instead driver exports a unique 16-bit handle for each
controller
+ * (refer to comments under MR_LINUX_GET_ADAPTER_MAP ioctl).
+ *
+ * Applications use this handle to delete or add logical drives (via FW
+ * commands). To make these logical driver appear or disappear to SCSI
layer,
+ * applications have to do a delete or scan on a SCSI host in sysfs tree.
+ * The applications have to have a way to find out the SCSI host number
+ * corresponding to the unique 16-bit handle.
+ *
+ * This function exports the unique 16-bit handle in sysfs under the SCSI
+ * host. Applications can traverse the list of hosts till they find a host
+ * that has the required handle.
+ */
+static ssize_t
+megasas_sysfs_show_app_hndl( struct class_device* cdev, char* buf )
+{
+ int i;
+ u32 hndl = 0;
+ struct Scsi_Host* shost;
+ struct megasas_instance* instance;
+
+ shost = class_to_shost( cdev );
+ instance = (struct megasas_instance*)SCSIHOST2ADAP( shost );
+
+ for (i = 0; i < megasas_mgmt_info.max_index; i++ ) {
+
+ if (instance == megasas_mgmt_info.instance[i])
+ hndl = ((i + 1) << 4) | 0xF;
+ }
+
+ return snprintf( buf, 8, "%u\n", hndl );
+}
+
+/*
+ * Sysfs attribute definition: Exports driver specific controller handle
+ */
+CLASS_DEVICE_ATTR(megaraid_sas_app_hndl, S_IRUSR,
megasas_sysfs_show_app_hndl,
+
NULL);
+/*
+ * Host template initializer for sysfs attributes
+ */
+static struct class_device_attribute* megasas_shost_attrs[] = {
+ &class_device_attr_megaraid_sas_app_hndl,
+ NULL,
+};
+
+/*
+ * Scsi host template for megaraid_sas driver
+ */
+static struct scsi_host_template megasas_template_g = {
+
+ .module = THIS_MODULE,
+ .name = "LSI Logic SAS based MegaRAID
driver",
+ .queuecommand = megasas_queue_command,
+ .eh_abort_handler = megasas_abort_handler,
+ .eh_device_reset_handler = megasas_reset_device,
+ .eh_bus_reset_handler = megasas_reset_bus_host,
+ .eh_host_reset_handler = megasas_reset_bus_host,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = megasas_shost_attrs,
+};
+
+/**
+ * megasas_complete_int_cmd - Completes an internal command
+ * @instance: Adapter soft state
+ * @cmd: Command to be completed
+ *
+ * The megasas_issue_blocked_cmd() function waits for a command to complete
+ * after it issues a command. This function wakes up that waiting routine
by
+ * calling wake_up() on the wait queue.
+ */
+static void
+megasas_complete_int_cmd(struct megasas_instance* instance,
+ struct megasas_cmd* cmd)
+{
+ cmd->cmd_status = cmd->frame->io.cmd_status;
+
+ if (cmd->cmd_status == ENODATA) {
+ cmd->cmd_status = 0;
+ }
+ wake_up( &instance->int_cmd_wait_q );
+}
+
+/**
+ * megasas_complete_abort - Completes aborting a command
+ * @instance: Adapter soft state
+ * @cmd: Cmd that was issued to abort another cmd
+ *
+ * The megasas_issue_blocked_abort_cmd() function waits on abort_cmd_wait_q
+ * after it issues an abort on a previously issued command. This function
+ * wakes up all functions waiting on the same wait queue.
+ */
+static void
+megasas_complete_abort( struct megasas_instance* instance,
+ struct megasas_cmd* cmd )
+{
+ if (cmd->sync_cmd) {
+ cmd->sync_cmd = 0;
+ wake_up( &instance->abort_cmd_wait_q );
+ }
+
+ return;
+}
+
+/**
+ * megasas_sync_buffers - Synchronozies CPU caches after IO transfer
+ * @instance: Adapter soft state
+ * @cmd: Completed command
+ */
+static inline void
+megasas_sync_buffers(struct megasas_instance* instance, struct megasas_cmd*
cmd)
+{
+ dma_addr_t buf_h;
+ u8 opcode;
+
+ if (cmd->scmd->use_sg) {
+ pci_unmap_sg( instance->pdev, cmd->scmd->request_buffer,
+ cmd->scmd->use_sg, cmd->scmd->sc_data_direction );
+ return;
+ }
+
+ if (!cmd->scmd->request_bufflen)
+ return;
+
+ opcode = cmd->frame->hdr.cmd;
+
+ if ((opcode == MFI_CMD_LD_READ) || (opcode == MFI_CMD_LD_WRITE)) {
+ if (IS_DMA64)
+ buf_h = cmd->frame->io.sgl.sge64[0].phys_addr;
+ else
+ buf_h = cmd->frame->io.sgl.sge32[0].phys_addr;
+ }
+ else {
+ if (IS_DMA64)
+ buf_h = cmd->frame->pthru.sgl.sge64[0].phys_addr;
+ else
+ buf_h = cmd->frame->pthru.sgl.sge32[0].phys_addr;
+ }
+
+ pci_unmap_page( instance->pdev, buf_h, cmd->scmd->request_bufflen,
+ cmd->scmd->sc_data_direction
);
+ return;
+}
+
+/**
+ * megasas_complete_cmd - Completes a command
+ * @instance: Adapter soft state
+ * @cmd: Command to be completed
+ * @alt_status: If non-zero, use this value as
status to
+ * SCSI mid-layer instead of the value returned
+ * by the FW. This should be used if caller
wants
+ * an alternate status (as in the case of
aborted
+ * commands)
+ */
+static inline void
+megasas_complete_cmd(struct megasas_instance* instance, struct megasas_cmd*
cmd,
+ u8
alt_status)
+{
+ int exception = 0;
+ struct megasas_header* hdr = &cmd->frame->hdr;
+
+ switch( hdr->cmd ) {
+
+ case MFI_CMD_PD_SCSI_IO:
+ case MFI_CMD_LD_SCSI_IO:
+
+ /*
+ * MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been
+ * issued either through an IO path or an IOCTL path. If it
+ * was via IOCTL, we will send it to internal completion.
+ */
+ if (cmd->sync_cmd) {
+ cmd->sync_cmd = 0;
+ megasas_complete_int_cmd( instance, cmd );
+ break;
+ }
+
+ /*
+ * Don't export physical disk devices to mid-layer.
+ */
+ if ( !MEGASAS_IS_LOGICAL(cmd->scmd) &&
+ (hdr->cmd_status == MFI_STAT_OK) &&
+ (cmd->scmd->cmnd[0] == INQUIRY) ) {
+
+ if (((*(u8*) cmd->scmd->request_buffer) & 0x1F) ==
+ TYPE_DISK) {
+ cmd->scmd->result = DID_BAD_TARGET << 16;
+ exception = 1;
+ }
+ }
+
+ case MFI_CMD_LD_READ:
+ case MFI_CMD_LD_WRITE:
+
+ if (alt_status) {
+ cmd->scmd->result = alt_status << 16;
+ exception = 1;
+ }
+
+
+ if (exception) {
+
+ spin_lock( instance->host_lock );
+ cmd->scmd->scsi_done( cmd->scmd );
+ spin_unlock( instance->host_lock );
+
+ instance->fw_outstanding--;
+ megasas_return_cmd( instance, cmd );
+ megasas_sync_buffers( instance, cmd );
+ break;
+ }
+
+
+ switch (hdr->cmd_status) {
+
+ case MFI_STAT_OK:
+ cmd->scmd->result = DID_OK << 16;
+ break;
+
+ case MFI_STAT_SCSI_IO_FAILED:
+ cmd->scmd->result = (DID_ERROR << 16)
|hdr->scsi_status;
+ break;
+
+ case MFI_STAT_SCSI_DONE_WITH_ERROR:
+
+ cmd->scmd->result = (DID_OK << 16) |
hdr->scsi_status;
+
+ if (hdr->scsi_status == SAM_STAT_CHECK_CONDITION) {
+ memset( cmd->scmd->sense_buffer, 0,
+
SCSI_SENSE_BUFFERSIZE );
+ memcpy( cmd->scmd->sense_buffer, cmd->sense,
+ hdr->sense_len );
+
+ cmd->scmd->result |= DRIVER_SENSE << 24;
+ }
+
+ break;
+
+ case MFI_STAT_DEVICE_NOT_FOUND:
+ cmd->scmd->result = DID_BAD_TARGET << 16;
+ break;
+
+ default:
+ printk( KERN_DEBUG "megasas: unhandled status
%#x\n",
+ hdr->cmd_status);
+ cmd->scmd->result = DID_ERROR << 16;
+ }
+
+ spin_lock( instance->host_lock );
+ cmd->scmd->scsi_done( cmd->scmd );
+ spin_unlock( instance->host_lock );
+
+ instance->fw_outstanding--;
+
+ megasas_return_cmd( instance, cmd );
+
+ megasas_sync_buffers( instance, cmd );
+ break;
+
+ case MFI_CMD_SMP:
+ case MFI_CMD_STP:
+ case MFI_CMD_DCMD:
+
+ /*
+ * See if got an event notification
+ */
+ if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_WAIT)
+ megasas_service_aen( instance, cmd );
+ else
+ megasas_complete_int_cmd( instance, cmd );
+
+ break;
+
+ case MFI_CMD_ABORT:
+ /*
+ * Cmd issued to abort another cmd returned
+ */
+ megasas_complete_abort( instance, cmd );
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * megasas_deplete_reply_queue - Processes all completed commands
+ * @instance: Adapter soft state
+ * @alt_status: Alternate status to be
returned to
+ * SCSI mid-layer instead of the status
+ * returned by the FW
+ */
+static inline int
+megasas_deplete_reply_queue( struct megasas_instance* instance, u8
alt_status )
+{
+ u32 status;
+ u32 producer;
+ u32 consumer;
+ u32 context;
+
+ struct megasas_register_set* reg_set;
+ struct megasas_cmd* cmd;
+
+ reg_set = instance->reg_set;
+
+ /*
+ * Check if it is our interrupt
+ */
+ status = RD_OB_INTR_STATUS(reg_set);
+
+ if (! (status & MFI_OB_INTR_STATUS_MASK)) {
+ return IRQ_NONE;
+ }
+
+ /*
+ * Clear the interrupt by writing back the same value
+ */
+ WR_OB_INTR_STATUS(status, reg_set);
+
+ producer = instance->producer;
+ consumer = instance->consumer;
+
+ while( consumer != producer ) {
+ context = instance->reply_queue[consumer];
+
+ cmd = instance->cmd_list[context];
+
+ megasas_complete_cmd( instance, cmd, alt_status );
+
+ consumer++;
+ if (consumer == (instance->max_fw_cmds + 1)) {
+ consumer = 0;
+ }
+ }
+
+ wmb();
+ instance->consumer = producer;
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * megasas_isr - isr entry point
+ */
+static irqreturn_t
+megasas_isr(int irq, void *devp, struct pt_regs *regs)
+{
+ return megasas_deplete_reply_queue( (struct megasas_instance*)devp,
+ DID_OK );
+}
+
+/**
+ * megasas_transition_to_ready - Move the FW to READY state
+ * @reg_set: MFI register set
+ *
+ * During the initialization, FW passes can potentially be in any one of
+ * several possible states. If the FW in operational, waiting-for-handshake
+ * states, driver must take steps to bring it to ready state. Otherwise, it
+ * has to wait for the ready state.
+ */
+static int
+megasas_transition_to_ready( struct megasas_register_set* reg_set )
+{
+ int i;
+ u8 max_wait;
+ u32 fw_state;
+ u32 cur_state;
+
+ fw_state = RD_OB_MSG_0(reg_set) & MFI_STATE_MASK;
+
+ while( fw_state != MFI_STATE_READY ) {
+
+ printk( KERN_INFO "megasas: Waitinf for FW to come to ready
\
+ state\n" );
+
+ switch( fw_state ) {
+
+ case MFI_STATE_FAULT:
+
+ printk(KERN_DEBUG "megasas: FW in FAULT state!!\n");
+ return -ENODEV;
+
+ case MFI_STATE_WAIT_HANDSHAKE:
+ /*
+ * Set the CLR bit in inbound doorbell
+ */
+ WR_IN_DOORBELL( MFI_INIT_CLEAR_HANDSHAKE, reg_set );
+
+ max_wait = 2;
+ cur_state = MFI_STATE_WAIT_HANDSHAKE;
+ break;
+
+ case MFI_STATE_OPERATIONAL:
+ /*
+ * Bring it to READY state; assuming max wait 2 secs
+ */
+ MFI_DISABLE_INTR(reg_set);
+ WR_IN_DOORBELL( MFI_INIT_READY, reg_set );
+
+ max_wait = 10;
+ cur_state = MFI_STATE_OPERATIONAL;
+ break;
+
+ case MFI_STATE_UNDEFINED:
+ /*
+ * This state should not last for more than 2
seconds
+ */
+ max_wait = 2;
+ cur_state = MFI_STATE_UNDEFINED;
+ break;
+
+ case MFI_STATE_BB_INIT:
+ max_wait = 2;
+ cur_state = MFI_STATE_BB_INIT;
+ break;
+
+ case MFI_STATE_FW_INIT:
+ max_wait = 20;
+ cur_state = MFI_STATE_FW_INIT;
+ break;
+
+ case MFI_STATE_FW_INIT_2:
+ max_wait = 20;
+ cur_state = MFI_STATE_FW_INIT_2;
+ break;
+
+ case MFI_STATE_DEVICE_SCAN:
+ max_wait = 20;
+ cur_state = MFI_STATE_DEVICE_SCAN;
+ break;
+
+ case MFI_STATE_FLUSH_CACHE:
+ max_wait = 20;
+ cur_state = MFI_STATE_FLUSH_CACHE;
+ break;
+
+ default:
+ printk(KERN_DEBUG "megasas: Unknown state 0x%x\n",
+ fw_state);
+ return -ENODEV;
+ }
+
+ /*
+ * The cur_state should not last for more than max_wait secs
+ */
+ for( i = 0; i < (max_wait * 1000); i++ ) {
+ fw_state = RD_OB_MSG_0(reg_set) & MFI_STATE_MASK;
+
+ if (fw_state == cur_state) {
+ msleep(1);
+ }
+ else
+ break;
+ }
+
+ /*
+ * Return error if fw_state hasn't changed after max_wait
+ */
+ if (fw_state == cur_state) {
+ printk(KERN_DEBUG "FW state [%d] hasn't changed \
+ in %d secs\n", fw_state, max_wait);
+ return -ENODEV;
+ }
+ };
+
+ return 0;
+}
+
+/**
+ * megasas_teardown_frame_pool - Destroy the cmd frame DMA pool
+ * @instance: Adapter soft state
+ */
+static void
+megasas_teardown_frame_pool( struct megasas_instance* instance )
+{
+ int i;
+ u32 max_cmd = instance->max_fw_cmds;
+ struct megasas_cmd* cmd;
+
+ if (!instance->frame_dma_pool)
+ return;
+
+ /*
+ * Return all frames to pool
+ */
+ for( i = 0; i < max_cmd; i++ ) {
+
+ cmd = instance->cmd_list[i];
+
+ if( cmd->frame)
+ pci_pool_free( instance->frame_dma_pool, cmd->frame,
+ cmd->frame_phys_addr );
+
+ if (cmd->sense)
+ pci_pool_free( instance->sense_dma_pool, cmd->frame,
+ cmd->sense_phys_addr );
+ }
+
+ /*
+ * Now destroy the pool itself
+ */
+ pci_pool_destroy( instance->frame_dma_pool );
+ pci_pool_destroy( instance->sense_dma_pool );
+
+ instance->frame_dma_pool = NULL;
+ instance->sense_dma_pool = NULL;
+}
+
+/**
+ * megasas_create_frame_pool - Creates DMA pool for cmd frames
+ * @instance: Adapter soft state
+ *
+ * Each command packet has an embedded DMA memory buffer that is used for
+ * filling MFI frame and the SG list that immediately follows the frame.
This
+ * function creates those DMA memory buffers for each command packet by
using
+ * PCI pool facility.
+ */
+static int
+megasas_create_frame_pool( struct megasas_instance* instance )
+{
+ int i;
+ u32 max_cmd;
+ u32 sge_sz;
+ u32 sgl_sz;
+ u32 total_sz ;
+ u32 frame_count;
+ struct megasas_cmd* cmd;
+
+ max_cmd = instance->max_fw_cmds;
+
+ /*
+ * Size of our frame is 64 bytes for MFI frame, followed by max SG
+ * elements and finally SCSI_SENSE_BUFFERSIZE bytes for sense buffer
+ */
+ sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
+ sizeof(struct megasas_sge32);
+
+ /*
+ * Calculated the number of 64byte frames required for SGL
+ */
+ sgl_sz = sge_sz * instance->max_num_sge;
+ frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE -
1)/MEGAMFI_FRAME_SIZE;
+
+ /*
+ * We need one extra frame for the MFI command
+ */
+ frame_count++;
+
+ total_sz = MEGAMFI_FRAME_SIZE * frame_count;
+ /*
+ * Use DMA pool facility provided by PCI layer
+ */
+ instance->frame_dma_pool = pci_pool_create( "megasas frame pool",
+ instance->pdev, total_sz, 64, 0 );
+
+ if (!instance->frame_dma_pool) {
+ printk( KERN_DEBUG "megasas: failed to setup frame pool\n"
);
+ return -ENOMEM;
+ }
+
+ instance->sense_dma_pool = pci_pool_create( "megasas sense pool",
+ instance->pdev, 128, 4, 0 );
+
+ if (!instance->sense_dma_pool) {
+ printk( KERN_DEBUG "megasas: failed to setup sense pool\n"
);
+
+ pci_pool_destroy( instance->frame_dma_pool );
+ instance->frame_dma_pool = NULL;
+
+ return -ENOMEM;
+ }
+
+ /*
+ * Allocate and attach a frame to each of the commands in cmd_list.
+ * By making cmd->index as the context instead of the &cmd, we can
+ * always use 32bit context regardless of the architecture
+ */
+ for( i = 0; i < max_cmd; i++ ) {
+
+ cmd = instance->cmd_list[i];
+
+ cmd->frame = pci_pool_alloc( instance->frame_dma_pool,
+ GFP_KERNEL, &cmd->frame_phys_addr );
+
+ cmd->sense = pci_pool_alloc( instance->sense_dma_pool,
+ GFP_KERNEL, &cmd->sense_phys_addr );
+
+ /*
+ * megasas_teardown_frame_pool() takes care of freeing
+ * whatever has been allocated
+ */
+ if (!cmd->frame || !cmd->sense) {
+ printk(KERN_DEBUG "megasas: pci_pool_alloc failed
\n");
+ megasas_teardown_frame_pool( instance );
+ return -ENOMEM;
+ }
+
+ cmd->frame->io.context = cmd->index;
+ }
+
+ return 0;
+}
+
+/**
+ * megasas_free_cmds - Free all the cmds in the free cmd pool
+ * @instance: Adapter soft state
+ */
+static void
+megasas_free_cmds( struct megasas_instance* instance )
+{
+ int i;
+ /* First free the MFI frame pool */
+ megasas_teardown_frame_pool( instance );
+
+ /* Free all the commands in the cmd_list */
+ for (i = 0; i < instance->max_fw_cmds; i++ )
+ kfree( instance->cmd_list[i] );
+
+ /* Free the cmd_list buffer itself */
+ kfree( instance->cmd_list );
+ instance->cmd_list = NULL;
+
+ INIT_LIST_HEAD( &instance->cmd_pool );
+}
+
+/**
+ * megasas_alloc_cmds - Allocates the command packets
+ * @instance: Adapter soft state
+ *
+ * Each command that is issued to the FW, whether IO commands from the OS
or
+ * internal commands like IOCTLs, are wrapped in local data structure
called
+ * megasas_cmd. The frame embedded in this megasas_cmd is actually issued
to
+ * the FW.
+ *
+ * Each frame has a 32-bit field called context (tag). This context is used
+ * to get back the megasas_cmd from the frame when a frame gets completed
in
+ * the ISR. Typically the address of the megasas_cmd itself would be used
as
+ * the context. But we wanted to keep the differences between 32 and 64 bit
+ * systems to the mininum. We always use 32 bit integers for the context.
In
+ * this driver, the 32 bit values are the indices into an array cmd_list.
+ * This array is used only to look up the megasas_cmd given the context.
The
+ * free commands themselves are maintained in a linked list called
cmd_pool.
+ */
+static int
+megasas_alloc_cmds( struct megasas_instance* instance )
+{
+ int i;
+ u32 max_cmd;
+ struct megasas_cmd* cmd;
+
+ max_cmd = instance->max_fw_cmds;
+
+ /*
+ * instance->cmd_list is an array of struct megasas_cmd pointers.
+ * Allocate the dynamic array first and then allocate individual
+ * commands.
+ */
+ instance->cmd_list = kmalloc( sizeof(struct megasas_cmd*) * max_cmd,
+ GFP_KERNEL
);
+
+ if (!instance->cmd_list) {
+ printk( KERN_DEBUG "megasas: out of memory\n" );
+ return -ENOMEM;
+ }
+
+ memset( instance->cmd_list, 0, sizeof(struct megasas_cmd*) * max_cmd
);
+
+ for( i = 0; i < max_cmd; i++ ) {
+ instance->cmd_list[i] = kmalloc( sizeof(struct megasas_cmd),
+ GFP_KERNEL
);
+
+ if (!instance->cmd_list[i]) {
+ int j;
+
+ for (j = 0; j < i; j++)
+ kfree( instance->cmd_list[j] );
+
+ kfree( instance->cmd_list );
+ instance->cmd_list = NULL;
+
+ return -ENOMEM;
+ }
+ }
+
+ /*
+ * Add all the commands to command pool (instance->cmd_pool)
+ */
+ for( i = 0; i < max_cmd; i++ ) {
+ cmd = instance->cmd_list[i];
+ memset( cmd, 0, sizeof(struct megasas_cmd));
+ cmd->index = i;
+
+ list_add_tail( &cmd->list, &instance->cmd_pool );
+ }
+
+ /*
+ * Create a frame pool and assign one frame to each cmd
+ */
+ if (megasas_create_frame_pool( instance )) {
+ printk(KERN_DEBUG "megasas: Error creating frame DMA
pool\n");
+ megasas_free_cmds( instance );
+ }
+
+ return 0;
+}
+
+/**
+ * megasas_get_controller_info - Returns FW's controller structure
+ * @instance: Adapter soft state
+ * @ctrl_info: Controller information structure
+ *
+ * Issues an internal command (DCMD) to get the FW's controller structure.
+ * This information is mainly used to find out the maximum IO transfer per
+ * command supported by the FW.
+ */
+static int
+megasas_get_ctrl_info( struct megasas_instance* instance,
+ struct megasas_ctrl_info* ctrl_info )
+{
+ int ret = 0;
+ struct megasas_cmd* cmd;
+ struct megasas_dcmd_frame* dcmd;
+ struct megasas_ctrl_info* ci;
+ dma_addr_t ci_h;
+
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd) {
+ printk( KERN_DEBUG "megasas: Failed to get a free cmd\n");
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ ci = pci_alloc_consistent( instance->pdev,
+ sizeof(struct megasas_ctrl_info), &ci_h );
+
+ if (!ci) {
+ printk( KERN_DEBUG "Failed to alloc mem for ctrl info\n" );
+ megasas_return_cmd( instance, cmd );
+ return -ENOMEM;
+ }
+
+ memset( ci, 0, sizeof(*ci));
+ memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0xFF;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = sizeof(struct megasas_ctrl_info);
+ dcmd->opcode = MR_DCMD_CTRL_GET_INFO;
+ dcmd->sgl.sge32[0].phys_addr = ci_h;
+ dcmd->sgl.sge32[0].length = sizeof(struct megasas_ctrl_info);
+
+ if (!megasas_issue_polled( instance, cmd )) {
+ ret = 0;
+ memcpy( ctrl_info, ci, sizeof(struct megasas_ctrl_info));
+ }
+ else {
+ ret = -1;
+ }
+
+ pci_free_consistent( instance->pdev, sizeof(struct
megasas_ctrl_info),
+ ci, ci_h );
+
+ megasas_return_cmd( instance, cmd );
+ return ret;
+}
+
+/**
+ * megasas_init_mfi - Initializes the FW
+ * @instance: Adapter soft state
+ *
+ * This is the main function for initializing MFI firmware.
+ */
+static int
+megasas_init_mfi( struct megasas_instance* instance )
+{
+ u32 context_sz;
+ u32 reply_q_sz;
+ u32 max_sectors_1;
+ u32 max_sectors_2;
+ struct megasas_register_set* 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;
+ dma_addr_t instance_h;
+
+ /*
+ * Map the message registers
+ */
+ instance->base_addr = pci_resource_start(instance->pdev, 0);
+
+ if (pci_request_regions(instance->pdev, "megasas: LSI Logic")) {
+ printk( KERN_DEBUG "megasas: IO memory region busy!\n");
+ return -EBUSY;
+ }
+
+ instance->reg_set = (struct megasas_register_set*) ioremap_nocache(
+ instance->base_addr, 8192);
+
+ if (!instance->reg_set) {
+ printk( KERN_DEBUG "megasas: Failed to map IO mem\n" );
+ goto fail_ioremap;
+ }
+
+ reg_set = instance->reg_set;
+
+ /*
+ * We expect the FW state to be READY
+ */
+ if (megasas_transition_to_ready(instance->reg_set))
+ goto fail_ready_state;
+
+ /*
+ * Get various operational parameters from status register
+ */
+ instance->max_num_sge = MFI_MAX_SUPP_SGES(reg_set);
+ instance->max_fw_cmds = MFI_MAX_SUPP_CMDS(reg_set);
+
+ /*
+ * Create a pool of commands
+ */
+ if (megasas_alloc_cmds(instance))
+ goto fail_alloc_cmds;
+
+ /*
+ * Allocate memory for reply queue. Length of reply queue should
+ * be _one_ more than the maximum commands handled by the firmware.
+ *
+ * Note: When FW completes commands, it places corresponding contex
+ * values in this circular reply queue. This circular queue is a
fairly
+ * typical producer-consumer queue. FW is the producer (of completed
+ * commands) and the driver is the consumer.
+ */
+ context_sz = sizeof(u32);
+ reply_q_sz = context_sz * (instance->max_fw_cmds + 1);
+
+ instance->reply_queue = pci_alloc_consistent( instance->pdev,
+ reply_q_sz, &instance->reply_queue_phys_addr
);
+
+ if (!instance->reply_queue) {
+ printk( KERN_DEBUG "megasas: Out of DMA mem for reply
queue\n");
+ 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);
+
+ instance_h = instance->phys_addr;
+ 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_phys_addr;
+
+ initq_info->producer_index_phys_addr_lo = instance_h + offsetof(
+ struct
megasas_instance,
+ producer);
+
+ initq_info->consumer_index_phys_addr_lo = instance_h + offsetof(
+ struct
megasas_instance,
+ consumer);
+
+ 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);
+
+ /*
+ * Issue the init frame in polled mode
+ */
+ if (megasas_issue_polled(instance, cmd )) {
+ printk( KERN_DEBUG "megasas: Failed to init firmware\n" );
+ goto fail_fw_init;
+ }
+
+ megasas_return_cmd( instance, cmd );
+
+ ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);
+
+ /*
+ * Compue the max allowed sectors per IO: The controller info has
two
+ * limits on max sectors. Driver should use the minimum of these
two.
+ *
+ * 1 << stripe_sz_ops.min = max sectors per strip
+ *
+ * Note that older firmwares ( < FW ver 30) didn't report
information
+ * to calculate max_sectors_1. So the number ended up as zero
always.
+ */
+ if (ctrl_info && !megasas_get_ctrl_info( instance, ctrl_info )) {
+
+ max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) *
+ ctrl_info->max_strips_per_io;
+ max_sectors_2 = ctrl_info->max_request_size;
+
+ /*
+ * Reasonable size to support older firmwares
+ */
+ if (!max_sectors_1)
+ max_sectors_1 = 2048; /* 1 MB */
+
+ instance->max_sectors_per_req = (max_sectors_1 <
max_sectors_2)
+ ? max_sectors_1 : max_sectors_2;
+ }
+ else
+ instance->max_sectors_per_req = instance->max_num_sge *
+ PAGE_SIZE / 512;
+
+ kfree( ctrl_info );
+
+ return 0;
+
+fail_fw_init:
+ megasas_return_cmd( instance, cmd );
+
+ pci_free_consistent( instance->pdev, reply_q_sz,
+ instance->reply_queue,
+ instance->reply_queue_phys_addr );
+fail_reply_queue:
+ megasas_free_cmds( instance );
+
+fail_alloc_cmds:
+fail_ready_state:
+ iounmap( instance->reg_set );
+
+fail_ioremap:
+ pci_release_regions( instance->pdev );
+
+ return -EINVAL;
+}
+
+/**
+ * megasas_release_mfi - Reverses the FW initialization
+ * @intance: Adapter soft state
+ */
+static void
+megasas_release_mfi( struct megasas_instance* instance )
+{
+ u32 reply_q_sz = sizeof(u32) * instance->max_fw_cmds;
+
+ pci_free_consistent( instance->pdev, reply_q_sz,
+ instance->reply_queue,
+ instance->reply_queue_phys_addr );
+
+ megasas_free_cmds( instance );
+
+ iounmap( instance->reg_set );
+
+ pci_release_regions( instance->pdev );
+}
+
+/**
+ * megasas_get_seq_num - Gets latest event sequence numbers
+ * @instance: Adapter soft state
+ * @eli: FW event log sequence numbers information
+ *
+ * FW maintains a log of all events in a non-volatile area. Upper layers
would
+ * usually find out the latest sequence number of the events, the seq
number at
+ * the boot etc. They would "read" all the events below the latest seq
number
+ * by issuing a direct fw cmd (DCMD). For the future events (beyond latest
seq
+ * number), they would subsribe to AEN (asynchronous event notification)
and
+ * wait for the events to happen.
+ */
+static int
+megasas_get_seq_num( struct megasas_instance* instance,
+ struct megasas_evt_log_info* eli)
+{
+ struct megasas_cmd* cmd;
+ struct megasas_dcmd_frame* dcmd;
+ struct megasas_evt_log_info* el_info;
+ dma_addr_t el_info_h;
+
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd) {
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+ el_info = pci_alloc_consistent( instance->pdev,
+ sizeof(struct megasas_evt_log_info),
+ &el_info_h );
+
+ if (!el_info) {
+ megasas_return_cmd( instance, cmd );
+ return -ENOMEM;
+ }
+
+ memset( el_info, 0, sizeof(*el_info) );
+ memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0x0;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = sizeof(struct
megasas_evt_log_info);
+ dcmd->opcode = MR_DCMD_CTRL_EVENT_GET_INFO;
+ dcmd->sgl.sge32[0].phys_addr = el_info_h;
+ dcmd->sgl.sge32[0].length = sizeof(struct
megasas_evt_log_info);
+
+ megasas_issue_blocked_cmd( instance, cmd );
+
+ /*
+ * Copy the data back into callers buffer
+ */
+ memcpy( eli, el_info, sizeof(struct megasas_evt_log_info) );
+
+ pci_free_consistent(instance->pdev, sizeof(struct
megasas_evt_log_info),
+ el_info, el_info_h);
+
+ megasas_return_cmd( instance, cmd );
+
+ return 0;
+}
+
+/**
+ * megasas_register_aen - Registers for asynchronous event
notification
+ * @instance: Adapter soft state
+ * @seq_num: The starting sequence number
+ * @locale: Class of the event
+ *
+ * This function subscribes for AEN for events beyond the @seq_num. It
requests
+ * to be notified if and only if the event is of type @locale
+ */
+static int
+megasas_register_aen( struct megasas_instance* instance, u32 seq_num,
+ u32 locale )
+{
+ struct megasas_cmd* cmd;
+ struct megasas_dcmd_frame* dcmd;
+ struct megasas_evt_detail* evt_detail;
+ dma_addr_t evt_detail_h;
+ u32* mbox_word;
+
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd) {
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+ mbox_word = (u32*) dcmd->mbox;
+ evt_detail = &instance->evt_detail;
+ evt_detail_h = instance->phys_addr +
+ offsetof(struct megasas_instance,
evt_detail);
+
+ memset( evt_detail, 0, sizeof(*evt_detail));
+
+ /*
+ * Prepare DCMD for aen registration
+ */
+ memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0x0;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = sizeof(struct megasas_evt_detail);
+ dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT;
+ mbox_word[0] = seq_num;
+ mbox_word[1] = locale;
+ dcmd->sgl.sge32[0].phys_addr = (u32)(evt_detail_h & 0xFFFF);
+ dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail);
+
+ /*
+ * Store reference to the cmd used to register for AEN. When an
+ * application wants us to register for AEN, we have to abort this
+ * cmd and re-register with a new EVENT LOCALE supplied by that app
+ */
+ instance->aen_cmd = cmd;
+
+ /*
+ * Issue the aen registration frame
+ */
+ WR_IN_QPORT( (cmd->frame_phys_addr >> 3), instance->reg_set );
+
+ return 0;
+}
+
+/**
+ * megasas_start_aen - Subscribes to AEN during driver load time
+ * @instance: Adapter soft state
+ */
+static int
+megasas_start_aen( struct megasas_instance* instance )
+{
+ struct megasas_evt_log_info eli;
+ union megasas_evt_class_locale class_locale;
+
+ /*
+ * Get the latest sequence number from FW
+ */
+ memset( &eli, 0, sizeof(eli) );
+
+ if (megasas_get_seq_num( instance, &eli ))
+ return -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;
+
+ return megasas_register_aen( instance, eli.newest_seq_num + 1,
+ class_locale.word );
+}
+
+/**
+ * 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;
+
+ host = scsi_host_alloc(&megasas_template_g, sizeof(void*));
+
+ if (!host) {
+ printk( KERN_DEBUG "megasas: scsi_host_alloc failed\n" );
+ return -ENODEV;
+ }
+
+ SCSIHOST2ADAP(host) = (caddr_t) instance;
+ instance->host = host;
+
+ /*
+ * Export parameters required by SCSI mid-layer
+ */
+ scsi_assign_lock( host, instance->host_lock );
+ scsi_set_device( host, &instance->pdev->dev );
+
+ host->irq = instance->pdev->irq;
+ host->unique_id = instance->unique_id;
+ host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS;
+ host->this_id = instance->init_id;
+ host->sg_tablesize = instance->max_num_sge;
+ host->max_sectors = instance->max_sectors_per_req;
+ host->cmd_per_lun = instance->max_fw_cmds - MEGASAS_INT_CMDS;
+ host->max_channel = MEGASAS_MAX_CHANNELS - 1;
+ host->max_id = MEGASAS_MAX_DEV_PER_CHANNEL;
+ host->max_lun = MEGASAS_MAX_LUN;
+
+ /*
+ * Notify the mid-layer about the new controller
+ */
+ if (scsi_add_host(host, &instance->pdev->dev)) {
+
+ printk( KERN_DEBUG "megasas: scsi_add_host failed\n" );
+ scsi_host_put( host );
+
+ return -ENODEV;
+ }
+
+ /*
+ * Trigger SCSI to scan our drives
+ */
+ scsi_scan_host( host );
+
+ return 0;
+}
+
+/**
+ * megasas_io_detach - Detach the driver from SCSI mid-layer
+ * @instance: Adapter soft state
+ */
+static void
+megasas_io_detach( struct megasas_instance* instance )
+{
+ if (instance->host)
+ scsi_remove_host( instance->host );
+}
+
+/**
+ * megasas_probe_one - PCI hotplug entry point
+ * @pdev: PCI device structure
+ * @id: PCI ids of supported hotplugged adapter
+ */
+static int __devinit
+megasas_probe_one( struct pci_dev *pdev, const struct pci_device_id *id )
+{
+ int rval;
+ dma_addr_t instance_h;
+ struct megasas_instance* instance;
+
+ /*
+ * Announce PCI information
+ */
+ printk( KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+
+ printk( KERN_INFO "megasas: bus %d:slot %d:func %d\n",
+ pdev->bus->number,
PCI_SLOT(pdev->devfn),PCI_FUNC(pdev->devfn));
+
+ /*
+ * PCI prepping: enable device set bus mastering and dma mask
+ */
+ rval = pci_enable_device(pdev);
+
+ if (rval) {
+ return rval;
+ }
+
+ 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;
+ }
+
+ /*
+ * We allocate DMA memory for instance soft state so that we can
+ * can directly pass adp->{member variable} to FW to get FW data.
+ * E.g, product information, configuration data etc.
+ */
+ instance = pci_alloc_consistent( pdev, sizeof(struct
megasas_instance),
+ &instance_h
);
+
+ if (!instance) {
+ printk(KERN_DEBUG "megasas: Failed to allocate memory \
+ for controller soft state \n" );
+ goto fail_alloc_instance;
+ }
+
+ memset( instance, 0, sizeof(*instance) );
+
+ /*
+ * Initialize locks and queues
+ */
+ INIT_LIST_HEAD( &instance->cmd_pool );
+
+ init_waitqueue_head( &instance->int_cmd_wait_q );
+ init_waitqueue_head( &instance->abort_cmd_wait_q );
+
+ spin_lock_init( &instance->cmd_pool_lock );
+ spin_lock_init( &instance->lock );
+
+ instance->host_lock = &instance->lock;
+
+ /*
+ * Initialize PCI related and misc parameters
+ */
+ instance->phys_addr = instance_h;
+ instance->pdev = pdev;
+ instance->unique_id = pdev->bus->number << 8 | pdev->devfn;
+ instance->init_id = MEGASAS_DEFAULT_INIT_ID;
+ instance->aen_cmd = NULL;
+
+ /*
+ * Initialize MFI Firmware
+ */
+ if (megasas_init_mfi( instance ))
+ goto fail_init_mfi;
+
+ /*
+ * Register IRQ
+ */
+ if (request_irq(pdev->irq, megasas_isr, SA_SHIRQ, "megasas",
+ instance)) {
+ printk( KERN_DEBUG "megasas: Failed to register IRQ\n" );
+ goto fail_irq;
+ }
+
+ MFI_ENABLE_INTR( instance->reg_set );
+
+ /*
+ * Store instance in PCI softstate
+ */
+ pci_set_drvdata( pdev, instance );
+
+ /*
+ * Add this controller to megasas_mgmt_info structure so that it
+ * can be exported to management applications
+ */
+ megasas_mgmt_info.count++;
+ megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = instance;
+ megasas_mgmt_info.max_index++;
+
+ /*
+ * Initiate AEN (Asynchronous Event Notification)
+ */
+ megasas_start_aen(instance);
+
+ /*
+ * Register with SCSI mid-layer
+ */
+ if (megasas_io_attach( instance ))
+ goto fail_io_attach;
+
+ return 0;
+
+fail_io_attach:
+ megasas_mgmt_info.count--;
+ megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL;
+ megasas_mgmt_info.max_index--;
+
+ pci_set_drvdata( pdev, NULL );
+ MFI_DISABLE_INTR(instance->reg_set);
+ free_irq( instance->pdev->irq, instance );
+
+ megasas_release_mfi( instance );
+
+fail_irq:
+fail_init_mfi:
+ pci_free_consistent( pdev, sizeof(struct megasas_instance),
+ instance, instance_h );
+fail_alloc_instance:
+fail_set_dma_mask:
+ pci_disable_device( pdev );
+
+ return -ENODEV;
+}
+
+/**
+ * megasas_flush_cache - Requests FW to flush all its caches
+ * @instance: Adapter soft state
+ */
+static void
+megasas_flush_cache( struct megasas_instance* instance )
+{
+ struct megasas_cmd* cmd;
+ struct megasas_dcmd_frame* dcmd;
+
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd)
+ return;
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0x0;
+ dcmd->sge_count = 0;
+ dcmd->flags = MFI_FRAME_DIR_NONE;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = 0;
+ dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH;
+ dcmd->mbox[0] = MR_FLUSH_CTRL_CACHE |
+ MR_FLUSH_DISK_CACHE;
+
+ megasas_issue_blocked_cmd( instance, cmd );
+
+ megasas_return_cmd( instance, cmd );
+
+ return;
+}
+
+/**
+ * megasas_shutdown_controller - Instructs FW to shutdown the
controller
+ * @instance: Adapter soft state
+ */
+static void
+megasas_shutdown_controller( struct megasas_instance* instance )
+{
+ struct megasas_cmd* cmd;
+ struct megasas_dcmd_frame* dcmd;
+
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd);
+ return;
+
+ if (instance->aen_cmd)
+ megasas_issue_blocked_abort_cmd( instance, instance->aen_cmd
);
+
+ dcmd = &cmd->frame->dcmd;
+
+ memset( dcmd->mbox, 0, MFI_MBOX_SIZE );
+
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0x0;
+ dcmd->sge_count = 0;
+ dcmd->flags = MFI_FRAME_DIR_NONE;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = 0;
+ dcmd->opcode = MR_DCMD_CTRL_SHUTDOWN;
+
+ megasas_issue_blocked_cmd( instance, cmd );
+
+ megasas_return_cmd( instance, cmd );
+
+ return;
+}
+
+/**
+ * megasas_detach_one - PCI hot"un"plug entry point
+ * @pdev: PCI device structure
+ */
+static void
+megasas_detach_one( struct pci_dev *pdev )
+{
+ int i;
+ struct Scsi_Host* host;
+ struct megasas_instance* instance;
+ dma_addr_t instance_h;
+
+ instance = pci_get_drvdata( pdev );
+ host = instance->host;
+ instance_h = instance->phys_addr;
+
+ megasas_io_detach( instance );
+
+ megasas_flush_cache( instance );
+ megasas_shutdown_controller( instance );
+
+ /*
+ * Take the instance off the instance array. Note that we will not
+ * decrement the max_index. We let this array be sparse array
+ */
+ for (i = 0; i < megasas_mgmt_info.max_index; i++ ) {
+ if (megasas_mgmt_info.instance[i] == instance) {
+ megasas_mgmt_info.count--;
+ megasas_mgmt_info.instance[i] = NULL;
+
+ break;
+ }
+ }
+
+ pci_set_drvdata( instance->pdev, NULL );
+
+ MFI_DISABLE_INTR(instance->reg_set);
+
+ free_irq( instance->pdev->irq, instance );
+
+ megasas_release_mfi( instance );
+
+ pci_free_consistent( instance->pdev, sizeof(struct
megasas_instance),
+ instance, instance_h
);
+ scsi_host_put( host );
+
+ pci_set_drvdata( pdev, NULL );
+
+ pci_disable_device( pdev );
+
+ return;
+}
+
+/**
+ * megasas_shutdown - Shutdown entry point
+ * @device: Generic device structure
+ */
+static void
+megasas_shutdown( struct device* device )
+{
+ int i;
+ struct megasas_instance* instance;
+
+ for( i = 0; i < megasas_mgmt_info.max_index; i++ ) {
+ instance = megasas_mgmt_info.instance[i];
+
+ if (instance)
+ megasas_shutdown_controller( instance );
+ }
+}
+
+/**
+ * megasas_mgmt_open - char node "open" entry point
+ */
+static int
+megasas_mgmt_open( struct inode* inode, struct file* filep )
+{
+ /*
+ * Allow only those users with admin rights
+ */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ return 0;
+}
+
+/**
+ * megasas_mgmt_release - char node "release" entry point
+ */
+static int
+megasas_mgmt_release( struct inode* inode, struct file* filep )
+{
+ return 0;
+}
+
+/**
+ * megasas_mgmt_fasync - Async notifier registration from
applications
+ *
+ * This function adds the calling process to a driver global queue. When an
+ * event occurs, SIGIO will be sent to all processes in this queue.
+ */
+static int
+megasas_mgmt_fasync( int fd, struct file* filep, int mode )
+{
+ int rc;
+
+ down( &megasas_async_queue_mutex );
+
+ rc = fasync_helper( fd, filep, mode, &megasas_async_queue );
+
+ up( &megasas_async_queue_mutex );
+
+ if (rc >0)
+ return 0;
+
+ printk( KERN_DEBUG "megasas: fasync_helper failed [%d]\n", rc );
+
+ return rc;
+}
+
+/**
+ * megasas_mgmt_fw_dcmd - Issues DCMD to the FW
+ * @instance: Adapter soft state
+ * @uioc: User's ioctl packet copied into kernel addr
+ * @argp: User's ioctl packet in user address
+ * @cmd: Command to be prepared and issued
+ *
+ * This function prepares direct command (DCMD) to FW from user's ioctl
packet.
+ * The driver allocates temporary buffer (if needed) for data transfer
+ *
+ * Note: The suffixes 'k' & 'u' mean 'kerne' and 'user' respectively
+ */
+static int
+megasas_mgmt_fw_dcmd( struct megasas_instance* instance, struct iocpacket*
uioc,
+ void __user* argp, struct megasas_cmd* cmd )
+{
+ int rc = 0;
+ void __user* ubuff;
+ struct megasas_dcmd_frame* kdcmd;
+ struct megasas_dcmd_frame __user* udcmd;
+ struct megasas_dcmd_frame* cmd_dcmd;
+ caddr_t kbuff;
+ dma_addr_t kbuff_h;
+ u32 xferlen;
+ u8 user_64bit_sgl = 0;
+
+ cmd_dcmd = &cmd->frame->dcmd;
+ kdcmd = (struct megasas_dcmd_frame*) &uioc->frame;
+ udcmd = (struct megasas_dcmd_frame*)
+ (((struct iocpacket*)argp)->frame);
+
+ if (kdcmd->flags & MFI_FRAME_SGL64 )
+ user_64bit_sgl = 1;
+
+ if (!user_64bit_sgl) {
+ xferlen = kdcmd->sgl.sge32[0].length;
+ ubuff = (void __user*) (ulong)
udcmd->sgl.sge32[0].phys_addr;
+ }
+ else {
+ xferlen = kdcmd->sgl.sge64[0].length;
+ ubuff = (void __user*) (ulong)
udcmd->sgl.sge64[0].phys_addr;
+ }
+
+ /*
+ * Allocate internal buffer for data transfer
+ */
+ if (xferlen)
+ kbuff = pci_alloc_consistent(instance->pdev, xferlen,
&kbuff_h);
+ else
+ kbuff = NULL;
+
+ if (xferlen && !kbuff) {
+ printk( KERN_DEBUG "megasas: Failed to allocate memory for \
+ DCMD internal buffer \n" );
+ return -ENOMEM;
+ }
+
+ if (xferlen && (kdcmd->flags & MFI_FRAME_DIR_WRITE)) {
+
+ if (copy_from_user(kbuff, ubuff, xferlen)) {
+ printk( KERN_DEBUG "megasas: Failed to copy from \
+ application
buffer\n");
+ rc = -EFAULT;
+ goto exit_label;
+ }
+ }
+
+ /*
+ * Copy the frame sent by user into driver's frame
+ */
+ cmd_dcmd->cmd = kdcmd->cmd;
+ cmd_dcmd->cmd_status = kdcmd->cmd_status;
+ cmd_dcmd->sge_count = kdcmd->sge_count;
+ cmd_dcmd->timeout = kdcmd->timeout;
+ cmd_dcmd->data_xfer_len = kdcmd->data_xfer_len;
+ cmd_dcmd->opcode = kdcmd->opcode;
+
+ memcpy( cmd_dcmd->mbox, kdcmd->mbox, MFI_MBOX_SIZE );
+
+ if (!user_64bit_sgl) {
+ cmd_dcmd->flags = kdcmd->flags;
+ cmd_dcmd->sgl.sge32[0].length =
kdcmd->sgl.sge32[0].length;
+ cmd_dcmd->sgl.sge32[0].phys_addr= kbuff_h;
+ }
+ else {
+ cmd_dcmd->flags = kdcmd->flags
|MFI_FRAME_SGL64;
+ cmd_dcmd->sgl.sge64[0].length =
kdcmd->sgl.sge64[0].length;
+ cmd_dcmd->sgl.sge64[0].phys_addr= kbuff_h;
+ }
+
+ megasas_issue_blocked_cmd( instance, cmd );) {
+
+ if (copy_to_user( ubuff, kbuff, xferlen)) {
+
+ printk( KERN_DEBUG "megasas: Failed to copy \
+ to application buffer\n");
+ rc = -EFAULT;
+ goto exit_label;
+ }
+ }
+
+ if (copy_to_user( &udcmd->cmd_status, &cmd_dcmd->cmd_status,
+ sizeof(u8))) {
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer\n");
+ rc = -EFAULT;
+ goto exit_label;
+ }
+
+exit_label:
+ pci_free_consistent( instance->pdev, xferlen, kbuff, kbuff_h );
+ return rc;
+}
+
+/**
+ * megasas_mgmt_fw_dcdb - Prepares and issues DCDB
+ * @instance: Adapter soft state
+ * @uioc: User's ioctl packet copied into kernel addr
+ * @argp: User's ioctl packet in user addr
+ * @cmd: Free command from the command pool
+ *
+ * Note that the suffixes 'k' and 'u' mean 'kernel' and 'user'
respectively.
+ */
+static int
+megasas_mgmt_fw_dcdb( struct megasas_instance* instance, struct iocpacket*
uioc,
+ void __user* argp, struct megasas_cmd* cmd )
+{
+ int rc = 0;
+ void __user* ubuff;
+ void __user* usense;
+ struct megasas_pthru_frame* kdcdb;
+ struct megasas_pthru_frame __user* udcdb;
+ struct megasas_pthru_frame* cmd_dcdb;
+ caddr_t kbuff;
+ dma_addr_t kbuff_h;
+ caddr_t ksense;
+ dma_addr_t ksense_h;
+ u32 xferlen;
+ u8 user_64bit_sgl = 0;
+ u64 temp;
+
+ ksense = NULL;
+ usense = NULL;
+ cmd_dcdb = &cmd->frame->pthru;
+ kdcdb = (struct megasas_pthru_frame*) &uioc->frame;
+ udcdb = (struct megasas_pthru_frame*)
+ (((struct iocpacket*)argp)->frame);
+
+ if (kdcdb->flags & MFI_FRAME_SGL64 )
+ user_64bit_sgl = 1;
+
+ if (!user_64bit_sgl) {
+ xferlen = kdcdb->sgl.sge32[0].length;
+ ubuff = (void __user*)(ulong)
(udcdb->sgl.sge32[0].phys_addr);
+ }
+ else {
+ xferlen = kdcdb->sgl.sge64[0].length;
+ ubuff = (void __user*) (ulong)
udcdb->sgl.sge64[0].phys_addr;
+ }
+
+ /*
+ * Allocate internal buffer for data transfer
+ */
+ if (xferlen)
+ kbuff = pci_alloc_consistent(instance->pdev, xferlen,
&kbuff_h);
+ else
+ kbuff = NULL;
+
+ if (xferlen && !kbuff) {
+ printk( KERN_DEBUG "megasas: Failed to allocate memory \
+ for DCDB internal buffer\n"
);
+ return -ENOMEM;
+ }
+
+ memset( kbuff, 0, xferlen );
+
+ /*
+ * Allocate internal buffer for request sense
+ */
+ if (kdcdb->sense_len) {
+ ksense = pci_alloc_consistent(instance->pdev,
kdcdb->sense_len,
+ &ksense_h);
+ if (!ksense) {
+ rc = -ENOMEM;
+ goto exit_label;
+ }
+
+ temp = kdcdb->sense_buf_phys_addr_hi;
+ temp = temp << 32 | kdcdb->sense_buf_phys_addr_lo;
+
+ usense = (void __user*)(ulong) temp;
+
+ }
+
+ if (xferlen && (kdcdb->flags & MFI_FRAME_DIR_WRITE)) {
+
+ if (copy_from_user(kbuff, ubuff, xferlen)) {
+ printk( KERN_DEBUG "megasas: Failed to copy from \
+ application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+ }
+
+ memcpy( cmd_dcdb, kdcdb, MEGAMFI_FRAME_SIZE );
+ cmd_dcdb->context = cmd->index;
+ cmd_dcdb->sge_count = 1;
+
+ if (!user_64bit_sgl) {
+ cmd_dcdb->flags = kdcdb->flags;
+ cmd_dcdb->sgl.sge32[0].length =
kdcdb->sgl.sge32[0].length;
+ cmd_dcdb->sgl.sge32[0].phys_addr= kbuff_h;
+ }
+ else {
+ cmd_dcdb->flags = kdcdb->flags
|MFI_FRAME_SGL64;
+ cmd_dcdb->sgl.sge64[0].length =
kdcdb->sgl.sge64[0].length;
+ cmd_dcdb->sgl.sge64[0].phys_addr= kbuff_h;
+ }
+
+ cmd_dcdb->sense_buf_phys_addr_hi = 0;
+ cmd_dcdb->sense_buf_phys_addr_lo = ksense_h ;
+
+ cmd->sync_cmd = 1;
+
+ megasas_issue_blocked_cmd( instance, cmd );
+
+ if (xferlen && (kdcdb->flags & MFI_FRAME_DIR_READ)) {
+
+ if (copy_to_user( ubuff, kbuff, xferlen)) {
+
+ printk(KERN_DEBUG "megasas: Failed to copy \
+ to application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+ }
+
+ if (copy_to_user( &udcdb->cmd_status, &cmd_dcdb->cmd_status,
+ sizeof(u8))) {
+ printk(KERN_DEBUG "megasas: Failed to copy to \
+ application buffer\n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+
+ if (kdcdb->sense_len) {
+ if (copy_to_user( usense, ksense, kdcdb->sense_len)) {
+ printk( KERN_DEBUG "megasas: Failed to copy \
+ to application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+ }
+
+exit_label:
+ if (ksense)
+ pci_free_consistent( instance->pdev, kdcdb->sense_len,
ksense,
+ ksense_h );
+ if (kbuff)
+ pci_free_consistent( instance->pdev, xferlen, kbuff, kbuff_h
);
+
+ return rc;
+}
+
+/**
+ * megasas_mgmt_fw_smp - Issues passthrough cmds to SAS devices
+ * @instance: Adapter soft state
+ * @uioc: User's ioctl packet in kernel address
+ * @argp: User's ioctl packet in user address
+ * @cmd: Command from free pool
+ *
+ * SMP frames have two SG elements at the end - response and request ( in
that
+ * order). This function allocates temporary DMA buffers for these SG
elements.
+ *
+ * Note that the suffixes 'k' and 'u' mean 'kernel' and 'user'
respectively.
+ */
+static int
+megasas_mgmt_fw_smp( struct megasas_instance* instance, struct iocpacket*
uioc,
+ void __user* argp, struct megasas_cmd* cmd )
+{
+ int rc = 0;
+ struct megasas_smp_frame* ksmp;
+ struct megasas_smp_frame __user* usmp;
+ struct megasas_smp_frame* cmd_smp;
+
+ caddr_t kreq;
+ caddr_t kresp;
+ dma_addr_t kreq_h;
+ dma_addr_t kresp_h;
+ void __user* ureq;
+ void __user* uresp;
+ u32 req_len;
+ u32 resp_len;
+
+ u8 user_64bit_sgl = 0;
+
+ cmd_smp = &cmd->frame->smp;
+ ksmp = (struct megasas_smp_frame*) &uioc->frame;
+ usmp = (struct megasas_smp_frame*)
+ (((struct iocpacket*)argp)->frame);
+
+ if (ksmp->flags & MFI_FRAME_SGL64 )
+ user_64bit_sgl = 1;
+
+ if (!user_64bit_sgl) {
+ resp_len = ksmp->sgl.sge32[0].length;
+ req_len = ksmp->sgl.sge32[1].length;
+
+ uresp = (void __user*) ((ulong)
usmp->sgl.sge32[0].phys_addr);
+ ureq = (void __user*) ((ulong)
usmp->sgl.sge32[1].phys_addr);
+ }
+ else {
+ resp_len = ksmp->sgl.sge64[0].length;
+ req_len = ksmp->sgl.sge64[1].length;
+
+ uresp = (void __user*) ((ulong)
usmp->sgl.sge64[0].phys_addr);
+ ureq = (void __user*) ((ulong)
usmp->sgl.sge64[1].phys_addr);
+ }
+
+ if (!req_len || !resp_len) {
+ return -EINVAL;
+ }
+
+ /*
+ * Allocate kernel buffers for SMP request and response
+ */
+ kreq = NULL;
+ kresp = NULL;
+
+ kreq = pci_alloc_consistent(instance->pdev, req_len, &kreq_h);
+
+ if (!kreq) {
+
+ printk( KERN_DEBUG "megasas: Failed to allocate memory \
+ for SMP request \n"
);
+ rc = -ENOMEM;
+ goto exit_label;
+ }
+
+ kresp = pci_alloc_consistent(instance->pdev, resp_len, &kresp_h);
+
+ if(!kresp) {
+ printk( KERN_DEBUG "megasas: Failed to allocate memory \
+ for SMP response \n"
);
+ rc = -ENOMEM;
+ goto exit_label;
+ }
+
+ if (copy_from_user(kreq, ureq, req_len)) {
+ printk( KERN_DEBUG "megasas: Failed to copy from \
+ application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+
+ memcpy (cmd_smp, ksmp, MEGAMFI_FRAME_SIZE );
+ cmd_smp->context = cmd->index;
+
+ if (!user_64bit_sgl) {
+ cmd_smp->flags = ksmp->flags;
+ cmd_smp->sgl.sge32[0].length = resp_len;
+ cmd_smp->sgl.sge32[0].phys_addr = kresp_h;
+ cmd_smp->sgl.sge32[1].length = req_len;
+ cmd_smp->sgl.sge32[1].phys_addr = kreq_h;
+ }
+ else {
+ cmd_smp->flags = ksmp->flags |
+ MFI_FRAME_SGL64;
+ cmd_smp->sgl.sge64[0].length = resp_len;
+ cmd_smp->sgl.sge64[0].phys_addr = kresp_h;
+ cmd_smp->sgl.sge64[1].length = req_len;
+ cmd_smp->sgl.sge64[1].phys_addr = kreq_h;
+ }
+
+ megasas_issue_blocked_cmd( instance, cmd );
+
+ if (copy_to_user( uresp, kresp, resp_len)) {
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+
+ if (copy_to_user( &usmp->cmd_status, &cmd_smp->cmd_status,
+ sizeof(u8))) {
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+
+exit_label:
+
+ if (kreq)
+ pci_free_consistent(instance->pdev, req_len, kreq, kreq_h);
+ if (kresp)
+ pci_free_consistent(instance->pdev, resp_len, kresp,
kresp_h);
+
+ return rc;
+}
+
+/**
+ * megasas_mgmt_fw_stp - Issues passthrough cmds to SATA drives
+ * @instance: Adapter soft state
+ * @uioc: User ioctl packet in kernel address
+ * @argp: User ioctl packet in user address
+ * @cmd: Command from free pool
+ *
+ * Note that the suffixes 'k' and 'u' mean 'kernel' and 'user'
respectively.
+ */
+static int
+megasas_mgmt_fw_stp( struct megasas_instance* instance, struct iocpacket*
uioc,
+ void __user* argp, struct megasas_cmd* cmd )
+{
+ int rc = 0;
+ struct megasas_stp_frame* kstp;
+ struct megasas_stp_frame __user* ustp;
+ struct megasas_stp_frame* cmd_stp;
+
+ caddr_t kdata;
+ caddr_t kresp;
+ dma_addr_t kdata_h;
+ dma_addr_t kresp_h;
+ void __user* udata;
+ void __user* uresp;
+ u32 data_len;
+ u32 resp_len;
+
+ u8 user_64bit_sgl = 0;
+
+ cmd_stp = &cmd->frame->stp;
+ kstp = (struct megasas_stp_frame *) &uioc->frame;
+ ustp = (struct megasas_stp_frame *)
+ (((struct iocpacket *)argp)->frame);
+
+ if (kstp->flags & MFI_FRAME_SGL64 )
+ user_64bit_sgl = 1;
+
+ if (!user_64bit_sgl) {
+
+ resp_len = ustp->sgl.sge32[0].length;
+ data_len = ustp->sgl.sge32[1].length;
+
+ uresp = (void __user*) ((ulong)
ustp->sgl.sge32[0].phys_addr);
+ udata = (void __user*) ((ulong)
ustp->sgl.sge32[1].phys_addr);
+ }
+ else {
+ resp_len = ustp->sgl.sge64[0].length;
+ data_len = ustp->sgl.sge64[1].length;
+
+ uresp = (void __user*) ((ulong)
ustp->sgl.sge64[0].phys_addr);
+ udata = (void __user*) ((ulong)
ustp->sgl.sge64[1].phys_addr);
+ }
+
+ if (!data_len || !resp_len) {
+ return -EINVAL;
+ }
+
+ /*
+ * Allocate kernel buffers for SMP request and response
+ */
+
+ kdata = NULL;
+ kresp = NULL;
+
+ kdata = pci_alloc_consistent(instance->pdev, data_len, &kdata_h);
+
+ if(!kdata) {
+
+ printk( KERN_DEBUG "megasas: Failed to allocate memory \
+ for STP request \n"
);
+ rc = -ENOMEM;
+ goto exit_label;
+ }
+
+ kresp = pci_alloc_consistent(instance->pdev, resp_len, &kresp_h);
+
+ if(!kresp) {
+ printk( KERN_DEBUG "megasas: Failed to allocate memory \
+ for STP response \n"
);
+ rc = -ENOMEM;
+ goto exit_label;
+ }
+
+ memcpy (cmd_stp, kstp, MEGAMFI_FRAME_SIZE );
+ cmd_stp->context = cmd->index;
+
+ if (!user_64bit_sgl) {
+ cmd_stp->flags = kstp->flags;
+ cmd_stp->sgl.sge32[0].length = resp_len;
+ cmd_stp->sgl.sge32[0].phys_addr = kresp_h;
+ cmd_stp->sgl.sge32[1].length = data_len;
+ cmd_stp->sgl.sge32[1].phys_addr = kdata_h;
+ }
+ else {
+ cmd_stp->flags = kstp->flags |
+ MFI_FRAME_SGL64;
+ cmd_stp->sgl.sge64[0].length = resp_len;
+ cmd_stp->sgl.sge64[0].phys_addr = kresp_h;
+ cmd_stp->sgl.sge64[1].length = data_len;
+ cmd_stp->sgl.sge64[1].phys_addr = kdata_h;
+ }
+
+ megasas_issue_blocked_cmd( instance, cmd );
+
+ if (copy_to_user( uresp, kresp, resp_len)) {
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+
+ if (copy_to_user( udata, kdata, data_len)) {
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+
+ if (copy_to_user( &ustp->cmd_status, &cmd_stp->cmd_status,
+ sizeof(u8))) {
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ rc = -EFAULT;
+ goto exit_label;
+ }
+
+exit_label:
+
+ if (kdata)
+ pci_free_consistent(instance->pdev, data_len, kdata,
kdata_h);
+ if (kresp)
+ pci_free_consistent(instance->pdev, resp_len, kresp,
kresp_h);
+
+ return rc;
+}
+
+/**
+ * megasas_mgmt_fw_ioctl - Issues management ioctls to FW
+ * @instance: Adapter soft state
+ * @argp: User's ioctl packet
+ */
+static int
+megasas_mgmt_fw_ioctl( struct megasas_instance* instance, void __user* argp
)
+{
+ int ret;
+ struct iocpacket* uioc;
+ struct megasas_header* hdr;
+ struct megasas_cmd* cmd;
+
+ uioc = kmalloc( sizeof(struct iocpacket), GFP_KERNEL );
+
+ if (!uioc) {
+ printk( KERN_DEBUG "megasas: Failed to allocate memory \
+ for application IOCTL packet\n" );
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(uioc, argp, sizeof(struct iocpacket))) {
+ printk( KERN_DEBUG "megasas: Failed to copy from \
+ application buffer \n" );
+ return -EFAULT;
+ }
+
+ cmd = megasas_get_cmd( instance );
+
+ if (!cmd) {
+ printk( KERN_DEBUG "megasas: Failed to get a cmd packet\n"
);
+ return -ENOMEM;
+ }
+
+ hdr = (struct megasas_header*) uioc->frame;
+
+ switch( hdr->cmd ) {
+
+ case MFI_CMD_DCMD:
+ ret = megasas_mgmt_fw_dcmd(instance, uioc, argp,
cmd);
+ break;
+
+ case MFI_CMD_PD_SCSI_IO:
+ case MFI_CMD_LD_SCSI_IO:
+ ret = megasas_mgmt_fw_dcdb(instance, uioc, argp,
cmd);
+ break;
+
+ case MFI_CMD_SMP:
+ ret = megasas_mgmt_fw_smp(instance, uioc, argp,
cmd);
+ break;
+
+ case MFI_CMD_STP:
+ ret = megasas_mgmt_fw_stp(instance, uioc, argp,
cmd);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ megasas_return_cmd( instance, cmd );
+ return ret;
+}
+
+/**
+ * megasas_fill_drv_ver - Fills the driver version info for
application
+ * @dv: Driver version information
+ */
+static void
+megasas_fill_drv_ver( struct megasas_drv_ver* dv )
+{
+ memset( dv, 0, sizeof(*dv) );
+
+ memcpy(dv->signature, "$LSI LOGIC$", strlen("$LSI LOGIC$") );
+ memcpy(dv->os_name, "Linux", strlen("Linux") );
+ memcpy(dv->os_ver, "Ver Indpndt", strlen("ver indpndt") );
+ memcpy(dv->drv_name, "megaraid_sas", strlen("megaraid_sas") );
+ memcpy(dv->drv_ver, MEGASAS_VERSION,strlen(MEGASAS_VERSION) );
+ memcpy(dv->drv_rel_date,MEGASAS_RELDATE,strlen(MEGASAS_RELDATE) );
+}
+
+/**
+ * megasas_mgmt_ioctl - char node ioctl entry point
+ *
+ * Few ioctl commands should be handled by driver itself (driver ioctls)
and
+ * the rest should be converted into appropriate commands for FW and
issued.
+ */
+static int
+megasas_mgmt_ioctl( struct inode* inode, struct file* filep,
+ unsigned int cmd, unsigned long arg )
+{
+ int i;
+ int j;
+ int rc;
+ u8 fw_status;
+ struct iocpacket* uioc;
+ void __user* argp;
+ void __user* udata_addr;
+ u8 user_64bit_sgl = 0;
+ u32 opcode;
+ u32 locale;
+ u32 seq_num;
+ u32* mbox_word;
+ struct megasas_cmd* old_cmd;
+
+ struct megasas_instance* instance;
+ struct megasas_dcmd_frame* kdcmd;
+ struct megasas_dcmd_frame __user* udcmd;
+ struct megasas_drv_ver* dv;
+ struct pci_dev* pdev;
+
+ argp = (void __user*) arg;
+ uioc = kmalloc( sizeof(struct iocpacket), GFP_KERNEL );
+
+ if (!uioc) {
+ printk( KERN_DEBUG "megasas: Failed to allocate memory \
+ for application IOCTL packet\n" );
+ return -ENOMEM;
+ }
+
+ if (copy_from_user( uioc, argp, sizeof(struct iocpacket))) {
+ printk( KERN_DEBUG "megasas: Failed to copy from \
+ application buffer \n" );
+ return -EINVAL;
+ }
+
+ if (strncmp(uioc->signature, IOC_SIGNATURE, strlen(IOC_SIGNATURE))
!=0){
+ printk( KERN_DEBUG "megasas: Invalid ioctl signature\n" );
+ return -EINVAL;
+ }
+
+ if (uioc->version != 0) {
+ printk( KERN_DEBUG "megasas: Invalid ioctl version %d\n",
+
uioc->version );
+ return -EINVAL;
+ }
+
+ instance = NULL;
+ kdcmd = (struct megasas_dcmd_frame*) uioc->frame;
+ udcmd = (struct megasas_dcmd_frame*)
+ (((struct iocpacket*)argp)->frame);
+
+ /*
+ * Find out if user has used 32 or 64 bit SGL
+ */
+ if (kdcmd->flags & MFI_FRAME_SGL64 )
+ user_64bit_sgl = 1;
+
+ if (!user_64bit_sgl)
+ udata_addr = (void __user *)
+
((ulong)kdcmd->sgl.sge32[0].phys_addr);
+ else
+ udata_addr = (void __user *)
+ ((ulong)
kdcmd->sgl.sge64[0].phys_addr);
+
+ i = ((uioc->controller_id & 0xF0) >> 4) - 1;
+
+ if (i < megasas_mgmt_info.max_index)
+ instance = megasas_mgmt_info.instance[i];
+ else
+ instance = NULL;
+
+ if ((uioc->control_code == MR_DRIVER_IOCTL_LINUX) ||
+ (uioc->control_code == MR_DRIVER_IOCTL_COMMON)) {
+ /*
+ * If MR_DRIVER_IOCTL_LINUX or MR_DRIVER_IOCTL_COMMON
+ * look at dcmd->opcode for the actual operation
+ */
+ opcode = kdcmd->opcode;
+ }
+ else {
+ /* FW Command */
+ opcode = uioc->control_code;
+ }
+
+ switch (opcode) {
+
+ case MR_DRIVER_IOCTL_DRIVER_VERSION:
+
+ dv = kmalloc( sizeof(struct megasas_drv_ver), GFP_KERNEL );
+
+ if (!dv) {
+ printk( KERN_DEBUG "megasas: Failed to allocate \
+ memory for driver version\n"
);
+ return -ENOMEM;
+ }
+
+ megasas_fill_drv_ver( dv );
+
+ if (copy_to_user(udata_addr, dv,
+ sizeof(struct megasas_drv_ver))) {
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ return -EFAULT;
+ }
+
+ rc = 0;
+ fw_status = MFI_STAT_OK;
+
+ if (copy_to_user( &udcmd->cmd_status, &fw_status,
+ sizeof(u8))) {
+ rc = -EFAULT;
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ }
+
+ break;
+
+ case MR_LINUX_GET_ADAPTER_COUNT:
+
+ if (copy_to_user(udata_addr, &megasas_mgmt_info.count,
+ sizeof(u16))) {
+ return -EFAULT;
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ }
+
+ rc = 0;
+ fw_status = MFI_STAT_OK;
+
+ if (copy_to_user( &udcmd->cmd_status, &fw_status,
+ sizeof(u8))) {
+ rc = -EFAULT;
+ printk( KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ }
+
+ break;
+
+ case MR_LINUX_GET_ADAPTER_MAP:
+ /*
+ * The applications _don't_ address our controllers using
zero
+ * based index. We give them an array of 16-bit unique
handles.
+ * These unique handles are simple encryptions of the
indices
+ *
+ * Encrypting logic - which converts a controller index into
a
+ * 16-bit value - is simply (index + 1) << 4 | 0x0F.
+ *
+ * Note also when controllers are hot plugged, our
controller
+ * array (megasas_mgmt_info) becomes sparse. We don't reuse
the
+ * vacated slots.
+ */
+ memset(megasas_mgmt_info.map, 0,
+ sizeof(u16) * MAX_MGMT_ADAPTERS);
+
+ j = 0;
+ for (i = 0; i < megasas_mgmt_info.max_index; i++) {
+ if (megasas_mgmt_info.instance[i]) {
+ megasas_mgmt_info.map[j].unique_hndl =
+ ((i + 1) << 4) | 0xF;
+
+ pdev = megasas_mgmt_info.instance[i]->pdev;
+
+ megasas_mgmt_info.map[j].bus_devfn =
+ ((pdev->bus->number) << 16 |
+ (PCI_SLOT(pdev->devfn)) << 8 |
+ (PCI_FUNC(pdev->devfn))) & 0xFFFFFF;
+
+ j++;
+ }
+ }
+
+ if ((j) && (copy_to_user(udata_addr, megasas_mgmt_info.map,
+ sizeof(struct megasas_adp_map) * j))) {
+
+ printk(KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ return -EFAULT;
+ }
+
+ fw_status = MFI_STAT_OK;
+ rc = 0;
+
+ if (copy_to_user( &udcmd->cmd_status, &fw_status,
+ sizeof(u8))) {
+ rc = -EFAULT;
+ printk(KERN_DEBUG "megasas: Failed to copy to \
+ application buffer \n" );
+ }
+
+ break;
+
+ case MR_LINUX_GET_AEN:
+
+ if (!instance) {
+ printk( KERN_DEBUG "megasas: Invalid instance \n" );
+ return -ENODEV;
+ }
+
+ mbox_word = (u32*) kdcmd->mbox;
+ seq_num = mbox_word[0];
+ locale = mbox_word[1];
+ old_cmd = instance->aen_cmd;
+
+ /*
+ * If there is an AEN pending already (old_cmd), abort that
+ * before issuing a fresh subsricption for AEN
+ */
+ if (old_cmd) {
+ old_cmd->abort_aen = 1;
+ rc = megasas_issue_blocked_abort_cmd(instance,
old_cmd);
+
+ if (rc) {
+ printk(KERN_DEBUG "megasas: Failed to abort
\
+ previous AEN\n" );
+ break;
+ }
+ }
+
+ rc = megasas_register_aen( instance, seq_num, locale );
+
+ break;
+
+ case IOC_CMD_FIRMWARE:
+
+ if (!instance) {
+ printk( KERN_DEBUG "megasas: Invalid instance \n" );
+ return -ENODEV;
+ }
+
+ rc = megasas_mgmt_fw_ioctl( instance, argp );
+
+ break;
+
+ default:
+ return -ENOTTY;
+ }
+
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+/**
+ * megasas_compat_ioctl - Handles conversions from 32-bit apps
+ */
+static int
+megasas_compat_ioctl( struct file* filep, unsigned int cmd, unsigned long
arg,
+ struct file* filep )
+{
+ return megasas_mgmt_ioctl( NULL, filep, cmd, arg );
+}
+#endif
+
+/*
+ * File operations structure for management interface
+ */
+static struct file_operations megasas_mgmt_fops = {
+ .owner = THIS_MODULE,
+ .open = megasas_mgmt_open,
+ .release = megasas_mgmt_release,
+ .fasync = megasas_mgmt_fasync,
+ .ioctl = megasas_mgmt_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = megasas_compat_ioctl,
+#endif
+};
+
+/*
+ * PCI hotplug support registration structure
+ */
+static struct pci_driver megasas_pci_driver = {
+
+ .name = "megaraid_sas",
+ .id_table = megasas_pci_table_g,
+ .probe = megasas_probe_one,
+ .remove = __devexit_p(megasas_detach_one),
+ .driver = {
+ .shutdown = megasas_shutdown,
+ }
+};
+
+/**
+ * megasas_init - Driver load entry point
+ */
+static int __init
+megasas_init( void )
+{
+ int rval;
+
+ /*
+ * Announce driver version and other information
+ */
+ printk( KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION,
+ MEGASAS_EXT_VERSION);
+
+ /*
+ * Register character device node
+ */
+ rval = register_chrdev(0, "megaraid_sas_ioctl",
&megasas_mgmt_fops);
+
+ if (rval < 0) {
+ printk( KERN_DEBUG "megasas: failed to open device node\n"
);
+ return rval;
+ }
+
+ megasas_mgmt_majorno = rval;
+
+ /*
+ * Register ourselves as PCI hotplug module
+ */
+ rval = pci_module_init( &megasas_pci_driver );
+
+ if( rval ) {
+ printk(KERN_DEBUG "megasas: PCI hotplug regisration failed
\n");
+ unregister_chrdev(megasas_mgmt_majorno,
"megaraid_sas_ioctl");
+ }
+
+ return rval;
+}
+
+/**
+ * megasas_exit - Driver unload entry point
+ */
+static void __exit
+megasas_exit( void )
+{
+ pci_unregister_driver( &megasas_pci_driver );
+ unregister_chrdev( megasas_mgmt_majorno, "megaraid_sas_ioctl" );
+
+ return;
+}
+
+module_init( megasas_init );
+module_exit( megasas_exit );
+
> +
> + if (exception) {
> +
> + spin_lock( instance->host_lock );
> + cmd->scmd->scsi_done( cmd->scmd );
> + spin_unlock( instance->host_lock );
are you really sure you don't want to use spin_lock_irqsave() here ?
> + unregister_chrdev( megasas_mgmt_majorno, "megaraid_sas_ioctl" );
I really don't like this idea of weird ioctl devices though...
On Mon, 2005-05-16 at 10:06 +0200, Arjan van de Ven wrote:
> > + spin_lock( instance->host_lock );
> > + cmd->scmd->scsi_done( cmd->scmd );
> > + spin_unlock( instance->host_lock );
>
> are you really sure you don't want to use spin_lock_irqsave() here ?
Actually, don't bother with the lock at all. scsi_done() is designed to
be called locklessly.
James
On Mon, 2005-05-16 at 09:18 -0500, James Bottomley wrote:
> On Mon, 2005-05-16 at 10:06 +0200, Arjan van de Ven wrote:
> > > + spin_lock( instance->host_lock );
> > > + cmd->scmd->scsi_done( cmd->scmd );
> > > + spin_unlock( instance->host_lock );
> >
> > are you really sure you don't want to use spin_lock_irqsave() here ?
>
> Actually, don't bother with the lock at all. scsi_done() is designed to
> be called locklessly.
doh sorry I missed that (but knew it)
proves I need to drink the coffee BEFORE the patch review ;)
On Mon, May 16, 2005 at 02:57:15AM -0400, Bagalkote, Sreenivas wrote:
> +#include "megaraid_sas.h"
Please put most of the system includes in here, not megaraid_sas.h.
megaraid_sas.h should only include the headers absolutely nessecary for it
top compile.
> +
> +MODULE_LICENSE ("GPL");
> +MODULE_VERSION (MEGASAS_VERSION);
> +MODULE_AUTHOR ("[email protected]");
> +MODULE_DESCRIPTION ("LSI Logic MegaRAID SAS Driver");
Normal style would be:
MODULE_LICENSE("GPL");
MODULE_VERSION(MEGASAS_VERSION);
MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");
> +
> +/*
> + * PCI ID table for all supported controllers
> + */
> +static struct pci_device_id megasas_pci_table_g[] = {
can be const
> +static int megasas_mgmt_majorno;
> +static struct megasas_mgmt_info megasas_mgmt_info;
> +static struct fasync_struct* megasas_async_queue;
> +static DECLARE_MUTEX (megasas_async_queue_mutex);
simiar a kind odd style. especially the * placement is very odd as
* is a mofifier on the variable and not the type in C (you have that
a few times more below). should be more like:
static int megasas_mgmt_majorno;
static struct megasas_mgmt_info megasas_mgmt_info;
static struct fasync_struct *megasas_async_queue;
static DECLARE_MUTEX(megasas_async_queue_mutex);
or better not at all. As this is all part of your rather questionable
managment interface that will need some major work to sort out.
While we're at it, is there a chance you could submit a basic driver
without managment functionality after fixing up the review comments
so that we can have it in when starting to design a better managment
interface?
> +/**
> + * megasas_get_cmd - Get a command from the free pool
> + * @instance: Adapter soft state
> + *
> + * Returns a free command from the pool
> + */
> +static inline struct megasas_cmd*
> +megasas_get_cmd( struct megasas_instance* instance )
> +{
> + unsigned long flags;
> + struct megasas_cmd* cmd;
> +
> + spin_lock_irqsave(&instance->cmd_pool_lock, flags);
> +
> + if ( list_empty(&instance->cmd_pool)) {
> + spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
> + return NULL;
> + }
> +
> + cmd = list_entry((&instance->cmd_pool)->next, struct megasas_cmd,
> list);
> +
> + list_del_init( &cmd->list );
> +
> + spin_unlock_irqrestore( &instance->cmd_pool_lock, flags );
> +
> + return cmd;
> +}
Again this is very different style from normal linux. Please read through
Documentation/CodingStyle and look at some other drivers, e.g.
drivers/net/tg3.c for how Linux code is written. This function should
for example look like:
/**
* megasas_get_cmd - Get a command from the free pool
* @instance: Adapter soft state
*
* Returns a free command from the pool
*/
static inline struct megasas_cmd *
megasas_get_cmd(struct megasas_instance *instance)
{
struct megasas_cmd *cmd = NULL;
unsigned long flags;
spin_lock_irqsave(&instance->cmd_pool_lock, flags);
if (!list_empty(&instance->cmd_pool)) {
cmd = list_entry(instance->cmd_pool.next,
struct megasas_cmd, list);
list_del_init(&cmd->list);
}
spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
return cmd;
}
> +static int
> +megasas_issue_polled(struct megasas_instance* instance, struct megasas_cmd*
> cmd)
> +{
> + int i;
> + u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000;
> +
> + struct megasas_header* frame_hdr = (struct
> megasas_header*)cmd->frame;
megasas_header is one of the member of union megasas_frame, which is the
type of cmd->frame. Please use Cs static typing.
> + for( i=0; i < msecs && (frame_hdr->cmd_status == 0xff); i++ ) {
> + rmb();
> + msleep(1);
> + }
who is supposed to change frame_hdr->cmd_status while this loop is running?
> + if (!scp->use_sg) {
> + page = virt_to_page( scp->request_buffer );
> + offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK);
> +
> + mfi_sgl->sge32[0].phys_addr =
> pci_map_page(instance->pdev,
> + page, offset,
please use pci_map_single and avoid all the black magic on the virtual
address.
> + page = virt_to_page( scp->request_buffer );
> + offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK);
> +
> + mfi_sgl->sge64[0].phys_addr =
> pci_map_page(instance->pdev,
> + page, offset,
> +
> scp->request_bufflen,
> +
> scp->sc_data_direction);
dito.
> + cmd = megasas_build_cmd( instance, scmd, &frame_count );
> +
> + if (!cmd) {
> + done(scmd);
> + return 0;
I think this should return HOST_BUSY?
> +/**
> + * megasas_abort_handler - Abort entry point
> + * @scmd: SCSI command to be aborted
> + */
> +static int
> +megasas_abort_handler( struct scsi_cmnd* scmd )
> +{
> + printk( KERN_NOTICE "megasas: ABORT -%ld cmd=%x <c=%d t=%d l=%d>\n",
> + scmd->serial_number, scmd->cmnd[0], SCP2CHANNEL(scmd),
> + SCP2TARGET(scmd), SCP2LUN(scmd));
> +
> + return FAILED;
> +}
I don't think you should implement an abort handler at all if you
don't do anything.
> +/*
> + * Scsi host template for megaraid_sas driver
> + */
> +static struct scsi_host_template megasas_template_g = {
please remove the _g postix.
> +
> + .module = THIS_MODULE,
> + .name = "LSI Logic SAS based MegaRAID
> driver",
> + .queuecommand = megasas_queue_command,
> + .eh_abort_handler = megasas_abort_handler,
> + .eh_device_reset_handler = megasas_reset_device,
> + .eh_bus_reset_handler = megasas_reset_bus_host,
> + .eh_host_reset_handler = megasas_reset_bus_host,
> + .use_clustering = ENABLE_CLUSTERING,
> + .shost_attrs = megasas_shost_attrs,
> +};
> +static inline void
> +megasas_sync_buffers(struct megasas_instance* instance, struct megasas_cmd*
this is a really odd name for a function that calls the dma unmapping
functions.
> + spin_lock( instance->host_lock );
> + cmd->scmd->scsi_done( cmd->scmd );
> + spin_unlock( instance->host_lock );
no need to lock around ->scsi_done
> + /*
> + * instance->cmd_list is an array of struct megasas_cmd pointers.
> + * Allocate the dynamic array first and then allocate individual
> + * commands.
> + */
> + instance->cmd_list = kmalloc( sizeof(struct megasas_cmd*) * max_cmd,
> + GFP_KERNEL
> );
> +
> + if (!instance->cmd_list) {
> + printk( KERN_DEBUG "megasas: out of memory\n" );
> + return -ENOMEM;
> + }
> +
> + memset( instance->cmd_list, 0, sizeof(struct megasas_cmd*) * max_cmd
you can use kcalloc here
> +/**
> + * 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;
> +
> + host = scsi_host_alloc(&megasas_template_g, sizeof(void*));
> +
> + if (!host) {
> + printk( KERN_DEBUG "megasas: scsi_host_alloc failed\n" );
> + return -ENODEV;
> + }
please allocate the scsi_host directly and early in your probe routing,
so you can allocate your private data directly through scsi_host_alloc
> + SCSIHOST2ADAP(host) = (caddr_t) instance;
please don't use caddr_t in kernel code.
> + instance->host = host;
> +
> + /*
> + * Export parameters required by SCSI mid-layer
> + */
> + scsi_assign_lock( host, instance->host_lock );
never use scsi_assign_lock in new drivers.
> + scsi_set_device( host, &instance->pdev->dev );
not needed, as it's done by scsi_add_host
> + /*
> + * Notify the mid-layer about the new controller
> + */
> + if (scsi_add_host(host, &instance->pdev->dev)) {
this should be done at the end of your probe routine.
> +/**
> + * megasas_io_detach - Detach the driver from SCSI mid-layer
> + * @instance: Adapter soft state
> + */
> +static void
> +megasas_io_detach( struct megasas_instance* instance )
> +{
> + if (instance->host)
> + scsi_remove_host( instance->host );
> +}
please avoid that wrapper. Also you shouldn't need the if
> + instance->host_lock = &instance->lock;
please avoid using the host_lock pointer.
> + * @device: Generic device structure
> + */
> +static void
> +megasas_shutdown( struct device* device )
> +{
> + int i;
> + struct megasas_instance* instance;
> +
> + for( i = 0; i < megasas_mgmt_info.max_index; i++ ) {
> + instance = megasas_mgmt_info.instance[i];
> +
> + if (instance)
> + megasas_shutdown_controller( instance );
> + }
this doesn't make any sense. you must only shutdown the instance you
were called for. Use drv_get_drvdata to get it from the devices private
data.
> +/**
> + * megasas_mgmt_ioctl - char node ioctl entry point
> + *
> + * Few ioctl commands should be handled by driver itself (driver ioctls)
> and
> + * the rest should be converted into appropriate commands for FW and
> issued.
> + */
> +static int
> +megasas_mgmt_ioctl( struct inode* inode, struct file* filep,
> + unsigned int cmd, unsigned long arg )
now this managmnet device is completely in-acceptable. Not only it's
based on ioctls, but also on rather odd ones. Things like stp and sata
passthrough don't belong into a driver at all but in a transport class.
Driver and firmware versions belong into sysfs, the adapter count can
already be found easily by looking through the sysfs subdirectory for
this driver, the purpose of the adapter map isn't completely clear to
me yet, but it should be addresses using sysfs aswell
> +#ifdef CONFIG_COMPAT
> +/**
> + * megasas_compat_ioctl - Handles conversions from 32-bit apps
> + */
> +static int
> +megasas_compat_ioctl( struct file* filep, unsigned int cmd, unsigned long
> arg,
> + struct file* filep )
> +{
> + return megasas_mgmt_ioctl( NULL, filep, cmd, arg );
> +}
> +#endif
the prototype doesn't match what's expect for ->compat_ioctl..
>
>On Mon, 2005-05-16 at 10:06 +0200, Arjan van de Ven wrote:
>> > + spin_lock( instance->host_lock );
>> > + cmd->scmd->scsi_done( cmd->scmd );
>> > + spin_unlock( instance->host_lock );
>>
>> are you really sure you don't want to use spin_lock_irqsave() here ?
>
>Actually, don't bother with the lock at all. scsi_done() is
>designed to
>be called locklessly.
>
>James
Okay. I will remove that.
Thanks,
Sreenivas
Christoph, thank you for your extensive review.
>
>On Mon, May 16, 2005 at 02:57:15AM -0400, Bagalkote, Sreenivas wrote:
>> +#include "megaraid_sas.h"
>
>Please put most of the system includes in here, not megaraid_sas.h.
>megaraid_sas.h should only include the headers absolutely
>nessecary for it
>top compile.
>
Okay.
>> +
>> +MODULE_LICENSE ("GPL");
>> +MODULE_VERSION (MEGASAS_VERSION);
>> +MODULE_AUTHOR ("[email protected]");
>> +MODULE_DESCRIPTION ("LSI Logic MegaRAID SAS Driver");
>
>Normal style would be:
>
>MODULE_LICENSE("GPL");
>MODULE_VERSION(MEGASAS_VERSION);
>MODULE_AUTHOR("[email protected]");
>MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");
>
I will remove the alignment. I am curious however, to know
why this is less readable or against the normal style.
>> +
>> +/*
>> + * PCI ID table for all supported controllers
>> + */
>> +static struct pci_device_id megasas_pci_table_g[] = {
>
>can be const
Okay. I will make it a constant.
>
>> +static int megasas_mgmt_majorno;
>> +static struct megasas_mgmt_info megasas_mgmt_info;
>> +static struct fasync_struct* megasas_async_queue;
>> +static DECLARE_MUTEX
>(megasas_async_queue_mutex);
>
>simiar a kind odd style. especially the * placement is very odd as
>* is a mofifier on the variable and not the type in C (you have that
>a few times more below). should be more like:
>
>static int megasas_mgmt_majorno;
>static struct megasas_mgmt_info megasas_mgmt_info;
>static struct fasync_struct *megasas_async_queue;
>static DECLARE_MUTEX(megasas_async_queue_mutex);
>
I will change the * placement. But does the alignment (of data types and
variables) offend Linux coding style? If it does, I will remove that.
>or better not at all. As this is all part of your rather questionable
>managment interface that will need some major work to sort out.
>
>While we're at it, is there a chance you could submit a basic driver
>without managment functionality after fixing up the review comments
>so that we can have it in when starting to design a better managment
>interface?
>
I will respond to all your comments about management interface in my next
post.
>> +/**
>> + * megasas_get_cmd - Get a command from the free pool
>> + * @instance: Adapter soft state
>> + *
>> + * Returns a free command from the pool
>> + */
>> +static inline struct megasas_cmd*
>> +megasas_get_cmd( struct megasas_instance* instance )
>> +{
>> + unsigned long flags;
>> + struct megasas_cmd* cmd;
>> +
>> + spin_lock_irqsave(&instance->cmd_pool_lock, flags);
>> +
>> + if ( list_empty(&instance->cmd_pool)) {
>> + spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
>> + return NULL;
>> + }
>> +
>> + cmd = list_entry((&instance->cmd_pool)->next, struct
>megasas_cmd,
>> list);
>> +
>> + list_del_init( &cmd->list );
>> +
>> + spin_unlock_irqrestore( &instance->cmd_pool_lock, flags );
>> +
>> + return cmd;
>> +}
>
>Again this is very different style from normal linux. Please
>read through
>Documentation/CodingStyle and look at some other drivers, e.g.
>drivers/net/tg3.c for how Linux code is written. This function should
>for example look like:
>
>/**
> * megasas_get_cmd - Get a command from the free pool
> * @instance: Adapter soft state
> *
> * Returns a free command from the pool
> */
>static inline struct megasas_cmd *
>megasas_get_cmd(struct megasas_instance *instance)
>{
> struct megasas_cmd *cmd = NULL;
> unsigned long flags;
>
> spin_lock_irqsave(&instance->cmd_pool_lock, flags);
> if (!list_empty(&instance->cmd_pool)) {
> cmd = list_entry(instance->cmd_pool.next,
> struct megasas_cmd, list);
> list_del_init(&cmd->list);
> }
> spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
> return cmd;
>}
>
Not that you pointed it out, it looks obvious. I will change all
such occurences. Thank you.
>> +static int
>> +megasas_issue_polled(struct megasas_instance* instance,
>struct megasas_cmd*
>> cmd)
>> +{
>> + int i;
>> + u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000;
>> +
>> + struct megasas_header* frame_hdr = (struct
>> megasas_header*)cmd->frame;
>
>megasas_header is one of the member of union megasas_frame,
>which is the
>type of cmd->frame. Please use Cs static typing.
>
Could you please elaborate this point? Are you saying use:
struct megasas_header* frame_hdr = &cmd->frame->hdr instead of what
I have above? If that's what you have in mind, then yes, I will do that.
>> + for( i=0; i < msecs && (frame_hdr->cmd_status == 0xff); i++ ) {
>> + rmb();
>> + msleep(1);
>> + }
>
>who is supposed to change frame_hdr->cmd_status while this
>loop is running?
In the previous statement, I am issuing the frame to the FW. FW will
change the cmd_status. I will add a comment.
>
>> + if (!scp->use_sg) {
>> + page = virt_to_page( scp->request_buffer );
>> + offset = ((unsigned long)scp->request_buffer &
>~PAGE_MASK);
>> +
>> + mfi_sgl->sge32[0].phys_addr =
>> pci_map_page(instance->pdev,
>> + page, offset,
>
>please use pci_map_single and avoid all the black magic on the virtual
>address.
>
Okay. I will change this.
>> + page = virt_to_page( scp->request_buffer );
>> + offset = ((unsigned long)scp->request_buffer &
>~PAGE_MASK);
>> +
>> + mfi_sgl->sge64[0].phys_addr =
>> pci_map_page(instance->pdev,
>> + page, offset,
>> +
>> scp->request_bufflen,
>> +
>> scp->sc_data_direction);
>
>dito.
>
I will change here also.
>> + cmd = megasas_build_cmd( instance, scmd, &frame_count );
>> +
>> + if (!cmd) {
>> + done(scmd);
>> + return 0;
>
>I think this should return HOST_BUSY?
>
I will add a readable comment here. I return NULL cmd when I can complete
the command from queue routine itself.
>> +/**
>> + * megasas_abort_handler - Abort entry point
>> + * @scmd: SCSI command to be aborted
>> + */
>> +static int
>> +megasas_abort_handler( struct scsi_cmnd* scmd )
>> +{
>> + printk( KERN_NOTICE "megasas: ABORT -%ld cmd=%x <c=%d
>t=%d l=%d>\n",
>> + scmd->serial_number, scmd->cmnd[0], SCP2CHANNEL(scmd),
>> + SCP2TARGET(scmd), SCP2LUN(scmd));
>> +
>> + return FAILED;
>> +}
>
>I don't think you should implement an abort handler at all if you
>don't do anything.
>
My reset handler will still get called, won't it be? Moreover, isn't
it a good diagnostic message to be printed from abort handler? Please
consider.
>> +/*
>> + * Scsi host template for megaraid_sas driver
>> + */
>> +static struct scsi_host_template megasas_template_g = {
>
>please remove the _g postix.
>
Okay.
>> +
>> + .module = THIS_MODULE,
>> + .name = "LSI Logic SAS based MegaRAID
>> driver",
>> + .queuecommand = megasas_queue_command,
>> + .eh_abort_handler = megasas_abort_handler,
>> + .eh_device_reset_handler = megasas_reset_device,
>> + .eh_bus_reset_handler = megasas_reset_bus_host,
>> + .eh_host_reset_handler = megasas_reset_bus_host,
>> + .use_clustering = ENABLE_CLUSTERING,
>> + .shost_attrs = megasas_shost_attrs,
>> +};
>
>> +static inline void
>> +megasas_sync_buffers(struct megasas_instance* instance,
>struct megasas_cmd*
>
>
>this is a really odd name for a function that calls the dma unmapping
>functions.
>
Okay, I will change the name.
>> + spin_lock( instance->host_lock );
>> + cmd->scmd->scsi_done( cmd->scmd );
>> + spin_unlock( instance->host_lock );
>
>no need to lock around ->scsi_done
>
Thanks. I will remove the lock.
>> + /*
>> + * instance->cmd_list is an array of struct megasas_cmd
>pointers.
>> + * Allocate the dynamic array first and then allocate individual
>> + * commands.
>> + */
>> + instance->cmd_list = kmalloc( sizeof(struct
>megasas_cmd*) * max_cmd,
>> +
>GFP_KERNEL
>> );
>> +
>> + if (!instance->cmd_list) {
>> + printk( KERN_DEBUG "megasas: out of memory\n" );
>> + return -ENOMEM;
>> + }
>> +
>> + memset( instance->cmd_list, 0, sizeof(struct
>megasas_cmd*) * max_cmd
>
>you can use kcalloc here
>
Okay.
>> +/**
>> + * 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;
>> +
>> + host = scsi_host_alloc(&megasas_template_g, sizeof(void*));
>> +
>> + if (!host) {
>> + printk( KERN_DEBUG "megasas: scsi_host_alloc
>failed\n" );
>> + return -ENODEV;
>> + }
>
>please allocate the scsi_host directly and early in your probe routing,
>so you can allocate your private data directly through scsi_host_alloc
>
The instance is allocated on DMA memory. scsi_host_alloc uses kmalloc().
I allocate instance on DMA memory because some of its members get their
values refreshed from FW. Currently only producer and consumer are used. But
in near future, we will have more such members.
>> + SCSIHOST2ADAP(host) = (caddr_t) instance;
>
>please don't use caddr_t in kernel code.
>
Okay.
>> + instance->host = host;
>> +
>> + /*
>> + * Export parameters required by SCSI mid-layer
>> + */
>> + scsi_assign_lock( host, instance->host_lock );
>
>never use scsi_assign_lock in new drivers.
>
Yes, of course. Thanks.
>> + scsi_set_device( host, &instance->pdev->dev );
>
>not needed, as it's done by scsi_add_host
>
Okay. This will be removed.
>> + /*
>> + * Notify the mid-layer about the new controller
>> + */
>> + if (scsi_add_host(host, &instance->pdev->dev)) {
>
>this should be done at the end of your probe routine.
>
It is indeed being done at the end of the probe routine.
>> +/**
>> + * megasas_io_detach - Detach the driver from SCSI mid-layer
>> + * @instance: Adapter soft state
>> + */
>> +static void
>> +megasas_io_detach( struct megasas_instance* instance )
>> +{
>> + if (instance->host)
>> + scsi_remove_host( instance->host );
>> +}
>
>please avoid that wrapper. Also you shouldn't need the if
>
Okay. I will remove megasas_io_detach() function.
>> + instance->host_lock = &instance->lock;
>
>please avoid using the host_lock pointer.
>
Okay. Am I allowed to use scsi host's per-host lock inside my
reset handler? Is my usage of lock inside the reset_handler correct?
>> + * @device: Generic device structure
>> + */
>> +static void
>> +megasas_shutdown( struct device* device )
>> +{
>> + int i;
>> + struct megasas_instance* instance;
>> +
>> + for( i = 0; i < megasas_mgmt_info.max_index; i++ ) {
>> + instance = megasas_mgmt_info.instance[i];
>> +
>> + if (instance)
>> + megasas_shutdown_controller( instance );
>> + }
>
>this doesn't make any sense. you must only shutdown the instance you
>were called for. Use drv_get_drvdata to get it from the
>devices private
>data.
>
Okay. I will change that. That was dumb.
>> +/**
>> + * megasas_mgmt_ioctl - char node ioctl entry point
>> + *
Please allow me to respond to your comments about management interface in
later post.
Thank you very much.
Sincerely,
Sreenivas Bagalkote
> >> +
> >> +MODULE_LICENSE ("GPL");
> >> +MODULE_VERSION (MEGASAS_VERSION);
> >> +MODULE_AUTHOR ("[email protected]");
> >> +MODULE_DESCRIPTION ("LSI Logic MegaRAID SAS Driver");
> >
> >Normal style would be:
> >
> >MODULE_LICENSE("GPL");
> >MODULE_VERSION(MEGASAS_VERSION);
> >MODULE_AUTHOR("[email protected]");
> >MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");
> >
>
> I will remove the alignment. I am curious however, to know
> why this is less readable or against the normal style.
I find it more readable. And apparently many core kernel hackers thing
the same.
> I will change the * placement. But does the alignment (of data types and
> variables) offend Linux coding style? If it does, I will remove that.
If you look at core code we generally don't do it. It's not a hard
requirement, but it makes reading the driver and thus finding and fixing
bugs much easier for core developers.
> >> +static int
> >> +megasas_issue_polled(struct megasas_instance* instance,
> >struct megasas_cmd*
> >> cmd)
> >> +{
> >> + int i;
> >> + u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000;
> >> +
> >> + struct megasas_header* frame_hdr = (struct
> >> megasas_header*)cmd->frame;
> >
> >megasas_header is one of the member of union megasas_frame,
> >which is the
> >type of cmd->frame. Please use Cs static typing.
> >
>
> Could you please elaborate this point? Are you saying use:
> struct megasas_header* frame_hdr = &cmd->frame->hdr instead of what
> I have above? If that's what you have in mind, then yes, I will do that.
Yes. In general most places you have to use casts in C are bugs - either
in your code or in the API (e.g. in the Linux timer APIs)
> >> + for( i=0; i < msecs && (frame_hdr->cmd_status == 0xff); i++ ) {
> >> + rmb();
> >> + msleep(1);
> >> + }
> >
> >who is supposed to change frame_hdr->cmd_status while this
> >loop is running?
>
> In the previous statement, I am issuing the frame to the FW. FW will
> change the cmd_status. I will add a comment.
So the frame_hdr is somewhere in iomremapped space? If so you need to
use the proper interfaces (read* or ioread*) to access it.
> I will change here also.
>
> >> + cmd = megasas_build_cmd( instance, scmd, &frame_count );
> >> +
> >> + if (!cmd) {
> >> + done(scmd);
> >> + return 0;
> >
> >I think this should return HOST_BUSY?
> >
>
> I will add a readable comment here. I return NULL cmd when I can complete
> the command from queue routine itself.
I'd have to look over the patch in detail again, but IIRC this is a
resource shortage, in which case not completing the command but returning
busy is the right thing to do. Let's look over it again in the next
revision.
> >I don't think you should implement an abort handler at all if you
> >don't do anything.
> >
>
> My reset handler will still get called, won't it be?
Yes.
> Moreover, isn't
> it a good diagnostic message to be printed from abort handler? Please
> consider.
Don't think so. You'll get a message about the resets from the midlayer
anyway.
> >please allocate the scsi_host directly and early in your probe routing,
> >so you can allocate your private data directly through scsi_host_alloc
> >
>
> The instance is allocated on DMA memory. scsi_host_alloc uses kmalloc().
> I allocate instance on DMA memory because some of its members get their
> values refreshed from FW. Currently only producer and consumer are used. But
> in near future, we will have more such members.
Okay. I'd suggest putting those DMAable members into a separate struct
that is kmalloced so we have them nicely separated.
> >> + instance->host_lock = &instance->lock;
> >
> >please avoid using the host_lock pointer.
> >
>
> Okay. Am I allowed to use scsi host's per-host lock inside my
> reset handler? Is my usage of lock inside the reset_handler correct?
->queuemand and the ->eh_*handler routines are called with the midlayers
per-host lock held. You can of course release and reaquire them inside
these routines (with spin_unlock_irq / spin_lock_irq)
James Bottomley writes:
> On Mon, 2005-05-16 at 10:06 +0200, Arjan van de Ven wrote:
>> > + spin_lock( instance->host_lock );
>> > + cmd->scmd->scsi_done( cmd->scmd );
>> > + spin_unlock( instance->host_lock );
>>
>> are you really sure you don't want to use spin_lock_irqsave() here ?
>
>Actually, don't bother with the lock at all. scsi_done() is designed
to
>be called locklessly.
Could I get an historical (2.4 & Distribution) perspective on this. At
which point, or what code/include/manifest/version delineating it, would
you say the driver is no longer, if ever, required to place a lock
(host_lock or io_request_lock) around the scsi_done call?
I expect (or hope) the answer to be: always needs io_request_lock in
2.4, never needed the host_lock in 2.5+.
-- Mark Salyzyn
> I expect (or hope) the answer to be: always needs io_request_lock in
> 2.4, never needed the host_lock in 2.5+.
afaik in RHEL3 you don't need it either but are allowed to.
On Tue, May 24, 2005 at 04:45:05PM -0400, Salyzyn, Mark wrote:
> Could I get an historical (2.4 & Distribution) perspective on this. At
> which point, or what code/include/manifest/version delineating it, would
> you say the driver is no longer, if ever, required to place a lock
> (host_lock or io_request_lock) around the scsi_done call?
>
> I expect (or hope) the answer to be: always needs io_request_lock in
> 2.4, never needed the host_lock in 2.5+.
You don't need any lock in 2.4 either.