Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 273 kB image not shown  

Quelle  hpsa.c   Sprache: C

 
/*
 *    Disk Array driver for HP Smart Array SAS controllers
 *    Copyright (c) 2019-2020 Microchip Technology Inc. and its subsidiaries
 *    Copyright 2016 Microsemi Corporation
 *    Copyright 2014-2015 PMC-Sierra, Inc.
 *    Copyright 2000,2009-2015 Hewlett-Packard Development Company, L.P.
 *
 *    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; version 2 of the License.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 *    NON INFRINGEMENT.  See the GNU General Public License for more details.
 *
 *    Questions/Comments/Bugfixes to esc.storagedev@microsemi.com
 *
 */


#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/compat.h>
#include <linux/blktrace_api.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
#include <linux/moduleparam.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_transport_sas.h>
#include <scsi/scsi_dbg.h>
#include <linux/cciss_ioctl.h>
#include <linux/string.h>
#include <linux/bitmap.h>
#include <linux/atomic.h>
#include <linux/jiffies.h>
#include <linux/percpu-defs.h>
#include <linux/percpu.h>
#include <linux/unaligned.h>
#include <asm/div64.h>
#include "hpsa_cmd.h"
#include "hpsa.h"

/*
 * HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.'
 * with an optional trailing '-' followed by a byte value (0-255).
 */

#define HPSA_DRIVER_VERSION "3.4.20-200"
#define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")"
#define HPSA "hpsa"

/* How long to wait for CISS doorbell communication */
#define CLEAR_EVENT_WAIT_INTERVAL 20 /* ms for each msleep() call */
#define MODE_CHANGE_WAIT_INTERVAL 10 /* ms for each msleep() call */
#define MAX_CLEAR_EVENT_WAIT 30000 /* times 20 ms = 600 s */
#define MAX_MODE_CHANGE_WAIT 2000 /* times 10 ms = 20 s */
#define MAX_IOCTL_CONFIG_WAIT 1000

/*define how many times we will try a command because of bus resets */
#define MAX_CMD_RETRIES 3
/* How long to wait before giving up on a command */
#define HPSA_EH_PTRAID_TIMEOUT (240 * HZ)

/* Embedded module documentation macros - see modules.h */
MODULE_AUTHOR("Hewlett-Packard Company");
MODULE_DESCRIPTION("Driver for HP Smart Array Controller version " \
 HPSA_DRIVER_VERSION);
MODULE_VERSION(HPSA_DRIVER_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("cciss");

static int hpsa_simple_mode;
module_param(hpsa_simple_mode, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(hpsa_simple_mode,
 "Use 'simple mode' rather than 'performant mode'");

/* define the PCI info for the cards we can control */
static const struct pci_device_id hpsa_pci_device_id[] = {
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3241},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3243},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3245},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3247},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3249},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x324A},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x324B},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3233},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSF,     0x103C, 0x3350},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSF,     0x103C, 0x3351},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSF,     0x103C, 0x3352},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSF,     0x103C, 0x3353},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSF,     0x103C, 0x3354},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSF,     0x103C, 0x3355},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSF,     0x103C, 0x3356},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103c, 0x1920},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103C, 0x1921},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103C, 0x1922},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103C, 0x1923},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103C, 0x1924},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103c, 0x1925},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103C, 0x1926},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103C, 0x1928},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSH,     0x103C, 0x1929},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21BD},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21BE},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21BF},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C0},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C1},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C2},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C3},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C4},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C5},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C6},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C7},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C8},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21C9},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21CA},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21CB},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21CC},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21CD},
 {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21CE},
 {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0580},
 {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0581},
 {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0582},
 {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0583},
 {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0584},
 {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0585},
 {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0076},
 {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0087},
 {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x007D},
 {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0088},
 {PCI_VENDOR_ID_HP, 0x333f, 0x103c, 0x333f},
 {PCI_VENDOR_ID_HP,     PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
  PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
 {PCI_VENDOR_ID_COMPAQ,     PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
  PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
 {0,}
};

MODULE_DEVICE_TABLE(pci, hpsa_pci_device_id);

/*  board_id = Subsystem Device ID & Vendor ID
 *  product = Marketing Name for the board
 *  access = Address of the struct of function pointers
 */

static struct board_type products[] = {
 {0x40700E11, "Smart Array 5300", &SA5A_access},
 {0x40800E11, "Smart Array 5i", &SA5B_access},
 {0x40820E11, "Smart Array 532", &SA5B_access},
 {0x40830E11, "Smart Array 5312", &SA5B_access},
 {0x409A0E11, "Smart Array 641", &SA5A_access},
 {0x409B0E11, "Smart Array 642", &SA5A_access},
 {0x409C0E11, "Smart Array 6400", &SA5A_access},
 {0x409D0E11, "Smart Array 6400 EM", &SA5A_access},
 {0x40910E11, "Smart Array 6i", &SA5A_access},
 {0x3225103C, "Smart Array P600", &SA5A_access},
 {0x3223103C, "Smart Array P800", &SA5A_access},
 {0x3234103C, "Smart Array P400", &SA5A_access},
 {0x3235103C, "Smart Array P400i", &SA5A_access},
 {0x3211103C, "Smart Array E200i", &SA5A_access},
 {0x3212103C, "Smart Array E200", &SA5A_access},
 {0x3213103C, "Smart Array E200i", &SA5A_access},
 {0x3214103C, "Smart Array E200i", &SA5A_access},
 {0x3215103C, "Smart Array E200i", &SA5A_access},
 {0x3237103C, "Smart Array E500", &SA5A_access},
 {0x323D103C, "Smart Array P700m", &SA5A_access},
 {0x3241103C, "Smart Array P212", &SA5_access},
 {0x3243103C, "Smart Array P410", &SA5_access},
 {0x3245103C, "Smart Array P410i", &SA5_access},
 {0x3247103C, "Smart Array P411", &SA5_access},
 {0x3249103C, "Smart Array P812", &SA5_access},
 {0x324A103C, "Smart Array P712m", &SA5_access},
 {0x324B103C, "Smart Array P711m", &SA5_access},
 {0x3233103C, "HP StorageWorks 1210m", &SA5_access}, /* alias of 333f */
 {0x3350103C, "Smart Array P222", &SA5_access},
 {0x3351103C, "Smart Array P420", &SA5_access},
 {0x3352103C, "Smart Array P421", &SA5_access},
 {0x3353103C, "Smart Array P822", &SA5_access},
 {0x3354103C, "Smart Array P420i", &SA5_access},
 {0x3355103C, "Smart Array P220i", &SA5_access},
 {0x3356103C, "Smart Array P721m", &SA5_access},
 {0x1920103C, "Smart Array P430i", &SA5_access},
 {0x1921103C, "Smart Array P830i", &SA5_access},
 {0x1922103C, "Smart Array P430", &SA5_access},
 {0x1923103C, "Smart Array P431", &SA5_access},
 {0x1924103C, "Smart Array P830", &SA5_access},
 {0x1925103C, "Smart Array P831", &SA5_access},
 {0x1926103C, "Smart Array P731m", &SA5_access},
 {0x1928103C, "Smart Array P230i", &SA5_access},
 {0x1929103C, "Smart Array P530", &SA5_access},
 {0x21BD103C, "Smart Array P244br", &SA5_access},
 {0x21BE103C, "Smart Array P741m", &SA5_access},
 {0x21BF103C, "Smart HBA H240ar", &SA5_access},
 {0x21C0103C, "Smart Array P440ar", &SA5_access},
 {0x21C1103C, "Smart Array P840ar", &SA5_access},
 {0x21C2103C, "Smart Array P440", &SA5_access},
 {0x21C3103C, "Smart Array P441", &SA5_access},
 {0x21C4103C, "Smart Array", &SA5_access},
 {0x21C5103C, "Smart Array P841", &SA5_access},
 {0x21C6103C, "Smart HBA H244br", &SA5_access},
 {0x21C7103C, "Smart HBA H240", &SA5_access},
 {0x21C8103C, "Smart HBA H241", &SA5_access},
 {0x21C9103C, "Smart Array", &SA5_access},
 {0x21CA103C, "Smart Array P246br", &SA5_access},
 {0x21CB103C, "Smart Array P840", &SA5_access},
 {0x21CC103C, "Smart Array", &SA5_access},
 {0x21CD103C, "Smart Array", &SA5_access},
 {0x21CE103C, "Smart HBA", &SA5_access},
 {0x05809005, "SmartHBA-SA", &SA5_access},
 {0x05819005, "SmartHBA-SA 8i", &SA5_access},
 {0x05829005, "SmartHBA-SA 8i8e", &SA5_access},
 {0x05839005, "SmartHBA-SA 8e", &SA5_access},
 {0x05849005, "SmartHBA-SA 16i", &SA5_access},
 {0x05859005, "SmartHBA-SA 4i4e", &SA5_access},
 {0x00761590, "HP Storage P1224 Array Controller", &SA5_access},
 {0x00871590, "HP Storage P1224e Array Controller", &SA5_access},
 {0x007D1590, "HP Storage P1228 Array Controller", &SA5_access},
 {0x00881590, "HP Storage P1228e Array Controller", &SA5_access},
 {0x333f103c, "HP StorageWorks 1210m Array Controller", &SA5_access},
 {0xFFFF103C, "Unknown Smart Array", &SA5_access},
};

static struct scsi_transport_template *hpsa_sas_transport_template;
static int hpsa_add_sas_host(struct ctlr_info *h);
static void hpsa_delete_sas_host(struct ctlr_info *h);
static int hpsa_add_sas_device(struct hpsa_sas_node *hpsa_sas_node,
   struct hpsa_scsi_dev_t *device);
static void hpsa_remove_sas_device(struct hpsa_scsi_dev_t *device);
static struct hpsa_scsi_dev_t
 *hpsa_find_device_by_sas_rphy(struct ctlr_info *h,
  struct sas_rphy *rphy);

#define SCSI_CMD_BUSY ((struct scsi_cmnd *)&hpsa_cmd_busy)
static const struct scsi_cmnd hpsa_cmd_busy;
#define SCSI_CMD_IDLE ((struct scsi_cmnd *)&hpsa_cmd_idle)
static const struct scsi_cmnd hpsa_cmd_idle;
static int number_of_controllers;

static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id);
static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id);
static int hpsa_ioctl(struct scsi_device *dev, unsigned int cmd,
        void __user *arg);
static int hpsa_passthru_ioctl(struct ctlr_info *h,
          IOCTL_Command_struct *iocommand);
static int hpsa_big_passthru_ioctl(struct ctlr_info *h,
       BIG_IOCTL_Command_struct *ioc);

#ifdef CONFIG_COMPAT
static int hpsa_compat_ioctl(struct scsi_device *dev, unsigned int cmd,
 void __user *arg);
#endif

static void cmd_free(struct ctlr_info *h, struct CommandList *c);
static struct CommandList *cmd_alloc(struct ctlr_info *h);
static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c);
static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
         struct scsi_cmnd *scmd);
static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
 void *buff, size_t size, u16 page_code, unsigned char *scsi3addr,
 int cmd_type);
static void hpsa_free_cmd_pool(struct ctlr_info *h);
#define VPD_PAGE (1 << 8)
#define HPSA_SIMPLE_ERROR_BITS 0x03

static int hpsa_scsi_queue_command(struct Scsi_Host *h, struct scsi_cmnd *cmd);
static void hpsa_scan_start(struct Scsi_Host *);
static int hpsa_scan_finished(struct Scsi_Host *sh,
 unsigned long elapsed_time);
static int hpsa_change_queue_depth(struct scsi_device *sdev, int qdepth);

static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
static int hpsa_sdev_init(struct scsi_device *sdev);
static int hpsa_sdev_configure(struct scsi_device *sdev,
          struct queue_limits *lim);
static void hpsa_sdev_destroy(struct scsi_device *sdev);

static void hpsa_update_scsi_devices(struct ctlr_info *h);
static int check_for_unit_attention(struct ctlr_info *h,
 struct CommandList *c);
static void check_ioctl_unit_attention(struct ctlr_info *h,
 struct CommandList *c);
/* performant mode helper functions */
static void calc_bucket_map(int *bucket, int num_buckets,
 int nsgs, int min_blocks, u32 *bucket_map);
static void hpsa_free_performant_mode(struct ctlr_info *h);
static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
static inline u32 next_command(struct ctlr_info *h, u8 q);
static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr,
          u32 *cfg_base_addr, u64 *cfg_base_addr_index,
          u64 *cfg_offset);
static int hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
        unsigned long *memory_bar);
static int hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id,
    bool *legacy_board);
static int wait_for_device_to_become_ready(struct ctlr_info *h,
        unsigned char lunaddr[],
        int reply_queue);
static int hpsa_wait_for_board_state(struct pci_dev *pdev, void __iomem *vaddr,
         int wait_for_ready);
static inline void finish_cmd(struct CommandList *c);
static int hpsa_wait_for_mode_change_ack(struct ctlr_info *h);
#define BOARD_NOT_READY 0
#define BOARD_READY 1
static void hpsa_drain_accel_commands(struct ctlr_info *h);
static void hpsa_flush_cache(struct ctlr_info *h);
static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h,
 struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len,
 u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk);
static void hpsa_command_resubmit_worker(struct work_struct *work);
static u32 lockup_detected(struct ctlr_info *h);
static int detect_controller_lockup(struct ctlr_info *h);
static void hpsa_disable_rld_caching(struct ctlr_info *h);
static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h,
 struct ReportExtendedLUNdata *buf, int bufsize);
static bool hpsa_vpd_page_supported(struct ctlr_info *h,
 unsigned char scsi3addr[], u8 page);
static int hpsa_luns_changed(struct ctlr_info *h);
static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c,
          struct hpsa_scsi_dev_t *dev,
          unsigned char *scsi3addr);

static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
{
 unsigned long *priv = shost_priv(sdev->host);
 return (struct ctlr_info *) *priv;
}

static inline struct ctlr_info *shost_to_hba(struct Scsi_Host *sh)
{
 unsigned long *priv = shost_priv(sh);
 return (struct ctlr_info *) *priv;
}

static inline bool hpsa_is_cmd_idle(struct CommandList *c)
{
 return c->scsi_cmd == SCSI_CMD_IDLE;
}

/* extract sense key, asc, and ascq from sense data.  -1 means invalid. */
static void decode_sense_data(const u8 *sense_data, int sense_data_len,
   u8 *sense_key, u8 *asc, u8 *ascq)
{
 struct scsi_sense_hdr sshdr;
 bool rc;

 *sense_key = -1;
 *asc = -1;
 *ascq = -1;

 if (sense_data_len < 1)
  return;

 rc = scsi_normalize_sense(sense_data, sense_data_len, &sshdr);
 if (rc) {
  *sense_key = sshdr.sense_key;
  *asc = sshdr.asc;
  *ascq = sshdr.ascq;
 }
}

static int check_for_unit_attention(struct ctlr_info *h,
 struct CommandList *c)
{
 u8 sense_key, asc, ascq;
 int sense_len;

 if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo))
  sense_len = sizeof(c->err_info->SenseInfo);
 else
  sense_len = c->err_info->SenseLen;

 decode_sense_data(c->err_info->SenseInfo, sense_len,
    &sense_key, &asc, &ascq);
 if (sense_key != UNIT_ATTENTION || asc == 0xff)
  return 0;

 switch (asc) {
 case STATE_CHANGED:
  dev_warn(&h->pdev->dev,
   "%s: a state change detected, command retried\n",
   h->devname);
  break;
 case LUN_FAILED:
  dev_warn(&h->pdev->dev,
   "%s: LUN failure detected\n", h->devname);
  break;
 case REPORT_LUNS_CHANGED:
  dev_warn(&h->pdev->dev,
   "%s: report LUN data changed\n", h->devname);
 /*
 * Note: this REPORT_LUNS_CHANGED condition only occurs on the external
 * target (array) devices.
 */

  break;
 case POWER_OR_RESET:
  dev_warn(&h->pdev->dev,
   "%s: a power on or device reset detected\n",
   h->devname);
  break;
 case UNIT_ATTENTION_CLEARED:
  dev_warn(&h->pdev->dev,
   "%s: unit attention cleared by another initiator\n",
   h->devname);
  break;
 default:
  dev_warn(&h->pdev->dev,
   "%s: unknown unit attention detected\n",
   h->devname);
  break;
 }
 return 1;
}

static int check_for_busy(struct ctlr_info *h, struct CommandList *c)
{
 if (c->err_info->CommandStatus != CMD_TARGET_STATUS ||
  (c->err_info->ScsiStatus != SAM_STAT_BUSY &&
   c->err_info->ScsiStatus != SAM_STAT_TASK_SET_FULL))
  return 0;
 dev_warn(&h->pdev->dev, HPSA "device busy");
 return 1;
}

static u32 lockup_detected(struct ctlr_info *h);
static ssize_t host_show_lockup_detected(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 int ld;
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);

 h = shost_to_hba(shost);
 ld = lockup_detected(h);

 return sprintf(buf, "ld=%d\n", ld);
}

static ssize_t host_store_hp_ssd_smart_path_status(struct device *dev,
      struct device_attribute *attr,
      const char *buf, size_t count)
{
 int status;
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);

 if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
  return -EACCES;
 if (kstrtoint(buf, 10, &status))
  return -EINVAL;
 h = shost_to_hba(shost);
 h->acciopath_status = !!status;
 dev_warn(&h->pdev->dev,
  "hpsa: HP SSD Smart Path %s via sysfs update.\n",
  h->acciopath_status ? "enabled" : "disabled");
 return count;
}

static ssize_t host_store_raid_offload_debug(struct device *dev,
      struct device_attribute *attr,
      const char *buf, size_t count)
{
 int debug_level;
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);

 if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
  return -EACCES;
 if (kstrtoint(buf, 10, &debug_level))
  return -EINVAL;
 if (debug_level < 0)
  debug_level = 0;
 h = shost_to_hba(shost);
 h->raid_offload_debug = debug_level;
 dev_warn(&h->pdev->dev, "hpsa: Set raid_offload_debug level = %d\n",
  h->raid_offload_debug);
 return count;
}

static ssize_t host_store_rescan(struct device *dev,
     struct device_attribute *attr,
     const char *buf, size_t count)
{
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);
 h = shost_to_hba(shost);
 hpsa_scan_start(h->scsi_host);
 return count;
}

static void hpsa_turn_off_ioaccel_for_device(struct hpsa_scsi_dev_t *device)
{
 device->offload_enabled = 0;
 device->offload_to_be_enabled = 0;
}

static ssize_t host_show_firmware_revision(struct device *dev,
      struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);
 unsigned char *fwrev;

 h = shost_to_hba(shost);
 if (!h->hba_inquiry_data)
  return 0;
 fwrev = &h->hba_inquiry_data[32];
 return snprintf(buf, 20, "%c%c%c%c\n",
  fwrev[0], fwrev[1], fwrev[2], fwrev[3]);
}

static ssize_t host_show_commands_outstanding(struct device *dev,
      struct device_attribute *attr, char *buf)
{
 struct Scsi_Host *shost = class_to_shost(dev);
 struct ctlr_info *h = shost_to_hba(shost);

 return snprintf(buf, 20, "%d\n",
   atomic_read(&h->commands_outstanding));
}

static ssize_t host_show_transport_mode(struct device *dev,
 struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);

 h = shost_to_hba(shost);
 return snprintf(buf, 20, "%s\n",
  h->transMethod & CFGTBL_Trans_Performant ?
   "performant" : "simple");
}

static ssize_t host_show_hp_ssd_smart_path_status(struct device *dev,
 struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);

 h = shost_to_hba(shost);
 return snprintf(buf, 30, "HP SSD Smart Path %s\n",
  (h->acciopath_status == 1) ?  "enabled" : "disabled");
}

/* List of controllers which cannot be hard reset on kexec with reset_devices */
static u32 unresettable_controller[] = {
 0x324a103C, /* Smart Array P712m */
 0x324b103C, /* Smart Array P711m */
 0x3223103C, /* Smart Array P800 */
 0x3234103C, /* Smart Array P400 */
 0x3235103C, /* Smart Array P400i */
 0x3211103C, /* Smart Array E200i */
 0x3212103C, /* Smart Array E200 */
 0x3213103C, /* Smart Array E200i */
 0x3214103C, /* Smart Array E200i */
 0x3215103C, /* Smart Array E200i */
 0x3237103C, /* Smart Array E500 */
 0x323D103C, /* Smart Array P700m */
 0x40800E11, /* Smart Array 5i */
 0x409C0E11, /* Smart Array 6400 */
 0x409D0E11, /* Smart Array 6400 EM */
 0x40700E11, /* Smart Array 5300 */
 0x40820E11, /* Smart Array 532 */
 0x40830E11, /* Smart Array 5312 */
 0x409A0E11, /* Smart Array 641 */
 0x409B0E11, /* Smart Array 642 */
 0x40910E11, /* Smart Array 6i */
};

/* List of controllers which cannot even be soft reset */
static u32 soft_unresettable_controller[] = {
 0x40800E11, /* Smart Array 5i */
 0x40700E11, /* Smart Array 5300 */
 0x40820E11, /* Smart Array 532 */
 0x40830E11, /* Smart Array 5312 */
 0x409A0E11, /* Smart Array 641 */
 0x409B0E11, /* Smart Array 642 */
 0x40910E11, /* Smart Array 6i */
 /* Exclude 640x boards.  These are two pci devices in one slot
 * which share a battery backed cache module.  One controls the
 * cache, the other accesses the cache through the one that controls
 * it.  If we reset the one controlling the cache, the other will
 * likely not be happy.  Just forbid resetting this conjoined mess.
 * The 640x isn't really supported by hpsa anyway.
 */

 0x409C0E11, /* Smart Array 6400 */
 0x409D0E11, /* Smart Array 6400 EM */
};

static int board_id_in_array(u32 a[], int nelems, u32 board_id)
{
 int i;

 for (i = 0; i < nelems; i++)
  if (a[i] == board_id)
   return 1;
 return 0;
}

static int ctlr_is_hard_resettable(u32 board_id)
{
 return !board_id_in_array(unresettable_controller,
   ARRAY_SIZE(unresettable_controller), board_id);
}

static int ctlr_is_soft_resettable(u32 board_id)
{
 return !board_id_in_array(soft_unresettable_controller,
   ARRAY_SIZE(soft_unresettable_controller), board_id);
}

static int ctlr_is_resettable(u32 board_id)
{
 return ctlr_is_hard_resettable(board_id) ||
  ctlr_is_soft_resettable(board_id);
}

static ssize_t host_show_resettable(struct device *dev,
 struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);

 h = shost_to_hba(shost);
 return snprintf(buf, 20, "%d\n", ctlr_is_resettable(h->board_id));
}

static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[])
{
 return (scsi3addr[3] & 0xC0) == 0x40;
}

static const char * const raid_label[] = { "0""4""1(+0)""5""5+1""6",
 "1(+0)ADM""UNKNOWN""PHYS DRV"
};
#define HPSA_RAID_0 0
#define HPSA_RAID_4 1
#define HPSA_RAID_1 2 /* also used for RAID 10 */
#define HPSA_RAID_5 3 /* also used for RAID 50 */
#define HPSA_RAID_51 4
#define HPSA_RAID_6 5 /* also used for RAID 60 */
#define HPSA_RAID_ADM 6 /* also used for RAID 1+0 ADM */
#define RAID_UNKNOWN (ARRAY_SIZE(raid_label) - 2)
#define PHYSICAL_DRIVE (ARRAY_SIZE(raid_label) - 1)

static inline bool is_logical_device(struct hpsa_scsi_dev_t *device)
{
 return !device->physical_device;
}

static ssize_t raid_level_show(struct device *dev,
      struct device_attribute *attr, char *buf)
{
 ssize_t l = 0;
 unsigned char rlevel;
 struct ctlr_info *h;
 struct scsi_device *sdev;
 struct hpsa_scsi_dev_t *hdev;
 unsigned long flags;

 sdev = to_scsi_device(dev);
 h = sdev_to_hba(sdev);
 spin_lock_irqsave(&h->lock, flags);
 hdev = sdev->hostdata;
 if (!hdev) {
  spin_unlock_irqrestore(&h->lock, flags);
  return -ENODEV;
 }

 /* Is this even a logical drive? */
 if (!is_logical_device(hdev)) {
  spin_unlock_irqrestore(&h->lock, flags);
  l = snprintf(buf, PAGE_SIZE, "N/A\n");
  return l;
 }

 rlevel = hdev->raid_level;
 spin_unlock_irqrestore(&h->lock, flags);
 if (rlevel > RAID_UNKNOWN)
  rlevel = RAID_UNKNOWN;
 l = snprintf(buf, PAGE_SIZE, "RAID %s\n", raid_label[rlevel]);
 return l;
}

static ssize_t lunid_show(struct device *dev,
      struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct scsi_device *sdev;
 struct hpsa_scsi_dev_t *hdev;
 unsigned long flags;
 unsigned char lunid[8];

 sdev = to_scsi_device(dev);
 h = sdev_to_hba(sdev);
 spin_lock_irqsave(&h->lock, flags);
 hdev = sdev->hostdata;
 if (!hdev) {
  spin_unlock_irqrestore(&h->lock, flags);
  return -ENODEV;
 }
 memcpy(lunid, hdev->scsi3addr, sizeof(lunid));
 spin_unlock_irqrestore(&h->lock, flags);
 return snprintf(buf, 20, "0x%8phN\n", lunid);
}

static ssize_t unique_id_show(struct device *dev,
      struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct scsi_device *sdev;
 struct hpsa_scsi_dev_t *hdev;
 unsigned long flags;
 unsigned char sn[16];

 sdev = to_scsi_device(dev);
 h = sdev_to_hba(sdev);
 spin_lock_irqsave(&h->lock, flags);
 hdev = sdev->hostdata;
 if (!hdev) {
  spin_unlock_irqrestore(&h->lock, flags);
  return -ENODEV;
 }
 memcpy(sn, hdev->device_id, sizeof(sn));
 spin_unlock_irqrestore(&h->lock, flags);
 return snprintf(buf, 16 * 2 + 2,
   "%02X%02X%02X%02X%02X%02X%02X%02X"
   "%02X%02X%02X%02X%02X%02X%02X%02X\n",
   sn[0], sn[1], sn[2], sn[3],
   sn[4], sn[5], sn[6], sn[7],
   sn[8], sn[9], sn[10], sn[11],
   sn[12], sn[13], sn[14], sn[15]);
}

static ssize_t sas_address_show(struct device *dev,
       struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct scsi_device *sdev;
 struct hpsa_scsi_dev_t *hdev;
 unsigned long flags;
 u64 sas_address;

 sdev = to_scsi_device(dev);
 h = sdev_to_hba(sdev);
 spin_lock_irqsave(&h->lock, flags);
 hdev = sdev->hostdata;
 if (!hdev || is_logical_device(hdev) || !hdev->expose_device) {
  spin_unlock_irqrestore(&h->lock, flags);
  return -ENODEV;
 }
 sas_address = hdev->sas_address;
 spin_unlock_irqrestore(&h->lock, flags);

 return snprintf(buf, PAGE_SIZE, "0x%016llx\n", sas_address);
}

static ssize_t host_show_hp_ssd_smart_path_enabled(struct device *dev,
      struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct scsi_device *sdev;
 struct hpsa_scsi_dev_t *hdev;
 unsigned long flags;
 int offload_enabled;

 sdev = to_scsi_device(dev);
 h = sdev_to_hba(sdev);
 spin_lock_irqsave(&h->lock, flags);
 hdev = sdev->hostdata;
 if (!hdev) {
  spin_unlock_irqrestore(&h->lock, flags);
  return -ENODEV;
 }
 offload_enabled = hdev->offload_enabled;
 spin_unlock_irqrestore(&h->lock, flags);

 if (hdev->devtype == TYPE_DISK || hdev->devtype == TYPE_ZBC)
  return snprintf(buf, 20, "%d\n", offload_enabled);
 else
  return snprintf(buf, 40, "%s\n",
    "Not applicable for a controller");
}

#define MAX_PATHS 8
static ssize_t path_info_show(struct device *dev,
      struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct scsi_device *sdev;
 struct hpsa_scsi_dev_t *hdev;
 unsigned long flags;
 int i;
 int output_len = 0;
 u8 box;
 u8 bay;
 u8 path_map_index = 0;
 char *active;
 unsigned char phys_connector[2];

 sdev = to_scsi_device(dev);
 h = sdev_to_hba(sdev);
 spin_lock_irqsave(&h->devlock, flags);
 hdev = sdev->hostdata;
 if (!hdev) {
  spin_unlock_irqrestore(&h->devlock, flags);
  return -ENODEV;
 }

 bay = hdev->bay;
 for (i = 0; i < MAX_PATHS; i++) {
  path_map_index = 1<<i;
  if (i == hdev->active_path_index)
   active = "Active";
  else if (hdev->path_map & path_map_index)
   active = "Inactive";
  else
   continue;

  output_len += scnprintf(buf + output_len,
    PAGE_SIZE - output_len,
    "[%d:%d:%d:%d] %20.20s ",
    h->scsi_host->host_no,
    hdev->bus, hdev->target, hdev->lun,
    scsi_device_type(hdev->devtype));

  if (hdev->devtype == TYPE_RAID || is_logical_device(hdev)) {
   output_len += scnprintf(buf + output_len,
      PAGE_SIZE - output_len,
      "%s\n", active);
   continue;
  }

  box = hdev->box[i];
  memcpy(&phys_connector, &hdev->phys_connector[i],
   sizeof(phys_connector));
  if (phys_connector[0] < '0')
   phys_connector[0] = '0';
  if (phys_connector[1] < '0')
   phys_connector[1] = '0';
  output_len += scnprintf(buf + output_len,
    PAGE_SIZE - output_len,
    "PORT: %.2s ",
    phys_connector);
  if ((hdev->devtype == TYPE_DISK || hdev->devtype == TYPE_ZBC) &&
   hdev->expose_device) {
   if (box == 0 || box == 0xFF) {
    output_len += scnprintf(buf + output_len,
     PAGE_SIZE - output_len,
     "BAY: %hhu %s\n",
     bay, active);
   } else {
    output_len += scnprintf(buf + output_len,
     PAGE_SIZE - output_len,
     "BOX: %hhu BAY: %hhu %s\n",
     box, bay, active);
   }
  } else if (box != 0 && box != 0xFF) {
   output_len += scnprintf(buf + output_len,
    PAGE_SIZE - output_len, "BOX: %hhu %s\n",
    box, active);
  } else
   output_len += scnprintf(buf + output_len,
    PAGE_SIZE - output_len, "%s\n", active);
 }

 spin_unlock_irqrestore(&h->devlock, flags);
 return output_len;
}

static ssize_t host_show_ctlr_num(struct device *dev,
 struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);

 h = shost_to_hba(shost);
 return snprintf(buf, 20, "%d\n", h->ctlr);
}

static ssize_t host_show_legacy_board(struct device *dev,
 struct device_attribute *attr, char *buf)
{
 struct ctlr_info *h;
 struct Scsi_Host *shost = class_to_shost(dev);

 h = shost_to_hba(shost);
 return snprintf(buf, 20, "%d\n", h->legacy_board ? 1 : 0);
}

static DEVICE_ATTR_RO(raid_level);
static DEVICE_ATTR_RO(lunid);
static DEVICE_ATTR_RO(unique_id);
static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
static DEVICE_ATTR_RO(sas_address);
static DEVICE_ATTR(hp_ssd_smart_path_enabled, S_IRUGO,
   host_show_hp_ssd_smart_path_enabled, NULL);
static DEVICE_ATTR_RO(path_info);
static DEVICE_ATTR(hp_ssd_smart_path_status, S_IWUSR|S_IRUGO|S_IROTH,
  host_show_hp_ssd_smart_path_status,
  host_store_hp_ssd_smart_path_status);
static DEVICE_ATTR(raid_offload_debug, S_IWUSR, NULL,
   host_store_raid_offload_debug);
static DEVICE_ATTR(firmware_revision, S_IRUGO,
 host_show_firmware_revision, NULL);
static DEVICE_ATTR(commands_outstanding, S_IRUGO,
 host_show_commands_outstanding, NULL);
static DEVICE_ATTR(transport_mode, S_IRUGO,
 host_show_transport_mode, NULL);
static DEVICE_ATTR(resettable, S_IRUGO,
 host_show_resettable, NULL);
static DEVICE_ATTR(lockup_detected, S_IRUGO,
 host_show_lockup_detected, NULL);
static DEVICE_ATTR(ctlr_num, S_IRUGO,
 host_show_ctlr_num, NULL);
static DEVICE_ATTR(legacy_board, S_IRUGO,
 host_show_legacy_board, NULL);

static struct attribute *hpsa_sdev_attrs[] = {
 &dev_attr_raid_level.attr,
 &dev_attr_lunid.attr,
 &dev_attr_unique_id.attr,
 &dev_attr_hp_ssd_smart_path_enabled.attr,
 &dev_attr_path_info.attr,
 &dev_attr_sas_address.attr,
 NULL,
};

ATTRIBUTE_GROUPS(hpsa_sdev);

static struct attribute *hpsa_shost_attrs[] = {
 &dev_attr_rescan.attr,
 &dev_attr_firmware_revision.attr,
 &dev_attr_commands_outstanding.attr,
 &dev_attr_transport_mode.attr,
 &dev_attr_resettable.attr,
 &dev_attr_hp_ssd_smart_path_status.attr,
 &dev_attr_raid_offload_debug.attr,
 &dev_attr_lockup_detected.attr,
 &dev_attr_ctlr_num.attr,
 &dev_attr_legacy_board.attr,
 NULL,
};

ATTRIBUTE_GROUPS(hpsa_shost);

#define HPSA_NRESERVED_CMDS (HPSA_CMDS_RESERVED_FOR_DRIVER +\
     HPSA_MAX_CONCURRENT_PASSTHRUS)

static const struct scsi_host_template hpsa_driver_template = {
 .module   = THIS_MODULE,
 .name   = HPSA,
 .proc_name  = HPSA,
 .queuecommand  = hpsa_scsi_queue_command,
 .scan_start  = hpsa_scan_start,
 .scan_finished  = hpsa_scan_finished,
 .change_queue_depth = hpsa_change_queue_depth,
 .this_id  = -1,
 .eh_device_reset_handler = hpsa_eh_device_reset_handler,
 .ioctl   = hpsa_ioctl,
 .sdev_init  = hpsa_sdev_init,
 .sdev_configure  = hpsa_sdev_configure,
 .sdev_destroy  = hpsa_sdev_destroy,
#ifdef CONFIG_COMPAT
 .compat_ioctl  = hpsa_compat_ioctl,
#endif
 .sdev_groups = hpsa_sdev_groups,
 .shost_groups = hpsa_shost_groups,
 .max_sectors = 2048,
 .no_write_same = 1,
};

static inline u32 next_command(struct ctlr_info *h, u8 q)
{
 u32 a;
 struct reply_queue_buffer *rq = &h->reply_queue[q];

 if (h->transMethod & CFGTBL_Trans_io_accel1)
  return h->access.command_completed(h, q);

 if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
  return h->access.command_completed(h, q);

 if ((rq->head[rq->current_entry] & 1) == rq->wraparound) {
  a = rq->head[rq->current_entry];
  rq->current_entry++;
  atomic_dec(&h->commands_outstanding);
 } else {
  a = FIFO_EMPTY;
 }
 /* Check for wraparound */
 if (rq->current_entry == h->max_commands) {
  rq->current_entry = 0;
  rq->wraparound ^= 1;
 }
 return a;
}

/*
 * There are some special bits in the bus address of the
 * command that we have to set for the controller to know
 * how to process the command:
 *
 * Normal performant mode:
 * bit 0: 1 means performant mode, 0 means simple mode.
 * bits 1-3 = block fetch table entry
 * bits 4-6 = command type (== 0)
 *
 * ioaccel1 mode:
 * bit 0 = "performant mode" bit.
 * bits 1-3 = block fetch table entry
 * bits 4-6 = command type (== 110)
 * (command type is needed because ioaccel1 mode
 * commands are submitted through the same register as normal
 * mode commands, so this is how the controller knows whether
 * the command is normal mode or ioaccel1 mode.)
 *
 * ioaccel2 mode:
 * bit 0 = "performant mode" bit.
 * bits 1-4 = block fetch table entry (note extra bit)
 * bits 4-6 = not needed, because ioaccel2 mode has
 * a separate special register for submitting commands.
 */


/*
 * set_performant_mode: Modify the tag for cciss performant
 * set bit 0 for pull model, bits 3-1 for block fetch
 * register number
 */

#define DEFAULT_REPLY_QUEUE (-1)
static void set_performant_mode(struct ctlr_info *h, struct CommandList *c,
     int reply_queue)
{
 if (likely(h->transMethod & CFGTBL_Trans_Performant)) {
  c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
  if (unlikely(!h->msix_vectors))
   return;
  c->Header.ReplyQueue = reply_queue;
 }
}

static void set_ioaccel1_performant_mode(struct ctlr_info *h,
      struct CommandList *c,
      int reply_queue)
{
 struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[c->cmdindex];

 /*
 * Tell the controller to post the reply to the queue for this
 * processor.  This seems to give the best I/O throughput.
 */

 cp->ReplyQueue = reply_queue;
 /*
 * Set the bits in the address sent down to include:
 *  - performant mode bit (bit 0)
 *  - pull count (bits 1-3)
 *  - command type (bits 4-6)
 */

 c->busaddr |= 1 | (h->ioaccel1_blockFetchTable[c->Header.SGList] << 1) |
     IOACCEL1_BUSADDR_CMDTYPE;
}

static void set_ioaccel2_tmf_performant_mode(struct ctlr_info *h,
      struct CommandList *c,
      int reply_queue)
{
 struct hpsa_tmf_struct *cp = (struct hpsa_tmf_struct *)
  &h->ioaccel2_cmd_pool[c->cmdindex];

 /* Tell the controller to post the reply to the queue for this
 * processor.  This seems to give the best I/O throughput.
 */

 cp->reply_queue = reply_queue;
 /* Set the bits in the address sent down to include:
 *  - performant mode bit not used in ioaccel mode 2
 *  - pull count (bits 0-3)
 *  - command type isn't needed for ioaccel2
 */

 c->busaddr |= h->ioaccel2_blockFetchTable[0];
}

static void set_ioaccel2_performant_mode(struct ctlr_info *h,
      struct CommandList *c,
      int reply_queue)
{
 struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex];

 /*
 * Tell the controller to post the reply to the queue for this
 * processor.  This seems to give the best I/O throughput.
 */

 cp->reply_queue = reply_queue;
 /*
 * Set the bits in the address sent down to include:
 *  - performant mode bit not used in ioaccel mode 2
 *  - pull count (bits 0-3)
 *  - command type isn't needed for ioaccel2
 */

 c->busaddr |= (h->ioaccel2_blockFetchTable[cp->sg_count]);
}

static int is_firmware_flash_cmd(u8 *cdb)
{
 return cdb[0] == BMIC_WRITE && cdb[6] == BMIC_FLASH_FIRMWARE;
}

/*
 * During firmware flash, the heartbeat register may not update as frequently
 * as it should.  So we dial down lockup detection during firmware flash. and
 * dial it back up when firmware flash completes.
 */

#define HEARTBEAT_SAMPLE_INTERVAL_DURING_FLASH (240 * HZ)
#define HEARTBEAT_SAMPLE_INTERVAL (30 * HZ)
#define HPSA_EVENT_MONITOR_INTERVAL (15 * HZ)
static void dial_down_lockup_detection_during_fw_flash(struct ctlr_info *h,
  struct CommandList *c)
{
 if (!is_firmware_flash_cmd(c->Request.CDB))
  return;
 atomic_inc(&h->firmware_flash_in_progress);
 h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL_DURING_FLASH;
}

static void dial_up_lockup_detection_on_fw_flash_complete(struct ctlr_info *h,
  struct CommandList *c)
{
 if (is_firmware_flash_cmd(c->Request.CDB) &&
  atomic_dec_and_test(&h->firmware_flash_in_progress))
  h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL;
}

static void __enqueue_cmd_and_start_io(struct ctlr_info *h,
 struct CommandList *c, int reply_queue)
{
 dial_down_lockup_detection_during_fw_flash(h, c);
 atomic_inc(&h->commands_outstanding);
 /*
 * Check to see if the command is being retried.
 */

 if (c->device && !c->retry_pending)
  atomic_inc(&c->device->commands_outstanding);

 reply_queue = h->reply_map[raw_smp_processor_id()];
 switch (c->cmd_type) {
 case CMD_IOACCEL1:
  set_ioaccel1_performant_mode(h, c, reply_queue);
  writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
  break;
 case CMD_IOACCEL2:
  set_ioaccel2_performant_mode(h, c, reply_queue);
  writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
  break;
 case IOACCEL2_TMF:
  set_ioaccel2_tmf_performant_mode(h, c, reply_queue);
  writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
  break;
 default:
  set_performant_mode(h, c, reply_queue);
  h->access.submit_command(h, c);
 }
}

static void enqueue_cmd_and_start_io(struct ctlr_info *h, struct CommandList *c)
{
 __enqueue_cmd_and_start_io(h, c, DEFAULT_REPLY_QUEUE);
}

static inline int is_hba_lunid(unsigned char scsi3addr[])
{
 return memcmp(scsi3addr, RAID_CTLR_LUNID, 8) == 0;
}

static inline int is_scsi_rev_5(struct ctlr_info *h)
{
 if (!h->hba_inquiry_data)
  return 0;
 if ((h->hba_inquiry_data[2] & 0x07) == 5)
  return 1;
 return 0;
}

static int hpsa_find_target_lun(struct ctlr_info *h,
 unsigned char scsi3addr[], int bus, int *target, int *lun)
{
 /* finds an unused bus, target, lun for a new physical device
 * assumes h->devlock is held
 */

 int i, found = 0;
 DECLARE_BITMAP(lun_taken, HPSA_MAX_DEVICES);

 bitmap_zero(lun_taken, HPSA_MAX_DEVICES);

 for (i = 0; i < h->ndevices; i++) {
  if (h->dev[i]->bus == bus && h->dev[i]->target != -1)
   __set_bit(h->dev[i]->target, lun_taken);
 }

 i = find_first_zero_bit(lun_taken, HPSA_MAX_DEVICES);
 if (i < HPSA_MAX_DEVICES) {
  /* *bus = 1; */
  *target = i;
  *lun = 0;
  found = 1;
 }
 return !found;
}

static void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
 struct hpsa_scsi_dev_t *dev, char *description)
{
#define LABEL_SIZE 25
 char label[LABEL_SIZE];

 if (h == NULL || h->pdev == NULL || h->scsi_host == NULL)
  return;

 switch (dev->devtype) {
 case TYPE_RAID:
  snprintf(label, LABEL_SIZE, "controller");
  break;
 case TYPE_ENCLOSURE:
  snprintf(label, LABEL_SIZE, "enclosure");
  break;
 case TYPE_DISK:
 case TYPE_ZBC:
  if (dev->external)
   snprintf(label, LABEL_SIZE, "external");
  else if (!is_logical_dev_addr_mode(dev->scsi3addr))
   snprintf(label, LABEL_SIZE, "%s",
    raid_label[PHYSICAL_DRIVE]);
  else
   snprintf(label, LABEL_SIZE, "RAID-%s",
    dev->raid_level > RAID_UNKNOWN ? "?" :
    raid_label[dev->raid_level]);
  break;
 case TYPE_ROM:
  snprintf(label, LABEL_SIZE, "rom");
  break;
 case TYPE_TAPE:
  snprintf(label, LABEL_SIZE, "tape");
  break;
 case TYPE_MEDIUM_CHANGER:
  snprintf(label, LABEL_SIZE, "changer");
  break;
 default:
  snprintf(label, LABEL_SIZE, "UNKNOWN");
  break;
 }

 dev_printk(level, &h->pdev->dev,
   "scsi %d:%d:%d:%d: %s %s %.8s %.16s %s SSDSmartPathCap%c En%c Exp=%d\n",
   h->scsi_host->host_no, dev->bus, dev->target, dev->lun,
   description,
   scsi_device_type(dev->devtype),
   dev->vendor,
   dev->model,
   label,
   dev->offload_config ? '+' : '-',
   dev->offload_to_be_enabled ? '+' : '-',
   dev->expose_device);
}

/* Add an entry into h->dev[] array. */
static int hpsa_scsi_add_entry(struct ctlr_info *h,
  struct hpsa_scsi_dev_t *device,
  struct hpsa_scsi_dev_t *added[], int *nadded)
{
 /* assumes h->devlock is held */
 int n = h->ndevices;
 int i;
 unsigned char addr1[8], addr2[8];
 struct hpsa_scsi_dev_t *sd;

 if (n >= HPSA_MAX_DEVICES) {
  dev_err(&h->pdev->dev, "too many devices, some will be "
   "inaccessible.\n");
  return -1;
 }

 /* physical devices do not have lun or target assigned until now. */
 if (device->lun != -1)
  /* Logical device, lun is already assigned. */
  goto lun_assigned;

 /* If this device a non-zero lun of a multi-lun device
 * byte 4 of the 8-byte LUN addr will contain the logical
 * unit no, zero otherwise.
 */

 if (device->scsi3addr[4] == 0) {
  /* This is not a non-zero lun of a multi-lun device */
  if (hpsa_find_target_lun(h, device->scsi3addr,
   device->bus, &device->target, &device->lun) != 0)
   return -1;
  goto lun_assigned;
 }

 /* This is a non-zero lun of a multi-lun device.
 * Search through our list and find the device which
 * has the same 8 byte LUN address, excepting byte 4 and 5.
 * Assign the same bus and target for this new LUN.
 * Use the logical unit number from the firmware.
 */

 memcpy(addr1, device->scsi3addr, 8);
 addr1[4] = 0;
 addr1[5] = 0;
 for (i = 0; i < n; i++) {
  sd = h->dev[i];
  memcpy(addr2, sd->scsi3addr, 8);
  addr2[4] = 0;
  addr2[5] = 0;
  /* differ only in byte 4 and 5? */
  if (memcmp(addr1, addr2, 8) == 0) {
   device->bus = sd->bus;
   device->target = sd->target;
   device->lun = device->scsi3addr[4];
   break;
  }
 }
 if (device->lun == -1) {
  dev_warn(&h->pdev->dev, "physical device with no LUN=0,"
   " suspect firmware bug or unsupported hardware "
   "configuration.\n");
  return -1;
 }

lun_assigned:

 h->dev[n] = device;
 h->ndevices++;
 added[*nadded] = device;
 (*nadded)++;
 hpsa_show_dev_msg(KERN_INFO, h, device,
  device->expose_device ? "added" : "masked");
 return 0;
}

/*
 * Called during a scan operation.
 *
 * Update an entry in h->dev[] array.
 */

static void hpsa_scsi_update_entry(struct ctlr_info *h,
 int entry, struct hpsa_scsi_dev_t *new_entry)
{
 /* assumes h->devlock is held */
 BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);

 /* Raid level changed. */
 h->dev[entry]->raid_level = new_entry->raid_level;

 /*
 * ioacccel_handle may have changed for a dual domain disk
 */

 h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;

 /* Raid offload parameters changed.  Careful about the ordering. */
 if (new_entry->offload_config && new_entry->offload_to_be_enabled) {
  /*
 * if drive is newly offload_enabled, we want to copy the
 * raid map data first.  If previously offload_enabled and
 * offload_config were set, raid map data had better be
 * the same as it was before. If raid map data has changed
 * then it had better be the case that
 * h->dev[entry]->offload_enabled is currently 0.
 */

  h->dev[entry]->raid_map = new_entry->raid_map;
  h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
 }
 if (new_entry->offload_to_be_enabled) {
  h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
  wmb(); /* set ioaccel_handle *before* hba_ioaccel_enabled */
 }
 h->dev[entry]->hba_ioaccel_enabled = new_entry->hba_ioaccel_enabled;
 h->dev[entry]->offload_config = new_entry->offload_config;
 h->dev[entry]->offload_to_mirror = new_entry->offload_to_mirror;
 h->dev[entry]->queue_depth = new_entry->queue_depth;

 /*
 * We can turn off ioaccel offload now, but need to delay turning
 * ioaccel on until we can update h->dev[entry]->phys_disk[], but we
 * can't do that until all the devices are updated.
 */

 h->dev[entry]->offload_to_be_enabled = new_entry->offload_to_be_enabled;

 /*
 * turn ioaccel off immediately if told to do so.
 */

 if (!new_entry->offload_to_be_enabled)
  h->dev[entry]->offload_enabled = 0;

 hpsa_show_dev_msg(KERN_INFO, h, h->dev[entry], "updated");
}

/* Replace an entry from h->dev[] array. */
static void hpsa_scsi_replace_entry(struct ctlr_info *h,
 int entry, struct hpsa_scsi_dev_t *new_entry,
 struct hpsa_scsi_dev_t *added[], int *nadded,
 struct hpsa_scsi_dev_t *removed[], int *nremoved)
{
 /* assumes h->devlock is held */
 BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);
 removed[*nremoved] = h->dev[entry];
 (*nremoved)++;

 /*
 * New physical devices won't have target/lun assigned yet
 * so we need to preserve the values in the slot we are replacing.
 */

 if (new_entry->target == -1) {
  new_entry->target = h->dev[entry]->target;
  new_entry->lun = h->dev[entry]->lun;
 }

 h->dev[entry] = new_entry;
 added[*nadded] = new_entry;
 (*nadded)++;

 hpsa_show_dev_msg(KERN_INFO, h, new_entry, "replaced");
}

/* Remove an entry from h->dev[] array. */
static void hpsa_scsi_remove_entry(struct ctlr_info *h, int entry,
 struct hpsa_scsi_dev_t *removed[], int *nremoved)
{
 /* assumes h->devlock is held */
 int i;
 struct hpsa_scsi_dev_t *sd;

 BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);

 sd = h->dev[entry];
 removed[*nremoved] = h->dev[entry];
 (*nremoved)++;

 for (i = entry; i < h->ndevices-1; i++)
  h->dev[i] = h->dev[i+1];
 h->ndevices--;
 hpsa_show_dev_msg(KERN_INFO, h, sd, "removed");
}

#define SCSI3ADDR_EQ(a, b) ( \
 (a)[7] == (b)[7] && \
 (a)[6] == (b)[6] && \
 (a)[5] == (b)[5] && \
 (a)[4] == (b)[4] && \
 (a)[3] == (b)[3] && \
 (a)[2] == (b)[2] && \
 (a)[1] == (b)[1] && \
 (a)[0] == (b)[0])

static void fixup_botched_add(struct ctlr_info *h,
 struct hpsa_scsi_dev_t *added)
{
 /* called when scsi_add_device fails in order to re-adjust
 * h->dev[] to match the mid layer's view.
 */

 unsigned long flags;
 int i, j;

 spin_lock_irqsave(&h->lock, flags);
 for (i = 0; i < h->ndevices; i++) {
  if (h->dev[i] == added) {
   for (j = i; j < h->ndevices-1; j++)
    h->dev[j] = h->dev[j+1];
   h->ndevices--;
   break;
  }
 }
 spin_unlock_irqrestore(&h->lock, flags);
 kfree(added);
}

static inline int device_is_the_same(struct hpsa_scsi_dev_t *dev1,
 struct hpsa_scsi_dev_t *dev2)
{
 /* we compare everything except lun and target as these
 * are not yet assigned.  Compare parts likely
 * to differ first
 */

 if (memcmp(dev1->scsi3addr, dev2->scsi3addr,
  sizeof(dev1->scsi3addr)) != 0)
  return 0;
 if (memcmp(dev1->device_id, dev2->device_id,
  sizeof(dev1->device_id)) != 0)
  return 0;
 if (memcmp(dev1->model, dev2->model, sizeof(dev1->model)) != 0)
  return 0;
 if (memcmp(dev1->vendor, dev2->vendor, sizeof(dev1->vendor)) != 0)
  return 0;
 if (dev1->devtype != dev2->devtype)
  return 0;
 if (dev1->bus != dev2->bus)
  return 0;
 return 1;
}

static inline int device_updated(struct hpsa_scsi_dev_t *dev1,
 struct hpsa_scsi_dev_t *dev2)
{
 /* Device attributes that can change, but don't mean
 * that the device is a different device, nor that the OS
 * needs to be told anything about the change.
 */

 if (dev1->raid_level != dev2->raid_level)
  return 1;
 if (dev1->offload_config != dev2->offload_config)
  return 1;
 if (dev1->offload_to_be_enabled != dev2->offload_to_be_enabled)
  return 1;
 if (!is_logical_dev_addr_mode(dev1->scsi3addr))
  if (dev1->queue_depth != dev2->queue_depth)
   return 1;
 /*
 * This can happen for dual domain devices. An active
 * path change causes the ioaccel handle to change
 *
 * for example note the handle differences between p0 and p1
 * Device                    WWN               ,WWN hash,Handle
 * D016 p0|0x3 [02]P2E:01:01,0x5000C5005FC4DACA,0x9B5616,0x01030003
 * p1                   0x5000C5005FC4DAC9,0x6798C0,0x00040004
 */

 if (dev1->ioaccel_handle != dev2->ioaccel_handle)
  return 1;
 return 0;
}

/* Find needle in haystack.  If exact match found, return DEVICE_SAME,
 * and return needle location in *index.  If scsi3addr matches, but not
 * vendor, model, serial num, etc. return DEVICE_CHANGED, and return needle
 * location in *index.
 * In the case of a minor device attribute change, such as RAID level, just
 * return DEVICE_UPDATED, along with the updated device's location in index.
 * If needle not found, return DEVICE_NOT_FOUND.
 */

static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle,
 struct hpsa_scsi_dev_t *haystack[], int haystack_size,
 int *index)
{
 int i;
#define DEVICE_NOT_FOUND 0
#define DEVICE_CHANGED 1
#define DEVICE_SAME 2
#define DEVICE_UPDATED 3
 if (needle == NULL)
  return DEVICE_NOT_FOUND;

 for (i = 0; i < haystack_size; i++) {
  if (haystack[i] == NULL) /* previously removed. */
   continue;
  if (SCSI3ADDR_EQ(needle->scsi3addr, haystack[i]->scsi3addr)) {
   *index = i;
   if (device_is_the_same(needle, haystack[i])) {
    if (device_updated(needle, haystack[i]))
     return DEVICE_UPDATED;
    return DEVICE_SAME;
   } else {
    /* Keep offline devices offline */
    if (needle->volume_offline)
     return DEVICE_NOT_FOUND;
    return DEVICE_CHANGED;
   }
  }
 }
 *index = -1;
 return DEVICE_NOT_FOUND;
}

static void hpsa_monitor_offline_device(struct ctlr_info *h,
     unsigned char scsi3addr[])
{
 struct offline_device_entry *device;
 unsigned long flags;

 /* Check to see if device is already on the list */
 spin_lock_irqsave(&h->offline_device_lock, flags);
 list_for_each_entry(device, &h->offline_device_list, offline_list) {
  if (memcmp(device->scsi3addr, scsi3addr,
   sizeof(device->scsi3addr)) == 0) {
   spin_unlock_irqrestore(&h->offline_device_lock, flags);
   return;
  }
 }
 spin_unlock_irqrestore(&h->offline_device_lock, flags);

 /* Device is not on the list, add it. */
 device = kmalloc(sizeof(*device), GFP_KERNEL);
 if (!device)
  return;

 memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr));
 spin_lock_irqsave(&h->offline_device_lock, flags);
 list_add_tail(&device->offline_list, &h->offline_device_list);
 spin_unlock_irqrestore(&h->offline_device_lock, flags);
}

/* Print a message explaining various offline volume states */
static void hpsa_show_volume_status(struct ctlr_info *h,
 struct hpsa_scsi_dev_t *sd)
{
 if (sd->volume_offline == HPSA_VPD_LV_STATUS_UNSUPPORTED)
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume status is not available through vital product data pages.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
 switch (sd->volume_offline) {
 case HPSA_LV_OK:
  break;
 case HPSA_LV_UNDERGOING_ERASE:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is undergoing background erase process.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_NOT_AVAILABLE:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is waiting for transforming volume.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_UNDERGOING_RPI:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is undergoing rapid parity init.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_PENDING_RPI:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is queued for rapid parity initialization process.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_ENCRYPTED_NO_KEY:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is encrypted and cannot be accessed because key is not present.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_PLAINTEXT_IN_ENCRYPT_ONLY_CONTROLLER:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is not encrypted and cannot be accessed because controller is in encryption-only mode.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_UNDERGOING_ENCRYPTION:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is undergoing encryption process.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_UNDERGOING_ENCRYPTION_REKEYING:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is undergoing encryption re-keying process.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is encrypted and cannot be accessed because controller does not have encryption enabled.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_PENDING_ENCRYPTION:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is pending migration to encrypted state, but process has not started.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 case HPSA_LV_PENDING_ENCRYPTION_REKEYING:
  dev_info(&h->pdev->dev,
   "C%d:B%d:T%d:L%d Volume is encrypted and is pending encryption rekeying.\n",
   h->scsi_host->host_no,
   sd->bus, sd->target, sd->lun);
  break;
 }
}

/*
 * Figure the list of physical drive pointers for a logical drive with
 * raid offload configured.
 */

static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
    struct hpsa_scsi_dev_t *dev[], int ndevices,
    struct hpsa_scsi_dev_t *logical_drive)
{
 struct raid_map_data *map = &logical_drive->raid_map;
 struct raid_map_disk_data *dd = &map->data[0];
 int i, j;
 int total_disks_per_row = le16_to_cpu(map->data_disks_per_row) +
    le16_to_cpu(map->metadata_disks_per_row);
 int nraid_map_entries = le16_to_cpu(map->row_cnt) *
    le16_to_cpu(map->layout_map_count) *
    total_disks_per_row;
 int nphys_disk = le16_to_cpu(map->layout_map_count) *
    total_disks_per_row;
 int qdepth;

 if (nraid_map_entries > RAID_MAP_MAX_ENTRIES)
  nraid_map_entries = RAID_MAP_MAX_ENTRIES;

 logical_drive->nphysical_disks = nraid_map_entries;

 qdepth = 0;
 for (i = 0; i < nraid_map_entries; i++) {
  logical_drive->phys_disk[i] = NULL;
  if (!logical_drive->offload_config)
   continue;
  for (j = 0; j < ndevices; j++) {
   if (dev[j] == NULL)
    continue;
   if (dev[j]->devtype != TYPE_DISK &&
       dev[j]->devtype != TYPE_ZBC)
    continue;
   if (is_logical_device(dev[j]))
    continue;
   if (dev[j]->ioaccel_handle != dd[i].ioaccel_handle)
    continue;

   logical_drive->phys_disk[i] = dev[j];
   if (i < nphys_disk)
    qdepth = min(h->nr_cmds, qdepth +
        logical_drive->phys_disk[i]->queue_depth);
   break;
  }

  /*
 * This can happen if a physical drive is removed and
 * the logical drive is degraded.  In that case, the RAID
 * map data will refer to a physical disk which isn't actually
 * present.  And in that case offload_enabled should already
 * be 0, but we'll turn it off here just in case
 */

  if (!logical_drive->phys_disk[i]) {
   dev_warn(&h->pdev->dev,
    "%s: [%d:%d:%d:%d] A phys disk component of LV is missing, turning off offload_enabled for LV.\n",
    __func__,
    h->scsi_host->host_no, logical_drive->bus,
    logical_drive->target, logical_drive->lun);
   hpsa_turn_off_ioaccel_for_device(logical_drive);
   logical_drive->queue_depth = 8;
  }
 }
 if (nraid_map_entries)
  /*
 * This is correct for reads, too high for full stripe writes,
 * way too high for partial stripe writes
 */

  logical_drive->queue_depth = qdepth;
 else {
  if (logical_drive->external)
   logical_drive->queue_depth = EXTERNAL_QD;
  else
   logical_drive->queue_depth = h->nr_cmds;
 }
}

static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
    struct hpsa_scsi_dev_t *dev[], int ndevices)
{
 int i;

 for (i = 0; i < ndevices; i++) {
  if (dev[i] == NULL)
   continue;
  if (dev[i]->devtype != TYPE_DISK &&
      dev[i]->devtype != TYPE_ZBC)
   continue;
  if (!is_logical_device(dev[i]))
   continue;

  /*
 * If offload is currently enabled, the RAID map and
 * phys_disk[] assignment *better* not be changing
 * because we would be changing ioaccel phsy_disk[] pointers
 * on a ioaccel volume processing I/O requests.
 *
 * If an ioaccel volume status changed, initially because it was
 * re-configured and thus underwent a transformation, or
 * a drive failed, we would have received a state change
 * request and ioaccel should have been turned off. When the
 * transformation completes, we get another state change
 * request to turn ioaccel back on. In this case, we need
 * to update the ioaccel information.
 *
 * Thus: If it is not currently enabled, but will be after
 * the scan completes, make sure the ioaccel pointers
 * are up to date.
 */


  if (!dev[i]->offload_enabled && dev[i]->offload_to_be_enabled)
   hpsa_figure_phys_disk_ptrs(h, dev, ndevices, dev[i]);
 }
}

static int hpsa_add_device(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
{
 int rc = 0;

 if (!h->scsi_host)
  return 1;

 if (is_logical_device(device)) /* RAID */
  rc = scsi_add_device(h->scsi_host, device->bus,
     device->target, device->lun);
 else /* HBA */
  rc = hpsa_add_sas_device(h->sas_host, device);

 return rc;
}

static int hpsa_find_outstanding_commands_for_dev(struct ctlr_info *h,
      struct hpsa_scsi_dev_t *dev)
{
 int i;
 int count = 0;

 for (i = 0; i < h->nr_cmds; i++) {
  struct CommandList *c = h->cmd_pool + i;
  int refcount = atomic_inc_return(&c->refcount);

  if (refcount > 1 && hpsa_cmd_dev_match(h, c, dev,
    dev->scsi3addr)) {
   unsigned long flags;

   spin_lock_irqsave(&h->lock, flags); /* Implied MB */
   if (!hpsa_is_cmd_idle(c))
    ++count;
   spin_unlock_irqrestore(&h->lock, flags);
  }

  cmd_free(h, c);
 }

 return count;
}

#define NUM_WAIT 20
static void hpsa_wait_for_outstanding_commands_for_dev(struct ctlr_info *h,
      struct hpsa_scsi_dev_t *device)
{
 int cmds = 0;
 int waits = 0;
 int num_wait = NUM_WAIT;

 if (device->external)
  num_wait = HPSA_EH_PTRAID_TIMEOUT;

 while (1) {
  cmds = hpsa_find_outstanding_commands_for_dev(h, device);
  if (cmds == 0)
   break;
  if (++waits > num_wait)
   break;
  msleep(1000);
 }

 if (waits > num_wait) {
  dev_warn(&h->pdev->dev,
   "%s: removing device [%d:%d:%d:%d] with %d outstanding commands!\n",
   __func__,
   h->scsi_host->host_no,
   device->bus, device->target, device->lun, cmds);
 }
}

static void hpsa_remove_device(struct ctlr_info *h,
   struct hpsa_scsi_dev_t *device)
{
 struct scsi_device *sdev = NULL;

 if (!h->scsi_host)
  return;

 /*
 * Allow for commands to drain
 */

 device->removed = 1;
 hpsa_wait_for_outstanding_commands_for_dev(h, device);

 if (is_logical_device(device)) { /* RAID */
  sdev = scsi_device_lookup(h->scsi_host, device->bus,
      device->target, device->lun);
  if (sdev) {
   scsi_remove_device(sdev);
   scsi_device_put(sdev);
  } else {
   /*
 * We don't expect to get here.  Future commands
 * to this device will get a selection timeout as
 * if the device were gone.
 */

   hpsa_show_dev_msg(KERN_WARNING, h, device,
     "didn't find device for removal.");
  }
 } else { /* HBA */

  hpsa_remove_sas_device(device);
 }
}

static void adjust_hpsa_scsi_table(struct ctlr_info *h,
 struct hpsa_scsi_dev_t *sd[], int nsds)
{
 /* sd contains scsi3 addresses and devtypes, and inquiry
 * data.  This function takes what's in sd to be the current
 * reality and updates h->dev[] to reflect that reality.
 */

 int i, entry, device_change, changes = 0;
 struct hpsa_scsi_dev_t *csd;
 unsigned long flags;
 struct hpsa_scsi_dev_t **added, **removed;
 int nadded, nremoved;

 /*
 * A reset can cause a device status to change
 * re-schedule the scan to see what happened.
 */

 spin_lock_irqsave(&h->reset_lock, flags);
 if (h->reset_in_progress) {
  h->drv_req_rescan = 1;
  spin_unlock_irqrestore(&h->reset_lock, flags);
  return;
 }
 spin_unlock_irqrestore(&h->reset_lock, flags);

 added = kcalloc(HPSA_MAX_DEVICES, sizeof(*added), GFP_KERNEL);
 removed = kcalloc(HPSA_MAX_DEVICES, sizeof(*removed), GFP_KERNEL);

 if (!added || !removed) {
  dev_warn(&h->pdev->dev, "out of memory in "
   "adjust_hpsa_scsi_table\n");
  goto free_and_out;
 }

 spin_lock_irqsave(&h->devlock, flags);

 /* find any devices in h->dev[] that are not in
 * sd[] and remove them from h->dev[], and for any
 * devices which have changed, remove the old device
 * info and add the new device info.
 * If minor device attributes change, just update
 * the existing device structure.
 */

 i = 0;
 nremoved = 0;
 nadded = 0;
 while (i < h->ndevices) {
  csd = h->dev[i];
  device_change = hpsa_scsi_find_entry(csd, sd, nsds, &entry);
  if (device_change == DEVICE_NOT_FOUND) {
   changes++;
   hpsa_scsi_remove_entry(h, i, removed, &nremoved);
   continue/* remove ^^^, hence i not incremented */
  } else if (device_change == DEVICE_CHANGED) {
   changes++;
   hpsa_scsi_replace_entry(h, i, sd[entry],
    added, &nadded, removed, &nremoved);
   /* Set it to NULL to prevent it from being freed
 * at the bottom of hpsa_update_scsi_devices()
 */

   sd[entry] = NULL;
  } else if (device_change == DEVICE_UPDATED) {
   hpsa_scsi_update_entry(h, i, sd[entry]);
  }
  i++;
 }

 /* Now, make sure every device listed in sd[] is also
 * listed in h->dev[], adding them if they aren't found
 */


 for (i = 0; i < nsds; i++) {
  if (!sd[i]) /* if already added above. */
   continue;

  /* Don't add devices which are NOT READY, FORMAT IN PROGRESS
 * as the SCSI mid-layer does not handle such devices well.
 * It relentlessly loops sending TUR at 3Hz, then READ(10)
 * at 160Hz, and prevents the system from coming up.
 */

  if (sd[i]->volume_offline) {
   hpsa_show_volume_status(h, sd[i]);
   hpsa_show_dev_msg(KERN_INFO, h, sd[i], "offline");
   continue;
  }

  device_change = hpsa_scsi_find_entry(sd[i], h->dev,
     h->ndevices, &entry);
  if (device_change == DEVICE_NOT_FOUND) {
   changes++;
   if (hpsa_scsi_add_entry(h, sd[i], added, &nadded) != 0)
    break;
   sd[i] = NULL; /* prevent from being freed later. */
  } else if (device_change == DEVICE_CHANGED) {
   /* should never happen... */
   changes++;
   dev_warn(&h->pdev->dev,
    "device unexpectedly changed.\n");
   /* but if it does happen, we just ignore that device */
  }
 }
 hpsa_update_log_drive_phys_drive_ptrs(h, h->dev, h->ndevices);

 /*
 * Now that h->dev[]->phys_disk[] is coherent, we can enable
 * any logical drives that need it enabled.
 *
 * The raid map should be current by now.
 *
 * We are updating the device list used for I/O requests.
 */

 for (i = 0; i < h->ndevices; i++) {
  if (h->dev[i] == NULL)
   continue;
  h->dev[i]->offload_enabled = h->dev[i]->offload_to_be_enabled;
 }

 spin_unlock_irqrestore(&h->devlock, flags);

 /* Monitor devices which are in one of several NOT READY states to be
 * brought online later. This must be done without holding h->devlock,
 * so don't touch h->dev[]
 */

 for (i = 0; i < nsds; i++) {
  if (!sd[i]) /* if already added above. */
   continue;
  if (sd[i]->volume_offline)
   hpsa_monitor_offline_device(h, sd[i]->scsi3addr);
 }

 /* Don't notify scsi mid layer of any changes the first time through
 * (or if there are no changes) scsi_scan_host will do it later the
 * first time through.
 */

 if (!changes)
  goto free_and_out;

 /* Notify scsi mid layer of any removed devices */
 for (i = 0; i < nremoved; i++) {
  if (removed[i] == NULL)
   continue;
  if (removed[i]->expose_device)
   hpsa_remove_device(h, removed[i]);
  kfree(removed[i]);
  removed[i] = NULL;
 }

 /* Notify scsi mid layer of any added devices */
 for (i = 0; i < nadded; i++) {
  int rc = 0;

  if (added[i] == NULL)
   continue;
  if (!(added[i]->expose_device))
   continue;
  rc = hpsa_add_device(h, added[i]);
  if (!rc)
   continue;
  dev_warn(&h->pdev->dev,
   "addition failed %d, device not added.", rc);
  /* now we have to remove it from h->dev,
 * since it didn't get added to scsi mid layer
 */

  fixup_botched_add(h, added[i]);
  h->drv_req_rescan = 1;
 }

free_and_out:
 kfree(added);
 kfree(removed);
}

/*
 * Lookup bus/target/lun and return corresponding struct hpsa_scsi_dev_t *
 * Assume's h->devlock is held.
 */

static struct hpsa_scsi_dev_t *lookup_hpsa_scsi_dev(struct ctlr_info *h,
 int bus, int target, int lun)
{
 int i;
 struct hpsa_scsi_dev_t *sd;

 for (i = 0; i < h->ndevices; i++) {
  sd = h->dev[i];
  if (sd->bus == bus && sd->target == target && sd->lun == lun)
   return sd;
 }
 return NULL;
}

static int hpsa_sdev_init(struct scsi_device *sdev)
{
 struct hpsa_scsi_dev_t *sd = NULL;
 unsigned long flags;
 struct ctlr_info *h;

 h = sdev_to_hba(sdev);
 spin_lock_irqsave(&h->devlock, flags);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=93 G=93

¤ Dauer der Verarbeitung: 0.33 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.