Commit e36f6fe1 authored by Vaibhav Jain's avatar Vaibhav Jain Committed by Michael Ellerman

cxl: Export AFU error buffer via sysfs

Export the "AFU Error Buffer" via sysfs attribute (afu_err_buf). AFU
error buffer is used by the AFU to report application specific
errors. The contents of this buffer are AFU specific and are intended to
be interpreted by the application interacting with the afu.
Suggested-by: default avatarMichael Neuling <mikey@neuling.org>
Signed-off-by: default avatarVaibhav Jain <vaibhav@linux.vnet.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 27d4dc71
...@@ -6,6 +6,17 @@ Example: The real path of the attribute /sys/class/cxl/afu0.0s/irqs_max is ...@@ -6,6 +6,17 @@ Example: The real path of the attribute /sys/class/cxl/afu0.0s/irqs_max is
Slave contexts (eg. /sys/class/cxl/afu0.0s): Slave contexts (eg. /sys/class/cxl/afu0.0s):
What: /sys/class/cxl/<afu>/afu_err_buf
Date: September 2014
Contact: linuxppc-dev@lists.ozlabs.org
Description: read only
AFU Error Buffer contents. The contents of this file are
application specific and depends on the AFU being used.
Applications interacting with the AFU can use this attribute
to know about the current error condition and take appropriate
action like logging the event etc.
What: /sys/class/cxl/<afu>/irqs_max What: /sys/class/cxl/<afu>/irqs_max
Date: September 2014 Date: September 2014
Contact: linuxppc-dev@lists.ozlabs.org Contact: linuxppc-dev@lists.ozlabs.org
......
...@@ -360,6 +360,10 @@ struct cxl_afu { ...@@ -360,6 +360,10 @@ struct cxl_afu {
struct mutex spa_mutex; struct mutex spa_mutex;
spinlock_t afu_cntl_lock; spinlock_t afu_cntl_lock;
/* AFU error buffer fields and bin attribute for sysfs */
u64 eb_len, eb_offset;
struct bin_attribute attr_eb;
/* /*
* Only the first part of the SPA is used for the process element * Only the first part of the SPA is used for the process element
* linked list. The only other part that software needs to worry about * linked list. The only other part that software needs to worry about
...@@ -561,6 +565,9 @@ static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg ...@@ -561,6 +565,9 @@ static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg
u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off); u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off);
u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off); u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off);
ssize_t cxl_afu_read_err_buffer(struct cxl_afu *afu, char *buf,
loff_t off, size_t count);
struct cxl_calls { struct cxl_calls {
void (*cxl_slbia)(struct mm_struct *mm); void (*cxl_slbia)(struct mm_struct *mm);
......
...@@ -593,6 +593,22 @@ static int cxl_read_afu_descriptor(struct cxl_afu *afu) ...@@ -593,6 +593,22 @@ static int cxl_read_afu_descriptor(struct cxl_afu *afu)
afu->crs_len = AFUD_CR_LEN(val) * 256; afu->crs_len = AFUD_CR_LEN(val) * 256;
afu->crs_offset = AFUD_READ_CR_OFF(afu); afu->crs_offset = AFUD_READ_CR_OFF(afu);
/* eb_len is in multiple of 4K */
afu->eb_len = AFUD_EB_LEN(AFUD_READ_EB(afu)) * 4096;
afu->eb_offset = AFUD_READ_EB_OFF(afu);
/* eb_off is 4K aligned so lower 12 bits are always zero */
if (EXTRACT_PPC_BITS(afu->eb_offset, 0, 11) != 0) {
dev_warn(&afu->dev,
"Invalid AFU error buffer offset %Lx\n",
afu->eb_offset);
dev_info(&afu->dev,
"Ignoring AFU error buffer in the descriptor\n");
/* indicate that no afu buffer exists */
afu->eb_len = 0;
}
return 0; return 0;
} }
...@@ -672,6 +688,50 @@ static int sanitise_afu_regs(struct cxl_afu *afu) ...@@ -672,6 +688,50 @@ static int sanitise_afu_regs(struct cxl_afu *afu)
return 0; return 0;
} }
#define ERR_BUFF_MAX_COPY_SIZE PAGE_SIZE
/*
* afu_eb_read:
* Called from sysfs and reads the afu error info buffer. The h/w only supports
* 4/8 bytes aligned access. So in case the requested offset/count arent 8 byte
* aligned the function uses a bounce buffer which can be max PAGE_SIZE.
*/
ssize_t cxl_afu_read_err_buffer(struct cxl_afu *afu, char *buf,
loff_t off, size_t count)
{
loff_t aligned_start, aligned_end;
size_t aligned_length;
void *tbuf;
const void __iomem *ebuf = afu->afu_desc_mmio + afu->eb_offset;
if (count == 0 || off < 0 || (size_t)off >= afu->eb_len)
return 0;
/* calculate aligned read window */
count = min((size_t)(afu->eb_len - off), count);
aligned_start = round_down(off, 8);
aligned_end = round_up(off + count, 8);
aligned_length = aligned_end - aligned_start;
/* max we can copy in one read is PAGE_SIZE */
if (aligned_length > ERR_BUFF_MAX_COPY_SIZE) {
aligned_length = ERR_BUFF_MAX_COPY_SIZE;
count = ERR_BUFF_MAX_COPY_SIZE - (off & 0x7);
}
/* use bounce buffer for copy */
tbuf = (void *)__get_free_page(GFP_TEMPORARY);
if (!tbuf)
return -ENOMEM;
/* perform aligned read from the mmio region */
memcpy_fromio(tbuf, ebuf + aligned_start, aligned_length);
memcpy(buf, tbuf + (off & 0x7), count);
free_page((unsigned long)tbuf);
return count;
}
static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
{ {
struct cxl_afu *afu; struct cxl_afu *afu;
......
...@@ -356,6 +356,16 @@ static ssize_t api_version_compatible_show(struct device *device, ...@@ -356,6 +356,16 @@ static ssize_t api_version_compatible_show(struct device *device,
return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE); return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE);
} }
static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct cxl_afu *afu = to_cxl_afu(container_of(kobj,
struct device, kobj));
return cxl_afu_read_err_buffer(afu, buf, off, count);
}
static struct device_attribute afu_attrs[] = { static struct device_attribute afu_attrs[] = {
__ATTR_RO(mmio_size), __ATTR_RO(mmio_size),
__ATTR_RO(irqs_min), __ATTR_RO(irqs_min),
...@@ -534,6 +544,10 @@ void cxl_sysfs_afu_remove(struct cxl_afu *afu) ...@@ -534,6 +544,10 @@ void cxl_sysfs_afu_remove(struct cxl_afu *afu)
struct afu_config_record *cr, *tmp; struct afu_config_record *cr, *tmp;
int i; int i;
/* remove the err buffer bin attribute */
if (afu->eb_len)
device_remove_bin_file(&afu->dev, &afu->attr_eb);
for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) for (i = 0; i < ARRAY_SIZE(afu_attrs); i++)
device_remove_file(&afu->dev, &afu_attrs[i]); device_remove_file(&afu->dev, &afu_attrs[i]);
...@@ -555,6 +569,22 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu) ...@@ -555,6 +569,22 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu)
goto err; goto err;
} }
/* conditionally create the add the binary file for error info buffer */
if (afu->eb_len) {
afu->attr_eb.attr.name = "afu_err_buff";
afu->attr_eb.attr.mode = S_IRUGO;
afu->attr_eb.size = afu->eb_len;
afu->attr_eb.read = afu_eb_read;
rc = device_create_bin_file(&afu->dev, &afu->attr_eb);
if (rc) {
dev_err(&afu->dev,
"Unable to create eb attr for the afu. Err(%d)\n",
rc);
goto err;
}
}
for (i = 0; i < afu->crs_num; i++) { for (i = 0; i < afu->crs_num; i++) {
cr = cxl_sysfs_afu_new_cr(afu, i); cr = cxl_sysfs_afu_new_cr(afu, i);
if (IS_ERR(cr)) { if (IS_ERR(cr)) {
...@@ -570,6 +600,9 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu) ...@@ -570,6 +600,9 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu)
cxl_sysfs_afu_remove(afu); cxl_sysfs_afu_remove(afu);
return rc; return rc;
err: err:
/* reset the eb_len as we havent created the bin attr */
afu->eb_len = 0;
for (i--; i >= 0; i--) for (i--; i >= 0; i--)
device_remove_file(&afu->dev, &afu_attrs[i]); device_remove_file(&afu->dev, &afu_attrs[i]);
return rc; return rc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment