linux/drivers/misc/cxl/guest.c

1199 lines
27 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015 IBM Corp.
*/
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include "cxl.h"
#include "hcalls.h"
#include "trace.h"
#define CXL_ERROR_DETECTED_EVENT 1
#define CXL_SLOT_RESET_EVENT 2
#define CXL_RESUME_EVENT 3
static void pci_error_handlers(struct cxl_afu *afu,
int bus_error_event,
pci_channel_state_t state)
{
struct pci_dev *afu_dev;
if (afu->phb == NULL)
return;
list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
if (!afu_dev->driver)
continue;
switch (bus_error_event) {
case CXL_ERROR_DETECTED_EVENT:
afu_dev->error_state = state;
if (afu_dev->driver->err_handler &&
afu_dev->driver->err_handler->error_detected)
afu_dev->driver->err_handler->error_detected(afu_dev, state);
break;
case CXL_SLOT_RESET_EVENT:
afu_dev->error_state = state;
if (afu_dev->driver->err_handler &&
afu_dev->driver->err_handler->slot_reset)
afu_dev->driver->err_handler->slot_reset(afu_dev);
break;
case CXL_RESUME_EVENT:
if (afu_dev->driver->err_handler &&
afu_dev->driver->err_handler->resume)
afu_dev->driver->err_handler->resume(afu_dev);
break;
}
}
}
static irqreturn_t guest_handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr,
u64 errstat)
{
pr_devel("in %s\n", __func__);
dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat);
return cxl_ops->ack_irq(ctx, 0, errstat);
}
static ssize_t guest_collect_vpd(struct cxl *adapter, struct cxl_afu *afu,
void *buf, size_t len)
{
unsigned int entries, mod;
unsigned long **vpd_buf = NULL;
struct sg_list *le;
int rc = 0, i, tocopy;
u64 out = 0;
if (buf == NULL)
return -EINVAL;
/* number of entries in the list */
entries = len / SG_BUFFER_SIZE;
mod = len % SG_BUFFER_SIZE;
if (mod)
entries++;
if (entries > SG_MAX_ENTRIES) {
entries = SG_MAX_ENTRIES;
len = SG_MAX_ENTRIES * SG_BUFFER_SIZE;
mod = 0;
}
treewide: kzalloc() -> kcalloc() The kzalloc() function has a 2-factor argument form, kcalloc(). This patch replaces cases of: kzalloc(a * b, gfp) with: kcalloc(a * b, gfp) as well as handling cases of: kzalloc(a * b * c, gfp) with: kzalloc(array3_size(a, b, c), gfp) as it's slightly less ugly than: kzalloc_array(array_size(a, b), c, gfp) This does, however, attempt to ignore constant size factors like: kzalloc(4 * 1024, gfp) though any constants defined via macros get caught up in the conversion. Any factors with a sizeof() of "unsigned char", "char", and "u8" were dropped, since they're redundant. The Coccinelle script used for this was: // Fix redundant parens around sizeof(). @@ type TYPE; expression THING, E; @@ ( kzalloc( - (sizeof(TYPE)) * E + sizeof(TYPE) * E , ...) | kzalloc( - (sizeof(THING)) * E + sizeof(THING) * E , ...) ) // Drop single-byte sizes and redundant parens. @@ expression COUNT; typedef u8; typedef __u8; @@ ( kzalloc( - sizeof(u8) * (COUNT) + COUNT , ...) | kzalloc( - sizeof(__u8) * (COUNT) + COUNT , ...) | kzalloc( - sizeof(char) * (COUNT) + COUNT , ...) | kzalloc( - sizeof(unsigned char) * (COUNT) + COUNT , ...) | kzalloc( - sizeof(u8) * COUNT + COUNT , ...) | kzalloc( - sizeof(__u8) * COUNT + COUNT , ...) | kzalloc( - sizeof(char) * COUNT + COUNT , ...) | kzalloc( - sizeof(unsigned char) * COUNT + COUNT , ...) ) // 2-factor product with sizeof(type/expression) and identifier or constant. @@ type TYPE; expression THING; identifier COUNT_ID; constant COUNT_CONST; @@ ( - kzalloc + kcalloc ( - sizeof(TYPE) * (COUNT_ID) + COUNT_ID, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * COUNT_ID + COUNT_ID, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * (COUNT_CONST) + COUNT_CONST, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * COUNT_CONST + COUNT_CONST, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * (COUNT_ID) + COUNT_ID, sizeof(THING) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * COUNT_ID + COUNT_ID, sizeof(THING) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * (COUNT_CONST) + COUNT_CONST, sizeof(THING) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * COUNT_CONST + COUNT_CONST, sizeof(THING) , ...) ) // 2-factor product, only identifiers. @@ identifier SIZE, COUNT; @@ - kzalloc + kcalloc ( - SIZE * COUNT + COUNT, SIZE , ...) // 3-factor product with 1 sizeof(type) or sizeof(expression), with // redundant parens removed. @@ expression THING; identifier STRIDE, COUNT; type TYPE; @@ ( kzalloc( - sizeof(TYPE) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kzalloc( - sizeof(TYPE) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kzalloc( - sizeof(TYPE) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kzalloc( - sizeof(TYPE) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kzalloc( - sizeof(THING) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kzalloc( - sizeof(THING) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kzalloc( - sizeof(THING) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kzalloc( - sizeof(THING) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) ) // 3-factor product with 2 sizeof(variable), with redundant parens removed. @@ expression THING1, THING2; identifier COUNT; type TYPE1, TYPE2; @@ ( kzalloc( - sizeof(TYPE1) * sizeof(TYPE2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | kzalloc( - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | kzalloc( - sizeof(THING1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | kzalloc( - sizeof(THING1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | kzalloc( - sizeof(TYPE1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) | kzalloc( - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) ) // 3-factor product, only identifiers, with redundant parens removed. @@ identifier STRIDE, SIZE, COUNT; @@ ( kzalloc( - (COUNT) * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - COUNT * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - COUNT * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - (COUNT) * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - COUNT * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - (COUNT) * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - (COUNT) * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - COUNT * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) ) // Any remaining multi-factor products, first at least 3-factor products, // when they're not all constants... @@ expression E1, E2, E3; constant C1, C2, C3; @@ ( kzalloc(C1 * C2 * C3, ...) | kzalloc( - (E1) * E2 * E3 + array3_size(E1, E2, E3) , ...) | kzalloc( - (E1) * (E2) * E3 + array3_size(E1, E2, E3) , ...) | kzalloc( - (E1) * (E2) * (E3) + array3_size(E1, E2, E3) , ...) | kzalloc( - E1 * E2 * E3 + array3_size(E1, E2, E3) , ...) ) // And then all remaining 2 factors products when they're not all constants, // keeping sizeof() as the second factor argument. @@ expression THING, E1, E2; type TYPE; constant C1, C2, C3; @@ ( kzalloc(sizeof(THING) * C2, ...) | kzalloc(sizeof(TYPE) * C2, ...) | kzalloc(C1 * C2 * C3, ...) | kzalloc(C1 * C2, ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * (E2) + E2, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * E2 + E2, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * (E2) + E2, sizeof(THING) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * E2 + E2, sizeof(THING) , ...) | - kzalloc + kcalloc ( - (E1) * E2 + E1, E2 , ...) | - kzalloc + kcalloc ( - (E1) * (E2) + E1, E2 , ...) | - kzalloc + kcalloc ( - E1 * E2 + E1, E2 , ...) ) Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 21:03:40 +00:00
vpd_buf = kcalloc(entries, sizeof(unsigned long *), GFP_KERNEL);
if (!vpd_buf)
return -ENOMEM;
le = (struct sg_list *)get_zeroed_page(GFP_KERNEL);
if (!le) {
rc = -ENOMEM;
goto err1;
}
for (i = 0; i < entries; i++) {
vpd_buf[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL);
if (!vpd_buf[i]) {
rc = -ENOMEM;
goto err2;
}
le[i].phys_addr = cpu_to_be64(virt_to_phys(vpd_buf[i]));
le[i].len = cpu_to_be64(SG_BUFFER_SIZE);
if ((i == (entries - 1)) && mod)
le[i].len = cpu_to_be64(mod);
}
if (adapter)
rc = cxl_h_collect_vpd_adapter(adapter->guest->handle,
virt_to_phys(le), entries, &out);
else
rc = cxl_h_collect_vpd(afu->guest->handle, 0,
virt_to_phys(le), entries, &out);
pr_devel("length of available (entries: %i), vpd: %#llx\n",
entries, out);
if (!rc) {
/*
* hcall returns in 'out' the size of available VPDs.
* It fills the buffer with as much data as possible.
*/
if (out < len)
len = out;
rc = len;
if (out) {
for (i = 0; i < entries; i++) {
if (len < SG_BUFFER_SIZE)
tocopy = len;
else
tocopy = SG_BUFFER_SIZE;
memcpy(buf, vpd_buf[i], tocopy);
buf += tocopy;
len -= tocopy;
}
}
}
err2:
for (i = 0; i < entries; i++) {
if (vpd_buf[i])
free_page((unsigned long) vpd_buf[i]);
}
free_page((unsigned long) le);
err1:
kfree(vpd_buf);
return rc;
}
static int guest_get_irq_info(struct cxl_context *ctx, struct cxl_irq_info *info)
{
return cxl_h_collect_int_info(ctx->afu->guest->handle, ctx->process_token, info);
}
static irqreturn_t guest_psl_irq(int irq, void *data)
{
struct cxl_context *ctx = data;
struct cxl_irq_info irq_info;
int rc;
pr_devel("%d: received PSL interrupt %i\n", ctx->pe, irq);
rc = guest_get_irq_info(ctx, &irq_info);
if (rc) {
WARN(1, "Unable to get IRQ info: %i\n", rc);
return IRQ_HANDLED;
}
rc = cxl_irq_psl8(irq, ctx, &irq_info);
return rc;
}
static int afu_read_error_state(struct cxl_afu *afu, int *state_out)
{
u64 state;
int rc = 0;
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
if (!afu)
return -EIO;
rc = cxl_h_read_error_state(afu->guest->handle, &state);
if (!rc) {
WARN_ON(state != H_STATE_NORMAL &&
state != H_STATE_DISABLE &&
state != H_STATE_TEMP_UNAVAILABLE &&
state != H_STATE_PERM_UNAVAILABLE);
*state_out = state & 0xffffffff;
}
return rc;
}
static irqreturn_t guest_slice_irq_err(int irq, void *data)
{
struct cxl_afu *afu = data;
int rc;
u64 serr, afu_error, dsisr;
rc = cxl_h_get_fn_error_interrupt(afu->guest->handle, &serr);
if (rc) {
dev_crit(&afu->dev, "Couldn't read PSL_SERR_An: %d\n", rc);
return IRQ_HANDLED;
}
afu_error = cxl_p2n_read(afu, CXL_AFU_ERR_An);
dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
cxl_afu_decode_psl_serr(afu, serr);
dev_crit(&afu->dev, "AFU_ERR_An: 0x%.16llx\n", afu_error);
dev_crit(&afu->dev, "PSL_DSISR_An: 0x%.16llx\n", dsisr);
rc = cxl_h_ack_fn_error_interrupt(afu->guest->handle, serr);
if (rc)
dev_crit(&afu->dev, "Couldn't ack slice error interrupt: %d\n",
rc);
return IRQ_HANDLED;
}
static int irq_alloc_range(struct cxl *adapter, int len, int *irq)
{
int i, n;
struct irq_avail *cur;
for (i = 0; i < adapter->guest->irq_nranges; i++) {
cur = &adapter->guest->irq_avail[i];
n = bitmap_find_next_zero_area(cur->bitmap, cur->range,
0, len, 0);
if (n < cur->range) {
bitmap_set(cur->bitmap, n, len);
*irq = cur->offset + n;
pr_devel("guest: allocate IRQs %#x->%#x\n",
*irq, *irq + len - 1);
return 0;
}
}
return -ENOSPC;
}
static int irq_free_range(struct cxl *adapter, int irq, int len)
{
int i, n;
struct irq_avail *cur;
if (len == 0)
return -ENOENT;
for (i = 0; i < adapter->guest->irq_nranges; i++) {
cur = &adapter->guest->irq_avail[i];
if (irq >= cur->offset &&
(irq + len) <= (cur->offset + cur->range)) {
n = irq - cur->offset;
bitmap_clear(cur->bitmap, n, len);
pr_devel("guest: release IRQs %#x->%#x\n",
irq, irq + len - 1);
return 0;
}
}
return -ENOENT;
}
static int guest_reset(struct cxl *adapter)
{
struct cxl_afu *afu = NULL;
int i, rc;
pr_devel("Adapter reset request\n");
spin_lock(&adapter->afu_list_lock);
for (i = 0; i < adapter->slices; i++) {
if ((afu = adapter->afu[i])) {
pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT,
pci_channel_io_frozen);
cxl_context_detach_all(afu);
}
}
rc = cxl_h_reset_adapter(adapter->guest->handle);
for (i = 0; i < adapter->slices; i++) {
if (!rc && (afu = adapter->afu[i])) {
pci_error_handlers(afu, CXL_SLOT_RESET_EVENT,
pci_channel_io_normal);
pci_error_handlers(afu, CXL_RESUME_EVENT, 0);
}
}
spin_unlock(&adapter->afu_list_lock);
return rc;
}
static int guest_alloc_one_irq(struct cxl *adapter)
{
int irq;
spin_lock(&adapter->guest->irq_alloc_lock);
if (irq_alloc_range(adapter, 1, &irq))
irq = -ENOSPC;
spin_unlock(&adapter->guest->irq_alloc_lock);
return irq;
}
static void guest_release_one_irq(struct cxl *adapter, int irq)
{
spin_lock(&adapter->guest->irq_alloc_lock);
irq_free_range(adapter, irq, 1);
spin_unlock(&adapter->guest->irq_alloc_lock);
}
static int guest_alloc_irq_ranges(struct cxl_irq_ranges *irqs,
struct cxl *adapter, unsigned int num)
{
int i, try, irq;
memset(irqs, 0, sizeof(struct cxl_irq_ranges));
spin_lock(&adapter->guest->irq_alloc_lock);
for (i = 0; i < CXL_IRQ_RANGES && num; i++) {
try = num;
while (try) {
if (irq_alloc_range(adapter, try, &irq) == 0)
break;
try /= 2;
}
if (!try)
goto error;
irqs->offset[i] = irq;
irqs->range[i] = try;
num -= try;
}
if (num)
goto error;
spin_unlock(&adapter->guest->irq_alloc_lock);
return 0;
error:
for (i = 0; i < CXL_IRQ_RANGES; i++)
irq_free_range(adapter, irqs->offset[i], irqs->range[i]);
spin_unlock(&adapter->guest->irq_alloc_lock);
return -ENOSPC;
}
static void guest_release_irq_ranges(struct cxl_irq_ranges *irqs,
struct cxl *adapter)
{
int i;
spin_lock(&adapter->guest->irq_alloc_lock);
for (i = 0; i < CXL_IRQ_RANGES; i++)
irq_free_range(adapter, irqs->offset[i], irqs->range[i]);
spin_unlock(&adapter->guest->irq_alloc_lock);
}
static int guest_register_serr_irq(struct cxl_afu *afu)
{
afu->err_irq_name = kasprintf(GFP_KERNEL, "cxl-%s-err",
dev_name(&afu->dev));
if (!afu->err_irq_name)
return -ENOMEM;
if (!(afu->serr_virq = cxl_map_irq(afu->adapter, afu->serr_hwirq,
guest_slice_irq_err, afu, afu->err_irq_name))) {
kfree(afu->err_irq_name);
afu->err_irq_name = NULL;
return -ENOMEM;
}
return 0;
}
static void guest_release_serr_irq(struct cxl_afu *afu)
{
cxl_unmap_irq(afu->serr_virq, afu);
cxl_ops->release_one_irq(afu->adapter, afu->serr_hwirq);
kfree(afu->err_irq_name);
}
static int guest_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask)
{
return cxl_h_control_faults(ctx->afu->guest->handle, ctx->process_token,
tfc >> 32, (psl_reset_mask != 0));
}
static void disable_afu_irqs(struct cxl_context *ctx)
{
irq_hw_number_t hwirq;
unsigned int virq;
int r, i;
pr_devel("Disabling AFU(%d) interrupts\n", ctx->afu->slice);
for (r = 0; r < CXL_IRQ_RANGES; r++) {
hwirq = ctx->irqs.offset[r];
for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
virq = irq_find_mapping(NULL, hwirq);
disable_irq(virq);
}
}
}
static void enable_afu_irqs(struct cxl_context *ctx)
{
irq_hw_number_t hwirq;
unsigned int virq;
int r, i;
pr_devel("Enabling AFU(%d) interrupts\n", ctx->afu->slice);
for (r = 0; r < CXL_IRQ_RANGES; r++) {
hwirq = ctx->irqs.offset[r];
for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
virq = irq_find_mapping(NULL, hwirq);
enable_irq(virq);
}
}
}
static int _guest_afu_cr_readXX(int sz, struct cxl_afu *afu, int cr_idx,
u64 offset, u64 *val)
{
unsigned long cr;
char c;
int rc = 0;
if (afu->crs_len < sz)
return -ENOENT;
if (unlikely(offset >= afu->crs_len))
return -ERANGE;
cr = get_zeroed_page(GFP_KERNEL);
if (!cr)
return -ENOMEM;
rc = cxl_h_get_config(afu->guest->handle, cr_idx, offset,
virt_to_phys((void *)cr), sz);
if (rc)
goto err;
switch (sz) {
case 1:
c = *((char *) cr);
*val = c;
break;
case 2:
*val = in_le16((u16 *)cr);
break;
case 4:
*val = in_le32((unsigned *)cr);
break;
case 8:
*val = in_le64((u64 *)cr);
break;
default:
WARN_ON(1);
}
err:
free_page(cr);
return rc;
}
static int guest_afu_cr_read32(struct cxl_afu *afu, int cr_idx, u64 offset,
u32 *out)
{
int rc;
u64 val;
rc = _guest_afu_cr_readXX(4, afu, cr_idx, offset, &val);
if (!rc)
*out = (u32) val;
return rc;
}
static int guest_afu_cr_read16(struct cxl_afu *afu, int cr_idx, u64 offset,
u16 *out)
{
int rc;
u64 val;
rc = _guest_afu_cr_readXX(2, afu, cr_idx, offset, &val);
if (!rc)
*out = (u16) val;
return rc;
}
static int guest_afu_cr_read8(struct cxl_afu *afu, int cr_idx, u64 offset,
u8 *out)
{
int rc;
u64 val;
rc = _guest_afu_cr_readXX(1, afu, cr_idx, offset, &val);
if (!rc)
*out = (u8) val;
return rc;
}
static int guest_afu_cr_read64(struct cxl_afu *afu, int cr_idx, u64 offset,
u64 *out)
{
return _guest_afu_cr_readXX(8, afu, cr_idx, offset, out);
}
static int guest_afu_cr_write32(struct cxl_afu *afu, int cr, u64 off, u32 in)
{
/* config record is not writable from guest */
return -EPERM;
}
static int guest_afu_cr_write16(struct cxl_afu *afu, int cr, u64 off, u16 in)
{
/* config record is not writable from guest */
return -EPERM;
}
static int guest_afu_cr_write8(struct cxl_afu *afu, int cr, u64 off, u8 in)
{
/* config record is not writable from guest */
return -EPERM;
}
static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr)
{
struct cxl_process_element_hcall *elem;
struct cxl *adapter = ctx->afu->adapter;
const struct cred *cred;
u32 pid, idx;
int rc, r, i;
u64 mmio_addr, mmio_size;
__be64 flags = 0;
/* Must be 8 byte aligned and cannot cross a 4096 byte boundary */
if (!(elem = (struct cxl_process_element_hcall *)
get_zeroed_page(GFP_KERNEL)))
return -ENOMEM;
elem->version = cpu_to_be64(CXL_PROCESS_ELEMENT_VERSION);
if (ctx->kernel) {
pid = 0;
flags |= CXL_PE_TRANSLATION_ENABLED;
flags |= CXL_PE_PRIVILEGED_PROCESS;
if (mfmsr() & MSR_SF)
flags |= CXL_PE_64_BIT;
} else {
pid = current->pid;
flags |= CXL_PE_PROBLEM_STATE;
flags |= CXL_PE_TRANSLATION_ENABLED;
if (!test_tsk_thread_flag(current, TIF_32BIT))
flags |= CXL_PE_64_BIT;
cred = get_current_cred();
if (uid_eq(cred->euid, GLOBAL_ROOT_UID))
flags |= CXL_PE_PRIVILEGED_PROCESS;
put_cred(cred);
}
elem->flags = cpu_to_be64(flags);
elem->common.tid = cpu_to_be32(0); /* Unused */
elem->common.pid = cpu_to_be32(pid);
elem->common.csrp = cpu_to_be64(0); /* disable */
elem->common.u.psl8.aurp0 = cpu_to_be64(0); /* disable */
elem->common.u.psl8.aurp1 = cpu_to_be64(0); /* disable */
cxl_prefault(ctx, wed);
elem->common.u.psl8.sstp0 = cpu_to_be64(ctx->sstp0);
elem->common.u.psl8.sstp1 = cpu_to_be64(ctx->sstp1);
/*
* Ensure we have at least one interrupt allocated to take faults for
* kernel contexts that may not have allocated any AFU IRQs at all:
*/
if (ctx->irqs.range[0] == 0) {
rc = afu_register_irqs(ctx, 0);
if (rc)
goto out_free;
}
for (r = 0; r < CXL_IRQ_RANGES; r++) {
for (i = 0; i < ctx->irqs.range[r]; i++) {
if (r == 0 && i == 0) {
elem->pslVirtualIsn = cpu_to_be32(ctx->irqs.offset[0]);
} else {
idx = ctx->irqs.offset[r] + i - adapter->guest->irq_base_offset;
elem->applicationVirtualIsnBitmap[idx / 8] |= 0x80 >> (idx % 8);
}
}
}
elem->common.amr = cpu_to_be64(amr);
elem->common.wed = cpu_to_be64(wed);
disable_afu_irqs(ctx);
rc = cxl_h_attach_process(ctx->afu->guest->handle, elem,
&ctx->process_token, &mmio_addr, &mmio_size);
if (rc == H_SUCCESS) {
if (ctx->master || !ctx->afu->pp_psa) {
ctx->psn_phys = ctx->afu->psn_phys;
ctx->psn_size = ctx->afu->adapter->ps_size;
} else {
ctx->psn_phys = mmio_addr;
ctx->psn_size = mmio_size;
}
if (ctx->afu->pp_psa && mmio_size &&
ctx->afu->pp_size == 0) {
/*
* There's no property in the device tree to read the
* pp_size. We only find out at the 1st attach.
* Compared to bare-metal, it is too late and we
* should really lock here. However, on powerVM,
* pp_size is really only used to display in /sys.
* Being discussed with pHyp for their next release.
*/
ctx->afu->pp_size = mmio_size;
}
/* from PAPR: process element is bytes 4-7 of process token */
ctx->external_pe = ctx->process_token & 0xFFFFFFFF;
pr_devel("CXL pe=%i is known as %i for pHyp, mmio_size=%#llx",
ctx->pe, ctx->external_pe, ctx->psn_size);
ctx->pe_inserted = true;
enable_afu_irqs(ctx);
}
out_free:
free_page((u64)elem);
return rc;
}
static int guest_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr)
{
pr_devel("in %s\n", __func__);
ctx->kernel = kernel;
if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
return attach_afu_directed(ctx, wed, amr);
/* dedicated mode not supported on FW840 */
return -EINVAL;
}
static int detach_afu_directed(struct cxl_context *ctx)
{
if (!ctx->pe_inserted)
return 0;
if (cxl_h_detach_process(ctx->afu->guest->handle, ctx->process_token))
return -1;
return 0;
}
static int guest_detach_process(struct cxl_context *ctx)
{
pr_devel("in %s\n", __func__);
trace_cxl_detach(ctx);
if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
return -EIO;
if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
return detach_afu_directed(ctx);
return -EINVAL;
}
static void guest_release_afu(struct device *dev)
{
struct cxl_afu *afu = to_cxl_afu(dev);
pr_devel("%s\n", __func__);
idr_destroy(&afu->contexts_idr);
kfree(afu->guest);
kfree(afu);
}
ssize_t cxl_guest_read_afu_vpd(struct cxl_afu *afu, void *buf, size_t len)
{
return guest_collect_vpd(NULL, afu, buf, len);
}
#define ERR_BUFF_MAX_COPY_SIZE PAGE_SIZE
static ssize_t guest_afu_read_err_buffer(struct cxl_afu *afu, char *buf,
loff_t off, size_t count)
{
void *tbuf = NULL;
int rc = 0;
tbuf = (void *) get_zeroed_page(GFP_KERNEL);
if (!tbuf)
return -ENOMEM;
rc = cxl_h_get_afu_err(afu->guest->handle,
off & 0x7,
virt_to_phys(tbuf),
count);
if (rc)
goto err;
if (count > ERR_BUFF_MAX_COPY_SIZE)
count = ERR_BUFF_MAX_COPY_SIZE - (off & 0x7);
memcpy(buf, tbuf, count);
err:
free_page((u64)tbuf);
return rc;
}
static int guest_afu_check_and_enable(struct cxl_afu *afu)
{
return 0;
}
static bool guest_support_attributes(const char *attr_name,
enum cxl_attrs type)
{
switch (type) {
case CXL_ADAPTER_ATTRS:
if ((strcmp(attr_name, "base_image") == 0) ||
(strcmp(attr_name, "load_image_on_perst") == 0) ||
(strcmp(attr_name, "perst_reloads_same_image") == 0) ||
(strcmp(attr_name, "image_loaded") == 0))
return false;
break;
case CXL_AFU_MASTER_ATTRS:
if ((strcmp(attr_name, "pp_mmio_off") == 0))
return false;
break;
case CXL_AFU_ATTRS:
break;
default:
break;
}
return true;
}
static int activate_afu_directed(struct cxl_afu *afu)
{
int rc;
dev_info(&afu->dev, "Activating AFU(%d) directed mode\n", afu->slice);
afu->current_mode = CXL_MODE_DIRECTED;
afu->num_procs = afu->max_procs_virtualised;
if ((rc = cxl_chardev_m_afu_add(afu)))
return rc;
if ((rc = cxl_sysfs_afu_m_add(afu)))
goto err;
if ((rc = cxl_chardev_s_afu_add(afu)))
goto err1;
return 0;
err1:
cxl_sysfs_afu_m_remove(afu);
err:
cxl_chardev_afu_remove(afu);
return rc;
}
static int guest_afu_activate_mode(struct cxl_afu *afu, int mode)
{
if (!mode)
return 0;
if (!(mode & afu->modes_supported))
return -EINVAL;
if (mode == CXL_MODE_DIRECTED)
return activate_afu_directed(afu);
if (mode == CXL_MODE_DEDICATED)
dev_err(&afu->dev, "Dedicated mode not supported\n");
return -EINVAL;
}
static int deactivate_afu_directed(struct cxl_afu *afu)
{
dev_info(&afu->dev, "Deactivating AFU(%d) directed mode\n", afu->slice);
afu->current_mode = 0;
afu->num_procs = 0;
cxl_sysfs_afu_m_remove(afu);
cxl_chardev_afu_remove(afu);
cxl_ops->afu_reset(afu);
return 0;
}
static int guest_afu_deactivate_mode(struct cxl_afu *afu, int mode)
{
if (!mode)
return 0;
if (!(mode & afu->modes_supported))
return -EINVAL;
if (mode == CXL_MODE_DIRECTED)
return deactivate_afu_directed(afu);
return 0;
}
static int guest_afu_reset(struct cxl_afu *afu)
{
pr_devel("AFU(%d) reset request\n", afu->slice);
return cxl_h_reset_afu(afu->guest->handle);
}
static int guest_map_slice_regs(struct cxl_afu *afu)
{
if (!(afu->p2n_mmio = ioremap(afu->guest->p2n_phys, afu->guest->p2n_size))) {
dev_err(&afu->dev, "Error mapping AFU(%d) MMIO regions\n",
afu->slice);
return -ENOMEM;
}
return 0;
}
static void guest_unmap_slice_regs(struct cxl_afu *afu)
{
if (afu->p2n_mmio)
iounmap(afu->p2n_mmio);
}
static int afu_update_state(struct cxl_afu *afu)
{
int rc, cur_state;
rc = afu_read_error_state(afu, &cur_state);
if (rc)
return rc;
if (afu->guest->previous_state == cur_state)
return 0;
pr_devel("AFU(%d) update state to %#x\n", afu->slice, cur_state);
switch (cur_state) {
case H_STATE_NORMAL:
afu->guest->previous_state = cur_state;
break;
case H_STATE_DISABLE:
pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT,
pci_channel_io_frozen);
cxl_context_detach_all(afu);
if ((rc = cxl_ops->afu_reset(afu)))
pr_devel("reset hcall failed %d\n", rc);
rc = afu_read_error_state(afu, &cur_state);
if (!rc && cur_state == H_STATE_NORMAL) {
pci_error_handlers(afu, CXL_SLOT_RESET_EVENT,
pci_channel_io_normal);
pci_error_handlers(afu, CXL_RESUME_EVENT, 0);
}
afu->guest->previous_state = 0;
break;
case H_STATE_TEMP_UNAVAILABLE:
afu->guest->previous_state = cur_state;
break;
case H_STATE_PERM_UNAVAILABLE:
dev_err(&afu->dev, "AFU is in permanent error state\n");
pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT,
pci_channel_io_perm_failure);
afu->guest->previous_state = cur_state;
break;
default:
pr_err("Unexpected AFU(%d) error state: %#x\n",
afu->slice, cur_state);
return -EINVAL;
}
return rc;
}
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
static void afu_handle_errstate(struct work_struct *work)
{
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
struct cxl_afu_guest *afu_guest =
container_of(to_delayed_work(work), struct cxl_afu_guest, work_err);
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
if (!afu_update_state(afu_guest->parent) &&
afu_guest->previous_state == H_STATE_PERM_UNAVAILABLE)
return;
if (afu_guest->handle_err)
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
schedule_delayed_work(&afu_guest->work_err,
msecs_to_jiffies(3000));
}
static bool guest_link_ok(struct cxl *cxl, struct cxl_afu *afu)
{
int state;
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
if (afu && (!afu_read_error_state(afu, &state))) {
if (state == H_STATE_NORMAL)
return true;
}
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
return false;
}
static int afu_properties_look_ok(struct cxl_afu *afu)
{
if (afu->pp_irqs < 0) {
dev_err(&afu->dev, "Unexpected per-process minimum interrupt value\n");
return -EINVAL;
}
if (afu->max_procs_virtualised < 1) {
dev_err(&afu->dev, "Unexpected max number of processes virtualised value\n");
return -EINVAL;
}
return 0;
}
int cxl_guest_init_afu(struct cxl *adapter, int slice, struct device_node *afu_np)
{
struct cxl_afu *afu;
bool free = true;
int rc;
pr_devel("in %s - AFU(%d)\n", __func__, slice);
if (!(afu = cxl_alloc_afu(adapter, slice)))
return -ENOMEM;
if (!(afu->guest = kzalloc(sizeof(struct cxl_afu_guest), GFP_KERNEL))) {
kfree(afu);
return -ENOMEM;
}
if ((rc = dev_set_name(&afu->dev, "afu%i.%i",
adapter->adapter_num,
slice)))
goto err1;
adapter->slices++;
if ((rc = cxl_of_read_afu_handle(afu, afu_np)))
goto err1;
if ((rc = cxl_ops->afu_reset(afu)))
goto err1;
if ((rc = cxl_of_read_afu_properties(afu, afu_np)))
goto err1;
if ((rc = afu_properties_look_ok(afu)))
goto err1;
if ((rc = guest_map_slice_regs(afu)))
goto err1;
if ((rc = guest_register_serr_irq(afu)))
goto err2;
/*
* After we call this function we must not free the afu directly, even
* if it returns an error!
*/
if ((rc = cxl_register_afu(afu)))
goto err_put1;
if ((rc = cxl_sysfs_afu_add(afu)))
goto err_put1;
/*
* pHyp doesn't expose the programming models supported by the
* AFU. pHyp currently only supports directed mode. If it adds
* dedicated mode later, this version of cxl has no way to
* detect it. So we'll initialize the driver, but the first
* attach will fail.
* Being discussed with pHyp to do better (likely new property)
*/
if (afu->max_procs_virtualised == 1)
afu->modes_supported = CXL_MODE_DEDICATED;
else
afu->modes_supported = CXL_MODE_DIRECTED;
if ((rc = cxl_afu_select_best_mode(afu)))
goto err_put2;
adapter->afu[afu->slice] = afu;
afu->enabled = true;
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
/*
* wake up the cpu periodically to check the state
* of the AFU using "afu" stored in the guest structure.
*/
afu->guest->parent = afu;
afu->guest->handle_err = true;
INIT_DELAYED_WORK(&afu->guest->work_err, afu_handle_errstate);
schedule_delayed_work(&afu->guest->work_err, msecs_to_jiffies(1000));
if ((rc = cxl_pci_vphb_add(afu)))
dev_info(&afu->dev, "Can't register vPHB\n");
return 0;
err_put2:
cxl_sysfs_afu_remove(afu);
err_put1:
device_unregister(&afu->dev);
free = false;
guest_release_serr_irq(afu);
err2:
guest_unmap_slice_regs(afu);
err1:
if (free) {
kfree(afu->guest);
kfree(afu);
}
return rc;
}
void cxl_guest_remove_afu(struct cxl_afu *afu)
{
if (!afu)
return;
cxl: Check periodically the coherent platform function's state In the PowerVM environment, the PHYP CoherentAccel component manages the state of the Coherent Accelerator Processor Interface adapter and virtualizes CAPI resources, handles CAPP, PSL, PSL Slice errors - and interrupts - and provides a new set of hcalls for the OS APIs to utilize Accelerator Function Unit (AFU). During the course of operation, a coherent platform function can encounter errors. Some possible reason for errors are: • Hardware recoverable and unrecoverable errors • Transient and over-threshold correctable errors PHYP implements its own state model for the coherent platform function. The state of the AFU is available through a hcall. The current implementation of the cxl driver, for the PowerVM environment, checks this state of the AFU only when an action is requested - open a device, ioctl command, memory map, attach/detach a process - from an external driver - cxlflash, libcxl. If an error is detected the cxl driver handles the error according the content of the Power Architecture Platform Requirements document. But in case of low-level troubles (or error injection), the PHYP component may reset the card and change the AFU state. The PHYP interface doesn't provide any way to be notified when that happens thus implies that the cxl driver: • cannot handle immediatly the state change of the AFU. • cannot notify other drivers (cxlflash, ...) The purpose of this patch is to wake up the cpu periodically to check the current state of each AFU and to see if we need to enter an error recovery path. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Acked-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-04-22 13:39:22 +00:00
/* flush and stop pending job */
afu->guest->handle_err = false;
flush_delayed_work(&afu->guest->work_err);
cxl_pci_vphb_remove(afu);
cxl_sysfs_afu_remove(afu);
spin_lock(&afu->adapter->afu_list_lock);
afu->adapter->afu[afu->slice] = NULL;
spin_unlock(&afu->adapter->afu_list_lock);
cxl_context_detach_all(afu);
cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
guest_release_serr_irq(afu);
guest_unmap_slice_regs(afu);
device_unregister(&afu->dev);
}
static void free_adapter(struct cxl *adapter)
{
struct irq_avail *cur;
int i;
if (adapter->guest) {
if (adapter->guest->irq_avail) {
for (i = 0; i < adapter->guest->irq_nranges; i++) {
cur = &adapter->guest->irq_avail[i];
kfree(cur->bitmap);
}
kfree(adapter->guest->irq_avail);
}
kfree(adapter->guest->status);
kfree(adapter->guest);
}
cxl_remove_adapter_nr(adapter);
kfree(adapter);
}
static int properties_look_ok(struct cxl *adapter)
{
/* The absence of this property means that the operational
* status is unknown or okay
*/
if (strlen(adapter->guest->status) &&
strcmp(adapter->guest->status, "okay")) {
pr_err("ABORTING:Bad operational status of the device\n");
return -EINVAL;
}
return 0;
}
ssize_t cxl_guest_read_adapter_vpd(struct cxl *adapter, void *buf, size_t len)
{
return guest_collect_vpd(adapter, NULL, buf, len);
}
void cxl_guest_remove_adapter(struct cxl *adapter)
{
pr_devel("in %s\n", __func__);
cxl_sysfs_adapter_remove(adapter);
cxl_guest_remove_chardev(adapter);
device_unregister(&adapter->dev);
}
static void release_adapter(struct device *dev)
{
free_adapter(to_cxl_adapter(dev));
}
struct cxl *cxl_guest_init_adapter(struct device_node *np, struct platform_device *pdev)
{
struct cxl *adapter;
bool free = true;
int rc;
if (!(adapter = cxl_alloc_adapter()))
return ERR_PTR(-ENOMEM);
if (!(adapter->guest = kzalloc(sizeof(struct cxl_guest), GFP_KERNEL))) {
free_adapter(adapter);
return ERR_PTR(-ENOMEM);
}
adapter->slices = 0;
adapter->guest->pdev = pdev;
adapter->dev.parent = &pdev->dev;
adapter->dev.release = release_adapter;
dev_set_drvdata(&pdev->dev, adapter);
/*
* Hypervisor controls PSL timebase initialization (p1 register).
* On FW840, PSL is initialized.
*/
adapter->psl_timebase_synced = true;
if ((rc = cxl_of_read_adapter_handle(adapter, np)))
goto err1;
if ((rc = cxl_of_read_adapter_properties(adapter, np)))
goto err1;
if ((rc = properties_look_ok(adapter)))
goto err1;
if ((rc = cxl_guest_add_chardev(adapter)))
goto err1;
/*
* After we call this function we must not free the adapter directly,
* even if it returns an error!
*/
if ((rc = cxl_register_adapter(adapter)))
goto err_put1;
if ((rc = cxl_sysfs_adapter_add(adapter)))
goto err_put1;
cxl: Prevent adapter reset if an active context exists This patch prevents resetting the cxl adapter via sysfs in presence of one or more active cxl_context on it. This protects against an unrecoverable error caused by PSL owning a dirty cache line even after reset and host tries to touch the same cache line. In case a force reset of the card is required irrespective of any active contexts, the int value -1 can be stored in the 'reset' sysfs attribute of the card. The patch introduces a new atomic_t member named contexts_num inside struct cxl that holds the number of active context attached to the card , which is checked against '0' before proceeding with the reset. To prevent against a race condition where a context is activated just after reset check is performed, the contexts_num is atomically set to '-1' after reset-check to indicate that no more contexts can be activated on the card anymore. Before activating a context we atomically test if contexts_num is non-negative and if so, increment its value by one. In case the value of contexts_num is negative then it indicates that the card is about to be reset and context activation is error-ed out at that point. Fixes: 62fa19d4b4fd ("cxl: Add ability to reset the card") Cc: stable@vger.kernel.org # v4.0+ Acked-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com> Reviewed-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> Signed-off-by: Vaibhav Jain <vaibhav@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2016-10-14 09:38:36 +00:00
/* release the context lock as the adapter is configured */
cxl_adapter_context_unlock(adapter);
return adapter;
err_put1:
device_unregister(&adapter->dev);
free = false;
cxl_guest_remove_chardev(adapter);
err1:
if (free)
free_adapter(adapter);
return ERR_PTR(rc);
}
void cxl_guest_reload_module(struct cxl *adapter)
{
struct platform_device *pdev;
pdev = adapter->guest->pdev;
cxl_guest_remove_adapter(adapter);
cxl_of_probe(pdev);
}
const struct cxl_backend_ops cxl_guest_ops = {
.module = THIS_MODULE,
.adapter_reset = guest_reset,
.alloc_one_irq = guest_alloc_one_irq,
.release_one_irq = guest_release_one_irq,
.alloc_irq_ranges = guest_alloc_irq_ranges,
.release_irq_ranges = guest_release_irq_ranges,
.setup_irq = NULL,
.handle_psl_slice_error = guest_handle_psl_slice_error,
.psl_interrupt = guest_psl_irq,
.ack_irq = guest_ack_irq,
.attach_process = guest_attach_process,
.detach_process = guest_detach_process,
.update_ivtes = NULL,
.support_attributes = guest_support_attributes,
.link_ok = guest_link_ok,
.release_afu = guest_release_afu,
.afu_read_err_buffer = guest_afu_read_err_buffer,
.afu_check_and_enable = guest_afu_check_and_enable,
.afu_activate_mode = guest_afu_activate_mode,
.afu_deactivate_mode = guest_afu_deactivate_mode,
.afu_reset = guest_afu_reset,
.afu_cr_read8 = guest_afu_cr_read8,
.afu_cr_read16 = guest_afu_cr_read16,
.afu_cr_read32 = guest_afu_cr_read32,
.afu_cr_read64 = guest_afu_cr_read64,
.afu_cr_write8 = guest_afu_cr_write8,
.afu_cr_write16 = guest_afu_cr_write16,
.afu_cr_write32 = guest_afu_cr_write32,
.read_adapter_vpd = cxl_guest_read_adapter_vpd,
};