scsi: ipr: Add asynchronous error notification

This patch implements functions for pushing HCAM (host controlled
asynchronous messages) error buffers to userspace through sysfs
attributes.  Reads to the "async_err_log" attribute will result in a
single HCAM buffer being copied to userspace; one can process the next
HCAM buffer by writing any string to the same attribute.

A new list was added to the ioa_cfg structure to store the HCAM buffers
for later reporting. We also send a KOBJ_CHANGE event whenever a new
HCAM buffer is made available to userspace.

Signed-off-by: Heitor Ricardo Alves de Siqueira <halves@linux.vnet.ibm.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@linux.vnet.ibm.com>
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Brian King 2016-08-24 12:56:51 -05:00 коммит произвёл Martin K. Petersen
Родитель 6328d9030f
Коммит afc3f83cb4
2 изменённых файлов: 118 добавлений и 14 удалений

Просмотреть файл

@ -1473,7 +1473,7 @@ static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd)
struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb;
u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc); u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
list_del(&hostrcb->queue); list_del_init(&hostrcb->queue);
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
if (ioasc) { if (ioasc) {
@ -2552,6 +2552,23 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg,
} }
} }
static struct ipr_hostrcb *ipr_get_free_hostrcb(struct ipr_ioa_cfg *ioa)
{
struct ipr_hostrcb *hostrcb;
hostrcb = list_first_entry_or_null(&ioa->hostrcb_free_q,
struct ipr_hostrcb, queue);
if (unlikely(!hostrcb)) {
dev_info(&ioa->pdev->dev, "Reclaiming async error buffers.");
hostrcb = list_first_entry_or_null(&ioa->hostrcb_report_q,
struct ipr_hostrcb, queue);
}
list_del_init(&hostrcb->queue);
return hostrcb;
}
/** /**
* ipr_process_error - Op done function for an adapter error log. * ipr_process_error - Op done function for an adapter error log.
* @ipr_cmd: ipr command struct * @ipr_cmd: ipr command struct
@ -2569,13 +2586,14 @@ static void ipr_process_error(struct ipr_cmnd *ipr_cmd)
struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb;
u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc); u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc);
u32 fd_ioasc; u32 fd_ioasc;
char *envp[] = { "ASYNC_ERR_LOG=1", NULL };
if (ioa_cfg->sis64) if (ioa_cfg->sis64)
fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error64.fd_ioasc); fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error64.fd_ioasc);
else else
fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error.fd_ioasc); fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error.fd_ioasc);
list_del(&hostrcb->queue); list_del_init(&hostrcb->queue);
list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
if (!ioasc) { if (!ioasc) {
@ -2588,6 +2606,10 @@ static void ipr_process_error(struct ipr_cmnd *ipr_cmd)
"Host RCB failed with IOASC: 0x%08X\n", ioasc); "Host RCB failed with IOASC: 0x%08X\n", ioasc);
} }
list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_report_q);
hostrcb = ipr_get_free_hostrcb(ioa_cfg);
kobject_uevent_env(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE, envp);
ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb); ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb);
} }
@ -4095,6 +4117,64 @@ static struct device_attribute ipr_ioa_fw_type_attr = {
.show = ipr_show_fw_type .show = ipr_show_fw_type
}; };
static ssize_t ipr_read_async_err_log(struct file *filep, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct device *cdev = container_of(kobj, struct device, kobj);
struct Scsi_Host *shost = class_to_shost(cdev);
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
struct ipr_hostrcb *hostrcb;
unsigned long lock_flags = 0;
int ret;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q,
struct ipr_hostrcb, queue);
if (!hostrcb) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return 0;
}
ret = memory_read_from_buffer(buf, count, &off, &hostrcb->hcam,
sizeof(hostrcb->hcam));
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return ret;
}
static ssize_t ipr_next_async_err_log(struct file *filep, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct device *cdev = container_of(kobj, struct device, kobj);
struct Scsi_Host *shost = class_to_shost(cdev);
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
struct ipr_hostrcb *hostrcb;
unsigned long lock_flags = 0;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q,
struct ipr_hostrcb, queue);
if (!hostrcb) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return count;
}
/* Reclaim hostrcb before exit */
list_move_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return count;
}
static struct bin_attribute ipr_ioa_async_err_log = {
.attr = {
.name = "async_err_log",
.mode = S_IRUGO | S_IWUSR,
},
.size = 0,
.read = ipr_read_async_err_log,
.write = ipr_next_async_err_log
};
static struct device_attribute *ipr_ioa_attrs[] = { static struct device_attribute *ipr_ioa_attrs[] = {
&ipr_fw_version_attr, &ipr_fw_version_attr,
&ipr_log_level_attr, &ipr_log_level_attr,
@ -7026,8 +7106,7 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd)
{ {
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
struct ipr_resource_entry *res; struct ipr_resource_entry *res;
struct ipr_hostrcb *hostrcb, *temp; int j;
int i = 0, j;
ENTER; ENTER;
ioa_cfg->in_reset_reload = 0; ioa_cfg->in_reset_reload = 0;
@ -7048,12 +7127,16 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd)
} }
schedule_work(&ioa_cfg->work_q); schedule_work(&ioa_cfg->work_q);
list_for_each_entry_safe(hostrcb, temp, &ioa_cfg->hostrcb_free_q, queue) { for (j = 0; j < IPR_NUM_HCAMS; j++) {
list_del(&hostrcb->queue); list_del_init(&ioa_cfg->hostrcb[j]->queue);
if (i++ < IPR_NUM_LOG_HCAMS) if (j < IPR_NUM_LOG_HCAMS)
ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb); ipr_send_hcam(ioa_cfg,
IPR_HCAM_CDB_OP_CODE_LOG_DATA,
ioa_cfg->hostrcb[j]);
else else
ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); ipr_send_hcam(ioa_cfg,
IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE,
ioa_cfg->hostrcb[j]);
} }
scsi_report_bus_reset(ioa_cfg->host, IPR_VSET_BUS); scsi_report_bus_reset(ioa_cfg->host, IPR_VSET_BUS);
@ -8335,7 +8418,7 @@ static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg)
hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next, hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next,
struct ipr_hostrcb, queue); struct ipr_hostrcb, queue);
list_del(&hostrcb->queue); list_del_init(&hostrcb->queue);
memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam)); memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam));
rc = ipr_get_ldump_data_section(ioa_cfg, rc = ipr_get_ldump_data_section(ioa_cfg,
@ -9332,7 +9415,7 @@ static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg)
dma_free_coherent(&ioa_cfg->pdev->dev, ioa_cfg->cfg_table_size, dma_free_coherent(&ioa_cfg->pdev->dev, ioa_cfg->cfg_table_size,
ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma); ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma);
for (i = 0; i < IPR_NUM_HCAMS; i++) { for (i = 0; i < IPR_MAX_HCAMS; i++) {
dma_free_coherent(&ioa_cfg->pdev->dev, dma_free_coherent(&ioa_cfg->pdev->dev,
sizeof(struct ipr_hostrcb), sizeof(struct ipr_hostrcb),
ioa_cfg->hostrcb[i], ioa_cfg->hostrcb[i],
@ -9572,7 +9655,7 @@ static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg)
if (!ioa_cfg->u.cfg_table) if (!ioa_cfg->u.cfg_table)
goto out_free_host_rrq; goto out_free_host_rrq;
for (i = 0; i < IPR_NUM_HCAMS; i++) { for (i = 0; i < IPR_MAX_HCAMS; i++) {
ioa_cfg->hostrcb[i] = dma_alloc_coherent(&pdev->dev, ioa_cfg->hostrcb[i] = dma_alloc_coherent(&pdev->dev,
sizeof(struct ipr_hostrcb), sizeof(struct ipr_hostrcb),
&ioa_cfg->hostrcb_dma[i], &ioa_cfg->hostrcb_dma[i],
@ -9714,6 +9797,7 @@ static void ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
INIT_LIST_HEAD(&ioa_cfg->hostrcb_free_q); INIT_LIST_HEAD(&ioa_cfg->hostrcb_free_q);
INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q); INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q);
INIT_LIST_HEAD(&ioa_cfg->hostrcb_report_q);
INIT_LIST_HEAD(&ioa_cfg->free_res_q); INIT_LIST_HEAD(&ioa_cfg->free_res_q);
INIT_LIST_HEAD(&ioa_cfg->used_res_q); INIT_LIST_HEAD(&ioa_cfg->used_res_q);
INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread); INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread);
@ -10352,6 +10436,8 @@ static void ipr_remove(struct pci_dev *pdev)
&ipr_trace_attr); &ipr_trace_attr);
ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj, ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_dump_attr); &ipr_dump_attr);
sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_ioa_async_err_log);
scsi_remove_host(ioa_cfg->host); scsi_remove_host(ioa_cfg->host);
__ipr_remove(pdev); __ipr_remove(pdev);
@ -10400,10 +10486,25 @@ static int ipr_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
return rc; return rc;
} }
rc = sysfs_create_bin_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_ioa_async_err_log);
if (rc) {
ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_dump_attr);
ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_trace_attr);
scsi_remove_host(ioa_cfg->host);
__ipr_remove(pdev);
return rc;
}
rc = ipr_create_dump_file(&ioa_cfg->host->shost_dev.kobj, rc = ipr_create_dump_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_dump_attr); &ipr_dump_attr);
if (rc) { if (rc) {
sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_ioa_async_err_log);
ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj, ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj,
&ipr_trace_attr); &ipr_trace_attr);
scsi_remove_host(ioa_cfg->host); scsi_remove_host(ioa_cfg->host);

Просмотреть файл

@ -154,7 +154,9 @@
#define IPR_DEFAULT_MAX_ERROR_DUMP 984 #define IPR_DEFAULT_MAX_ERROR_DUMP 984
#define IPR_NUM_LOG_HCAMS 2 #define IPR_NUM_LOG_HCAMS 2
#define IPR_NUM_CFG_CHG_HCAMS 2 #define IPR_NUM_CFG_CHG_HCAMS 2
#define IPR_NUM_HCAM_QUEUE 12
#define IPR_NUM_HCAMS (IPR_NUM_LOG_HCAMS + IPR_NUM_CFG_CHG_HCAMS) #define IPR_NUM_HCAMS (IPR_NUM_LOG_HCAMS + IPR_NUM_CFG_CHG_HCAMS)
#define IPR_MAX_HCAMS (IPR_NUM_HCAMS + IPR_NUM_HCAM_QUEUE)
#define IPR_MAX_SIS64_TARGETS_PER_BUS 1024 #define IPR_MAX_SIS64_TARGETS_PER_BUS 1024
#define IPR_MAX_SIS64_LUNS_PER_TARGET 0xffffffff #define IPR_MAX_SIS64_LUNS_PER_TARGET 0xffffffff
@ -1532,10 +1534,11 @@ struct ipr_ioa_cfg {
char ipr_hcam_label[8]; char ipr_hcam_label[8];
#define IPR_HCAM_LABEL "hcams" #define IPR_HCAM_LABEL "hcams"
struct ipr_hostrcb *hostrcb[IPR_NUM_HCAMS]; struct ipr_hostrcb *hostrcb[IPR_MAX_HCAMS];
dma_addr_t hostrcb_dma[IPR_NUM_HCAMS]; dma_addr_t hostrcb_dma[IPR_MAX_HCAMS];
struct list_head hostrcb_free_q; struct list_head hostrcb_free_q;
struct list_head hostrcb_pending_q; struct list_head hostrcb_pending_q;
struct list_head hostrcb_report_q;
struct ipr_hrr_queue hrrq[IPR_MAX_HRRQ_NUM]; struct ipr_hrr_queue hrrq[IPR_MAX_HRRQ_NUM];
u32 hrrq_num; u32 hrrq_num;