* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (84 commits)
[SCSI] be2iscsi: SGE Len == 64K
[SCSI] be2iscsi: Remove premature free of cid
[SCSI] be2iscsi: More time for FW
[SCSI] libsas: fix bug for vacant phy
[SCSI] sd: Fix overflow with big physical blocks
[SCSI] st: add MTWEOFI to write filemarks without flushing drive buffer
[SCSI] libsas: Don't issue commands to devices that have been hot-removed
[SCSI] megaraid_sas: Add Online Controller Reset to MegaRAID SAS drive
[SCSI] lpfc 8.3.17: Update lpfc driver version to 8.3.17
[SCSI] lpfc 8.3.17: Replace function reset methodology
[SCSI] lpfc 8.3.17: SCSI fixes
[SCSI] lpfc 8.3.17: BSG fixes
[SCSI] lpfc 8.3.17: SLI Additions and Fixes
[SCSI] lpfc 8.3.17: Code Cleanup and Locking fixes
[SCSI] zfcp: Remove scsi_cmnd->serial_number from debug traces
[SCSI] ipr: fix array error logging
[SCSI] aha152x: enable PCMCIA on 64bit
[SCSI] scsi_dh_alua: Handle all states correctly
[SCSI] cxgb4i: connection and ddp setting update
[SCSI] cxgb3i: fixed connection over vlan
...
* zfcp device driver
*
* Userspace interface for accessing the
- * Access Control Lists / Control File Data Channel
+ * Access Control Lists / Control File Data Channel;
+ * handling of response code and states for ports and LUNs.
*
- * Copyright IBM Corporation 2008, 2009
+ * Copyright IBM Corporation 2008, 2010
*/
#define KMSG_COMPONENT "zfcp"
.open = nonseekable_open,
.unlocked_ioctl = zfcp_cfdc_dev_ioctl,
#ifdef CONFIG_COMPAT
- .compat_ioctl = zfcp_cfdc_dev_ioctl
+ .compat_ioctl = zfcp_cfdc_dev_ioctl,
#endif
+ .llseek = no_llseek,
};
struct miscdevice zfcp_cfdc_misc = {
.name = "zfcp_cfdc",
.fops = &zfcp_cfdc_fops,
};
+
+ /**
+ * zfcp_cfdc_adapter_access_changed - Process change in adapter ACT
+ * @adapter: Adapter where the Access Control Table (ACT) changed
+ *
+ * After a change in the adapter ACT, check if access to any
+ * previously denied resources is now possible.
+ */
+ void zfcp_cfdc_adapter_access_changed(struct zfcp_adapter *adapter)
+ {
+ unsigned long flags;
+ struct zfcp_port *port;
+ struct scsi_device *sdev;
+ struct zfcp_scsi_dev *zfcp_sdev;
+ int status;
+
+ if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
+ return;
+
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list) {
+ status = atomic_read(&port->status);
+ if ((status & ZFCP_STATUS_COMMON_ACCESS_DENIED) ||
+ (status & ZFCP_STATUS_COMMON_ACCESS_BOXED))
+ zfcp_erp_port_reopen(port,
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ "cfaac_1", NULL);
+ }
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
+
+ shost_for_each_device(sdev, port->adapter->scsi_host) {
+ zfcp_sdev = sdev_to_zfcp(sdev);
+ status = atomic_read(&zfcp_sdev->status);
+ if ((status & ZFCP_STATUS_COMMON_ACCESS_DENIED) ||
+ (status & ZFCP_STATUS_COMMON_ACCESS_BOXED))
+ zfcp_erp_lun_reopen(sdev,
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ "cfaac_2", NULL);
+ }
+ }
+
+ static void zfcp_act_eval_err(struct zfcp_adapter *adapter, u32 table)
+ {
+ u16 subtable = table >> 16;
+ u16 rule = table & 0xffff;
+ const char *act_type[] = { "unknown", "OS", "WWPN", "DID", "LUN" };
+
+ if (subtable && subtable < ARRAY_SIZE(act_type))
+ dev_warn(&adapter->ccw_device->dev,
+ "Access denied according to ACT rule type %s, "
+ "rule %d\n", act_type[subtable], rule);
+ }
+
+ /**
+ * zfcp_cfdc_port_denied - Process "access denied" for port
+ * @port: The port where the acces has been denied
+ * @qual: The FSF status qualifier for the access denied FSF status
+ */
+ void zfcp_cfdc_port_denied(struct zfcp_port *port,
+ union fsf_status_qual *qual)
+ {
+ dev_warn(&port->adapter->ccw_device->dev,
+ "Access denied to port 0x%016Lx\n",
+ (unsigned long long)port->wwpn);
+
+ zfcp_act_eval_err(port->adapter, qual->halfword[0]);
+ zfcp_act_eval_err(port->adapter, qual->halfword[1]);
+ zfcp_erp_set_port_status(port,
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ ZFCP_STATUS_COMMON_ACCESS_DENIED);
+ }
+
+ /**
+ * zfcp_cfdc_lun_denied - Process "access denied" for LUN
+ * @sdev: The SCSI device / LUN where the access has been denied
+ * @qual: The FSF status qualifier for the access denied FSF status
+ */
+ void zfcp_cfdc_lun_denied(struct scsi_device *sdev,
+ union fsf_status_qual *qual)
+ {
+ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+ dev_warn(&zfcp_sdev->port->adapter->ccw_device->dev,
+ "Access denied to LUN 0x%016Lx on port 0x%016Lx\n",
+ zfcp_scsi_dev_lun(sdev),
+ (unsigned long long)zfcp_sdev->port->wwpn);
+ zfcp_act_eval_err(zfcp_sdev->port->adapter, qual->halfword[0]);
+ zfcp_act_eval_err(zfcp_sdev->port->adapter, qual->halfword[1]);
+ zfcp_erp_set_lun_status(sdev,
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ ZFCP_STATUS_COMMON_ACCESS_DENIED);
+
+ atomic_clear_mask(ZFCP_STATUS_LUN_SHARED, &zfcp_sdev->status);
+ atomic_clear_mask(ZFCP_STATUS_LUN_READONLY, &zfcp_sdev->status);
+ }
+
+ /**
+ * zfcp_cfdc_lun_shrng_vltn - Evaluate LUN sharing violation status
+ * @sdev: The LUN / SCSI device where sharing violation occurred
+ * @qual: The FSF status qualifier from the LUN sharing violation
+ */
+ void zfcp_cfdc_lun_shrng_vltn(struct scsi_device *sdev,
+ union fsf_status_qual *qual)
+ {
+ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+ if (qual->word[0])
+ dev_warn(&zfcp_sdev->port->adapter->ccw_device->dev,
+ "LUN 0x%Lx on port 0x%Lx is already in "
+ "use by CSS%d, MIF Image ID %x\n",
+ zfcp_scsi_dev_lun(sdev),
+ (unsigned long long)zfcp_sdev->port->wwpn,
+ qual->fsf_queue_designator.cssid,
+ qual->fsf_queue_designator.hla);
+ else
+ zfcp_act_eval_err(zfcp_sdev->port->adapter, qual->word[2]);
+
+ zfcp_erp_set_lun_status(sdev,
+ ZFCP_STATUS_COMMON_ERP_FAILED |
+ ZFCP_STATUS_COMMON_ACCESS_DENIED);
+ atomic_clear_mask(ZFCP_STATUS_LUN_SHARED, &zfcp_sdev->status);
+ atomic_clear_mask(ZFCP_STATUS_LUN_READONLY, &zfcp_sdev->status);
+ }
+
+ /**
+ * zfcp_cfdc_open_lun_eval - Eval access ctrl. status for successful "open lun"
+ * @sdev: The SCSI device / LUN where to evaluate the status
+ * @bottom: The qtcb bottom with the status from the "open lun"
+ *
+ * Returns: 0 if LUN is usable, -EACCES if the access control table
+ * reports an unsupported configuration.
+ */
+ int zfcp_cfdc_open_lun_eval(struct scsi_device *sdev,
+ struct fsf_qtcb_bottom_support *bottom)
+ {
+ int shared, rw;
+ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+ struct zfcp_adapter *adapter = zfcp_sdev->port->adapter;
+
+ if ((adapter->connection_features & FSF_FEATURE_NPIV_MODE) ||
+ !(adapter->adapter_features & FSF_FEATURE_LUN_SHARING) ||
+ zfcp_ccw_priv_sch(adapter))
+ return 0;
+
+ shared = !(bottom->lun_access_info & FSF_UNIT_ACCESS_EXCLUSIVE);
+ rw = (bottom->lun_access_info & FSF_UNIT_ACCESS_OUTBOUND_TRANSFER);
+
+ if (shared)
+ atomic_set_mask(ZFCP_STATUS_LUN_SHARED, &zfcp_sdev->status);
+
+ if (!rw) {
+ atomic_set_mask(ZFCP_STATUS_LUN_READONLY, &zfcp_sdev->status);
+ dev_info(&adapter->ccw_device->dev, "SCSI device at LUN "
+ "0x%016Lx on port 0x%016Lx opened read-only\n",
+ zfcp_scsi_dev_lun(sdev),
+ (unsigned long long)zfcp_sdev->port->wwpn);
+ }
+
+ if (!shared && !rw) {
+ dev_err(&adapter->ccw_device->dev, "Exclusive read-only access "
+ "not supported (LUN 0x%016Lx, port 0x%016Lx)\n",
+ zfcp_scsi_dev_lun(sdev),
+ (unsigned long long)zfcp_sdev->port->wwpn);
+ zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ERP_FAILED);
+ zfcp_erp_lun_shutdown(sdev, 0, "fsouh_6", NULL);
+ return -EACCES;
+ }
+
+ if (shared && rw) {
+ dev_err(&adapter->ccw_device->dev,
+ "Shared read-write access not supported "
+ "(LUN 0x%016Lx, port 0x%016Lx)\n",
+ zfcp_scsi_dev_lun(sdev),
+ (unsigned long long)zfcp_sdev->port->wwpn);
+ zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ERP_FAILED);
+ zfcp_erp_lun_shutdown(sdev, 0, "fsosh_8", NULL);
+ return -EACCES;
+ }
+
+ return 0;
+ }
return sdev->queue_depth;
}
- static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
+ static void zfcp_scsi_slave_destroy(struct scsi_device *sdev)
{
- struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
- unit->device = NULL;
- put_device(&unit->dev);
+ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
+
+ zfcp_erp_lun_shutdown_wait(sdev, "scssd_1");
+ put_device(&zfcp_sdev->port->dev);
}
static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
void (*done) (struct scsi_cmnd *))
{
- struct zfcp_unit *unit;
- struct zfcp_adapter *adapter;
- int status, scsi_result, ret;
+ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device);
+ struct zfcp_adapter *adapter = zfcp_sdev->port->adapter;
struct fc_rport *rport = starget_to_rport(scsi_target(scpnt->device));
+ int status, scsi_result, ret;
/* reset the status for this request */
scpnt->result = 0;
scpnt->host_scribble = NULL;
scpnt->scsi_done = done;
- /*
- * figure out adapter and target device
- * (stored there by zfcp_scsi_slave_alloc)
- */
- adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
- unit = scpnt->device->hostdata;
-
scsi_result = fc_remote_port_chkready(rport);
if (unlikely(scsi_result)) {
scpnt->result = scsi_result;
return 0;
}
- status = atomic_read(&unit->status);
+ status = atomic_read(&zfcp_sdev->status);
if (unlikely(status & ZFCP_STATUS_COMMON_ERP_FAILED) &&
- !(atomic_read(&unit->port->status) &
+ !(atomic_read(&zfcp_sdev->port->status) &
ZFCP_STATUS_COMMON_ERP_FAILED)) {
- /* only unit access denied, but port is good
+ /* only LUN access denied, but port is good
* not covered by FC transport, have to fail here */
zfcp_scsi_command_fail(scpnt, DID_ERROR);
return 0;
if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) {
/* This could be either
- * open unit pending: this is temporary, will result in
- * open unit or ERP_FAILED, so retry command
+ * open LUN pending: this is temporary, will result in
+ * open LUN or ERP_FAILED, so retry command
* call to rport_delete pending: mimic retry from
* fc_remote_port_chkready until rport is BLOCKED
*/
return 0;
}
- ret = zfcp_fsf_send_fcp_command_task(unit, scpnt);
+ ret = zfcp_fsf_fcp_cmnd(scpnt);
if (unlikely(ret == -EBUSY))
return SCSI_MLQUEUE_DEVICE_BUSY;
else if (unlikely(ret < 0))
return ret;
}
- static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *adapter,
- unsigned int id, u64 lun)
+ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev)
{
- unsigned long flags;
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct zfcp_adapter *adapter =
+ (struct zfcp_adapter *) sdev->host->hostdata[0];
+ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
struct zfcp_port *port;
- struct zfcp_unit *unit = NULL;
+ struct zfcp_unit *unit;
- read_lock_irqsave(&adapter->port_list_lock, flags);
- list_for_each_entry(port, &adapter->port_list, list) {
- if (!port->rport || (id != port->rport->scsi_target_id))
- continue;
- unit = zfcp_get_unit_by_lun(port, lun);
- if (unit)
- break;
- }
- read_unlock_irqrestore(&adapter->port_list_lock, flags);
+ port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
+ if (!port)
+ return -ENXIO;
- return unit;
- }
+ unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev));
+ if (unit)
+ put_device(&unit->dev);
- static int zfcp_scsi_slave_alloc(struct scsi_device *sdp)
- {
- struct zfcp_adapter *adapter;
- struct zfcp_unit *unit;
- u64 lun;
+ if (!unit && !(adapter->connection_features & FSF_FEATURE_NPIV_MODE)) {
+ put_device(&port->dev);
+ return -ENXIO;
+ }
- adapter = (struct zfcp_adapter *) sdp->host->hostdata[0];
- if (!adapter)
- goto out;
+ zfcp_sdev->port = port;
+ zfcp_sdev->latencies.write.channel.min = 0xFFFFFFFF;
+ zfcp_sdev->latencies.write.fabric.min = 0xFFFFFFFF;
+ zfcp_sdev->latencies.read.channel.min = 0xFFFFFFFF;
+ zfcp_sdev->latencies.read.fabric.min = 0xFFFFFFFF;
+ zfcp_sdev->latencies.cmd.channel.min = 0xFFFFFFFF;
+ zfcp_sdev->latencies.cmd.fabric.min = 0xFFFFFFFF;
+ spin_lock_init(&zfcp_sdev->latencies.lock);
- int_to_scsilun(sdp->lun, (struct scsi_lun *)&lun);
- unit = zfcp_unit_lookup(adapter, sdp->id, lun);
- if (unit) {
- sdp->hostdata = unit;
- unit->device = sdp;
- return 0;
- }
- out:
- return -ENXIO;
+ zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_RUNNING);
+ zfcp_erp_lun_reopen(sdev, 0, "scsla_1", NULL);
+ zfcp_erp_wait(port->adapter);
+
+ return 0;
}
static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
struct Scsi_Host *scsi_host = scpnt->device->host;
struct zfcp_adapter *adapter =
(struct zfcp_adapter *) scsi_host->hostdata[0];
- struct zfcp_unit *unit = scpnt->device->hostdata;
struct zfcp_fsf_req *old_req, *abrt_req;
unsigned long flags;
unsigned long old_reqid = (unsigned long) scpnt->host_scribble;
write_unlock_irqrestore(&adapter->abort_lock, flags);
while (retry--) {
- abrt_req = zfcp_fsf_abort_fcp_command(old_reqid, unit);
+ abrt_req = zfcp_fsf_abort_fcp_cmnd(scpnt);
if (abrt_req)
break;
static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
{
- struct zfcp_unit *unit = scpnt->device->hostdata;
- struct zfcp_adapter *adapter = unit->port->adapter;
+ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device);
+ struct zfcp_adapter *adapter = zfcp_sdev->port->adapter;
struct zfcp_fsf_req *fsf_req = NULL;
int retval = SUCCESS, ret;
int retry = 3;
while (retry--) {
- fsf_req = zfcp_fsf_send_fcp_ctm(unit, tm_flags);
+ fsf_req = zfcp_fsf_fcp_task_mgmt(scpnt, tm_flags);
if (fsf_req)
break;
if (!(atomic_read(&adapter->status) &
ZFCP_STATUS_COMMON_RUNNING)) {
- zfcp_dbf_scsi_devreset("nres", tm_flags, unit, scpnt);
+ zfcp_dbf_scsi_devreset("nres", scpnt, tm_flags);
return SUCCESS;
}
}
wait_for_completion(&fsf_req->completion);
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
- zfcp_dbf_scsi_devreset("fail", tm_flags, unit, scpnt);
+ zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags);
retval = FAILED;
} else
- zfcp_dbf_scsi_devreset("okay", tm_flags, unit, scpnt);
+ zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags);
zfcp_fsf_req_free(fsf_req);
return retval;
static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
{
- struct zfcp_unit *unit = scpnt->device->hostdata;
- struct zfcp_adapter *adapter = unit->port->adapter;
+ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device);
+ struct zfcp_adapter *adapter = zfcp_sdev->port->adapter;
int ret;
zfcp_erp_adapter_reopen(adapter, 0, "schrh_1", scpnt);
}
/* tell the SCSI stack some characteristics of this adapter */
- adapter->scsi_host->max_id = 1;
- adapter->scsi_host->max_lun = 1;
+ adapter->scsi_host->max_id = 511;
+ adapter->scsi_host->max_lun = 0xFFFFFFFF;
adapter->scsi_host->max_channel = 0;
adapter->scsi_host->unique_id = dev_id.devno;
adapter->scsi_host->max_cmd_len = 16; /* in struct fcp_cmnd */
}
}
- static void zfcp_scsi_queue_unit_register(struct zfcp_port *port)
- {
- struct zfcp_unit *unit;
-
- read_lock_irq(&port->unit_list_lock);
- list_for_each_entry(unit, &port->unit_list, list) {
- get_device(&unit->dev);
- if (scsi_queue_work(port->adapter->scsi_host,
- &unit->scsi_work) <= 0)
- put_device(&unit->dev);
- }
- read_unlock_irq(&port->unit_list_lock);
- }
-
static void zfcp_scsi_rport_register(struct zfcp_port *port)
{
struct fc_rport_identifiers ids;
port->rport = rport;
port->starget_id = rport->scsi_target_id;
- zfcp_scsi_queue_unit_register(port);
+ zfcp_unit_queue_scsi_scan(port);
}
static void zfcp_scsi_rport_block(struct zfcp_port *port)
put_device(&port->dev);
}
- /**
- * zfcp_scsi_scan - Register LUN with SCSI midlayer
- * @unit: The LUN/unit to register
- */
- void zfcp_scsi_scan(struct zfcp_unit *unit)
- {
- struct fc_rport *rport = unit->port->rport;
-
- if (rport && rport->port_state == FC_PORTSTATE_ONLINE)
- scsi_scan_target(&rport->dev, 0, rport->scsi_target_id,
- scsilun_to_int((struct scsi_lun *)
- &unit->fcp_lun), 0);
- }
-
- void zfcp_scsi_scan_work(struct work_struct *work)
- {
- struct zfcp_unit *unit = container_of(work, struct zfcp_unit,
- scsi_work);
-
- zfcp_scsi_scan(unit);
- put_device(&unit->dev);
- }
-
/**
* zfcp_scsi_set_prot - Configure DIF/DIX support in scsi_host
* @adapter: The adapter where to configure DIF/DIX for the SCSI host
adapter->adapter_features & FSF_FEATURE_DIX_PROT_TCPIP) {
mask |= SHOST_DIX_TYPE1_PROTECTION;
scsi_host_set_guard(shost, SHOST_DIX_GUARD_IP);
+ shost->sg_prot_tablesize = ZFCP_QDIO_MAX_SBALES_PER_REQ / 2;
shost->sg_tablesize = ZFCP_QDIO_MAX_SBALES_PER_REQ / 2;
shost->max_sectors = ZFCP_QDIO_MAX_SBALES_PER_REQ * 8 / 2;
}
.show_host_port_type = 1,
.show_host_speed = 1,
.show_host_port_id = 1,
- .disable_target_scan = 1,
.dd_bsg_size = sizeof(struct zfcp_fsf_ct_els),
};
enum iscsi_host_param param, char *buf)
{
struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost);
- int len = 0;
- int status;
+ int status = 0;
SE_DEBUG(DBG_LVL_8, "In beiscsi_get_host_param, param= %d\n", param);
switch (param) {
default:
return iscsi_host_get_param(shost, param, buf);
}
- return len;
+ return status;
}
int beiscsi_get_macaddr(char *buf, struct beiscsi_hba *phba)
if (beiscsi_ep->ep_cid > (phba->fw_config.iscsi_cid_start +
phba->params.cxns_per_ctrl * 2)) {
SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n");
- beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
goto free_ep;
}
SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed"
" status = %d extd_status = %d\n",
status, extd_status);
- beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
free_mcc_tag(&phba->ctrl, tag);
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
beiscsi_ep->cid_vld = 1;
SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
}
- beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return 0;
sc->scsi_done(sc);
}
-static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd)
-{
- enum task_attribute ta = TASK_ATTR_SIMPLE;
- if (cmd->request && blk_rq_tagged(cmd->request)) {
- if (cmd->device->ordered_tags &&
- (cmd->request->cmd_flags & REQ_HARDBARRIER))
- ta = TASK_ATTR_ORDERED;
- }
- return ta;
-}
-
static struct sas_task *sas_create_task(struct scsi_cmnd *cmd,
struct domain_device *dev,
gfp_t gfp_flags)
task->ssp_task.retry_count = 1;
int_to_scsilun(cmd->device->lun, &lun);
memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8);
- task->ssp_task.task_attr = sas_scsi_get_task_attr(cmd);
+ task->ssp_task.task_attr = TASK_ATTR_SIMPLE;
memcpy(task->ssp_task.cdb, cmd->cmnd, 16);
task->scatter = scsi_sglist(cmd);
goto out;
}
+ /* If the device fell off, no sense in issuing commands */
+ if (dev->gone) {
+ cmd->result = DID_BAD_TARGET << 16;
+ scsi_done(cmd);
+ goto out;
+ }
+
res = -ENOMEM;
task = sas_create_task(cmd, dev, GFP_ATOMIC);
if (!task)
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <linux/smp_lock.h>
#include <linux/uio.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
MODULE_AUTHOR("megaraidlinux@lsi.com");
MODULE_DESCRIPTION("LSI MegaRAID SAS Driver");
+ static int megasas_transition_to_ready(struct megasas_instance *instance);
+ static int megasas_get_pd_list(struct megasas_instance *instance);
+ static int megasas_issue_init_mfi(struct megasas_instance *instance);
+ static int megasas_register_aen(struct megasas_instance *instance,
+ u32 seq_num, u32 class_locale_word);
/*
* PCI ID table for all supported controllers
*/
static inline void
megasas_enable_intr_xscale(struct megasas_register_set __iomem * regs)
{
- writel(1, &(regs)->outbound_intr_mask);
+ writel(0, &(regs)->outbound_intr_mask);
/* Dummy readl to force pci flush */
readl(®s->outbound_intr_mask);
megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs)
{
u32 status;
+ u32 mfiStatus = 0;
/*
* Check if it is our interrupt
*/
status = readl(®s->outbound_intr_status);
- if (!(status & MFI_OB_INTR_STATUS_MASK)) {
- return 1;
- }
+ if (status & MFI_OB_INTR_STATUS_MASK)
+ mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE;
+ if (status & MFI_XSCALE_OMR0_CHANGE_INTERRUPT)
+ mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE;
/*
* Clear the interrupt by writing back the same value
*/
- writel(status, ®s->outbound_intr_status);
+ if (mfiStatus)
+ writel(status, ®s->outbound_intr_status);
/* Dummy readl to force pci flush */
readl(®s->outbound_intr_status);
- return 0;
+ return mfiStatus;
}
/**
u32 frame_count,
struct megasas_register_set __iomem *regs)
{
+ unsigned long flags;
+ spin_lock_irqsave(&instance->hba_lock, flags);
writel((frame_phys_addr >> 3)|(frame_count),
&(regs)->inbound_queue_port);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ }
+
+ /**
+ * megasas_adp_reset_xscale - For controller reset
+ * @regs: MFI register set
+ */
+ static int
+ megasas_adp_reset_xscale(struct megasas_instance *instance,
+ struct megasas_register_set __iomem *regs)
+ {
+ u32 i;
+ u32 pcidata;
+ writel(MFI_ADP_RESET, ®s->inbound_doorbell);
+
+ for (i = 0; i < 3; i++)
+ msleep(1000); /* sleep for 3 secs */
+ pcidata = 0;
+ pci_read_config_dword(instance->pdev, MFI_1068_PCSR_OFFSET, &pcidata);
+ printk(KERN_NOTICE "pcidata = %x\n", pcidata);
+ if (pcidata & 0x2) {
+ printk(KERN_NOTICE "mfi 1068 offset read=%x\n", pcidata);
+ pcidata &= ~0x2;
+ pci_write_config_dword(instance->pdev,
+ MFI_1068_PCSR_OFFSET, pcidata);
+
+ for (i = 0; i < 2; i++)
+ msleep(1000); /* need to wait 2 secs again */
+
+ pcidata = 0;
+ pci_read_config_dword(instance->pdev,
+ MFI_1068_FW_HANDSHAKE_OFFSET, &pcidata);
+ printk(KERN_NOTICE "1068 offset handshake read=%x\n", pcidata);
+ if ((pcidata & 0xffff0000) == MFI_1068_FW_READY) {
+ printk(KERN_NOTICE "1068 offset pcidt=%x\n", pcidata);
+ pcidata = 0;
+ pci_write_config_dword(instance->pdev,
+ MFI_1068_FW_HANDSHAKE_OFFSET, pcidata);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * megasas_check_reset_xscale - For controller reset check
+ * @regs: MFI register set
+ */
+ static int
+ megasas_check_reset_xscale(struct megasas_instance *instance,
+ struct megasas_register_set __iomem *regs)
+ {
+ u32 consumer;
+ consumer = *instance->consumer;
+
+ if ((instance->adprecovery != MEGASAS_HBA_OPERATIONAL) &&
+ (*instance->consumer == MEGASAS_ADPRESET_INPROG_SIGN)) {
+ return 1;
+ }
+ return 0;
}
static struct megasas_instance_template megasas_instance_template_xscale = {
.disable_intr = megasas_disable_intr_xscale,
.clear_intr = megasas_clear_intr_xscale,
.read_fw_status_reg = megasas_read_fw_status_reg_xscale,
+ .adp_reset = megasas_adp_reset_xscale,
+ .check_reset = megasas_check_reset_xscale,
};
/**
{
writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear);
- writel(~0x80000004, &(regs)->outbound_intr_mask);
+ writel(~0x80000000, &(regs)->outbound_intr_mask);
/* Dummy readl to force pci flush */
readl(®s->outbound_intr_mask);
status = readl(®s->outbound_intr_status);
if (!(status & MFI_REPLY_1078_MESSAGE_INTERRUPT)) {
- return 1;
+ return 0;
}
/*
/* Dummy readl to force pci flush */
readl(®s->outbound_doorbell_clear);
- return 0;
+ return 1;
}
/**
* megasas_fire_cmd_ppc - Sends command to the FW
u32 frame_count,
struct megasas_register_set __iomem *regs)
{
+ unsigned long flags;
+ spin_lock_irqsave(&instance->hba_lock, flags);
writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_queue_port);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
}
+ /**
+ * megasas_adp_reset_ppc - For controller reset
+ * @regs: MFI register set
+ */
+ static int
+ megasas_adp_reset_ppc(struct megasas_instance *instance,
+ struct megasas_register_set __iomem *regs)
+ {
+ return 0;
+ }
+
+ /**
+ * megasas_check_reset_ppc - For controller reset check
+ * @regs: MFI register set
+ */
+ static int
+ megasas_check_reset_ppc(struct megasas_instance *instance,
+ struct megasas_register_set __iomem *regs)
+ {
+ return 0;
+ }
static struct megasas_instance_template megasas_instance_template_ppc = {
.fire_cmd = megasas_fire_cmd_ppc,
.disable_intr = megasas_disable_intr_ppc,
.clear_intr = megasas_clear_intr_ppc,
.read_fw_status_reg = megasas_read_fw_status_reg_ppc,
+ .adp_reset = megasas_adp_reset_ppc,
+ .check_reset = megasas_check_reset_ppc,
};
/**
status = readl(®s->outbound_intr_status);
if (!(status & MFI_SKINNY_ENABLE_INTERRUPT_MASK)) {
- return 1;
+ return 0;
}
/*
*/
readl(®s->outbound_intr_status);
- return 0;
+ return 1;
}
/**
struct megasas_register_set __iomem *regs)
{
unsigned long flags;
- spin_lock_irqsave(&instance->fire_lock, flags);
+ spin_lock_irqsave(&instance->hba_lock, flags);
writel(0, &(regs)->inbound_high_queue_port);
writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_low_queue_port);
- spin_unlock_irqrestore(&instance->fire_lock, flags);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ }
+
+ /**
+ * megasas_adp_reset_skinny - For controller reset
+ * @regs: MFI register set
+ */
+ static int
+ megasas_adp_reset_skinny(struct megasas_instance *instance,
+ struct megasas_register_set __iomem *regs)
+ {
+ return 0;
+ }
+
+ /**
+ * megasas_check_reset_skinny - For controller reset check
+ * @regs: MFI register set
+ */
+ static int
+ megasas_check_reset_skinny(struct megasas_instance *instance,
+ struct megasas_register_set __iomem *regs)
+ {
+ return 0;
}
static struct megasas_instance_template megasas_instance_template_skinny = {
.disable_intr = megasas_disable_intr_skinny,
.clear_intr = megasas_clear_intr_skinny,
.read_fw_status_reg = megasas_read_fw_status_reg_skinny,
+ .adp_reset = megasas_adp_reset_skinny,
+ .check_reset = megasas_check_reset_skinny,
};
megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs)
{
u32 status;
+ u32 mfiStatus = 0;
/*
* Check if it is our interrupt
*/
status = readl(®s->outbound_intr_status);
- if (!(status & MFI_GEN2_ENABLE_INTERRUPT_MASK))
- return 1;
+ if (status & MFI_GEN2_ENABLE_INTERRUPT_MASK) {
+ mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE;
+ }
+ if (status & MFI_G2_OUTBOUND_DOORBELL_CHANGE_INTERRUPT) {
+ mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE;
+ }
/*
* Clear the interrupt by writing back the same value
*/
- writel(status, ®s->outbound_doorbell_clear);
+ if (mfiStatus)
+ writel(status, ®s->outbound_doorbell_clear);
/* Dummy readl to force pci flush */
readl(®s->outbound_intr_status);
- return 0;
+ return mfiStatus;
}
/**
* megasas_fire_cmd_gen2 - Sends command to the FW
u32 frame_count,
struct megasas_register_set __iomem *regs)
{
+ unsigned long flags;
+ spin_lock_irqsave(&instance->hba_lock, flags);
writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_queue_port);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ }
+
+ /**
+ * megasas_adp_reset_gen2 - For controller reset
+ * @regs: MFI register set
+ */
+ static int
+ megasas_adp_reset_gen2(struct megasas_instance *instance,
+ struct megasas_register_set __iomem *reg_set)
+ {
+ u32 retry = 0 ;
+ u32 HostDiag;
+
+ writel(0, ®_set->seq_offset);
+ writel(4, ®_set->seq_offset);
+ writel(0xb, ®_set->seq_offset);
+ writel(2, ®_set->seq_offset);
+ writel(7, ®_set->seq_offset);
+ writel(0xd, ®_set->seq_offset);
+ msleep(1000);
+
+ HostDiag = (u32)readl(®_set->host_diag);
+
+ while ( !( HostDiag & DIAG_WRITE_ENABLE) ) {
+ msleep(100);
+ HostDiag = (u32)readl(®_set->host_diag);
+ printk(KERN_NOTICE "RESETGEN2: retry=%x, hostdiag=%x\n",
+ retry, HostDiag);
+
+ if (retry++ >= 100)
+ return 1;
+
+ }
+
+ printk(KERN_NOTICE "ADP_RESET_GEN2: HostDiag=%x\n", HostDiag);
+
+ writel((HostDiag | DIAG_RESET_ADAPTER), ®_set->host_diag);
+
+ ssleep(10);
+
+ HostDiag = (u32)readl(®_set->host_diag);
+ while ( ( HostDiag & DIAG_RESET_ADAPTER) ) {
+ msleep(100);
+ HostDiag = (u32)readl(®_set->host_diag);
+ printk(KERN_NOTICE "RESET_GEN2: retry=%x, hostdiag=%x\n",
+ retry, HostDiag);
+
+ if (retry++ >= 1000)
+ return 1;
+
+ }
+ return 0;
+ }
+
+ /**
+ * megasas_check_reset_gen2 - For controller reset check
+ * @regs: MFI register set
+ */
+ static int
+ megasas_check_reset_gen2(struct megasas_instance *instance,
+ struct megasas_register_set __iomem *regs)
+ {
+ return 0;
}
static struct megasas_instance_template megasas_instance_template_gen2 = {
.disable_intr = megasas_disable_intr_gen2,
.clear_intr = megasas_clear_intr_gen2,
.read_fw_status_reg = megasas_read_fw_status_reg_gen2,
+ .adp_reset = megasas_adp_reset_gen2,
+ .check_reset = megasas_check_reset_gen2,
};
/**
* This is the end of set of functions & definitions
- * specific to ppc (deviceid : 0x60) controllers
+ * specific to gen2 (deviceid : 0x78, 0x79) controllers
*/
/**
instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr, 0, instance->reg_set);
- wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA),
- MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
+ wait_event(instance->int_cmd_wait_q, cmd->cmd_status != ENODATA);
return 0;
}
/*
* Wait for this cmd to complete
*/
- wait_event_timeout(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF),
- MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
+ wait_event(instance->abort_cmd_wait_q, cmd->cmd_status != 0xFF);
+ cmd->sync_cmd = 0;
megasas_return_cmd(instance, cmd);
return 0;
u32 frame_count;
struct megasas_cmd *cmd;
struct megasas_instance *instance;
+ unsigned long flags;
instance = (struct megasas_instance *)
scmd->device->host->hostdata;
- /* Don't process if we have already declared adapter dead */
- if (instance->hw_crit_error)
+ if (instance->issuepend_done == 0)
return SCSI_MLQUEUE_HOST_BUSY;
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) {
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
scmd->scsi_done = done;
scmd->result = 0;
return 0;
}
+ static void megaraid_sas_kill_hba(struct megasas_instance *instance)
+ {
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ writel(MFI_STOP_ADP,
+ &instance->reg_set->reserved_0[0]);
+ } else {
+ writel(MFI_STOP_ADP,
+ &instance->reg_set->inbound_doorbell);
+ }
+ }
+
/**
* megasas_complete_cmd_dpc - Returns FW's controller structure
* @instance_addr: Address of adapter soft state
unsigned long flags;
/* If we have already declared adapter dead, donot complete cmds */
- if (instance->hw_crit_error)
+ if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR )
return;
spin_lock_irqsave(&instance->completion_lock, flags);
while (consumer != producer) {
context = instance->reply_queue[consumer];
+ if (context >= instance->max_fw_cmds) {
+ printk(KERN_ERR "Unexpected context value %x\n",
+ context);
+ BUG();
+ }
cmd = instance->cmd_list[context];
static int megasas_wait_for_outstanding(struct megasas_instance *instance)
{
int i;
+ u32 reset_index;
u32 wait_time = MEGASAS_RESET_WAIT_TIME;
+ u8 adprecovery;
+ unsigned long flags;
+ struct list_head clist_local;
+ struct megasas_cmd *reset_cmd;
+
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ adprecovery = instance->adprecovery;
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
+ if (adprecovery != MEGASAS_HBA_OPERATIONAL) {
+
+ INIT_LIST_HEAD(&clist_local);
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ list_splice_init(&instance->internal_reset_pending_q,
+ &clist_local);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
+ printk(KERN_NOTICE "megasas: HBA reset wait ...\n");
+ for (i = 0; i < wait_time; i++) {
+ msleep(1000);
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ adprecovery = instance->adprecovery;
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ if (adprecovery == MEGASAS_HBA_OPERATIONAL)
+ break;
+ }
+
+ if (adprecovery != MEGASAS_HBA_OPERATIONAL) {
+ printk(KERN_NOTICE "megasas: reset: Stopping HBA.\n");
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ return FAILED;
+ }
+
+ reset_index = 0;
+ while (!list_empty(&clist_local)) {
+ reset_cmd = list_entry((&clist_local)->next,
+ struct megasas_cmd, list);
+ list_del_init(&reset_cmd->list);
+ if (reset_cmd->scmd) {
+ reset_cmd->scmd->result = DID_RESET << 16;
+ printk(KERN_NOTICE "%d:%p reset [%02x], %#lx\n",
+ reset_index, reset_cmd,
+ reset_cmd->scmd->cmnd[0],
+ reset_cmd->scmd->serial_number);
+
+ reset_cmd->scmd->scsi_done(reset_cmd->scmd);
+ megasas_return_cmd(instance, reset_cmd);
+ } else if (reset_cmd->sync_cmd) {
+ printk(KERN_NOTICE "megasas:%p synch cmds"
+ "reset queue\n",
+ reset_cmd);
+
+ reset_cmd->cmd_status = ENODATA;
+ instance->instancet->fire_cmd(instance,
+ reset_cmd->frame_phys_addr,
+ 0, instance->reg_set);
+ } else {
+ printk(KERN_NOTICE "megasas: %p unexpected"
+ "cmds lst\n",
+ reset_cmd);
+ }
+ reset_index++;
+ }
+
+ return SUCCESS;
+ }
for (i = 0; i < wait_time; i++) {
}
if (atomic_read(&instance->fw_outstanding)) {
+ printk(KERN_NOTICE "megaraid_sas: pending cmds after reset\n");
/*
* Send signal to FW to stop processing any pending cmds.
* The controller will be taken offline by the OS now.
&instance->reg_set->inbound_doorbell);
}
megasas_dump_pending_frames(instance);
- instance->hw_crit_error = 1;
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
return FAILED;
}
+ printk(KERN_NOTICE "megaraid_sas: no pending cmds after reset\n");
+
return SUCCESS;
}
scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x retries=%x\n",
scmd->serial_number, scmd->cmnd[0], scmd->retries);
- if (instance->hw_crit_error) {
+ if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
printk(KERN_ERR "megasas: cannot recover from previous reset "
"failures\n");
return FAILED;
instance->aen_cmd = NULL;
megasas_return_cmd(instance, cmd);
- if (instance->unload == 0) {
+ if ((instance->unload == 0) &&
+ ((instance->issuepend_done == 1))) {
struct megasas_aen_event *ev;
ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
if (!ev) {
struct megasas_header *hdr = &cmd->frame->hdr;
unsigned long flags;
+ /* flag for the retry reset */
+ cmd->retry_for_fw_reset = 0;
+
if (cmd->scmd)
cmd->scmd->SCp.ptr = NULL;
}
}
+ /**
+ * megasas_issue_pending_cmds_again - issue all pending cmds
+ * in FW again because of the fw reset
+ * @instance: Adapter soft state
+ */
+ static inline void
+ megasas_issue_pending_cmds_again(struct megasas_instance *instance)
+ {
+ struct megasas_cmd *cmd;
+ struct list_head clist_local;
+ union megasas_evt_class_locale class_locale;
+ unsigned long flags;
+ u32 seq_num;
+
+ INIT_LIST_HEAD(&clist_local);
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ list_splice_init(&instance->internal_reset_pending_q, &clist_local);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
+ while (!list_empty(&clist_local)) {
+ cmd = list_entry((&clist_local)->next,
+ struct megasas_cmd, list);
+ list_del_init(&cmd->list);
+
+ if (cmd->sync_cmd || cmd->scmd) {
+ printk(KERN_NOTICE "megaraid_sas: command %p, %p:%d"
+ "detected to be pending while HBA reset.\n",
+ cmd, cmd->scmd, cmd->sync_cmd);
+
+ cmd->retry_for_fw_reset++;
+
+ if (cmd->retry_for_fw_reset == 3) {
+ printk(KERN_NOTICE "megaraid_sas: cmd %p, %p:%d"
+ "was tried multiple times during reset."
+ "Shutting down the HBA\n",
+ cmd, cmd->scmd, cmd->sync_cmd);
+ megaraid_sas_kill_hba(instance);
+
+ instance->adprecovery =
+ MEGASAS_HW_CRITICAL_ERROR;
+ return;
+ }
+ }
+
+ if (cmd->sync_cmd == 1) {
+ if (cmd->scmd) {
+ printk(KERN_NOTICE "megaraid_sas: unexpected"
+ "cmd attached to internal command!\n");
+ }
+ printk(KERN_NOTICE "megasas: %p synchronous cmd"
+ "on the internal reset queue,"
+ "issue it again.\n", cmd);
+ cmd->cmd_status = ENODATA;
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr ,
+ 0, instance->reg_set);
+ } else if (cmd->scmd) {
+ printk(KERN_NOTICE "megasas: %p scsi cmd [%02x],%#lx"
+ "detected on the internal queue, issue again.\n",
+ cmd, cmd->scmd->cmnd[0], cmd->scmd->serial_number);
+
+ atomic_inc(&instance->fw_outstanding);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr,
+ cmd->frame_count-1, instance->reg_set);
+ } else {
+ printk(KERN_NOTICE "megasas: %p unexpected cmd on the"
+ "internal reset defer list while re-issue!!\n",
+ cmd);
+ }
+ }
+
+ if (instance->aen_cmd) {
+ printk(KERN_NOTICE "megaraid_sas: aen_cmd in def process\n");
+ megasas_return_cmd(instance, instance->aen_cmd);
+
+ instance->aen_cmd = NULL;
+ }
+
+ /*
+ * Initiate AEN (Asynchronous Event Notification)
+ */
+ seq_num = instance->last_seq_num;
+ class_locale.members.reserved = 0;
+ class_locale.members.locale = MR_EVT_LOCALE_ALL;
+ class_locale.members.class = MR_EVT_CLASS_DEBUG;
+
+ megasas_register_aen(instance, seq_num, class_locale.word);
+ }
+
+ /**
+ * Move the internal reset pending commands to a deferred queue.
+ *
+ * We move the commands pending at internal reset time to a
+ * pending queue. This queue would be flushed after successful
+ * completion of the internal reset sequence. if the internal reset
+ * did not complete in time, the kernel reset handler would flush
+ * these commands.
+ **/
+ static void
+ megasas_internal_reset_defer_cmds(struct megasas_instance *instance)
+ {
+ struct megasas_cmd *cmd;
+ int i;
+ u32 max_cmd = instance->max_fw_cmds;
+ u32 defer_index;
+ unsigned long flags;
+
+ defer_index = 0;
+ spin_lock_irqsave(&instance->cmd_pool_lock, flags);
+ for (i = 0; i < max_cmd; i++) {
+ cmd = instance->cmd_list[i];
+ if (cmd->sync_cmd == 1 || cmd->scmd) {
+ printk(KERN_NOTICE "megasas: moving cmd[%d]:%p:%d:%p"
+ "on the defer queue as internal\n",
+ defer_index, cmd, cmd->sync_cmd, cmd->scmd);
+
+ if (!list_empty(&cmd->list)) {
+ printk(KERN_NOTICE "megaraid_sas: ERROR while"
+ " moving this cmd:%p, %d %p, it was"
+ "discovered on some list?\n",
+ cmd, cmd->sync_cmd, cmd->scmd);
+
+ list_del_init(&cmd->list);
+ }
+ defer_index++;
+ list_add_tail(&cmd->list,
+ &instance->internal_reset_pending_q);
+ }
+ }
+ spin_unlock_irqrestore(&instance->cmd_pool_lock, flags);
+ }
+
+
+ static void
+ process_fw_state_change_wq(struct work_struct *work)
+ {
+ struct megasas_instance *instance =
+ container_of(work, struct megasas_instance, work_init);
+ u32 wait;
+ unsigned long flags;
+
+ if (instance->adprecovery != MEGASAS_ADPRESET_SM_INFAULT) {
+ printk(KERN_NOTICE "megaraid_sas: error, recovery st %x \n",
+ instance->adprecovery);
+ return ;
+ }
+
+ if (instance->adprecovery == MEGASAS_ADPRESET_SM_INFAULT) {
+ printk(KERN_NOTICE "megaraid_sas: FW detected to be in fault"
+ "state, restarting it...\n");
+
+ instance->instancet->disable_intr(instance->reg_set);
+ atomic_set(&instance->fw_outstanding, 0);
+
+ atomic_set(&instance->fw_reset_no_pci_access, 1);
+ instance->instancet->adp_reset(instance, instance->reg_set);
+ atomic_set(&instance->fw_reset_no_pci_access, 0 );
+
+ printk(KERN_NOTICE "megaraid_sas: FW restarted successfully,"
+ "initiating next stage...\n");
+
+ printk(KERN_NOTICE "megaraid_sas: HBA recovery state machine,"
+ "state 2 starting...\n");
+
+ /*waitting for about 20 second before start the second init*/
+ for (wait = 0; wait < 30; wait++) {
+ msleep(1000);
+ }
+
+ if (megasas_transition_to_ready(instance)) {
+ printk(KERN_NOTICE "megaraid_sas:adapter not ready\n");
+
+ megaraid_sas_kill_hba(instance);
+ instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
+ return ;
+ }
+
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) ||
+ (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_VERDE_ZCR)
+ ) {
+ *instance->consumer = *instance->producer;
+ } else {
+ *instance->consumer = 0;
+ *instance->producer = 0;
+ }
+
+ megasas_issue_init_mfi(instance);
+
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ instance->instancet->enable_intr(instance->reg_set);
+
+ megasas_issue_pending_cmds_again(instance);
+ instance->issuepend_done = 1;
+ }
+ return ;
+ }
+
/**
* megasas_deplete_reply_queue - Processes all completed commands
* @instance: Adapter soft state
* @alt_status: Alternate status to be returned to
* SCSI mid-layer instead of the status
* returned by the FW
+ * Note: this must be called with hba lock held
*/
static int
- megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status)
+ megasas_deplete_reply_queue(struct megasas_instance *instance,
+ u8 alt_status)
{
- /*
- * Check if it is our interrupt
- * Clear the interrupt
- */
- if(instance->instancet->clear_intr(instance->reg_set))
+ u32 mfiStatus;
+ u32 fw_state;
+
+ if ((mfiStatus = instance->instancet->check_reset(instance,
+ instance->reg_set)) == 1) {
+ return IRQ_HANDLED;
+ }
+
+ if ((mfiStatus = instance->instancet->clear_intr(
+ instance->reg_set)
+ ) == 0) {
return IRQ_NONE;
+ }
+
+ instance->mfiStatus = mfiStatus;
+
+ if ((mfiStatus & MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE)) {
+ fw_state = instance->instancet->read_fw_status_reg(
+ instance->reg_set) & MFI_STATE_MASK;
+
+ if (fw_state != MFI_STATE_FAULT) {
+ printk(KERN_NOTICE "megaraid_sas: fw state:%x\n",
+ fw_state);
+ }
+
+ if ((fw_state == MFI_STATE_FAULT) &&
+ (instance->disableOnlineCtrlReset == 0)) {
+ printk(KERN_NOTICE "megaraid_sas: wait adp restart\n");
+
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS1064R) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_DELL_PERC5) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_VERDE_ZCR)) {
+
+ *instance->consumer =
+ MEGASAS_ADPRESET_INPROG_SIGN;
+ }
+
+
+ instance->instancet->disable_intr(instance->reg_set);
+ instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT;
+ instance->issuepend_done = 0;
+
+ atomic_set(&instance->fw_outstanding, 0);
+ megasas_internal_reset_defer_cmds(instance);
+
+ printk(KERN_NOTICE "megasas: fwState=%x, stage:%d\n",
+ fw_state, instance->adprecovery);
+
+ schedule_work(&instance->work_init);
+ return IRQ_HANDLED;
+
+ } else {
+ printk(KERN_NOTICE "megasas: fwstate:%x, dis_OCR=%x\n",
+ fw_state, instance->disableOnlineCtrlReset);
+ }
+ }
- if (instance->hw_crit_error)
- goto out_done;
- /*
- * Schedule the tasklet for cmd completion
- */
tasklet_schedule(&instance->isr_tasklet);
- out_done:
return IRQ_HANDLED;
}
-
/**
* megasas_isr - isr entry point
*/
static irqreturn_t megasas_isr(int irq, void *devp)
{
- return megasas_deplete_reply_queue((struct megasas_instance *)devp,
- DID_OK);
+ struct megasas_instance *instance;
+ unsigned long flags;
+ irqreturn_t rc;
+
+ if (atomic_read(
+ &(((struct megasas_instance *)devp)->fw_reset_no_pci_access)))
+ return IRQ_HANDLED;
+
+ instance = (struct megasas_instance *)devp;
+
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ rc = megasas_deplete_reply_queue(instance, DID_OK);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
+ return rc;
}
/**
"in %d secs\n", fw_state, max_wait);
return -ENODEV;
}
- };
+ }
printk(KERN_INFO "megasas: FW now in Ready state\n");
return 0;
*/
sgl_sz = sge_sz * instance->max_num_sge;
frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE - 1) / MEGAMFI_FRAME_SIZE;
+ frame_count = 15;
/*
* We need one extra frame for the MFI command
cmd = instance->cmd_list[i];
memset(cmd, 0, sizeof(struct megasas_cmd));
cmd->index = i;
+ cmd->scmd = NULL;
cmd->instance = instance;
list_add_tail(&cmd->list, &instance->cmd_pool);
/* the following function will get the instance PD LIST */
- if ((ret == 0) && (ci->ldCount < MAX_LOGICAL_DRIVES)) {
+ if ((ret == 0) && (ci->ldCount <= MAX_LOGICAL_DRIVES)) {
memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
for (ld_index = 0; ld_index < ci->ldCount; ld_index++) {
if (megasas_issue_init_mfi(instance))
goto fail_fw_init;
+ instance->fw_support_ieee = 0;
+ instance->fw_support_ieee =
+ (instance->instancet->read_fw_status_reg(reg_set) &
+ 0x04000000);
+
+ printk(KERN_NOTICE "megasas_init_mfi: fw_support_ieee=%d",
+ instance->fw_support_ieee);
+
+ if (instance->fw_support_ieee)
+ instance->flag_ieee = 1;
+
+ /** for passthrough
+ * the following function will get the PD LIST.
+ */
+
memset(instance->pd_list, 0 ,
(MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
megasas_get_pd_list(instance);
max_sectors_2 = ctrl_info->max_request_size;
tmp_sectors = min_t(u32, max_sectors_1 , max_sectors_2);
+ instance->disableOnlineCtrlReset =
+ ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset;
}
instance->max_sectors_per_req = instance->max_num_sge *
dcmd->flags = MFI_FRAME_DIR_READ;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
+ instance->last_seq_num = seq_num;
dcmd->data_xfer_len = sizeof(struct megasas_evt_detail);
dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT;
dcmd->mbox.w[0] = seq_num;
instance = (struct megasas_instance *)host->hostdata;
memset(instance, 0, sizeof(*instance));
+ atomic_set( &instance->fw_reset_no_pci_access, 0 );
instance->producer = pci_alloc_consistent(pdev, sizeof(u32),
&instance->producer_h);
megasas_poll_wait_aen = 0;
instance->flag_ieee = 0;
instance->ev = NULL;
+ instance->issuepend_done = 1;
+ instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
+ megasas_poll_wait_aen = 0;
instance->evt_detail = pci_alloc_consistent(pdev,
sizeof(struct
* Initialize locks and queues
*/
INIT_LIST_HEAD(&instance->cmd_pool);
+ INIT_LIST_HEAD(&instance->internal_reset_pending_q);
atomic_set(&instance->fw_outstanding,0);
init_waitqueue_head(&instance->abort_cmd_wait_q);
spin_lock_init(&instance->cmd_pool_lock);
- spin_lock_init(&instance->fire_lock);
+ spin_lock_init(&instance->hba_lock);
spin_lock_init(&instance->completion_lock);
spin_lock_init(&poll_aen_lock);
instance->flag = 0;
instance->unload = 1;
instance->last_time = 0;
+ instance->disableOnlineCtrlReset = 1;
+
+ INIT_WORK(&instance->work_init, process_fw_state_change_wq);
/*
* Initialize MFI Firmware
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
+ if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR)
+ return;
+
cmd = megasas_get_cmd(instance);
if (!cmd)
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
+ if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR)
+ return;
+
cmd = megasas_get_cmd(instance);
if (!cmd)
*/
static int megasas_mgmt_open(struct inode *inode, struct file *filep)
{
- cycle_kernel_lock();
/*
* Allow only those users with admin rights
*/
struct megasas_iocpacket *ioc;
struct megasas_instance *instance;
int error;
+ int i;
+ unsigned long flags;
+ u32 wait_time = MEGASAS_RESET_WAIT_TIME;
ioc = kmalloc(sizeof(*ioc), GFP_KERNEL);
if (!ioc)
goto out_kfree_ioc;
}
- if (instance->hw_crit_error == 1) {
- printk(KERN_DEBUG "Controller in Crit ERROR\n");
+ if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
+ printk(KERN_ERR "Controller in crit error\n");
error = -ENODEV;
goto out_kfree_ioc;
}
error = -ERESTARTSYS;
goto out_kfree_ioc;
}
+
+ for (i = 0; i < wait_time; i++) {
+
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ if (instance->adprecovery == MEGASAS_HBA_OPERATIONAL) {
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ break;
+ }
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
+ if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
+ printk(KERN_NOTICE "megasas: waiting"
+ "for controller reset to finish\n");
+ }
+
+ msleep(1000);
+ }
+
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) {
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
+ printk(KERN_ERR "megaraid_sas: timed out while"
+ "waiting for HBA to recover\n");
+ error = -ENODEV;
+ goto out_kfree_ioc;
+ }
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
error = megasas_mgmt_fw_ioctl(instance, user_ioc, ioc);
up(&instance->ioctl_sem);
struct megasas_instance *instance;
struct megasas_aen aen;
int error;
+ int i;
+ unsigned long flags;
+ u32 wait_time = MEGASAS_RESET_WAIT_TIME;
if (file->private_data != file) {
printk(KERN_DEBUG "megasas: fasync_helper was not "
if (!instance)
return -ENODEV;
- if (instance->hw_crit_error == 1) {
- error = -ENODEV;
+ if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
+ return -ENODEV;
}
if (instance->unload == 1) {
return -ENODEV;
}
+ for (i = 0; i < wait_time; i++) {
+
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ if (instance->adprecovery == MEGASAS_HBA_OPERATIONAL) {
+ spin_unlock_irqrestore(&instance->hba_lock,
+ flags);
+ break;
+ }
+
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
+ if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
+ printk(KERN_NOTICE "megasas: waiting for"
+ "controller reset to finish\n");
+ }
+
+ msleep(1000);
+ }
+
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) {
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+ printk(KERN_ERR "megaraid_sas: timed out while waiting"
+ "for HBA to recover.\n");
+ return -ENODEV;
+ }
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+
mutex_lock(&instance->aen_mutex);
error = megasas_register_aen(instance, aen.seq_num,
aen.class_locale_word);
#ifdef CONFIG_COMPAT
.compat_ioctl = megasas_mgmt_compat_ioctl,
#endif
+ .llseek = noop_llseek,
};
/*
if (!fcport)
return;
- if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags))
- return;
-
- if (unlikely(pci_channel_offline(fcport->vha->hw->pdev))) {
- qla2x00_abort_all_cmds(fcport->vha, DID_NO_CONNECT << 16);
- return;
- }
-
/*
* Transport has effectively 'deleted' the rport, clear
* all local references.
*/
spin_lock_irq(host->host_lock);
- fcport->rport = NULL;
+ fcport->rport = fcport->drport = NULL;
*((fc_port_t **)rport->dd_data) = NULL;
spin_unlock_irq(host->host_lock);
+
+ if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags))
+ return;
+
+ if (unlikely(pci_channel_offline(fcport->vha->hw->pdev))) {
+ qla2x00_abort_all_cmds(fcport->vha, DID_NO_CONNECT << 16);
+ return;
+ }
}
static void
qla2x00_get_host_fabric_name(struct Scsi_Host *shost)
{
scsi_qla_host_t *vha = shost_priv(shost);
- u64 node_name;
+ uint8_t node_name[WWN_SIZE] = { 0xFF, 0xFF, 0xFF, 0xFF, \
+ 0xFF, 0xFF, 0xFF, 0xFF};
+ u64 fabric_name = wwn_to_u64(node_name);
if (vha->device_flags & SWITCH_FOUND)
- node_name = wwn_to_u64(vha->fabric_node_name);
- else
- node_name = wwn_to_u64(vha->node_name);
+ fabric_name = wwn_to_u64(vha->fabric_node_name);
- fc_host_fabric_name(shost) = node_name;
+ fc_host_fabric_name(shost) = fabric_name;
}
static void
}
/* initialize attributes */
+ fc_host_dev_loss_tmo(vha->host) = ha->port_down_retry_count;
fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
fc_host_supported_classes(vha->host) =
qla24xx_disable_vp(vha);
+ vha->flags.delete_progress = 1;
+
fc_remove_host(vha->host);
scsi_remove_host(vha->host);
- qla2x00_free_fcports(vha);
+ if (vha->timer_active) {
+ qla2x00_vp_stop_timer(vha);
+ DEBUG15(printk(KERN_INFO "scsi(%ld): timer for the vport[%d]"
+ " = %p has stopped\n", vha->host_no, vha->vp_idx, vha));
+ }
qla24xx_deallocate_vp_id(vha);
+ /* No pending activities shall be there on the vha now */
+ DEBUG(msleep(random32()%10)); /* Just to see if something falls on
+ * the net we have placed below */
+
+ BUG_ON(atomic_read(&vha->vref_count));
+
+ qla2x00_free_fcports(vha);
+
mutex_lock(&ha->vport_lock);
ha->cur_vport_count--;
clear_bit(vha->vp_idx, ha->vp_idx_map);
mutex_unlock(&ha->vport_lock);
- if (vha->timer_active) {
- qla2x00_vp_stop_timer(vha);
- DEBUG15(printk ("scsi(%ld): timer for the vport[%d] = %p "
- "has stopped\n",
- vha->host_no, vha->vp_idx, vha));
- }
-
if (vha->req->id && !ha->flags.cpu_affinity_enabled) {
if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS)
qla_printk(KERN_WARNING, ha,
struct qla_hw_data *ha = vha->hw;
u32 speed = FC_PORTSPEED_UNKNOWN;
+ fc_host_dev_loss_tmo(vha->host) = ha->port_down_retry_count;
fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
#define MBC_SET_PORT_CONFIG 0x122 /* Set port configuration */
#define MBC_GET_PORT_CONFIG 0x123 /* Get port configuration */
+ /*
+ * ISP81xx mailbox commands
+ */
+ #define MBC_WRITE_MPI_REGISTER 0x01 /* Write MPI Register. */
+
/* Firmware return data sizes */
#define FCAL_MAP_SIZE 128
#define MBX_UPDATE_FLASH_ACTIVE 3
struct mutex vport_lock; /* Virtual port synchronization */
+ spinlock_t vport_slock; /* order is hardware_lock, then vport_slock */
struct completion mbx_cmd_comp; /* Serialize mbx access */
struct completion mbx_intr_comp; /* Used for completion notification */
struct completion dcbx_comp; /* For set port config notification */
uint32_t management_server_logged_in :1;
uint32_t process_response_queue :1;
uint32_t difdix_supported:1;
+ uint32_t delete_progress:1;
} flags;
atomic_t loop_state;
#define NPIV_CONFIG_NEEDED 16
#define ISP_UNRECOVERABLE 17
#define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */
+ #define MPI_RESET_NEEDED 19 /* Initiate MPI FW reset */
uint32_t device_flags;
#define SWITCH_FOUND BIT_0
struct req_que *req;
int fw_heartbeat_counter;
int seconds_since_last_heartbeat;
+
+ atomic_t vref_count;
} scsi_qla_host_t;
/*
test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) || \
atomic_read(&ha->loop_state) == LOOP_DOWN)
+#define QLA_VHA_MARK_BUSY(__vha, __bail) do { \
+ atomic_inc(&__vha->vref_count); \
+ mb(); \
+ if (__vha->flags.delete_progress) { \
+ atomic_dec(&__vha->vref_count); \
+ __bail = 1; \
+ } else { \
+ __bail = 0; \
+ } \
+} while (0)
+
+#define QLA_VHA_MARK_NOT_BUSY(__vha) do { \
+ atomic_dec(&__vha->vref_count); \
+} while (0)
+
+
#define qla_printk(level, ha, format, arg...) \
dev_printk(level , &((ha)->pdev->dev) , format , ## arg)
#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr)
+ #define QLA_SG_ALL 1024
+
enum nexus_wait_type {
WAIT_HOST = 0,
WAIT_TARGET,
{
struct srb_ctx *ctx = sp->ctx;
struct srb_iocb *iocb = ctx->u.iocb_cmd;
+ struct scsi_qla_host *vha = sp->fcport->vha;
del_timer_sync(&iocb->timer);
kfree(iocb);
kfree(ctx);
mempool_free(sp, sp->fcport->vha->hw->srb_mempool);
+
+ QLA_VHA_MARK_NOT_BUSY(vha);
}
inline srb_t *
qla2x00_get_ctx_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size,
unsigned long tmo)
{
- srb_t *sp;
+ srb_t *sp = NULL;
struct qla_hw_data *ha = vha->hw;
struct srb_ctx *ctx;
struct srb_iocb *iocb;
+ uint8_t bail;
+
+ QLA_VHA_MARK_BUSY(vha, bail);
+ if (bail)
+ return NULL;
sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL);
if (!sp)
iocb->timer.function = qla2x00_ctx_sp_timeout;
add_timer(&iocb->timer);
done:
+ if (!sp)
+ QLA_VHA_MARK_NOT_BUSY(vha);
return sp;
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
+ /**
+ * qla81xx_reset_mpi() - Reset's MPI FW via Write MPI Register MBC.
+ *
+ * Returns 0 on success.
+ */
+ int
+ qla81xx_reset_mpi(scsi_qla_host_t *vha)
+ {
+ uint16_t mb[4] = {0x1010, 0, 1, 0};
+
+ return qla81xx_write_mpi_register(vha, mb);
+ }
+
/**
* qla24xx_reset_risc() - Perform full reset of ISP24xx RISC.
* @ha: HA context
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
uint32_t cnt, d2;
uint16_t wd;
+ static int abts_cnt; /* ISP abort retry counts */
spin_lock_irqsave(&ha->hardware_lock, flags);
barrier();
}
+ /* If required, do an MPI FW reset now */
+ if (test_and_clear_bit(MPI_RESET_NEEDED, &vha->dpc_flags)) {
+ if (qla81xx_reset_mpi(vha) != QLA_SUCCESS) {
+ if (++abts_cnt < 5) {
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ set_bit(MPI_RESET_NEEDED, &vha->dpc_flags);
+ } else {
+ /*
+ * We exhausted the ISP abort retries. We have to
+ * set the board offline.
+ */
+ abts_cnt = 0;
+ vha->flags.online = 0;
+ }
+ }
+ }
+
WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_RESET);
RD_REG_DWORD(®->hccr);
qla2x00_init_response_q_entries(rsp);
}
+ spin_lock_irqsave(&ha->vport_slock, flags);
/* Clear RSCN queue. */
list_for_each_entry(vp, &ha->vp_list, list) {
vp->rscn_in_ptr = 0;
vp->rscn_out_ptr = 0;
}
+
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
ha->isp_ops->config_rings(vha);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (!IS_IIDMA_CAPABLE(ha))
return;
+ if (atomic_read(&fcport->state) != FCS_ONLINE)
+ return;
+
if (fcport->fp_speed == PORT_SPEED_UNKNOWN ||
fcport->fp_speed > ha->link_data_rate)
return;
/* Bypass virtual ports of the same host. */
found = 0;
if (ha->num_vhosts) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
if (new_fcport->d_id.b24 == vp->d_id.b24) {
found = 1;
break;
}
}
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
if (found)
continue;
}
struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *vp;
struct scsi_qla_host *tvp;
+ unsigned long flags = 0;
rval = QLA_SUCCESS;
/* Check for loop ID being already in use. */
found = 0;
fcport = NULL;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
list_for_each_entry(fcport, &vp->vp_fcports, list) {
if (fcport->loop_id == dev->loop_id &&
if (found)
break;
}
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
/* If not in use then it is free to use. */
if (!found) {
qla2x00_update_fcports(scsi_qla_host_t *base_vha)
{
fc_port_t *fcport;
- struct scsi_qla_host *tvp, *vha;
+ struct scsi_qla_host *vha;
+ struct qla_hw_data *ha = base_vha->hw;
+ unsigned long flags;
+ spin_lock_irqsave(&ha->vport_slock, flags);
/* Go with deferred removal of rport references. */
- list_for_each_entry_safe(vha, tvp, &base_vha->hw->vp_list, list)
- list_for_each_entry(fcport, &vha->vp_fcports, list)
+ list_for_each_entry(vha, &base_vha->hw->vp_list, list) {
+ atomic_inc(&vha->vref_count);
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport && fcport->drport &&
- atomic_read(&fcport->state) != FCS_UNCONFIGURED)
+ atomic_read(&fcport->state) != FCS_UNCONFIGURED) {
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
qla2x00_rport_del(fcport);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ }
+ }
+ atomic_dec(&vha->vref_count);
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
}
void
{
struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *vp, *base_vha = pci_get_drvdata(ha->pdev);
- struct scsi_qla_host *tvp;
+ unsigned long flags;
vha->flags.online = 0;
ha->flags.chip_reset_done = 0;
if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
atomic_set(&vha->loop_state, LOOP_DOWN);
qla2x00_mark_all_devices_lost(vha, 0);
- list_for_each_entry_safe(vp, tvp, &base_vha->hw->vp_list, list)
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &base_vha->hw->vp_list, list) {
+ atomic_inc(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
qla2x00_mark_all_devices_lost(vp, 0);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
} else {
if (!atomic_read(&vha->loop_down_timer))
atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME);
}
- /* Make sure for ISP 82XX IO DMA is complete */
- if (IS_QLA82XX(ha)) {
- if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0,
- WAIT_HOST) == QLA_SUCCESS) {
- DEBUG2(qla_printk(KERN_INFO, ha,
- "Done wait for pending commands\n"));
+ if (!ha->flags.eeh_busy) {
+ /* Make sure for ISP 82XX IO DMA is complete */
+ if (IS_QLA82XX(ha)) {
+ if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0,
+ WAIT_HOST) == QLA_SUCCESS) {
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "Done wait for pending commands\n"));
+ }
}
- }
- /* Requeue all commands in outstanding command list. */
- qla2x00_abort_all_cmds(vha, DID_RESET << 16);
+ /* Requeue all commands in outstanding command list. */
+ qla2x00_abort_all_cmds(vha, DID_RESET << 16);
+ }
}
/*
uint8_t status = 0;
struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *vp;
- struct scsi_qla_host *tvp;
struct req_que *req = ha->req_q_map[0];
+ unsigned long flags;
if (vha->flags.online) {
qla2x00_abort_isp_cleanup(vha);
DEBUG(printk(KERN_INFO
"qla2x00_abort_isp(%ld): succeeded.\n",
vha->host_no));
- list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
- if (vp->vp_idx)
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx) {
+ atomic_inc(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
qla2x00_vp_abort_isp(vp);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ }
}
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
} else {
qla_printk(KERN_INFO, ha,
"qla2x00_abort_isp: **** FAILED ****\n");
struct req_que *req = ha->req_q_map[0];
struct rsp_que *rsp = ha->rsp_q_map[0];
struct scsi_qla_host *vp;
- struct scsi_qla_host *tvp;
+ unsigned long flags;
status = qla2x00_init_rings(vha);
if (!status) {
DEBUG(printk(KERN_INFO
"qla82xx_restart_isp(%ld): succeeded.\n",
vha->host_no));
- list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
- if (vp->vp_idx)
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx) {
+ atomic_inc(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
qla2x00_vp_abort_isp(vp);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ }
}
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
} else {
qla_printk(KERN_INFO, ha,
"qla82xx_restart_isp: **** FAILED ****\n");
"Unrecoverable Hardware Error: adapter "
"marked OFFLINE!\n");
vha->flags.online = 0;
- } else
+ } else {
+ /* Check to see if MPI timeout occured */
+ if ((mbx & MBX_3) && (ha->flags.port0))
+ set_bit(MPI_RESET_NEEDED,
+ &vha->dpc_flags);
+
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ }
} else if (mb[1] == 0) {
qla_printk(KERN_INFO, ha,
"Unrecoverable Hardware Error: adapter marked "
cp->result = DID_ERROR << 16;
break;
}
- } else if (!lscsi_status) {
+ } else {
DEBUG2(qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d) Dropped frame(s) detected (0x%x "
"of 0x%x bytes).\n", vha->host_no, cp->device->id,
cp->device->lun, resid, scsi_bufflen(cp)));
- cp->result = DID_ERROR << 16;
- break;
+ cp->result = DID_ERROR << 16 | lscsi_status;
+ goto check_scsi_status;
}
cp->result = DID_OK << 16 | lscsi_status;
logit = 0;
+check_scsi_status:
/*
* Check to see if SCSI Status is non zero. If so report SCSI
* Status.
uint16_t stat = le16_to_cpu(rptid_entry->vp_idx);
struct qla_hw_data *ha = vha->hw;
scsi_qla_host_t *vp;
- scsi_qla_host_t *tvp;
+ unsigned long flags;
if (rptid_entry->entry_status != 0)
return;
return;
}
- list_for_each_entry_safe(vp, tvp, &ha->vp_list, list)
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list)
if (vp_idx == vp->vp_idx)
break;
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
if (!vp)
return;
/* Copy mailbox information */
memcpy( mresp, mcp->mb, 64);
- mresp[3] = mcp->mb[18];
- mresp[4] = mcp->mb[19];
return rval;
}
}
/* Copy mailbox information */
- memcpy( mresp, mcp->mb, 32);
+ memcpy(mresp, mcp->mb, 64);
return rval;
}
+
int
qla84xx_reset_chip(scsi_qla_host_t *ha, uint16_t enable_diagnostic)
{
return rval;
}
+ int
+ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
+ {
+ int rval;
+ uint32_t stat, timer;
+ uint16_t mb0 = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ rval = QLA_SUCCESS;
+
+ DEBUG11(qla_printk(KERN_INFO, ha,
+ "%s(%ld): entered.\n", __func__, vha->host_no));
+
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+ /* Write the MBC data to the registers */
+ WRT_REG_WORD(®->mailbox0, MBC_WRITE_MPI_REGISTER);
+ WRT_REG_WORD(®->mailbox1, mb[0]);
+ WRT_REG_WORD(®->mailbox2, mb[1]);
+ WRT_REG_WORD(®->mailbox3, mb[2]);
+ WRT_REG_WORD(®->mailbox4, mb[3]);
+
+ WRT_REG_DWORD(®->hccr, HCCRX_SET_HOST_INT);
+
+ /* Poll for MBC interrupt */
+ for (timer = 6000000; timer; timer--) {
+ /* Check for pending interrupts. */
+ stat = RD_REG_DWORD(®->host_status);
+ if (stat & HSRX_RISC_INT) {
+ stat &= 0xff;
+
+ if (stat == 0x1 || stat == 0x2 ||
+ stat == 0x10 || stat == 0x11) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+ mb0 = RD_REG_WORD(®->mailbox0);
+ WRT_REG_DWORD(®->hccr,
+ HCCRX_CLR_RISC_INT);
+ RD_REG_DWORD(®->hccr);
+ break;
+ }
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags))
+ rval = mb0 & MBS_MASK;
+ else
+ rval = QLA_FUNCTION_FAILED;
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2_3_11(printk(KERN_INFO "%s(%ld): failed=%x mb[0]=%x.\n",
+ __func__, vha->host_no, rval, mb[0]));
+ } else {
+ DEBUG11(printk(KERN_INFO
+ "%s(%ld): done.\n", __func__, vha->host_no));
+ }
+
+ return rval;
+ }
int
qla2x00_get_data_rate(scsi_qla_host_t *vha)
{
return off;
}
+ static int
+ qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong *off)
+ {
+ struct crb_128M_2M_sub_block_map *m;
+
+ if (*off >= QLA82XX_CRB_MAX)
+ return -1;
+
+ if (*off >= QLA82XX_PCI_CAMQM && (*off < QLA82XX_PCI_CAMQM_2M_END)) {
+ *off = (*off - QLA82XX_PCI_CAMQM) +
+ QLA82XX_PCI_CAMQM_2M_BASE + ha->nx_pcibase;
+ return 0;
+ }
+
+ if (*off < QLA82XX_PCI_CRBSPACE)
+ return -1;
+
+ *off -= QLA82XX_PCI_CRBSPACE;
+
+ /* Try direct map */
+ m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)];
+
+ if (m->valid && (m->start_128M <= *off) && (m->end_128M > *off)) {
+ *off = *off + m->start_2M - m->start_128M + ha->nx_pcibase;
+ return 0;
+ }
+ /* Not in direct map, use crb window */
+ return 1;
+ }
+
+ #define CRB_WIN_LOCK_TIMEOUT 100000000
+ static int qla82xx_crb_win_lock(struct qla_hw_data *ha)
+ {
+ int done = 0, timeout = 0;
+
+ while (!done) {
+ /* acquire semaphore3 from PCI HW block */
+ done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_LOCK));
+ if (done == 1)
+ break;
+ if (timeout >= CRB_WIN_LOCK_TIMEOUT)
+ return -1;
+ timeout++;
+ }
+ qla82xx_wr_32(ha, QLA82XX_CRB_WIN_LOCK_ID, ha->portnum);
+ return 0;
+ }
+
int
qla82xx_wr_32(struct qla_hw_data *ha, ulong off, u32 data)
{
return data;
}
- #define CRB_WIN_LOCK_TIMEOUT 100000000
- int qla82xx_crb_win_lock(struct qla_hw_data *ha)
- {
- int done = 0, timeout = 0;
-
- while (!done) {
- /* acquire semaphore3 from PCI HW block */
- done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_LOCK));
- if (done == 1)
- break;
- if (timeout >= CRB_WIN_LOCK_TIMEOUT)
- return -1;
- timeout++;
- }
- qla82xx_wr_32(ha, QLA82XX_CRB_WIN_LOCK_ID, ha->portnum);
- return 0;
- }
-
#define IDC_LOCK_TIMEOUT 100000000
int qla82xx_idc_lock(struct qla_hw_data *ha)
{
qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_UNLOCK));
}
- int
- qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong *off)
- {
- struct crb_128M_2M_sub_block_map *m;
-
- if (*off >= QLA82XX_CRB_MAX)
- return -1;
-
- if (*off >= QLA82XX_PCI_CAMQM && (*off < QLA82XX_PCI_CAMQM_2M_END)) {
- *off = (*off - QLA82XX_PCI_CAMQM) +
- QLA82XX_PCI_CAMQM_2M_BASE + ha->nx_pcibase;
- return 0;
- }
-
- if (*off < QLA82XX_PCI_CRBSPACE)
- return -1;
-
- *off -= QLA82XX_PCI_CRBSPACE;
-
- /* Try direct map */
- m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)];
-
- if (m->valid && (m->start_128M <= *off) && (m->end_128M > *off)) {
- *off = *off + m->start_2M - m->start_128M + ha->nx_pcibase;
- return 0;
- }
- /* Not in direct map, use crb window */
- return 1;
- }
-
/* PCI Windowing for DDR regions. */
#define QLA82XX_ADDR_IN_RANGE(addr, low, high) \
(((addr) <= (high)) && ((addr) >= (low)))
int qla82xx_pci_set_window_warning_count;
- unsigned long
+ static unsigned long
qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr)
{
int window;
}
#define MTU_FUDGE_FACTOR 100
- unsigned long qla82xx_decode_crb_addr(unsigned long addr)
+ static unsigned long
+ qla82xx_decode_crb_addr(unsigned long addr)
{
int i;
unsigned long base_addr, offset, pci_base;
static long rom_max_timeout = 100;
static long qla82xx_rom_lock_timeout = 100;
- int
+ static int
qla82xx_rom_lock(struct qla_hw_data *ha)
{
int done = 0, timeout = 0;
return 0;
}
- int
+ static int
qla82xx_wait_rom_busy(struct qla_hw_data *ha)
{
long timeout = 0;
return 0;
}
- int
+ static int
qla82xx_wait_rom_done(struct qla_hw_data *ha)
{
long timeout = 0;
return 0;
}
- int
+ static int
qla82xx_do_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
{
qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, addr);
return 0;
}
- int
+ static int
qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
{
int ret, loops = 0;
return ret;
}
- int
+ static int
qla82xx_read_status_reg(struct qla_hw_data *ha, uint32_t *val)
{
qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_RDSR);
return 0;
}
- int
+ static int
qla82xx_flash_wait_write_finish(struct qla_hw_data *ha)
{
long timeout = 0;
return ret;
}
- int
+ static int
qla82xx_flash_set_write_enable(struct qla_hw_data *ha)
{
uint32_t val;
return 0;
}
- int
+ static int
qla82xx_write_status_reg(struct qla_hw_data *ha, uint32_t val)
{
if (qla82xx_flash_set_write_enable(ha))
return qla82xx_flash_wait_write_finish(ha);
}
- int
+ static int
qla82xx_write_disable_flash(struct qla_hw_data *ha)
{
qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_WRDI);
return 0;
}
- int
+ static int
ql82xx_rom_lock_d(struct qla_hw_data *ha)
{
int loops = 0;
return 0;;
}
- int
+ static int
qla82xx_write_flash_dword(struct qla_hw_data *ha, uint32_t flashaddr,
uint32_t data)
{
/* This routine does CRB initialize sequence
* to put the ISP into operational state
*/
- int qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
+ static int
+ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
{
int addr, val;
int i ;
return 0;
}
- int qla82xx_check_for_bad_spd(struct qla_hw_data *ha)
+ static int
+ qla82xx_check_for_bad_spd(struct qla_hw_data *ha)
{
u32 val = 0;
val = qla82xx_rd_32(ha, BOOT_LOADER_DIMM_STATUS);
return 0;
}
- int
+ static int
+ qla82xx_pci_mem_write_2M(struct qla_hw_data *ha,
+ u64 off, void *data, int size)
+ {
+ int i, j, ret = 0, loop, sz[2], off0;
+ int scale, shift_amount, startword;
+ uint32_t temp;
+ uint64_t off8, mem_crb, tmpw, word[2] = {0, 0};
+
+ /*
+ * If not MN, go check for MS or invalid.
+ */
+ if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX)
+ mem_crb = QLA82XX_CRB_QDR_NET;
+ else {
+ mem_crb = QLA82XX_CRB_DDR_NET;
+ if (qla82xx_pci_mem_bound_check(ha, off, size) == 0)
+ return qla82xx_pci_mem_write_direct(ha,
+ off, data, size);
+ }
+
+ off0 = off & 0x7;
+ sz[0] = (size < (8 - off0)) ? size : (8 - off0);
+ sz[1] = size - sz[0];
+
+ off8 = off & 0xfffffff0;
+ loop = (((off & 0xf) + size - 1) >> 4) + 1;
+ shift_amount = 4;
+ scale = 2;
+ startword = (off & 0xf)/8;
+
+ for (i = 0; i < loop; i++) {
+ if (qla82xx_pci_mem_read_2M(ha, off8 +
+ (i << shift_amount), &word[i * scale], 8))
+ return -1;
+ }
+
+ switch (size) {
+ case 1:
+ tmpw = *((uint8_t *)data);
+ break;
+ case 2:
+ tmpw = *((uint16_t *)data);
+ break;
+ case 4:
+ tmpw = *((uint32_t *)data);
+ break;
+ case 8:
+ default:
+ tmpw = *((uint64_t *)data);
+ break;
+ }
+
+ if (sz[0] == 8) {
+ word[startword] = tmpw;
+ } else {
+ word[startword] &=
+ ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8));
+ word[startword] |= tmpw << (off0 * 8);
+ }
+ if (sz[1] != 0) {
+ word[startword+1] &= ~(~0ULL << (sz[1] * 8));
+ word[startword+1] |= tmpw >> (sz[0] * 8);
+ }
+
+ /*
+ * don't lock here - write_wx gets the lock if each time
+ * write_lock_irqsave(&adapter->adapter_lock, flags);
+ * netxen_nic_pci_change_crbwindow_128M(adapter, 0);
+ */
+ for (i = 0; i < loop; i++) {
+ temp = off8 + (i << shift_amount);
+ qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_LO, temp);
+ temp = 0;
+ qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_HI, temp);
+ temp = word[i * scale] & 0xffffffff;
+ qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_LO, temp);
+ temp = (word[i * scale] >> 32) & 0xffffffff;
+ qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_HI, temp);
+ temp = word[i*scale + 1] & 0xffffffff;
+ qla82xx_wr_32(ha, mem_crb +
+ MIU_TEST_AGT_WRDATA_UPPER_LO, temp);
+ temp = (word[i*scale + 1] >> 32) & 0xffffffff;
+ qla82xx_wr_32(ha, mem_crb +
+ MIU_TEST_AGT_WRDATA_UPPER_HI, temp);
+
+ temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE;
+ qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp);
+ temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE;
+ qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp);
+
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
+ temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL);
+ if ((temp & MIU_TA_CTL_BUSY) == 0)
+ break;
+ }
+
+ if (j >= MAX_CTL_CHECK) {
+ if (printk_ratelimit())
+ dev_err(&ha->pdev->dev,
+ "failed to write through agent\n");
+ ret = -1;
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ static int
qla82xx_fw_load_from_flash(struct qla_hw_data *ha)
{
int i;
return 0;
}
- int
- qla82xx_pci_mem_write_2M(struct qla_hw_data *ha,
- u64 off, void *data, int size)
- {
- int i, j, ret = 0, loop, sz[2], off0;
- int scale, shift_amount, startword;
- uint32_t temp;
- uint64_t off8, mem_crb, tmpw, word[2] = {0, 0};
-
- /*
- * If not MN, go check for MS or invalid.
- */
- if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX)
- mem_crb = QLA82XX_CRB_QDR_NET;
- else {
- mem_crb = QLA82XX_CRB_DDR_NET;
- if (qla82xx_pci_mem_bound_check(ha, off, size) == 0)
- return qla82xx_pci_mem_write_direct(ha,
- off, data, size);
- }
-
- off0 = off & 0x7;
- sz[0] = (size < (8 - off0)) ? size : (8 - off0);
- sz[1] = size - sz[0];
-
- off8 = off & 0xfffffff0;
- loop = (((off & 0xf) + size - 1) >> 4) + 1;
- shift_amount = 4;
- scale = 2;
- startword = (off & 0xf)/8;
-
- for (i = 0; i < loop; i++) {
- if (qla82xx_pci_mem_read_2M(ha, off8 +
- (i << shift_amount), &word[i * scale], 8))
- return -1;
- }
-
- switch (size) {
- case 1:
- tmpw = *((uint8_t *)data);
- break;
- case 2:
- tmpw = *((uint16_t *)data);
- break;
- case 4:
- tmpw = *((uint32_t *)data);
- break;
- case 8:
- default:
- tmpw = *((uint64_t *)data);
- break;
- }
-
- if (sz[0] == 8) {
- word[startword] = tmpw;
- } else {
- word[startword] &=
- ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8));
- word[startword] |= tmpw << (off0 * 8);
- }
- if (sz[1] != 0) {
- word[startword+1] &= ~(~0ULL << (sz[1] * 8));
- word[startword+1] |= tmpw >> (sz[0] * 8);
- }
-
- /*
- * don't lock here - write_wx gets the lock if each time
- * write_lock_irqsave(&adapter->adapter_lock, flags);
- * netxen_nic_pci_change_crbwindow_128M(adapter, 0);
- */
- for (i = 0; i < loop; i++) {
- temp = off8 + (i << shift_amount);
- qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_LO, temp);
- temp = 0;
- qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_HI, temp);
- temp = word[i * scale] & 0xffffffff;
- qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_LO, temp);
- temp = (word[i * scale] >> 32) & 0xffffffff;
- qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_HI, temp);
- temp = word[i*scale + 1] & 0xffffffff;
- qla82xx_wr_32(ha, mem_crb +
- MIU_TEST_AGT_WRDATA_UPPER_LO, temp);
- temp = (word[i*scale + 1] >> 32) & 0xffffffff;
- qla82xx_wr_32(ha, mem_crb +
- MIU_TEST_AGT_WRDATA_UPPER_HI, temp);
-
- temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE;
- qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp);
- temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE;
- qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp);
-
- for (j = 0; j < MAX_CTL_CHECK; j++) {
- temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL);
- if ((temp & MIU_TA_CTL_BUSY) == 0)
- break;
- }
-
- if (j >= MAX_CTL_CHECK) {
- if (printk_ratelimit())
- dev_err(&ha->pdev->dev,
- "failed to write through agent\n");
- ret = -1;
- break;
- }
- }
-
- return ret;
- }
static struct qla82xx_uri_table_desc *
qla82xx_get_table_desc(const u8 *unirom, int section)
ha->isp_ops->disable_intrs(ha);
}
- int qla82xx_fw_load_from_blob(struct qla_hw_data *ha)
+ static int
+ qla82xx_fw_load_from_blob(struct qla_hw_data *ha)
{
u64 *ptr64;
u32 i, flashaddr, size;
return 0;
}
- int qla82xx_check_cmdpeg_state(struct qla_hw_data *ha)
+ static int
+ qla82xx_check_cmdpeg_state(struct qla_hw_data *ha)
{
u32 val = 0;
int retries = 60;
return QLA_FUNCTION_FAILED;
}
- int qla82xx_check_rcvpeg_state(struct qla_hw_data *ha)
+ static int
+ qla82xx_check_rcvpeg_state(struct qla_hw_data *ha)
{
u32 val = 0;
int retries = 60;
* @ha: SCSI driver HA context
* @mb0: Mailbox0 register
*/
- void
+ static void
qla82xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
{
uint16_t cnt;
ha->nx_legacy_intr.pci_int_reg = nx_legacy_intr->pci_int_reg;
}
- static inline void
+ inline void
qla82xx_set_drv_active(scsi_qla_host_t *vha)
{
uint32_t drv_active;
/* If reset value is all FF's, initialize DRV_ACTIVE */
if (drv_active == 0xffffffff) {
- qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE,
+ QLA82XX_DRV_NOT_ACTIVE);
drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
}
- drv_active |= (1 << (ha->portnum * 4));
+ drv_active |= (QLA82XX_DRV_ACTIVE << (ha->portnum * 4));
qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
}
uint32_t drv_active;
drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
- drv_active &= ~(1 << (ha->portnum * 4));
+ drv_active &= ~(QLA82XX_DRV_ACTIVE << (ha->portnum * 4));
qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
}
int rval;
drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
- rval = drv_state & (1 << (ha->portnum * 4));
+ rval = drv_state & (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
return rval;
}
/* If reset value is all FF's, initialize DRV_STATE */
if (drv_state == 0xffffffff) {
- qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, QLA82XX_DRVST_NOT_RDY);
drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
}
drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state);
}
- int qla82xx_load_fw(scsi_qla_host_t *vha)
+ static int
+ qla82xx_load_fw(scsi_qla_host_t *vha)
{
int rst;
struct fw_blob *blob;
return QLA_FUNCTION_FAILED;
}
- static int
+ int
qla82xx_start_firmware(scsi_qla_host_t *vha)
{
int pcie_cap;
struct qla_hw_data *ha = vha->hw;
/* scrub dma mask expansion register */
- qla82xx_wr_32(ha, CRB_DMA_SHIFT, 0x55555555);
+ qla82xx_wr_32(ha, CRB_DMA_SHIFT, QLA82XX_DMA_SHIFT_VALUE);
/* Put both the PEG CMD and RCV PEG to default state
* of 0 before resetting the hardware
sufficient_dsds:
req_cnt = 1;
+ if (req->cnt < (req_cnt + 2)) {
+ cnt = (uint16_t)RD_REG_DWORD_RELAXED(
+ ®->req_q_out[0]);
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
+ else
+ req->cnt = req->length -
+ (req->ring_index - cnt);
+ }
+
+ if (req->cnt < (req_cnt + 2))
+ goto queuing_error;
+
ctx = sp->ctx = mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
if (!sp->ctx) {
DEBUG(printk(KERN_INFO
return QLA_FUNCTION_FAILED;
}
- uint32_t *
+ static uint32_t *
qla82xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
uint32_t length)
{
return dwptr;
}
- int
+ static int
qla82xx_unprotect_flash(struct qla_hw_data *ha)
{
int ret;
return ret;
}
- int
+ static int
qla82xx_protect_flash(struct qla_hw_data *ha)
{
int ret;
return ret;
}
- int
+ static int
qla82xx_erase_sector(struct qla_hw_data *ha, int addr)
{
int ret = 0;
}
}
+ void qla82xx_rom_lock_recovery(struct qla_hw_data *ha)
+ {
+ if (qla82xx_rom_lock(ha))
+ /* Someone else is holding the lock. */
+ qla_printk(KERN_INFO, ha, "Resetting rom_lock\n");
+
+ /*
+ * Either we got the lock, or someone
+ * else died while holding it.
+ * In either case, unlock.
+ */
+ qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+ }
+
/*
* qla82xx_device_bootstrap
* Initialize device, set DEV_READY, start fw
static int
qla82xx_device_bootstrap(scsi_qla_host_t *vha)
{
- int rval, i, timeout;
+ int rval = QLA_SUCCESS;
+ int i, timeout;
uint32_t old_count, count;
struct qla_hw_data *ha = vha->hw;
+ int need_reset = 0, peg_stuck = 1;
- if (qla82xx_need_reset(ha))
- goto dev_initialize;
+ need_reset = qla82xx_need_reset(ha);
old_count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
if (count != old_count)
+ peg_stuck = 0;
+ }
+
+ if (need_reset) {
+ /* We are trying to perform a recovery here. */
+ if (peg_stuck)
+ qla82xx_rom_lock_recovery(ha);
+ goto dev_initialize;
+ } else {
+ /* Start of day for this ha context. */
+ if (peg_stuck) {
+ /* Either we are the first or recovery in progress. */
+ qla82xx_rom_lock_recovery(ha);
+ goto dev_initialize;
+ } else
+ /* Firmware already running. */
goto dev_ready;
}
+ return rval;
+
dev_initialize:
/* set to DEV_INITIALIZING */
qla_printk(KERN_INFO, ha, "HW State: INITIALIZING\n");
struct qla_hw_data *ha = vha->hw;
fw_heartbeat_counter = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
+ /* all 0xff, assume AER/EEH in progress, ignore */
+ if (fw_heartbeat_counter == 0xffffffff)
+ return;
if (vha->fw_heartbeat_counter == fw_heartbeat_counter) {
vha->seconds_since_last_heartbeat++;
/* FW not alive after 2 seconds */
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
qla2xxx_wake_dpc(vha);
+ ha->flags.fw_hung = 1;
if (ha->flags.mbox_busy) {
- ha->flags.fw_hung = 1;
ha->flags.mbox_int = 1;
DEBUG2(qla_printk(KERN_ERR, ha,
- "Due to fw hung, doing premature "
- "completion of mbx command\n"));
- complete(&ha->mbx_intr_comp);
+ "Due to fw hung, doing premature "
+ "completion of mbx command\n"));
+ if (test_bit(MBX_INTR_WAIT,
+ &ha->mbx_cmd_flags))
+ complete(&ha->mbx_intr_comp);
}
}
- }
+ } else
+ vha->seconds_since_last_heartbeat = 0;
vha->fw_heartbeat_counter = fw_heartbeat_counter;
}
"%s(): Adapter reset needed!\n", __func__);
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
+ ha->flags.fw_hung = 1;
if (ha->flags.mbox_busy) {
- ha->flags.fw_hung = 1;
ha->flags.mbox_int = 1;
DEBUG2(qla_printk(KERN_ERR, ha,
- "Need reset, doing premature "
- "completion of mbx command\n"));
- complete(&ha->mbx_intr_comp);
+ "Need reset, doing premature "
+ "completion of mbx command\n"));
+ if (test_bit(MBX_INTR_WAIT,
+ &ha->mbx_cmd_flags))
+ complete(&ha->mbx_intr_comp);
}
} else {
qla82xx_check_fw_alive(vha);
qla2xxx_slave_configure(struct scsi_device *sdev)
{
scsi_qla_host_t *vha = shost_priv(sdev->host);
- struct qla_hw_data *ha = vha->hw;
- struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
struct req_que *req = vha->req;
if (sdev->tagged_supported)
scsi_activate_tcq(sdev, req->max_q_depth);
else
scsi_deactivate_tcq(sdev, req->max_q_depth);
-
- rport->dev_loss_tmo = ha->port_down_retry_count;
-
return 0;
}
else
base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER +
base_vha->vp_idx;
- if (IS_QLA2100(ha))
- host->sg_tablesize = 32;
+
+ /* Set the SG table size based on ISP type */
+ if (!IS_FWI2_CAPABLE(ha)) {
+ if (IS_QLA2100(ha))
+ host->sg_tablesize = 32;
+ } else {
+ if (!IS_QLA82XX(ha))
+ host->sg_tablesize = QLA_SG_ALL;
+ }
+
host->max_id = max_id;
host->this_id = 255;
host->cmd_per_lun = 3;
static void
qla2x00_remove_one(struct pci_dev *pdev)
{
- scsi_qla_host_t *base_vha, *vha, *temp;
+ scsi_qla_host_t *base_vha, *vha;
struct qla_hw_data *ha;
+ unsigned long flags;
base_vha = pci_get_drvdata(pdev);
ha = base_vha->hw;
- list_for_each_entry_safe(vha, temp, &ha->vp_list, list) {
- if (vha && vha->fc_vport)
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vha, &ha->vp_list, list) {
+ atomic_inc(&vha->vref_count);
+
+ if (vha && vha->fc_vport) {
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
fc_vport_terminate(vha->fc_vport);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ }
+
+ atomic_dec(&vha->vref_count);
}
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
set_bit(UNLOADING, &base_vha->dpc_flags);
qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
{
struct qla_work_evt *e;
+ uint8_t bail;
+
+ QLA_VHA_MARK_BUSY(vha, bail);
+ if (bail)
+ return NULL;
e = kzalloc(sizeof(struct qla_work_evt), GFP_ATOMIC);
- if (!e)
+ if (!e) {
+ QLA_VHA_MARK_NOT_BUSY(vha);
return NULL;
+ }
INIT_LIST_HEAD(&e->list);
e->type = type;
}
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
+
+ /* For each work completed decrement vha ref count */
+ QLA_VHA_MARK_NOT_BUSY(vha);
}
}
struct qla_hw_data *ha = vha->hw;
struct req_que *req;
+ if (ha->flags.eeh_busy) {
+ qla2x00_restart_timer(vha, WATCH_INTERVAL);
+ return;
+ }
+
if (IS_QLA82XX(ha))
qla82xx_watchdog(vha);
return PCI_ERS_RESULT_CAN_RECOVER;
case pci_channel_io_frozen:
ha->flags.eeh_busy = 1;
+ /* For ISP82XX complete any pending mailbox cmd */
+ if (IS_QLA82XX(ha)) {
+ ha->flags.fw_hung = 1;
+ if (ha->flags.mbox_busy) {
+ ha->flags.mbox_int = 1;
+ DEBUG2(qla_printk(KERN_ERR, ha,
+ "Due to pci channel io frozen, doing premature "
+ "completion of mbx command\n"));
+ complete(&ha->mbx_intr_comp);
+ }
+ }
qla2x00_free_irqs(vha);
pci_disable_device(pdev);
+ /* Return back all IOs */
+ qla2x00_abort_all_cmds(vha, DID_RESET << 16);
return PCI_ERS_RESULT_NEED_RESET;
case pci_channel_io_perm_failure:
ha->flags.pci_channel_io_perm_failure = 1;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
+ if (IS_QLA82XX(ha))
+ return PCI_ERS_RESULT_RECOVERED;
+
spin_lock_irqsave(&ha->hardware_lock, flags);
if (IS_QLA2100(ha) || IS_QLA2200(ha)){
stat = RD_REG_DWORD(®->hccr);
return PCI_ERS_RESULT_RECOVERED;
}
+ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha)
+ {
+ uint32_t rval = QLA_FUNCTION_FAILED;
+ uint32_t drv_active = 0;
+ struct qla_hw_data *ha = base_vha->hw;
+ int fn;
+ struct pci_dev *other_pdev = NULL;
+
+ DEBUG17(qla_printk(KERN_INFO, ha,
+ "scsi(%ld): In qla82xx_error_recovery\n", base_vha->host_no));
+
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+
+ if (base_vha->flags.online) {
+ /* Abort all outstanding commands,
+ * so as to be requeued later */
+ qla2x00_abort_isp_cleanup(base_vha);
+ }
+
+
+ fn = PCI_FUNC(ha->pdev->devfn);
+ while (fn > 0) {
+ fn--;
+ DEBUG17(qla_printk(KERN_INFO, ha,
+ "Finding pci device at function = 0x%x\n", fn));
+ other_pdev =
+ pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus),
+ ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn),
+ fn));
+
+ if (!other_pdev)
+ continue;
+ if (atomic_read(&other_pdev->enable_cnt)) {
+ DEBUG17(qla_printk(KERN_INFO, ha,
+ "Found PCI func availabe and enabled at 0x%x\n",
+ fn));
+ pci_dev_put(other_pdev);
+ break;
+ }
+ pci_dev_put(other_pdev);
+ }
+
+ if (!fn) {
+ /* Reset owner */
+ DEBUG17(qla_printk(KERN_INFO, ha,
+ "This devfn is reset owner = 0x%x\n", ha->pdev->devfn));
+ qla82xx_idc_lock(ha);
+
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_INITIALIZING);
+
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION,
+ QLA82XX_IDC_VERSION);
+
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ DEBUG17(qla_printk(KERN_INFO, ha,
+ "drv_active = 0x%x\n", drv_active));
+
+ qla82xx_idc_unlock(ha);
+ /* Reset if device is not already reset
+ * drv_active would be 0 if a reset has already been done
+ */
+ if (drv_active)
+ rval = qla82xx_start_firmware(base_vha);
+ else
+ rval = QLA_SUCCESS;
+ qla82xx_idc_lock(ha);
+
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_INFO, ha, "HW State: FAILED\n");
+ qla82xx_clear_drv_active(ha);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_FAILED);
+ } else {
+ qla_printk(KERN_INFO, ha, "HW State: READY\n");
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_READY);
+ qla82xx_idc_unlock(ha);
+ ha->flags.fw_hung = 0;
+ rval = qla82xx_restart_isp(base_vha);
+ qla82xx_idc_lock(ha);
+ /* Clear driver state register */
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0);
+ qla82xx_set_drv_active(base_vha);
+ }
+ qla82xx_idc_unlock(ha);
+ } else {
+ DEBUG17(qla_printk(KERN_INFO, ha,
+ "This devfn is not reset owner = 0x%x\n", ha->pdev->devfn));
+ if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==
+ QLA82XX_DEV_READY)) {
+ ha->flags.fw_hung = 0;
+ rval = qla82xx_restart_isp(base_vha);
+ qla82xx_idc_lock(ha);
+ qla82xx_set_drv_active(base_vha);
+ qla82xx_idc_unlock(ha);
+ }
+ }
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+
+ return rval;
+ }
+
static pci_ers_result_t
qla2xxx_pci_slot_reset(struct pci_dev *pdev)
{
if (rc) {
qla_printk(KERN_WARNING, ha,
"Can't re-enable PCI device after reset.\n");
- return ret;
+ goto exit_slot_reset;
}
rsp = ha->rsp_q_map[0];
if (qla2x00_request_irqs(ha, rsp))
- return ret;
+ goto exit_slot_reset;
if (ha->isp_ops->pci_config(base_vha))
- return ret;
+ goto exit_slot_reset;
+
+ if (IS_QLA82XX(ha)) {
+ if (qla82xx_error_recovery(base_vha) == QLA_SUCCESS) {
+ ret = PCI_ERS_RESULT_RECOVERED;
+ goto exit_slot_reset;
+ } else
+ goto exit_slot_reset;
+ }
while (ha->flags.mbox_busy && retries--)
msleep(1000);
clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ exit_slot_reset:
DEBUG17(qla_printk(KERN_WARNING, ha,
"slot_reset-return:ret=%x\n", ret));
static struct file_operations apidev_fops = {
.owner = THIS_MODULE,
+ .llseek = noop_llseek,
};
/**
static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq)
{
- rq->timeout = SD_TIMEOUT;
+ rq->timeout = SD_FLUSH_TIMEOUT;
rq->retries = SD_MAX_RETRIES;
rq->cmd[0] = SYNCHRONIZE_CACHE;
rq->cmd_len = 10;
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
- if (atomic_dec_return(&sdkp->openers) && sdev->removable) {
+ if (atomic_dec_return(&sdkp->openers) == 0 && sdev->removable) {
if (scsi_block_when_processing_errors(sdev))
scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
}
* flush everything.
*/
res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+ SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL);
if (res == 0)
break;
}
}
/* Logical blocks per physical block exponent */
- sdkp->hw_sector_size = (1 << (buffer[13] & 0xf)) * sector_size;
+ sdkp->physical_block_size = (1 << (buffer[13] & 0xf)) * sector_size;
/* Lowest aligned logical block */
alignment = ((buffer[14] & 0x3f) << 8 | buffer[15]) * sector_size;
struct request_queue *q = sdp->request_queue;
sdkp->thin_provisioning = 1;
- q->limits.discard_granularity = sdkp->hw_sector_size;
+ q->limits.discard_granularity = sdkp->physical_block_size;
q->limits.max_discard_sectors = 0xffffffff;
if (buffer[14] & 0x40) /* TPRZ */
}
sdkp->capacity = lba + 1;
- sdkp->hw_sector_size = sector_size;
+ sdkp->physical_block_size = sector_size;
return sector_size;
}
(unsigned long long)sdkp->capacity,
sector_size, cap_str_10, cap_str_2);
- if (sdkp->hw_sector_size != sector_size)
+ if (sdkp->physical_block_size != sector_size)
sd_printk(KERN_NOTICE, sdkp,
"%u-byte physical blocks\n",
- sdkp->hw_sector_size);
+ sdkp->physical_block_size);
}
}
else if (sector_size == 256)
sdkp->capacity >>= 1;
- blk_queue_physical_block_size(sdp->request_queue, sdkp->hw_sector_size);
+ blk_queue_physical_block_size(sdp->request_queue,
+ sdkp->physical_block_size);
sdkp->device->sector_size = sector_size;
}
lba_count = get_unaligned_be32(&buffer[20]);
desc_count = get_unaligned_be32(&buffer[24]);
- if (lba_count) {
- q->limits.max_discard_sectors =
- lba_count * sector_sz >> 9;
-
- if (desc_count)
+ if (lba_count && desc_count) {
+ if (sdkp->tpvpd && !sdkp->tpu)
+ sdkp->unmap = 0;
+ else
sdkp->unmap = 1;
}
+ if (sdkp->tpvpd && !sdkp->tpu && !sdkp->tpws) {
+ sd_printk(KERN_ERR, sdkp, "Thin provisioning is " \
+ "enabled but neither TPU, nor TPWS are " \
+ "set. Disabling discard!\n");
+ goto out;
+ }
+
+ if (lba_count)
+ q->limits.max_discard_sectors =
+ lba_count * sector_sz >> 9;
+
granularity = get_unaligned_be32(&buffer[28]);
if (granularity)
kfree(buffer);
}
+ /**
+ * sd_read_thin_provisioning - Query thin provisioning VPD page
+ * @disk: disk to query
+ */
+ static void sd_read_thin_provisioning(struct scsi_disk *sdkp)
+ {
+ unsigned char *buffer;
+ const int vpd_len = 8;
+
+ if (sdkp->thin_provisioning == 0)
+ return;
+
+ buffer = kmalloc(vpd_len, GFP_KERNEL);
+
+ if (!buffer || scsi_get_vpd_page(sdkp->device, 0xb2, buffer, vpd_len))
+ goto out;
+
+ sdkp->tpvpd = 1;
+ sdkp->tpu = (buffer[5] >> 7) & 1; /* UNMAP */
+ sdkp->tpws = (buffer[5] >> 6) & 1; /* WRITE SAME(16) with UNMAP */
+
+ out:
+ kfree(buffer);
+ }
+
static int sd_try_extended_inquiry(struct scsi_device *sdp)
{
/*
struct scsi_disk *sdkp = scsi_disk(disk);
struct scsi_device *sdp = sdkp->device;
unsigned char *buffer;
- unsigned ordered;
+ unsigned flush = 0;
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
"sd_revalidate_disk\n"));
sd_read_capacity(sdkp, buffer);
if (sd_try_extended_inquiry(sdp)) {
+ sd_read_thin_provisioning(sdkp);
sd_read_block_limits(sdkp);
sd_read_block_characteristics(sdkp);
}
/*
* We now have all cache related info, determine how we deal
- * with ordered requests. Note that as the current SCSI
- * dispatch function can alter request order, we cannot use
- * QUEUE_ORDERED_TAG_* even when ordered tag is supported.
+ * with flush requests.
*/
- if (sdkp->WCE)
- ordered = sdkp->DPOFUA
- ? QUEUE_ORDERED_DRAIN_FUA : QUEUE_ORDERED_DRAIN_FLUSH;
- else
- ordered = QUEUE_ORDERED_DRAIN;
+ if (sdkp->WCE) {
+ flush |= REQ_FLUSH;
+ if (sdkp->DPOFUA)
+ flush |= REQ_FUA;
+ }
- blk_queue_ordered(sdkp->disk->queue, ordered);
+ blk_queue_flush(sdkp->disk->queue, flush);
set_capacity(disk, sdkp->capacity);
kfree(buffer);
index = sdkp->index;
dev = &sdp->sdev_gendev;
- if (index < SD_MAX_DISKS) {
- gd->major = sd_major((index & 0xf0) >> 4);
- gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
- gd->minors = SD_MINORS;
- }
+ gd->major = sd_major((index & 0xf0) >> 4);
+ gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
+ gd->minors = SD_MINORS;
+
gd->fops = &sd_fops;
gd->private_data = &sdkp->driver;
gd->queue = sdkp->device->request_queue;
if (error)
goto out_put;
+ if (index >= SD_MAX_DISKS) {
+ error = -ENODEV;
+ sdev_printk(KERN_WARNING, sdp, "SCSI disk (sd) name space exhausted.\n");
+ goto out_free_index;
+ }
+
error = sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN);
if (error)
goto out_free_index;
static void sd_print_sense_hdr(struct scsi_disk *sdkp,
struct scsi_sense_hdr *sshdr)
{
- sd_printk(KERN_INFO, sdkp, "");
+ sd_printk(KERN_INFO, sdkp, " ");
scsi_show_sense_hdr(sshdr);
- sd_printk(KERN_INFO, sdkp, "");
+ sd_printk(KERN_INFO, sdkp, " ");
scsi_show_extd_sense(sshdr->asc, sshdr->ascq);
}
static void sd_print_result(struct scsi_disk *sdkp, int result)
{
- sd_printk(KERN_INFO, sdkp, "");
+ sd_printk(KERN_INFO, sdkp, " ");
scsi_show_result(result);
}
Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
Michael Schaefer, J"org Weule, and Eric Youngdale.
- Copyright 1992 - 2008 Kai Makisara
+ Copyright 1992 - 2010 Kai Makisara
email Kai.Makisara@kolumbus.fi
Some small formal changes - aeb, 950809
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
*/
- static const char *verstr = "20081215";
+ static const char *verstr = "20100829";
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/mutex.h>
-#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
#include "st_options.h"
#include "st.h"
+static DEFINE_MUTEX(st_mutex);
static int buffer_kbs;
static int max_sg_segs;
static int try_direct_io = TRY_DIRECT_IO;
int dev = TAPE_NR(inode);
char *name;
- lock_kernel();
+ mutex_lock(&st_mutex);
/*
* We really want to do nonseekable_open(inode, filp); here, but some
* versions of tar incorrectly call lseek on tapes and bail out if that
filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
if (!(STp = scsi_tape_get(dev))) {
- unlock_kernel();
+ mutex_unlock(&st_mutex);
return -ENXIO;
}
if (STp->in_use) {
write_unlock(&st_dev_arr_lock);
scsi_tape_put(STp);
- unlock_kernel();
+ mutex_unlock(&st_mutex);
DEB( printk(ST_DEB_MSG "%s: Device already in use.\n", name); )
return (-EBUSY);
}
retval = (-EIO);
goto err_out;
}
- unlock_kernel();
+ mutex_unlock(&st_mutex);
return 0;
err_out:
normalize_buffer(STp->buffer);
STp->in_use = 0;
scsi_tape_put(STp);
- unlock_kernel();
+ mutex_unlock(&st_mutex);
return retval;
}
}
break;
case MTWEOF:
+ case MTWEOFI:
case MTWSM:
if (STp->write_prot)
return (-EACCES);
cmd[0] = WRITE_FILEMARKS;
if (cmd_in == MTWSM)
cmd[1] = 2;
+ if (cmd_in == MTWEOFI)
+ cmd[1] |= 1;
cmd[2] = (arg >> 16);
cmd[3] = (arg >> 8);
cmd[4] = arg;
timeout = STp->device->request_queue->rq_timeout;
DEBC(
- if (cmd_in == MTWEOF)
+ if (cmd_in != MTWSM)
printk(ST_DEB_MSG "%s: Writing %d filemarks.\n", name,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
else
else if (chg_eof)
STps->eof = ST_NOEOF;
- if (cmd_in == MTWEOF)
- STps->rw = ST_IDLE;
+ if (cmd_in == MTWEOF || cmd_in == MTWEOFI)
+ STps->rw = ST_IDLE; /* prevent automatic WEOF at close */
} else { /* SCSI command was not completely successful. Don't return
from this block without releasing the SCSI command block! */
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
else
undone = 0;
- if (cmd_in == MTWEOF &&
+ if ((cmd_in == MTWEOF || cmd_in == MTWEOFI) &&
cmdstatp->have_sense &&
(cmdstatp->flags & SENSE_EOM)) {
if (cmdstatp->sense_hdr.sense_key == NO_SENSE ||