Commit 0d400f77 authored by Christophe Lombard's avatar Christophe Lombard Committed by Michael Ellerman
Browse files

cxl: Adapter failure handling



Check the AFU state whenever an API is called. The hypervisor may
issue a reset of the adapter when it detects a fault. When it happens,
it launches an error recovery which will either move the AFU to a
permanent failure state, or in the disabled state.
If the AFU is found to be disabled, detach all existing contexts from
it before issuing a AFU reset to re-enable it.

Before detaching contexts, notify any kernel driver through the EEH
callbacks of the AFU pci device.

Co-authored-by: default avatarFrederic Barrat <fbarrat@linux.vnet.ibm.com>
Signed-off-by: default avatarFrederic Barrat <fbarrat@linux.vnet.ibm.com>
Signed-off-by: default avatarChristophe Lombard <clombard@linux.vnet.ibm.com>
Reviewed-by: default avatarManoj Kumar <manoj@linux.vnet.ibm.com>
Acked-by: default avatarIan Munsie <imunsie@au1.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent d601ea91
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -220,7 +220,7 @@ int __detach_context(struct cxl_context *ctx)
	 * If detach fails when hw is down, we don't care.
	 */
	WARN_ON(cxl_ops->detach_process(ctx) &&
		cxl_ops->link_ok(ctx->afu->adapter));
		cxl_ops->link_ok(ctx->afu->adapter, ctx->afu));
	flush_work(&ctx->fault_work); /* Only needed for dedicated process */

	/* release the reference to the group leader and mm handling pid */
+10 −8
Original line number Diff line number Diff line
@@ -379,6 +379,8 @@ struct cxl_afu_guest {
	phys_addr_t p2n_phys;
	u64 p2n_size;
	int max_ints;
	struct mutex recovery_lock;
	int previous_state;
};

struct cxl_afu {
@@ -617,7 +619,7 @@ struct cxl_process_element {
	__be32 software_state;
} __packed;

static inline bool cxl_adapter_link_ok(struct cxl *cxl)
static inline bool cxl_adapter_link_ok(struct cxl *cxl, struct cxl_afu *afu)
{
	struct pci_dev *pdev;

@@ -636,13 +638,13 @@ static inline void __iomem *_cxl_p1_addr(struct cxl *cxl, cxl_p1_reg_t reg)

static inline void cxl_p1_write(struct cxl *cxl, cxl_p1_reg_t reg, u64 val)
{
	if (likely(cxl_adapter_link_ok(cxl)))
	if (likely(cxl_adapter_link_ok(cxl, NULL)))
		out_be64(_cxl_p1_addr(cxl, reg), val);
}

static inline u64 cxl_p1_read(struct cxl *cxl, cxl_p1_reg_t reg)
{
	if (likely(cxl_adapter_link_ok(cxl)))
	if (likely(cxl_adapter_link_ok(cxl, NULL)))
		return in_be64(_cxl_p1_addr(cxl, reg));
	else
		return ~0ULL;
@@ -656,13 +658,13 @@ static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg

static inline void cxl_p1n_write(struct cxl_afu *afu, cxl_p1n_reg_t reg, u64 val)
{
	if (likely(cxl_adapter_link_ok(afu->adapter)))
	if (likely(cxl_adapter_link_ok(afu->adapter, afu)))
		out_be64(_cxl_p1n_addr(afu, reg), val);
}

static inline u64 cxl_p1n_read(struct cxl_afu *afu, cxl_p1n_reg_t reg)
{
	if (likely(cxl_adapter_link_ok(afu->adapter)))
	if (likely(cxl_adapter_link_ok(afu->adapter, afu)))
		return in_be64(_cxl_p1n_addr(afu, reg));
	else
		return ~0ULL;
@@ -675,13 +677,13 @@ static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg

static inline void cxl_p2n_write(struct cxl_afu *afu, cxl_p2n_reg_t reg, u64 val)
{
	if (likely(cxl_adapter_link_ok(afu->adapter)))
	if (likely(cxl_adapter_link_ok(afu->adapter, afu)))
		out_be64(_cxl_p2n_addr(afu, reg), val);
}

static inline u64 cxl_p2n_read(struct cxl_afu *afu, cxl_p2n_reg_t reg)
{
	if (likely(cxl_adapter_link_ok(afu->adapter)))
	if (likely(cxl_adapter_link_ok(afu->adapter, afu)))
		return in_be64(_cxl_p2n_addr(afu, reg));
	else
		return ~0ULL;
@@ -857,7 +859,7 @@ struct cxl_backend_ops {
			u64 wed, u64 amr);
	int (*detach_process)(struct cxl_context *ctx);
	bool (*support_attributes)(const char *attr_name, enum cxl_attrs type);
	bool (*link_ok)(struct cxl *cxl);
	bool (*link_ok)(struct cxl *cxl, struct cxl_afu *afu);
	void (*release_afu)(struct device *dev);
	ssize_t (*afu_read_err_buffer)(struct cxl_afu *afu, char *buf,
				loff_t off, size_t count);
+5 −5
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ static int __afu_open(struct inode *inode, struct file *file, bool master)
	if (!afu->current_mode)
		goto err_put_afu;

	if (!cxl_ops->link_ok(adapter)) {
	if (!cxl_ops->link_ok(adapter, afu)) {
		rc = -EIO;
		goto err_put_afu;
	}
@@ -257,7 +257,7 @@ long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
	if (ctx->status == CLOSED)
		return -EIO;

	if (!cxl_ops->link_ok(ctx->afu->adapter))
	if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
		return -EIO;

	pr_devel("afu_ioctl\n");
@@ -287,7 +287,7 @@ int afu_mmap(struct file *file, struct vm_area_struct *vm)
	if (ctx->status != STARTED)
		return -EIO;

	if (!cxl_ops->link_ok(ctx->afu->adapter))
	if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
		return -EIO;

	return cxl_context_iomap(ctx, vm);
@@ -334,7 +334,7 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count,
	int rc;
	DEFINE_WAIT(wait);

	if (!cxl_ops->link_ok(ctx->afu->adapter))
	if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
		return -EIO;

	if (count < CXL_READ_MIN_SIZE)
@@ -347,7 +347,7 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count,
		if (ctx_event_pending(ctx))
			break;

		if (!cxl_ops->link_ok(ctx->afu->adapter)) {
		if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu)) {
			rc = -EIO;
			goto out;
		}
+164 −3
Original line number Diff line number Diff line
@@ -15,6 +15,46 @@
#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)
@@ -133,6 +173,22 @@ static irqreturn_t guest_psl_irq(int irq, void *data)
	return rc;
}

static int afu_read_error_state(struct cxl_afu *afu, int *state_out)
{
	u64 state;
	int rc = 0;

	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;
@@ -201,10 +257,26 @@ static int irq_free_range(struct cxl *adapter, int irq, int len)

static int guest_reset(struct cxl *adapter)
{
	int rc;
	struct cxl_afu *afu = NULL;
	int i, rc;

	pr_devel("Adapter reset request\n");
	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);
		}
	}
	return rc;
}

@@ -556,7 +628,7 @@ 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))
	if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu))
		return -EIO;

	if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
@@ -730,8 +802,95 @@ static void guest_unmap_slice_regs(struct cxl_afu *afu)
		iounmap(afu->p2n_mmio);
}

static bool guest_link_ok(struct cxl *cxl)
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;
		rc = 1;
		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);
			rc = 1;
		}
		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;
}

static int afu_do_recovery(struct cxl_afu *afu)
{
	int rc;

	/* many threads can arrive here, in case of detach_all for example.
	 * Only one needs to drive the recovery
	 */
	if (mutex_trylock(&afu->guest->recovery_lock)) {
		rc = afu_update_state(afu);
		mutex_unlock(&afu->guest->recovery_lock);
		return rc;
	}
	return 0;
}

static bool guest_link_ok(struct cxl *cxl, struct cxl_afu *afu)
{
	int state;

	if (afu) {
		if (afu_read_error_state(afu, &state) ||
			state != H_STATE_NORMAL) {
			if (afu_do_recovery(afu) > 0) {
				/* check again in case we've just fixed it */
				if (!afu_read_error_state(afu, &state) &&
					state == H_STATE_NORMAL)
					return true;
			}
			return false;
		}
	}

	return true;
}

@@ -770,6 +929,8 @@ int cxl_guest_init_afu(struct cxl *adapter, int slice, struct device_node *afu_n
		return -ENOMEM;
	}

	mutex_init(&afu->guest->recovery_lock);

	if ((rc = dev_set_name(&afu->dev, "afu%i.%i",
					  adapter->adapter_num,
					  slice)))
+1 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ int cxl_afu_slbia(struct cxl_afu *afu)
		/* If the adapter has gone down, we can assume that we
		 * will PERST it and that will invalidate everything.
		 */
		if (!cxl_ops->link_ok(afu->adapter))
		if (!cxl_ops->link_ok(afu->adapter, afu))
			return -EIO;
		cpu_relax();
	}
Loading