// SPDX-License-Identifier: GPL-2.0
/*
* Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers
*
* Copyright 2017 Hannes Reinecke, SUSE Linux GmbH <hare@suse.com>
*
* Based on the original DAC960 driver,
* Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com>
* Portions Copyright 2002 by Mylex (An IBM Business Unit)
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/raid_class.h>
#include <linux/unaligned.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_tcq.h>
#include "myrb.h"
static struct raid_template *myrb_raid_template;
static void myrb_monitor(struct work_struct *work);
static inline void myrb_translate_devstate(void *DeviceState);
static inline int myrb_logical_channel(struct Scsi_Host *shost)
{
return shost->max_channel - 1;
}
static struct myrb_devstate_name_entry {
enum myrb_devstate state;
const char *name;
} myrb_devstate_name_list[] = {
{ MYRB_DEVICE_DEAD, "Dead" },
{ MYRB_DEVICE_WO, "WriteOnly" },
{ MYRB_DEVICE_ONLINE, "Online" },
{ MYRB_DEVICE_CRITICAL, "Critical" },
{ MYRB_DEVICE_STANDBY, "Standby" },
{ MYRB_DEVICE_OFFLINE, "Offline" },
};
static const char *myrb_devstate_name(enum myrb_devstate state)
{
struct myrb_devstate_name_entry *entry = myrb_devstate_name_list;
int i;
for (i = 0; i < ARRAY_SIZE(myrb_devstate_name_list); i++) {
if (entry[i].state == state)
return entry[i].name;
}
return "Unknown" ;
}
static struct myrb_raidlevel_name_entry {
enum myrb_raidlevel level;
const char *name;
} myrb_raidlevel_name_list[] = {
{ MYRB_RAID_LEVEL0, "RAID0" },
{ MYRB_RAID_LEVEL1, "RAID1" },
{ MYRB_RAID_LEVEL3, "RAID3" },
{ MYRB_RAID_LEVEL5, "RAID5" },
{ MYRB_RAID_LEVEL6, "RAID6" },
{ MYRB_RAID_JBOD, "JBOD" },
};
static const char *myrb_raidlevel_name(enum myrb_raidlevel level)
{
struct myrb_raidlevel_name_entry *entry = myrb_raidlevel_name_list;
int i;
for (i = 0; i < ARRAY_SIZE(myrb_raidlevel_name_list); i++) {
if (entry[i].level == level)
return entry[i].name;
}
return NULL;
}
/*
* myrb_create_mempools - allocates auxiliary data structures
*
* Return: true on success, false otherwise.
*/
static bool myrb_create_mempools(struct pci_dev *pdev, struct myrb_hba *cb)
{
size_t elem_size, elem_align;
elem_align = sizeof (struct myrb_sge);
elem_size = cb->host->sg_tablesize * elem_align;
cb->sg_pool = dma_pool_create("myrb_sg" , &pdev->dev,
elem_size, elem_align, 0);
if (cb->sg_pool == NULL) {
shost_printk(KERN_ERR, cb->host,
"Failed to allocate SG pool\n" );
return false ;
}
cb->dcdb_pool = dma_pool_create("myrb_dcdb" , &pdev->dev,
sizeof (struct myrb_dcdb),
sizeof (unsigned int ), 0);
if (!cb->dcdb_pool) {
dma_pool_destroy(cb->sg_pool);
cb->sg_pool = NULL;
shost_printk(KERN_ERR, cb->host,
"Failed to allocate DCDB pool\n" );
return false ;
}
cb->work_q = alloc_ordered_workqueue("myrb_wq_%d" , WQ_MEM_RECLAIM,
cb->host->host_no);
if (!cb->work_q) {
dma_pool_destroy(cb->dcdb_pool);
cb->dcdb_pool = NULL;
dma_pool_destroy(cb->sg_pool);
cb->sg_pool = NULL;
shost_printk(KERN_ERR, cb->host,
"Failed to create workqueue\n" );
return false ;
}
/*
* Initialize the Monitoring Timer.
*/
INIT_DELAYED_WORK(&cb->monitor_work, myrb_monitor);
queue_delayed_work(cb->work_q, &cb->monitor_work, 1);
return true ;
}
/*
* myrb_destroy_mempools - tears down the memory pools for the controller
*/
static void myrb_destroy_mempools(struct myrb_hba *cb)
{
cancel_delayed_work_sync(&cb->monitor_work);
destroy_workqueue(cb->work_q);
dma_pool_destroy(cb->sg_pool);
dma_pool_destroy(cb->dcdb_pool);
}
/*
* myrb_reset_cmd - reset command block
*/
static inline void myrb_reset_cmd(struct myrb_cmdblk *cmd_blk)
{
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
memset(mbox, 0, sizeof (union myrb_cmd_mbox));
cmd_blk->status = 0;
}
/*
* myrb_qcmd - queues command block for execution
*/
static void myrb_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk)
{
void __iomem *base = cb->io_base;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
union myrb_cmd_mbox *next_mbox = cb->next_cmd_mbox;
cb->write_cmd_mbox(next_mbox, mbox);
if (cb->prev_cmd_mbox1->words[0] == 0 ||
cb->prev_cmd_mbox2->words[0] == 0)
cb->get_cmd_mbox(base);
cb->prev_cmd_mbox2 = cb->prev_cmd_mbox1;
cb->prev_cmd_mbox1 = next_mbox;
if (++next_mbox > cb->last_cmd_mbox)
next_mbox = cb->first_cmd_mbox;
cb->next_cmd_mbox = next_mbox;
}
/*
* myrb_exec_cmd - executes command block and waits for completion.
*
* Return: command status
*/
static unsigned short myrb_exec_cmd(struct myrb_hba *cb,
struct myrb_cmdblk *cmd_blk)
{
DECLARE_COMPLETION_ONSTACK(cmpl);
unsigned long flags;
cmd_blk->completion = &cmpl;
spin_lock_irqsave(&cb->queue_lock, flags);
cb->qcmd(cb, cmd_blk);
spin_unlock_irqrestore(&cb->queue_lock, flags);
wait_for_completion(&cmpl);
return cmd_blk->status;
}
/*
* myrb_exec_type3 - executes a type 3 command and waits for completion.
*
* Return: command status
*/
static unsigned short myrb_exec_type3(struct myrb_hba *cb,
enum myrb_cmd_opcode op, dma_addr_t addr)
{
struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
unsigned short status;
mutex_lock(&cb->dcmd_mutex);
myrb_reset_cmd(cmd_blk);
mbox->type3.id = MYRB_DCMD_TAG;
mbox->type3.opcode = op;
mbox->type3.addr = addr;
status = myrb_exec_cmd(cb, cmd_blk);
mutex_unlock(&cb->dcmd_mutex);
return status;
}
/*
* myrb_exec_type3D - executes a type 3D command and waits for completion.
*
* Return: command status
*/
static unsigned short myrb_exec_type3D(struct myrb_hba *cb,
enum myrb_cmd_opcode op, struct scsi_device *sdev,
struct myrb_pdev_state *pdev_info)
{
struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
unsigned short status;
dma_addr_t pdev_info_addr;
pdev_info_addr = dma_map_single(&cb->pdev->dev, pdev_info,
sizeof (struct myrb_pdev_state),
DMA_FROM_DEVICE);
if (dma_mapping_error(&cb->pdev->dev, pdev_info_addr))
return MYRB_STATUS_SUBSYS_FAILED;
mutex_lock(&cb->dcmd_mutex);
myrb_reset_cmd(cmd_blk);
mbox->type3D.id = MYRB_DCMD_TAG;
mbox->type3D.opcode = op;
mbox->type3D.channel = sdev->channel;
mbox->type3D.target = sdev->id;
mbox->type3D.addr = pdev_info_addr;
status = myrb_exec_cmd(cb, cmd_blk);
mutex_unlock(&cb->dcmd_mutex);
dma_unmap_single(&cb->pdev->dev, pdev_info_addr,
sizeof (struct myrb_pdev_state), DMA_FROM_DEVICE);
if (status == MYRB_STATUS_SUCCESS &&
mbox->type3D.opcode == MYRB_CMD_GET_DEVICE_STATE_OLD)
myrb_translate_devstate(pdev_info);
return status;
}
static char *myrb_event_msg[] = {
"killed because write recovery failed" ,
"killed because of SCSI bus reset failure" ,
"killed because of double check condition" ,
"killed because it was removed" ,
"killed because of gross error on SCSI chip" ,
"killed because of bad tag returned from drive" ,
"killed because of timeout on SCSI command" ,
"killed because of reset SCSI command issued from system" ,
"killed because busy or parity error count exceeded limit" ,
"killed because of 'kill drive' command from system" ,
"killed because of selection timeout" ,
"killed due to SCSI phase sequence error" ,
"killed due to unknown status" ,
};
/**
* myrb_get_event - get event log from HBA
* @cb: pointer to the hba structure
* @event: number of the event
*
* Execute a type 3E command and logs the event message
*/
static void myrb_get_event(struct myrb_hba *cb, unsigned int event)
{
struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
struct myrb_log_entry *ev_buf;
dma_addr_t ev_addr;
unsigned short status;
ev_buf = dma_alloc_coherent(&cb->pdev->dev,
sizeof (struct myrb_log_entry),
&ev_addr, GFP_KERNEL);
if (!ev_buf)
return ;
myrb_reset_cmd(cmd_blk);
mbox->type3E.id = MYRB_MCMD_TAG;
mbox->type3E.opcode = MYRB_CMD_EVENT_LOG_OPERATION;
mbox->type3E.optype = DAC960_V1_GetEventLogEntry;
mbox->type3E.opqual = 1;
mbox->type3E.ev_seq = event;
mbox->type3E.addr = ev_addr;
status = myrb_exec_cmd(cb, cmd_blk);
if (status != MYRB_STATUS_SUCCESS)
shost_printk(KERN_INFO, cb->host,
"Failed to get event log %d, status %04x\n" ,
event, status);
else if (ev_buf->seq_num == event) {
struct scsi_sense_hdr sshdr;
memset(&sshdr, 0, sizeof (sshdr));
scsi_normalize_sense(ev_buf->sense, 32, &sshdr);
if (sshdr.sense_key == VENDOR_SPECIFIC &&
sshdr.asc == 0x80 &&
sshdr.ascq < ARRAY_SIZE(myrb_event_msg))
shost_printk(KERN_CRIT, cb->host,
"Physical drive %d:%d: %s\n" ,
ev_buf->channel, ev_buf->target,
myrb_event_msg[sshdr.ascq]);
else
shost_printk(KERN_CRIT, cb->host,
"Physical drive %d:%d: Sense: %X/%02X/%02X\n" ,
ev_buf->channel, ev_buf->target,
sshdr.sense_key, sshdr.asc, sshdr.ascq);
}
dma_free_coherent(&cb->pdev->dev, sizeof (struct myrb_log_entry),
ev_buf, ev_addr);
}
/*
* myrb_get_errtable - retrieves the error table from the controller
*
* Executes a type 3 command and logs the error table from the controller.
*/
static void myrb_get_errtable(struct myrb_hba *cb)
{
struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
unsigned short status;
struct myrb_error_entry old_table[MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS];
memcpy(&old_table, cb->err_table, sizeof (old_table));
myrb_reset_cmd(cmd_blk);
mbox->type3.id = MYRB_MCMD_TAG;
mbox->type3.opcode = MYRB_CMD_GET_ERROR_TABLE;
mbox->type3.addr = cb->err_table_addr;
status = myrb_exec_cmd(cb, cmd_blk);
if (status == MYRB_STATUS_SUCCESS) {
struct myrb_error_entry *table = cb->err_table;
struct myrb_error_entry *new , *old;
size_t err_table_offset;
struct scsi_device *sdev;
shost_for_each_device(sdev, cb->host) {
if (sdev->channel >= myrb_logical_channel(cb->host))
continue ;
err_table_offset = sdev->channel * MYRB_MAX_TARGETS
+ sdev->id;
new = table + err_table_offset;
old = &old_table[err_table_offset];
if (new->parity_err == old->parity_err &&
new->soft_err == old->soft_err &&
new->hard_err == old->hard_err &&
new->misc_err == old->misc_err)
continue ;
sdev_printk(KERN_CRIT, sdev,
"Errors: Parity = %d, Soft = %d, Hard = %d, Misc = %d\n" ,
new->parity_err, new->soft_err,
new->hard_err, new->misc_err);
}
}
}
/*
* myrb_get_ldev_info - retrieves the logical device table from the controller
*
* Executes a type 3 command and updates the logical device table.
*
* Return: command status
*/
static unsigned short myrb_get_ldev_info(struct myrb_hba *cb)
{
unsigned short status;
int ldev_num, ldev_cnt = cb->enquiry->ldev_count;
struct Scsi_Host *shost = cb->host;
status = myrb_exec_type3(cb, MYRB_CMD_GET_LDEV_INFO,
cb->ldev_info_addr);
if (status != MYRB_STATUS_SUCCESS)
return status;
for (ldev_num = 0; ldev_num < ldev_cnt; ldev_num++) {
struct myrb_ldev_info *old = NULL;
struct myrb_ldev_info *new = cb->ldev_info_buf + ldev_num;
struct scsi_device *sdev;
sdev = scsi_device_lookup(shost, myrb_logical_channel(shost),
ldev_num, 0);
if (!sdev) {
if (new->state == MYRB_DEVICE_OFFLINE)
continue ;
shost_printk(KERN_INFO, shost,
"Adding Logical Drive %d in state %s\n" ,
ldev_num, myrb_devstate_name(new->state));
scsi_add_device(shost, myrb_logical_channel(shost),
ldev_num, 0);
continue ;
}
old = sdev->hostdata;
if (new->state != old->state)
shost_printk(KERN_INFO, shost,
"Logical Drive %d is now %s\n" ,
ldev_num, myrb_devstate_name(new->state));
if (new->wb_enabled != old->wb_enabled)
sdev_printk(KERN_INFO, sdev,
"Logical Drive is now WRITE %s\n" ,
(new->wb_enabled ? "BACK" : "THRU" ));
memcpy(old, new , sizeof (*new ));
scsi_device_put(sdev);
}
return status;
}
/*
* myrb_get_rbld_progress - get rebuild progress information
*
* Executes a type 3 command and returns the rebuild progress
* information.
*
* Return: command status
*/
static unsigned short myrb_get_rbld_progress(struct myrb_hba *cb,
struct myrb_rbld_progress *rbld)
{
struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
struct myrb_rbld_progress *rbld_buf;
dma_addr_t rbld_addr;
unsigned short status;
rbld_buf = dma_alloc_coherent(&cb->pdev->dev,
sizeof (struct myrb_rbld_progress),
&rbld_addr, GFP_KERNEL);
if (!rbld_buf)
return MYRB_STATUS_RBLD_NOT_CHECKED;
myrb_reset_cmd(cmd_blk);
mbox->type3.id = MYRB_MCMD_TAG;
mbox->type3.opcode = MYRB_CMD_GET_REBUILD_PROGRESS;
mbox->type3.addr = rbld_addr;
status = myrb_exec_cmd(cb, cmd_blk);
if (rbld)
memcpy(rbld, rbld_buf, sizeof (struct myrb_rbld_progress));
dma_free_coherent(&cb->pdev->dev, sizeof (struct myrb_rbld_progress),
rbld_buf, rbld_addr);
return status;
}
/*
* myrb_update_rbld_progress - updates the rebuild status
*
* Updates the rebuild status for the attached logical devices.
*/
static void myrb_update_rbld_progress(struct myrb_hba *cb)
{
struct myrb_rbld_progress rbld_buf;
unsigned short status;
status = myrb_get_rbld_progress(cb, &rbld_buf);
if (status == MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS &&
cb->last_rbld_status == MYRB_STATUS_SUCCESS)
status = MYRB_STATUS_RBLD_SUCCESS;
if (status != MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS) {
unsigned int blocks_done =
rbld_buf.ldev_size - rbld_buf.blocks_left;
struct scsi_device *sdev;
sdev = scsi_device_lookup(cb->host,
myrb_logical_channel(cb->host),
rbld_buf.ldev_num, 0);
if (!sdev)
return ;
switch (status) {
case MYRB_STATUS_SUCCESS:
sdev_printk(KERN_INFO, sdev,
"Rebuild in Progress, %d%% completed\n" ,
(100 * (blocks_done >> 7))
/ (rbld_buf.ldev_size >> 7));
break ;
case MYRB_STATUS_RBLD_FAILED_LDEV_FAILURE:
sdev_printk(KERN_INFO, sdev,
"Rebuild Failed due to Logical Drive Failure\n" );
break ;
case MYRB_STATUS_RBLD_FAILED_BADBLOCKS:
sdev_printk(KERN_INFO, sdev,
"Rebuild Failed due to Bad Blocks on Other Drives\n" );
break ;
case MYRB_STATUS_RBLD_FAILED_NEW_DRIVE_FAILED:
sdev_printk(KERN_INFO, sdev,
"Rebuild Failed due to Failure of Drive Being Rebuilt\n" );
break ;
case MYRB_STATUS_RBLD_SUCCESS:
sdev_printk(KERN_INFO, sdev,
"Rebuild Completed Successfully\n" );
break ;
case MYRB_STATUS_RBLD_SUCCESS_TERMINATED:
sdev_printk(KERN_INFO, sdev,
"Rebuild Successfully Terminated\n" );
break ;
default :
break ;
}
scsi_device_put(sdev);
}
cb->last_rbld_status = status;
}
/*
* myrb_get_cc_progress - retrieve the rebuild status
*
* Execute a type 3 Command and fetch the rebuild / consistency check
* status.
*/
static void myrb_get_cc_progress(struct myrb_hba *cb)
{
struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
struct myrb_rbld_progress *rbld_buf;
dma_addr_t rbld_addr;
unsigned short status;
rbld_buf = dma_alloc_coherent(&cb->pdev->dev,
sizeof (struct myrb_rbld_progress),
&rbld_addr, GFP_KERNEL);
if (!rbld_buf) {
cb->need_cc_status = true ;
return ;
}
myrb_reset_cmd(cmd_blk);
mbox->type3.id = MYRB_MCMD_TAG;
mbox->type3.opcode = MYRB_CMD_REBUILD_STAT;
mbox->type3.addr = rbld_addr;
status = myrb_exec_cmd(cb, cmd_blk);
if (status == MYRB_STATUS_SUCCESS) {
unsigned int ldev_num = rbld_buf->ldev_num;
unsigned int ldev_size = rbld_buf->ldev_size;
unsigned int blocks_done =
ldev_size - rbld_buf->blocks_left;
struct scsi_device *sdev;
sdev = scsi_device_lookup(cb->host,
myrb_logical_channel(cb->host),
ldev_num, 0);
if (sdev) {
sdev_printk(KERN_INFO, sdev,
"Consistency Check in Progress: %d%% completed\n" ,
(100 * (blocks_done >> 7))
/ (ldev_size >> 7));
scsi_device_put(sdev);
}
}
dma_free_coherent(&cb->pdev->dev, sizeof (struct myrb_rbld_progress),
rbld_buf, rbld_addr);
}
/*
* myrb_bgi_control - updates background initialisation status
*
* Executes a type 3B command and updates the background initialisation status
*/
static void myrb_bgi_control(struct myrb_hba *cb)
{
struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
struct myrb_bgi_status *bgi, *last_bgi;
dma_addr_t bgi_addr;
struct scsi_device *sdev = NULL;
unsigned short status;
bgi = dma_alloc_coherent(&cb->pdev->dev, sizeof (struct myrb_bgi_status),
&bgi_addr, GFP_KERNEL);
if (!bgi) {
shost_printk(KERN_ERR, cb->host,
"Failed to allocate bgi memory\n" );
return ;
}
myrb_reset_cmd(cmd_blk);
mbox->type3B.id = MYRB_DCMD_TAG;
mbox->type3B.opcode = MYRB_CMD_BGI_CONTROL;
mbox->type3B.optype = 0x20;
mbox->type3B.addr = bgi_addr;
status = myrb_exec_cmd(cb, cmd_blk);
last_bgi = &cb->bgi_status;
sdev = scsi_device_lookup(cb->host,
myrb_logical_channel(cb->host),
bgi->ldev_num, 0);
switch (status) {
case MYRB_STATUS_SUCCESS:
switch (bgi->status) {
case MYRB_BGI_INVALID:
break ;
case MYRB_BGI_STARTED:
if (!sdev)
break ;
sdev_printk(KERN_INFO, sdev,
"Background Initialization Started\n" );
break ;
case MYRB_BGI_INPROGRESS:
if (!sdev)
break ;
if (bgi->blocks_done == last_bgi->blocks_done &&
bgi->ldev_num == last_bgi->ldev_num)
break ;
sdev_printk(KERN_INFO, sdev,
"Background Initialization in Progress: %d%% completed\n" ,
(100 * (bgi->blocks_done >> 7))
/ (bgi->ldev_size >> 7));
break ;
case MYRB_BGI_SUSPENDED:
if (!sdev)
break ;
sdev_printk(KERN_INFO, sdev,
"Background Initialization Suspended\n" );
break ;
case MYRB_BGI_CANCELLED:
if (!sdev)
break ;
sdev_printk(KERN_INFO, sdev,
"Background Initialization Cancelled\n" );
break ;
}
memcpy(&cb->bgi_status, bgi, sizeof (struct myrb_bgi_status));
break ;
case MYRB_STATUS_BGI_SUCCESS:
if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS)
sdev_printk(KERN_INFO, sdev,
"Background Initialization Completed Successfully\n" );
cb->bgi_status.status = MYRB_BGI_INVALID;
break ;
case MYRB_STATUS_BGI_ABORTED:
if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS)
sdev_printk(KERN_INFO, sdev,
"Background Initialization Aborted\n" );
fallthrough;
case MYRB_STATUS_NO_BGI_INPROGRESS:
cb->bgi_status.status = MYRB_BGI_INVALID;
break ;
}
if (sdev)
scsi_device_put(sdev);
dma_free_coherent(&cb->pdev->dev, sizeof (struct myrb_bgi_status),
bgi, bgi_addr);
}
/*
* myrb_hba_enquiry - updates the controller status
*
* Executes a DAC_V1_Enquiry command and updates the controller status.
*
* Return: command status
*/
static unsigned short myrb_hba_enquiry(struct myrb_hba *cb)
{
struct myrb_enquiry old, *new ;
unsigned short status;
memcpy(&old, cb->enquiry, sizeof (struct myrb_enquiry));
status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY, cb->enquiry_addr);
if (status != MYRB_STATUS_SUCCESS)
return status;
new = cb->enquiry;
if (new->ldev_count > old.ldev_count) {
int ldev_num = old.ldev_count - 1;
while (++ldev_num < new->ldev_count)
shost_printk(KERN_CRIT, cb->host,
"Logical Drive %d Now Exists\n" ,
ldev_num);
}
if (new->ldev_count < old.ldev_count) {
int ldev_num = new->ldev_count - 1;
while (++ldev_num < old.ldev_count)
shost_printk(KERN_CRIT, cb->host,
"Logical Drive %d No Longer Exists\n" ,
ldev_num);
}
if (new->status.deferred != old.status.deferred)
shost_printk(KERN_CRIT, cb->host,
"Deferred Write Error Flag is now %s\n" ,
(new->status.deferred ? "TRUE" : "FALSE" ));
if (new->ev_seq != old.ev_seq) {
cb->new_ev_seq = new->ev_seq;
cb->need_err_info = true ;
shost_printk(KERN_INFO, cb->host,
"Event log %d/%d (%d/%d) available\n" ,
cb->old_ev_seq, cb->new_ev_seq,
old.ev_seq, new->ev_seq);
}
if ((new->ldev_critical > 0 &&
new->ldev_critical != old.ldev_critical) ||
(new->ldev_offline > 0 &&
new->ldev_offline != old.ldev_offline) ||
(new->ldev_count != old.ldev_count)) {
shost_printk(KERN_INFO, cb->host,
"Logical drive count changed (%d/%d/%d)\n" ,
new->ldev_critical,
new->ldev_offline,
new->ldev_count);
cb->need_ldev_info = true ;
}
if (new->pdev_dead > 0 ||
new->pdev_dead != old.pdev_dead ||
time_after_eq(jiffies, cb->secondary_monitor_time
+ MYRB_SECONDARY_MONITOR_INTERVAL)) {
cb->need_bgi_status = cb->bgi_status_supported;
cb->secondary_monitor_time = jiffies;
}
if (new->rbld == MYRB_STDBY_RBLD_IN_PROGRESS ||
new->rbld == MYRB_BG_RBLD_IN_PROGRESS ||
old.rbld == MYRB_STDBY_RBLD_IN_PROGRESS ||
old.rbld == MYRB_BG_RBLD_IN_PROGRESS) {
cb->need_rbld = true ;
cb->rbld_first = (new->ldev_critical < old.ldev_critical);
}
if (old.rbld == MYRB_BG_CHECK_IN_PROGRESS)
switch (new->rbld) {
case MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS:
shost_printk(KERN_INFO, cb->host,
"Consistency Check Completed Successfully\n" );
break ;
case MYRB_STDBY_RBLD_IN_PROGRESS:
case MYRB_BG_RBLD_IN_PROGRESS:
break ;
case MYRB_BG_CHECK_IN_PROGRESS:
cb->need_cc_status = true ;
break ;
case MYRB_STDBY_RBLD_COMPLETED_WITH_ERROR:
shost_printk(KERN_INFO, cb->host,
"Consistency Check Completed with Error\n" );
break ;
case MYRB_BG_RBLD_OR_CHECK_FAILED_DRIVE_FAILED:
shost_printk(KERN_INFO, cb->host,
"Consistency Check Failed - Physical Device Failed\n" );
break ;
case MYRB_BG_RBLD_OR_CHECK_FAILED_LDEV_FAILED:
shost_printk(KERN_INFO, cb->host,
"Consistency Check Failed - Logical Drive Failed\n" );
break ;
case MYRB_BG_RBLD_OR_CHECK_FAILED_OTHER:
shost_printk(KERN_INFO, cb->host,
"Consistency Check Failed - Other Causes\n" );
break ;
case MYRB_BG_RBLD_OR_CHECK_SUCCESS_TERMINATED:
shost_printk(KERN_INFO, cb->host,
"Consistency Check Successfully Terminated\n" );
break ;
}
else if (new->rbld == MYRB_BG_CHECK_IN_PROGRESS)
cb->need_cc_status = true ;
return MYRB_STATUS_SUCCESS;
}
/*
* myrb_set_pdev_state - sets the device state for a physical device
*
* Return: command status
*/
static unsigned short myrb_set_pdev_state(struct myrb_hba *cb,
struct scsi_device *sdev, enum myrb_devstate state)
{
struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk;
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
unsigned short status;
mutex_lock(&cb->dcmd_mutex);
mbox->type3D.opcode = MYRB_CMD_START_DEVICE;
mbox->type3D.id = MYRB_DCMD_TAG;
mbox->type3D.channel = sdev->channel;
mbox->type3D.target = sdev->id;
mbox->type3D.state = state & 0x1F;
status = myrb_exec_cmd(cb, cmd_blk);
mutex_unlock(&cb->dcmd_mutex);
return status;
}
/*
* myrb_enable_mmio - enables the Memory Mailbox Interface
*
* PD and P controller types have no memory mailbox, but still need the
* other dma mapped memory.
*
* Return: true on success, false otherwise.
*/
static bool myrb_enable_mmio(struct myrb_hba *cb, mbox_mmio_init_t mmio_init_fn)
{
void __iomem *base = cb->io_base;
struct pci_dev *pdev = cb->pdev;
size_t err_table_size;
size_t ldev_info_size;
union myrb_cmd_mbox *cmd_mbox_mem;
struct myrb_stat_mbox *stat_mbox_mem;
union myrb_cmd_mbox mbox;
unsigned short status;
memset(&mbox, 0, sizeof (union myrb_cmd_mbox));
if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
dev_err(&pdev->dev, "DMA mask out of range\n" );
return false ;
}
cb->enquiry = dma_alloc_coherent(&pdev->dev,
sizeof (struct myrb_enquiry),
&cb->enquiry_addr, GFP_KERNEL);
if (!cb->enquiry)
return false ;
err_table_size = sizeof (struct myrb_error_entry) *
MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS;
cb->err_table = dma_alloc_coherent(&pdev->dev, err_table_size,
&cb->err_table_addr, GFP_KERNEL);
if (!cb->err_table)
return false ;
ldev_info_size = sizeof (struct myrb_ldev_info) * MYRB_MAX_LDEVS;
cb->ldev_info_buf = dma_alloc_coherent(&pdev->dev, ldev_info_size,
&cb->ldev_info_addr, GFP_KERNEL);
if (!cb->ldev_info_buf)
return false ;
/*
* Skip mailbox initialisation for PD and P Controllers
*/
if (!mmio_init_fn)
return true ;
/* These are the base addresses for the command memory mailbox array */
cb->cmd_mbox_size = MYRB_CMD_MBOX_COUNT * sizeof (union myrb_cmd_mbox);
cb->first_cmd_mbox = dma_alloc_coherent(&pdev->dev,
cb->cmd_mbox_size,
&cb->cmd_mbox_addr,
GFP_KERNEL);
if (!cb->first_cmd_mbox)
return false ;
cmd_mbox_mem = cb->first_cmd_mbox;
cmd_mbox_mem += MYRB_CMD_MBOX_COUNT - 1;
cb->last_cmd_mbox = cmd_mbox_mem;
cb->next_cmd_mbox = cb->first_cmd_mbox;
cb->prev_cmd_mbox1 = cb->last_cmd_mbox;
cb->prev_cmd_mbox2 = cb->last_cmd_mbox - 1;
/* These are the base addresses for the status memory mailbox array */
cb->stat_mbox_size = MYRB_STAT_MBOX_COUNT *
sizeof (struct myrb_stat_mbox);
cb->first_stat_mbox = dma_alloc_coherent(&pdev->dev,
cb->stat_mbox_size,
&cb->stat_mbox_addr,
GFP_KERNEL);
if (!cb->first_stat_mbox)
return false ;
stat_mbox_mem = cb->first_stat_mbox;
stat_mbox_mem += MYRB_STAT_MBOX_COUNT - 1;
cb->last_stat_mbox = stat_mbox_mem;
cb->next_stat_mbox = cb->first_stat_mbox;
/* Enable the Memory Mailbox Interface. */
cb->dual_mode_interface = true ;
mbox.typeX.opcode = 0x2B;
mbox.typeX.id = 0;
mbox.typeX.opcode2 = 0x14;
mbox.typeX.cmd_mbox_addr = cb->cmd_mbox_addr;
mbox.typeX.stat_mbox_addr = cb->stat_mbox_addr;
status = mmio_init_fn(pdev, base, &mbox);
if (status != MYRB_STATUS_SUCCESS) {
cb->dual_mode_interface = false ;
mbox.typeX.opcode2 = 0x10;
status = mmio_init_fn(pdev, base, &mbox);
if (status != MYRB_STATUS_SUCCESS) {
dev_err(&pdev->dev,
"Failed to enable mailbox, status %02X\n" ,
status);
return false ;
}
}
return true ;
}
/*
* myrb_get_hba_config - reads the configuration information
*
* Reads the configuration information from the controller and
* initializes the controller structure.
*
* Return: 0 on success, errno otherwise
*/
static int myrb_get_hba_config(struct myrb_hba *cb)
{
struct myrb_enquiry2 *enquiry2;
dma_addr_t enquiry2_addr;
struct myrb_config2 *config2;
dma_addr_t config2_addr;
struct Scsi_Host *shost = cb->host;
struct pci_dev *pdev = cb->pdev;
int pchan_max = 0, pchan_cur = 0;
unsigned short status;
int ret = -ENODEV, memsize = 0;
enquiry2 = dma_alloc_coherent(&pdev->dev, sizeof (struct myrb_enquiry2),
&enquiry2_addr, GFP_KERNEL);
if (!enquiry2) {
shost_printk(KERN_ERR, cb->host,
"Failed to allocate V1 enquiry2 memory\n" );
return -ENOMEM;
}
config2 = dma_alloc_coherent(&pdev->dev, sizeof (struct myrb_config2),
&config2_addr, GFP_KERNEL);
if (!config2) {
shost_printk(KERN_ERR, cb->host,
"Failed to allocate V1 config2 memory\n" );
dma_free_coherent(&pdev->dev, sizeof (struct myrb_enquiry2),
enquiry2, enquiry2_addr);
return -ENOMEM;
}
mutex_lock(&cb->dma_mutex);
status = myrb_hba_enquiry(cb);
mutex_unlock(&cb->dma_mutex);
if (status != MYRB_STATUS_SUCCESS) {
shost_printk(KERN_WARNING, cb->host,
"Failed it issue V1 Enquiry\n" );
goto out_free;
}
status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY2, enquiry2_addr);
if (status != MYRB_STATUS_SUCCESS) {
shost_printk(KERN_WARNING, cb->host,
"Failed to issue V1 Enquiry2\n" );
goto out_free;
}
status = myrb_exec_type3(cb, MYRB_CMD_READ_CONFIG2, config2_addr);
if (status != MYRB_STATUS_SUCCESS) {
shost_printk(KERN_WARNING, cb->host,
"Failed to issue ReadConfig2\n" );
goto out_free;
}
status = myrb_get_ldev_info(cb);
if (status != MYRB_STATUS_SUCCESS) {
shost_printk(KERN_WARNING, cb->host,
"Failed to get logical drive information\n" );
goto out_free;
}
/*
* Initialize the Controller Model Name and Full Model Name fields.
*/
switch (enquiry2->hw.sub_model) {
case DAC960_V1_P_PD_PU:
if (enquiry2->scsi_cap.bus_speed == MYRB_SCSI_SPEED_ULTRA)
strcpy(cb->model_name, "DAC960PU" );
else
strcpy(cb->model_name, "DAC960PD" );
break ;
case DAC960_V1_PL:
strcpy(cb->model_name, "DAC960PL" );
break ;
case DAC960_V1_PG:
strcpy(cb->model_name, "DAC960PG" );
break ;
case DAC960_V1_PJ:
strcpy(cb->model_name, "DAC960PJ" );
break ;
case DAC960_V1_PR:
strcpy(cb->model_name, "DAC960PR" );
break ;
case DAC960_V1_PT:
strcpy(cb->model_name, "DAC960PT" );
break ;
case DAC960_V1_PTL0:
strcpy(cb->model_name, "DAC960PTL0" );
break ;
case DAC960_V1_PRL:
strcpy(cb->model_name, "DAC960PRL" );
break ;
case DAC960_V1_PTL1:
strcpy(cb->model_name, "DAC960PTL1" );
break ;
case DAC960_V1_1164P:
strcpy(cb->model_name, "eXtremeRAID 1100" );
break ;
default :
shost_printk(KERN_WARNING, cb->host,
"Unknown Model %X\n" ,
enquiry2->hw.sub_model);
goto out;
}
/*
* Initialize the Controller Firmware Version field and verify that it
* is a supported firmware version.
* The supported firmware versions are:
*
* DAC1164P 5.06 and above
* DAC960PTL/PRL/PJ/PG 4.06 and above
* DAC960PU/PD/PL 3.51 and above
* DAC960PU/PD/PL/P 2.73 and above
*/
#if defined (CONFIG_ALPHA)
/*
* DEC Alpha machines were often equipped with DAC960 cards that were
* OEMed from Mylex, and had their own custom firmware. Version 2.70,
* the last custom FW revision to be released by DEC for these older
* controllers, appears to work quite well with this driver.
*
* Cards tested successfully were several versions each of the PD and
* PU, called by DEC the KZPSC and KZPAC, respectively, and having
* the Manufacturer Numbers (from Mylex), usually on a sticker on the
* back of the board, of:
*
* KZPSC: D040347 (1-channel) or D040348 (2-channel)
* or D040349 (3-channel)
* KZPAC: D040395 (1-channel) or D040396 (2-channel)
* or D040397 (3-channel)
*/
# define FIRMWARE_27X "2.70"
#else
# define FIRMWARE_27X "2.73"
#endif
if (enquiry2->fw.major_version == 0) {
enquiry2->fw.major_version = cb->enquiry->fw_major_version;
enquiry2->fw.minor_version = cb->enquiry->fw_minor_version;
enquiry2->fw.firmware_type = '0' ;
enquiry2->fw.turn_id = 0;
}
snprintf(cb->fw_version, sizeof (cb->fw_version),
"%u.%02u-%c-%02u" ,
enquiry2->fw.major_version,
enquiry2->fw.minor_version,
enquiry2->fw.firmware_type,
enquiry2->fw.turn_id);
if (!((enquiry2->fw.major_version == 5 &&
enquiry2->fw.minor_version >= 6) ||
(enquiry2->fw.major_version == 4 &&
enquiry2->fw.minor_version >= 6) ||
(enquiry2->fw.major_version == 3 &&
enquiry2->fw.minor_version >= 51) ||
(enquiry2->fw.major_version == 2 &&
strcmp(cb->fw_version, FIRMWARE_27X) >= 0))) {
shost_printk(KERN_WARNING, cb->host,
"Firmware Version '%s' unsupported\n" ,
cb->fw_version);
goto out;
}
/*
* Initialize the Channels, Targets, Memory Size, and SAF-TE
* Enclosure Management Enabled fields.
*/
switch (enquiry2->hw.model) {
case MYRB_5_CHANNEL_BOARD:
pchan_max = 5;
break ;
case MYRB_3_CHANNEL_BOARD:
case MYRB_3_CHANNEL_ASIC_DAC:
pchan_max = 3;
break ;
case MYRB_2_CHANNEL_BOARD:
pchan_max = 2;
break ;
default :
pchan_max = enquiry2->cfg_chan;
break ;
}
pchan_cur = enquiry2->cur_chan;
if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_32BIT)
cb->bus_width = 32;
else if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_16BIT)
cb->bus_width = 16;
else
cb->bus_width = 8;
cb->ldev_block_size = enquiry2->ldev_block_size;
shost->max_channel = pchan_cur;
shost->max_id = enquiry2->max_targets;
memsize = enquiry2->mem_size >> 20;
cb->safte_enabled = (enquiry2->fault_mgmt == MYRB_FAULT_SAFTE);
/*
* Initialize the Controller Queue Depth, Driver Queue Depth,
* Logical Drive Count, Maximum Blocks per Command, Controller
* Scatter/Gather Limit, and Driver Scatter/Gather Limit.
* The Driver Queue Depth must be at most one less than the
* Controller Queue Depth to allow for an automatic drive
* rebuild operation.
*/
shost->can_queue = cb->enquiry->max_tcq;
if (shost->can_queue < 3)
shost->can_queue = enquiry2->max_cmds;
if (shost->can_queue < 3)
/* Play safe and disable TCQ */
shost->can_queue = 1;
if (shost->can_queue > MYRB_CMD_MBOX_COUNT - 2)
shost->can_queue = MYRB_CMD_MBOX_COUNT - 2;
shost->max_sectors = enquiry2->max_sectors;
shost->sg_tablesize = enquiry2->max_sge;
if (shost->sg_tablesize > MYRB_SCATTER_GATHER_LIMIT)
shost->sg_tablesize = MYRB_SCATTER_GATHER_LIMIT;
/*
* Initialize the Stripe Size, Segment Size, and Geometry Translation.
*/
cb->stripe_size = config2->blocks_per_stripe * config2->block_factor
>> (10 - MYRB_BLKSIZE_BITS);
cb->segment_size = config2->blocks_per_cacheline * config2->block_factor
>> (10 - MYRB_BLKSIZE_BITS);
/* Assume 255/63 translation */
cb->ldev_geom_heads = 255;
cb->ldev_geom_sectors = 63;
if (config2->drive_geometry) {
cb->ldev_geom_heads = 128;
cb->ldev_geom_sectors = 32;
}
/*
* Initialize the Background Initialization Status.
*/
if ((cb->fw_version[0] == '4' &&
strcmp(cb->fw_version, "4.08" ) >= 0) ||
(cb->fw_version[0] == '5' &&
strcmp(cb->fw_version, "5.08" ) >= 0)) {
cb->bgi_status_supported = true ;
myrb_bgi_control(cb);
}
cb->last_rbld_status = MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS;
ret = 0;
out:
shost_printk(KERN_INFO, cb->host,
"Configuring %s PCI RAID Controller\n" , cb->model_name);
shost_printk(KERN_INFO, cb->host,
" Firmware Version: %s, Memory Size: %dMB\n" ,
cb->fw_version, memsize);
if (cb->io_addr == 0)
shost_printk(KERN_INFO, cb->host,
" I/O Address: n/a, PCI Address: 0x%lX, IRQ Channel: %d\n" ,
(unsigned long )cb->pci_addr, cb->irq);
else
shost_printk(KERN_INFO, cb->host,
" I/O Address: 0x%lX, PCI Address: 0x%lX, IRQ Channel: %d\n" ,
(unsigned long )cb->io_addr, (unsigned long )cb->pci_addr,
cb->irq);
shost_printk(KERN_INFO, cb->host,
" Controller Queue Depth: %d, Maximum Blocks per Command: %d\n" ,
cb->host->can_queue, cb->host->max_sectors);
shost_printk(KERN_INFO, cb->host,
" Driver Queue Depth: %d, Scatter/Gather Limit: %d of %d Segments\n" ,
cb->host->can_queue, cb->host->sg_tablesize,
MYRB_SCATTER_GATHER_LIMIT);
shost_printk(KERN_INFO, cb->host,
" Stripe Size: %dKB, Segment Size: %dKB, BIOS Geometry: %d/%d%s\n" ,
cb->stripe_size, cb->segment_size,
cb->ldev_geom_heads, cb->ldev_geom_sectors,
cb->safte_enabled ?
" SAF-TE Enclosure Management Enabled" : "" );
shost_printk(KERN_INFO, cb->host,
" Physical: %d/%d channels %d/%d/%d devices\n" ,
pchan_cur, pchan_max, 0, cb->enquiry->pdev_dead,
cb->host->max_id);
shost_printk(KERN_INFO, cb->host,
" Logical: 1/1 channels, %d/%d disks\n" ,
cb->enquiry->ldev_count, MYRB_MAX_LDEVS);
out_free:
dma_free_coherent(&pdev->dev, sizeof (struct myrb_enquiry2),
enquiry2, enquiry2_addr);
dma_free_coherent(&pdev->dev, sizeof (struct myrb_config2),
config2, config2_addr);
return ret;
}
/*
* myrb_unmap - unmaps controller structures
*/
static void myrb_unmap(struct myrb_hba *cb)
{
if (cb->ldev_info_buf) {
size_t ldev_info_size = sizeof (struct myrb_ldev_info) *
MYRB_MAX_LDEVS;
dma_free_coherent(&cb->pdev->dev, ldev_info_size,
cb->ldev_info_buf, cb->ldev_info_addr);
cb->ldev_info_buf = NULL;
}
if (cb->err_table) {
size_t err_table_size = sizeof (struct myrb_error_entry) *
MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS;
dma_free_coherent(&cb->pdev->dev, err_table_size,
cb->err_table, cb->err_table_addr);
cb->err_table = NULL;
}
if (cb->enquiry) {
dma_free_coherent(&cb->pdev->dev, sizeof (struct myrb_enquiry),
cb->enquiry, cb->enquiry_addr);
cb->enquiry = NULL;
}
if (cb->first_stat_mbox) {
dma_free_coherent(&cb->pdev->dev, cb->stat_mbox_size,
cb->first_stat_mbox, cb->stat_mbox_addr);
cb->first_stat_mbox = NULL;
}
if (cb->first_cmd_mbox) {
dma_free_coherent(&cb->pdev->dev, cb->cmd_mbox_size,
cb->first_cmd_mbox, cb->cmd_mbox_addr);
cb->first_cmd_mbox = NULL;
}
}
/*
* myrb_cleanup - cleanup controller structures
*/
static void myrb_cleanup(struct myrb_hba *cb)
{
struct pci_dev *pdev = cb->pdev;
/* Free the memory mailbox, status, and related structures */
myrb_unmap(cb);
if (cb->mmio_base) {
if (cb->disable_intr)
cb->disable_intr(cb->io_base);
iounmap(cb->mmio_base);
}
if (cb->irq)
free_irq(cb->irq, cb);
if (cb->io_addr)
release_region(cb->io_addr, 0x80);
pci_set_drvdata(pdev, NULL);
pci_disable_device(pdev);
scsi_host_put(cb->host);
}
static int myrb_host_reset(struct scsi_cmnd *scmd)
{
struct Scsi_Host *shost = scmd->device->host;
struct myrb_hba *cb = shost_priv(shost);
cb->reset(cb->io_base);
return SUCCESS;
}
static int myrb_pthru_queuecommand(struct Scsi_Host *shost,
struct scsi_cmnd *scmd)
{
struct request *rq = scsi_cmd_to_rq(scmd);
struct myrb_hba *cb = shost_priv(shost);
struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd);
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
struct myrb_dcdb *dcdb;
dma_addr_t dcdb_addr;
struct scsi_device *sdev = scmd->device;
struct scatterlist *sgl;
unsigned long flags;
int nsge;
myrb_reset_cmd(cmd_blk);
dcdb = dma_pool_alloc(cb->dcdb_pool, GFP_ATOMIC, &dcdb_addr);
if (!dcdb)
return SCSI_MLQUEUE_HOST_BUSY;
nsge = scsi_dma_map(scmd);
if (nsge > 1) {
dma_pool_free(cb->dcdb_pool, dcdb, dcdb_addr);
scmd->result = (DID_ERROR << 16);
scsi_done(scmd);
return 0;
}
mbox->type3.opcode = MYRB_CMD_DCDB;
mbox->type3.id = rq->tag + 3;
mbox->type3.addr = dcdb_addr;
dcdb->channel = sdev->channel;
dcdb->target = sdev->id;
switch (scmd->sc_data_direction) {
case DMA_NONE:
dcdb->data_xfer = MYRB_DCDB_XFER_NONE;
break ;
case DMA_TO_DEVICE:
dcdb->data_xfer = MYRB_DCDB_XFER_SYSTEM_TO_DEVICE;
break ;
case DMA_FROM_DEVICE:
dcdb->data_xfer = MYRB_DCDB_XFER_DEVICE_TO_SYSTEM;
break ;
default :
dcdb->data_xfer = MYRB_DCDB_XFER_ILLEGAL;
break ;
}
dcdb->early_status = false ;
if (rq->timeout <= 10)
dcdb->timeout = MYRB_DCDB_TMO_10_SECS;
else if (rq->timeout <= 60)
dcdb->timeout = MYRB_DCDB_TMO_60_SECS;
else if (rq->timeout <= 600)
dcdb->timeout = MYRB_DCDB_TMO_10_MINS;
else
dcdb->timeout = MYRB_DCDB_TMO_24_HRS;
dcdb->no_autosense = false ;
dcdb->allow_disconnect = true ;
sgl = scsi_sglist(scmd);
dcdb->dma_addr = sg_dma_address(sgl);
if (sg_dma_len(sgl) > USHRT_MAX) {
dcdb->xfer_len_lo = sg_dma_len(sgl) & 0xffff;
dcdb->xfer_len_hi4 = sg_dma_len(sgl) >> 16;
} else {
dcdb->xfer_len_lo = sg_dma_len(sgl);
dcdb->xfer_len_hi4 = 0;
}
dcdb->cdb_len = scmd->cmd_len;
dcdb->sense_len = sizeof (dcdb->sense);
memcpy(&dcdb->cdb, scmd->cmnd, scmd->cmd_len);
spin_lock_irqsave(&cb->queue_lock, flags);
cb->qcmd(cb, cmd_blk);
spin_unlock_irqrestore(&cb->queue_lock, flags);
return 0;
}
static void myrb_inquiry(struct myrb_hba *cb,
struct scsi_cmnd *scmd)
{
unsigned char inq[36] = {
0x00, 0x00, 0x03, 0x02, 0x20, 0x00, 0x01, 0x00,
0x4d, 0x59, 0x4c, 0x45, 0x58, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20,
};
if (cb->bus_width > 16)
inq[7] |= 1 << 6;
if (cb->bus_width > 8)
inq[7] |= 1 << 5;
memcpy(&inq[16], cb->model_name, 16);
memcpy(&inq[32], cb->fw_version, 1);
memcpy(&inq[33], &cb->fw_version[2], 2);
memcpy(&inq[35], &cb->fw_version[7], 1);
scsi_sg_copy_from_buffer(scmd, (void *)inq, 36);
}
static void
myrb_mode_sense(struct myrb_hba *cb, struct scsi_cmnd *scmd,
struct myrb_ldev_info *ldev_info)
{
unsigned char modes[32], *mode_pg;
bool dbd;
size_t mode_len;
dbd = (scmd->cmnd[1] & 0x08) == 0x08;
if (dbd) {
mode_len = 24;
mode_pg = &modes[4];
} else {
mode_len = 32;
mode_pg = &modes[12];
}
memset(modes, 0, sizeof (modes));
modes[0] = mode_len - 1;
if (!dbd) {
unsigned char *block_desc = &modes[4];
modes[3] = 8;
put_unaligned_be32(ldev_info->size, &block_desc[0]);
put_unaligned_be32(cb->ldev_block_size, &block_desc[5]);
}
mode_pg[0] = 0x08;
mode_pg[1] = 0x12;
if (ldev_info->wb_enabled)
mode_pg[2] |= 0x04;
if (cb->segment_size) {
mode_pg[2] |= 0x08;
put_unaligned_be16(cb->segment_size, &mode_pg[14]);
}
scsi_sg_copy_from_buffer(scmd, modes, mode_len);
}
static void myrb_request_sense(struct myrb_hba *cb,
struct scsi_cmnd *scmd)
{
scsi_build_sense(scmd, 0, NO_SENSE, 0, 0);
scsi_sg_copy_from_buffer(scmd, scmd->sense_buffer,
SCSI_SENSE_BUFFERSIZE);
}
static void myrb_read_capacity(struct myrb_hba *cb, struct scsi_cmnd *scmd,
struct myrb_ldev_info *ldev_info)
{
unsigned char data[8];
dev_dbg(&scmd->device->sdev_gendev,
"Capacity %u, blocksize %u\n" ,
ldev_info->size, cb->ldev_block_size);
put_unaligned_be32(ldev_info->size - 1, &data[0]);
put_unaligned_be32(cb->ldev_block_size, &data[4]);
scsi_sg_copy_from_buffer(scmd, data, 8);
}
static int myrb_ldev_queuecommand(struct Scsi_Host *shost,
struct scsi_cmnd *scmd)
{
struct myrb_hba *cb = shost_priv(shost);
struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd);
union myrb_cmd_mbox *mbox = &cmd_blk->mbox;
struct myrb_ldev_info *ldev_info;
struct scsi_device *sdev = scmd->device;
struct scatterlist *sgl;
unsigned long flags;
u64 lba;
u32 block_cnt;
int nsge;
ldev_info = sdev->hostdata;
if (ldev_info->state != MYRB_DEVICE_ONLINE &&
ldev_info->state != MYRB_DEVICE_WO) {
dev_dbg(&shost->shost_gendev, "ldev %u in state %x, skip\n" ,
sdev->id, ldev_info ? ldev_info->state : 0xff);
scmd->result = (DID_BAD_TARGET << 16);
scsi_done(scmd);
return 0;
}
switch (scmd->cmnd[0]) {
case TEST_UNIT_READY:
scmd->result = (DID_OK << 16);
scsi_done(scmd);
return 0;
case INQUIRY:
if (scmd->cmnd[1] & 1) {
/* Illegal request, invalid field in CDB */
scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
} else {
myrb_inquiry(cb, scmd);
scmd->result = (DID_OK << 16);
}
scsi_done(scmd);
return 0;
case SYNCHRONIZE_CACHE:
scmd->result = (DID_OK << 16);
scsi_done(scmd);
return 0;
case MODE_SENSE:
if ((scmd->cmnd[2] & 0x3F) != 0x3F &&
(scmd->cmnd[2] & 0x3F) != 0x08) {
/* Illegal request, invalid field in CDB */
scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
} else {
myrb_mode_sense(cb, scmd, ldev_info);
scmd->result = (DID_OK << 16);
}
scsi_done(scmd);
return 0;
case READ_CAPACITY:
if ((scmd->cmnd[1] & 1) ||
(scmd->cmnd[8] & 1)) {
/* Illegal request, invalid field in CDB */
scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
scsi_done(scmd);
return 0;
}
lba = get_unaligned_be32(&scmd->cmnd[2]);
if (lba) {
/* Illegal request, invalid field in CDB */
scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
scsi_done(scmd);
return 0;
}
myrb_read_capacity(cb, scmd, ldev_info);
scsi_done(scmd);
return 0;
case REQUEST_SENSE:
myrb_request_sense(cb, scmd);
scmd->result = (DID_OK << 16);
return 0;
case SEND_DIAGNOSTIC:
if (scmd->cmnd[1] != 0x04) {
/* Illegal request, invalid field in CDB */
scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
} else {
/* Assume good status */
scmd->result = (DID_OK << 16);
}
scsi_done(scmd);
return 0;
case READ_6:
if (ldev_info->state == MYRB_DEVICE_WO) {
/* Data protect, attempt to read invalid data */
scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06);
scsi_done(scmd);
return 0;
}
fallthrough;
case WRITE_6:
lba = (((scmd->cmnd[1] & 0x1F) << 16) |
(scmd->cmnd[2] << 8) |
scmd->cmnd[3]);
block_cnt = scmd->cmnd[4];
break ;
case READ_10:
if (ldev_info->state == MYRB_DEVICE_WO) {
/* Data protect, attempt to read invalid data */
scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06);
scsi_done(scmd);
return 0;
}
fallthrough;
case WRITE_10:
case VERIFY: /* 0x2F */
case WRITE_VERIFY: /* 0x2E */
lba = get_unaligned_be32(&scmd->cmnd[2]);
block_cnt = get_unaligned_be16(&scmd->cmnd[7]);
break ;
case READ_12:
if (ldev_info->state == MYRB_DEVICE_WO) {
/* Data protect, attempt to read invalid data */
scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06);
scsi_done(scmd);
return 0;
}
fallthrough;
case WRITE_12:
case VERIFY_12: /* 0xAF */
case WRITE_VERIFY_12: /* 0xAE */
lba = get_unaligned_be32(&scmd->cmnd[2]);
block_cnt = get_unaligned_be32(&scmd->cmnd[6]);
break ;
default :
/* Illegal request, invalid opcode */
scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x20, 0);
scsi_done(scmd);
return 0;
}
myrb_reset_cmd(cmd_blk);
mbox->type5.id = scsi_cmd_to_rq(scmd)->tag + 3;
if (scmd->sc_data_direction == DMA_NONE)
goto submit;
nsge = scsi_dma_map(scmd);
if (nsge == 1) {
sgl = scsi_sglist(scmd);
if (scmd->sc_data_direction == DMA_FROM_DEVICE)
mbox->type5.opcode = MYRB_CMD_READ;
else
mbox->type5.opcode = MYRB_CMD_WRITE;
mbox->type5.ld.xfer_len = block_cnt;
mbox->type5.ld.ldev_num = sdev->id;
mbox->type5.lba = lba;
mbox->type5.addr = (u32)sg_dma_address(sgl);
} else {
struct myrb_sge *hw_sgl;
dma_addr_t hw_sgl_addr;
int i;
hw_sgl = dma_pool_alloc(cb->sg_pool, GFP_ATOMIC, &hw_sgl_addr);
if (!hw_sgl)
return SCSI_MLQUEUE_HOST_BUSY;
cmd_blk->sgl = hw_sgl;
cmd_blk->sgl_addr = hw_sgl_addr;
if (scmd->sc_data_direction == DMA_FROM_DEVICE)
mbox->type5.opcode = MYRB_CMD_READ_SG;
else
mbox->type5.opcode = MYRB_CMD_WRITE_SG;
mbox->type5.ld.xfer_len = block_cnt;
mbox->type5.ld.ldev_num = sdev->id;
mbox->type5.lba = lba;
mbox->type5.addr = hw_sgl_addr;
mbox->type5.sg_count = nsge;
scsi_for_each_sg(scmd, sgl, nsge, i) {
hw_sgl->sge_addr = (u32)sg_dma_address(sgl);
hw_sgl->sge_count = (u32)sg_dma_len(sgl);
hw_sgl++;
}
}
submit:
spin_lock_irqsave(&cb->queue_lock, flags);
cb->qcmd(cb, cmd_blk);
spin_unlock_irqrestore(&cb->queue_lock, flags);
return 0;
}
static int myrb_queuecommand(struct Scsi_Host *shost,
struct scsi_cmnd *scmd)
{
struct scsi_device *sdev = scmd->device;
if (sdev->channel > myrb_logical_channel(shost)) {
scmd->result = (DID_BAD_TARGET << 16);
scsi_done(scmd);
return 0;
}
if (sdev->channel == myrb_logical_channel(shost))
return myrb_ldev_queuecommand(shost, scmd);
return myrb_pthru_queuecommand(shost, scmd);
}
static int myrb_ldev_sdev_init(struct scsi_device *sdev)
{
struct myrb_hba *cb = shost_priv(sdev->host);
struct myrb_ldev_info *ldev_info;
unsigned short ldev_num = sdev->id;
enum raid_level level;
ldev_info = cb->ldev_info_buf + ldev_num;
sdev->hostdata = kzalloc(sizeof (*ldev_info), GFP_KERNEL);
if (!sdev->hostdata)
return -ENOMEM;
dev_dbg(&sdev->sdev_gendev,
"slave alloc ldev %d state %x\n" ,
ldev_num, ldev_info->state);
memcpy(sdev->hostdata, ldev_info,
sizeof (*ldev_info));
switch (ldev_info->raid_level) {
case MYRB_RAID_LEVEL0:
level = RAID_LEVEL_LINEAR;
break ;
case MYRB_RAID_LEVEL1:
level = RAID_LEVEL_1;
break ;
case MYRB_RAID_LEVEL3:
level = RAID_LEVEL_3;
break ;
case MYRB_RAID_LEVEL5:
level = RAID_LEVEL_5;
break ;
case MYRB_RAID_LEVEL6:
level = RAID_LEVEL_6;
break ;
case MYRB_RAID_JBOD:
level = RAID_LEVEL_JBOD;
break ;
default :
level = RAID_LEVEL_UNKNOWN;
break ;
}
raid_set_level(myrb_raid_template, &sdev->sdev_gendev, level);
return 0;
}
static int myrb_pdev_sdev_init(struct scsi_device *sdev)
{
struct myrb_hba *cb = shost_priv(sdev->host);
struct myrb_pdev_state *pdev_info;
unsigned short status;
if (sdev->id > MYRB_MAX_TARGETS)
return -ENXIO;
pdev_info = kzalloc(sizeof (*pdev_info), GFP_KERNEL);
if (!pdev_info)
return -ENOMEM;
status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE,
sdev, pdev_info);
if (status != MYRB_STATUS_SUCCESS) {
dev_dbg(&sdev->sdev_gendev,
"Failed to get device state, status %x\n" ,
status);
kfree(pdev_info);
return -ENXIO;
}
if (!pdev_info->present) {
dev_dbg(&sdev->sdev_gendev,
"device not present, skip\n" );
kfree(pdev_info);
return -ENXIO;
}
dev_dbg(&sdev->sdev_gendev,
"slave alloc pdev %d:%d state %x\n" ,
sdev->channel, sdev->id, pdev_info->state);
sdev->hostdata = pdev_info;
return 0;
}
static int myrb_sdev_init(struct scsi_device *sdev)
{
if (sdev->channel > myrb_logical_channel(sdev->host))
return -ENXIO;
if (sdev->lun > 0)
return -ENXIO;
if (sdev->channel == myrb_logical_channel(sdev->host))
return myrb_ldev_sdev_init(sdev);
return myrb_pdev_sdev_init(sdev);
}
static int myrb_sdev_configure(struct scsi_device *sdev,
struct queue_limits *lim)
{
struct myrb_ldev_info *ldev_info;
if (sdev->channel > myrb_logical_channel(sdev->host))
return -ENXIO;
if (sdev->channel < myrb_logical_channel(sdev->host)) {
sdev->no_uld_attach = 1;
return 0;
}
if (sdev->lun != 0)
return -ENXIO;
ldev_info = sdev->hostdata;
if (!ldev_info)
return -ENXIO;
if (ldev_info->state != MYRB_DEVICE_ONLINE)
sdev_printk(KERN_INFO, sdev,
"Logical drive is %s\n" ,
myrb_devstate_name(ldev_info->state));
sdev->tagged_supported = 1;
return 0;
}
static void myrb_sdev_destroy(struct scsi_device *sdev)
{
kfree(sdev->hostdata);
}
static int myrb_biosparam(struct scsi_device *sdev, struct block_device *bdev,
sector_t capacity, int geom[])
{
struct myrb_hba *cb = shost_priv(sdev->host);
geom[0] = cb->ldev_geom_heads;
geom[1] = cb->ldev_geom_sectors;
geom[2] = sector_div(capacity, geom[0] * geom[1]);
return 0;
}
static ssize_t raid_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct myrb_hba *cb = shost_priv(sdev->host);
int ret;
if (!sdev->hostdata)
return snprintf(buf, 16, "Unknown\n" );
if (sdev->channel == myrb_logical_channel(sdev->host)) {
struct myrb_ldev_info *ldev_info = sdev->hostdata;
const char *name;
name = myrb_devstate_name(ldev_info->state);
if (name)
ret = snprintf(buf, 64, "%s\n" , name);
else
ret = snprintf(buf, 64, "Invalid (%02X)\n" ,
ldev_info->state);
} else {
struct myrb_pdev_state *pdev_info = sdev->hostdata;
unsigned short status;
const char *name;
status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE,
sdev, pdev_info);
if (status != MYRB_STATUS_SUCCESS)
sdev_printk(KERN_INFO, sdev,
"Failed to get device state, status %x\n" ,
status);
if (!pdev_info->present)
name = "Removed" ;
else
name = myrb_devstate_name(pdev_info->state);
if (name)
ret = snprintf(buf, 64, "%s\n" , name);
else
ret = snprintf(buf, 64, "Invalid (%02X)\n" ,
pdev_info->state);
}
return ret;
}
static ssize_t raid_state_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct myrb_hba *cb = shost_priv(sdev->host);
struct myrb_pdev_state *pdev_info;
enum myrb_devstate new_state;
unsigned short status;
if (!strncmp(buf, "kill" , 4) ||
!strncmp(buf, "offline" , 7))
new_state = MYRB_DEVICE_DEAD;
else if (!strncmp(buf, "online" , 6))
new_state = MYRB_DEVICE_ONLINE;
else if (!strncmp(buf, "standby" , 7))
new_state = MYRB_DEVICE_STANDBY;
else
return -EINVAL;
pdev_info = sdev->hostdata;
if (!pdev_info) {
sdev_printk(KERN_INFO, sdev,
"Failed - no physical device information\n" );
return -ENXIO;
}
if (!pdev_info->present) {
sdev_printk(KERN_INFO, sdev,
"Failed - device not present\n" );
return -ENXIO;
}
if (pdev_info->state == new_state)
return count;
status = myrb_set_pdev_state(cb, sdev, new_state);
switch (status) {
case MYRB_STATUS_SUCCESS:
break ;
case MYRB_STATUS_START_DEVICE_FAILED:
sdev_printk(KERN_INFO, sdev,
"Failed - Unable to Start Device\n" );
count = -EAGAIN;
break ;
case MYRB_STATUS_NO_DEVICE:
sdev_printk(KERN_INFO, sdev,
"Failed - No Device at Address\n" );
count = -ENODEV;
break ;
case MYRB_STATUS_INVALID_CHANNEL_OR_TARGET:
sdev_printk(KERN_INFO, sdev,
"Failed - Invalid Channel or Target or Modifier\n" );
count = -EINVAL;
break ;
case MYRB_STATUS_CHANNEL_BUSY:
sdev_printk(KERN_INFO, sdev,
"Failed - Channel Busy\n" );
count = -EBUSY;
break ;
default :
sdev_printk(KERN_INFO, sdev,
"Failed - Unexpected Status %04X\n" , status);
count = -EIO;
break ;
}
return count;
}
static DEVICE_ATTR_RW(raid_state);
static ssize_t raid_level_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scsi_device *sdev = to_scsi_device(dev);
if (sdev->channel == myrb_logical_channel(sdev->host)) {
struct myrb_ldev_info *ldev_info = sdev->hostdata;
const char *name;
if (!ldev_info)
return -ENXIO;
name = myrb_raidlevel_name(ldev_info->raid_level);
if (!name)
return snprintf(buf, 64, "Invalid (%02X)\n" ,
ldev_info->state);
return snprintf(buf, 64, "%s\n" , name);
}
return snprintf(buf, 64, "Physical Drive\n" );
}
static DEVICE_ATTR_RO(raid_level);
static ssize_t rebuild_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct myrb_hba *cb = shost_priv(sdev->host);
struct myrb_rbld_progress rbld_buf;
unsigned char status;
if (sdev->channel < myrb_logical_channel(sdev->host))
return snprintf(buf, 64, "physical device - not rebuilding\n" );
status = myrb_get_rbld_progress(cb, &rbld_buf);
if (rbld_buf.ldev_num != sdev->id ||
status != MYRB_STATUS_SUCCESS)
return snprintf(buf, 64, "not rebuilding\n" );
return snprintf(buf, 64, "rebuilding block %u of %u\n" ,
rbld_buf.ldev_size - rbld_buf.blocks_left,
rbld_buf.ldev_size);
}
static ssize_t rebuild_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct myrb_hba *cb = shost_priv(sdev->host);
struct myrb_cmdblk *cmd_blk;
union myrb_cmd_mbox *mbox;
unsigned short status;
int rc, start;
const char *msg;
rc = kstrtoint(buf, 0, &start);
if (rc)
return rc;
if (sdev->channel >= myrb_logical_channel(sdev->host))
return -ENXIO;
status = myrb_get_rbld_progress(cb, NULL);
if (start) {
if (status == MYRB_STATUS_SUCCESS) {
sdev_printk(KERN_INFO, sdev,
"Rebuild Not Initiated; already in progress\n" );
return -EALREADY;
}
mutex_lock(&cb->dcmd_mutex);
cmd_blk = &cb->dcmd_blk;
myrb_reset_cmd(cmd_blk);
mbox = &cmd_blk->mbox;
mbox->type3D.opcode = MYRB_CMD_REBUILD_ASYNC;
mbox->type3D.id = MYRB_DCMD_TAG;
mbox->type3D.channel = sdev->channel;
mbox->type3D.target = sdev->id;
status = myrb_exec_cmd(cb, cmd_blk);
mutex_unlock(&cb->dcmd_mutex);
} else {
struct pci_dev *pdev = cb->pdev;
unsigned char *rate;
dma_addr_t rate_addr;
if (status != MYRB_STATUS_SUCCESS) {
sdev_printk(KERN_INFO, sdev,
"Rebuild Not Cancelled; not in progress\n" );
return 0;
}
rate = dma_alloc_coherent(&pdev->dev, sizeof (char ),
&rate_addr, GFP_KERNEL);
if (rate == NULL) {
sdev_printk(KERN_INFO, sdev,
"Cancellation of Rebuild Failed - Out of Memory\n" );
return -ENOMEM;
}
mutex_lock(&cb->dcmd_mutex);
cmd_blk = &cb->dcmd_blk;
myrb_reset_cmd(cmd_blk);
mbox = &cmd_blk->mbox;
mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL;
mbox->type3R.id = MYRB_DCMD_TAG;
mbox->type3R.rbld_rate = 0xFF;
mbox->type3R.addr = rate_addr;
status = myrb_exec_cmd(cb, cmd_blk);
dma_free_coherent(&pdev->dev, sizeof (char ), rate, rate_addr);
mutex_unlock(&cb->dcmd_mutex);
}
if (status == MYRB_STATUS_SUCCESS) {
sdev_printk(KERN_INFO, sdev, "Rebuild %s\n" ,
start ? "Initiated" : "Cancelled" );
return count;
}
if (!start) {
sdev_printk(KERN_INFO, sdev,
"Rebuild Not Cancelled, status 0x%x\n" ,
status);
return -EIO;
}
switch (status) {
case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE:
msg = "Attempt to Rebuild Online or Unresponsive Drive" ;
break ;
case MYRB_STATUS_RBLD_NEW_DISK_FAILED:
msg = "New Disk Failed During Rebuild" ;
break ;
case MYRB_STATUS_INVALID_ADDRESS:
msg = "Invalid Device Address" ;
break ;
case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS:
msg = "Already in Progress" ;
break ;
default :
msg = NULL;
break ;
}
if (msg)
sdev_printk(KERN_INFO, sdev,
"Rebuild Failed - %s\n" , msg);
else
sdev_printk(KERN_INFO, sdev,
"Rebuild Failed, status 0x%x\n" , status);
return -EIO;
}
static DEVICE_ATTR_RW(rebuild);
static ssize_t consistency_check_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct myrb_hba *cb = shost_priv(sdev->host);
struct myrb_rbld_progress rbld_buf;
struct myrb_cmdblk *cmd_blk;
union myrb_cmd_mbox *mbox;
unsigned short ldev_num = 0xFFFF;
unsigned short status;
int rc, start;
const char *msg;
rc = kstrtoint(buf, 0, &start);
if (rc)
return rc;
if (sdev->channel < myrb_logical_channel(sdev->host))
return -ENXIO;
status = myrb_get_rbld_progress(cb, &rbld_buf);
if (start) {
if (status == MYRB_STATUS_SUCCESS) {
sdev_printk(KERN_INFO, sdev,
"Check Consistency Not Initiated; already in progress\n" );
return -EALREADY;
}
mutex_lock(&cb->dcmd_mutex);
cmd_blk = &cb->dcmd_blk;
myrb_reset_cmd(cmd_blk);
mbox = &cmd_blk->mbox;
mbox->type3C.opcode = MYRB_CMD_CHECK_CONSISTENCY_ASYNC;
mbox->type3C.id = MYRB_DCMD_TAG;
mbox->type3C.ldev_num = sdev->id;
mbox->type3C.auto_restore = true ;
status = myrb_exec_cmd(cb, cmd_blk);
mutex_unlock(&cb->dcmd_mutex);
} else {
struct pci_dev *pdev = cb->pdev;
unsigned char *rate;
dma_addr_t rate_addr;
if (ldev_num != sdev->id) {
sdev_printk(KERN_INFO, sdev,
"Check Consistency Not Cancelled; not in progress\n" );
return 0;
}
rate = dma_alloc_coherent(&pdev->dev, sizeof (char ),
&rate_addr, GFP_KERNEL);
if (rate == NULL) {
sdev_printk(KERN_INFO, sdev,
"Cancellation of Check Consistency Failed - Out of Memory\n" );
return -ENOMEM;
}
mutex_lock(&cb->dcmd_mutex);
cmd_blk = &cb->dcmd_blk;
myrb_reset_cmd(cmd_blk);
mbox = &cmd_blk->mbox;
mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL;
mbox->type3R.id = MYRB_DCMD_TAG;
mbox->type3R.rbld_rate = 0xFF;
mbox->type3R.addr = rate_addr;
status = myrb_exec_cmd(cb, cmd_blk);
dma_free_coherent(&pdev->dev, sizeof (char ), rate, rate_addr);
mutex_unlock(&cb->dcmd_mutex);
}
if (status == MYRB_STATUS_SUCCESS) {
sdev_printk(KERN_INFO, sdev, "Check Consistency %s\n" ,
start ? "Initiated" : "Cancelled" );
return count;
}
if (!start) {
sdev_printk(KERN_INFO, sdev,
"Check Consistency Not Cancelled, status 0x%x\n" ,
status);
return -EIO;
}
switch (status) {
case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE:
msg = "Dependent Physical Device is DEAD" ;
break ;
case MYRB_STATUS_RBLD_NEW_DISK_FAILED:
msg = "New Disk Failed During Rebuild" ;
break ;
case MYRB_STATUS_INVALID_ADDRESS:
msg = "Invalid or Nonredundant Logical Drive" ;
break ;
case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS:
msg = "Already in Progress" ;
break ;
default :
msg = NULL;
break ;
}
if (msg)
sdev_printk(KERN_INFO, sdev,
"Check Consistency Failed - %s\n" , msg);
else
sdev_printk(KERN_INFO, sdev,
"Check Consistency Failed, status 0x%x\n" , status);
return -EIO;
}
static ssize_t consistency_check_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return rebuild_show(dev, attr, buf);
}
static DEVICE_ATTR_RW(consistency_check);
static ssize_t ctlr_num_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct myrb_hba *cb = shost_priv(shost);
return snprintf(buf, 20, "%u\n" , cb->ctlr_num);
}
static DEVICE_ATTR_RO(ctlr_num);
static ssize_t firmware_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct myrb_hba *cb = shost_priv(shost);
return snprintf(buf, 16, "%s\n" , cb->fw_version);
}
static DEVICE_ATTR_RO(firmware);
static ssize_t model_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct myrb_hba *cb = shost_priv(shost);
return snprintf(buf, 16, "%s\n" , cb->model_name);
}
static DEVICE_ATTR_RO(model);
static ssize_t flush_cache_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct myrb_hba *cb = shost_priv(shost);
unsigned short status;
status = myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0);
if (status == MYRB_STATUS_SUCCESS) {
shost_printk(KERN_INFO, shost,
"Cache Flush Completed\n" );
return count;
}
shost_printk(KERN_INFO, shost,
"Cache Flush Failed, status %x\n" , status);
return -EIO;
}
static DEVICE_ATTR_WO(flush_cache);
static struct attribute *myrb_sdev_attrs[] = {
&dev_attr_rebuild.attr,
&dev_attr_consistency_check.attr,
&dev_attr_raid_state.attr,
&dev_attr_raid_level.attr,
NULL,
};
ATTRIBUTE_GROUPS(myrb_sdev);
static struct attribute *myrb_shost_attrs[] = {
&dev_attr_ctlr_num.attr,
&dev_attr_model.attr,
&dev_attr_firmware.attr,
&dev_attr_flush_cache.attr,
NULL,
};
ATTRIBUTE_GROUPS(myrb_shost);
static const struct scsi_host_template myrb_template = {
.module = THIS_MODULE,
.name = "DAC960" ,
.proc_name = "myrb" ,
.queuecommand = myrb_queuecommand,
.eh_host_reset_handler = myrb_host_reset,
.sdev_init = myrb_sdev_init,
.sdev_configure = myrb_sdev_configure,
.sdev_destroy = myrb_sdev_destroy,
.bios_param = myrb_biosparam,
.cmd_size = sizeof (struct myrb_cmdblk),
.shost_groups = myrb_shost_groups,
.sdev_groups = myrb_sdev_groups,
.this_id = -1,
};
/**
* myrb_is_raid - return boolean indicating device is raid volume
* @dev: the device struct object
*/
static int myrb_is_raid(struct device *dev)
{
struct scsi_device *sdev = to_scsi_device(dev);
return sdev->channel == myrb_logical_channel(sdev->host);
}
/**
* myrb_get_resync - get raid volume resync percent complete
* @dev: the device struct object
*/
static void myrb_get_resync(struct device *dev)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct myrb_hba *cb = shost_priv(sdev->host);
struct myrb_rbld_progress rbld_buf;
unsigned int percent_complete = 0;
unsigned short status;
unsigned int ldev_size = 0, remaining = 0;
if (sdev->channel < myrb_logical_channel(sdev->host))
return ;
status = myrb_get_rbld_progress(cb, &rbld_buf);
if (status == MYRB_STATUS_SUCCESS) {
if (rbld_buf.ldev_num == sdev->id) {
ldev_size = rbld_buf.ldev_size;
remaining = rbld_buf.blocks_left;
}
}
if (remaining && ldev_size)
percent_complete = (ldev_size - remaining) * 100 / ldev_size;
raid_set_resync(myrb_raid_template, dev, percent_complete);
}
/**
* myrb_get_state - get raid volume status
* @dev: the device struct object
*/
static void myrb_get_state(struct device *dev)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct myrb_hba *cb = shost_priv(sdev->host);
struct myrb_ldev_info *ldev_info = sdev->hostdata;
enum raid_state state = RAID_STATE_UNKNOWN;
unsigned short status;
if (sdev->channel < myrb_logical_channel(sdev->host) || !ldev_info)
state = RAID_STATE_UNKNOWN;
else {
status = myrb_get_rbld_progress(cb, NULL);
if (status == MYRB_STATUS_SUCCESS)
state = RAID_STATE_RESYNCING;
else {
switch (ldev_info->state) {
case MYRB_DEVICE_ONLINE:
state = RAID_STATE_ACTIVE;
break ;
case MYRB_DEVICE_WO:
case MYRB_DEVICE_CRITICAL:
state = RAID_STATE_DEGRADED;
break ;
default :
state = RAID_STATE_OFFLINE;
}
}
}
raid_set_state(myrb_raid_template, dev, state);
}
static struct raid_function_template myrb_raid_functions = {
.cookie = &myrb_template,
.is_raid = myrb_is_raid,
.get_resync = myrb_get_resync,
.get_state = myrb_get_state,
};
static void myrb_handle_scsi(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk,
struct scsi_cmnd *scmd)
{
unsigned short status;
if (!cmd_blk)
return ;
scsi_dma_unmap(scmd);
if (cmd_blk->dcdb) {
memcpy(scmd->sense_buffer, &cmd_blk->dcdb->sense, 64);
dma_pool_free(cb->dcdb_pool, cmd_blk->dcdb,
cmd_blk->dcdb_addr);
cmd_blk->dcdb = NULL;
}
if (cmd_blk->sgl) {
dma_pool_free(cb->sg_pool, cmd_blk->sgl, cmd_blk->sgl_addr);
cmd_blk->sgl = NULL;
cmd_blk->sgl_addr = 0;
}
status = cmd_blk->status;
switch (status) {
case MYRB_STATUS_SUCCESS:
case MYRB_STATUS_DEVICE_BUSY:
scmd->result = (DID_OK << 16) | status;
break ;
case MYRB_STATUS_BAD_DATA:
dev_dbg(&scmd->device->sdev_gendev,
"Bad Data Encountered\n" );
if (scmd->sc_data_direction == DMA_FROM_DEVICE)
/* Unrecovered read error */
scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x11, 0);
else
/* Write error */
scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x0C, 0);
break ;
case MYRB_STATUS_IRRECOVERABLE_DATA_ERROR:
scmd_printk(KERN_ERR, scmd, "Irrecoverable Data Error\n" );
if (scmd->sc_data_direction == DMA_FROM_DEVICE)
/* Unrecovered read error, auto-reallocation failed */
scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x11, 0x04);
else
/* Write error, auto-reallocation failed */
scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x0C, 0x02);
break ;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=94 H=92 G=92
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland