Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e98e9702 authored by Murali Nalajala's avatar Murali Nalajala Committed by Gerrit - the friendly Code Review server
Browse files

haven: irq_lend: add support for IRQ notifications



Currently IRQ APIs are combined with notifications to VMs for sharing the
handles. This limit the clients for managing the error handling from the
drivers. Hence separate the notifications from the actual APIs so that
clients manage the error handling better. Add support for notifications
like IRQ_LEND/ACCEPT/RELEASE from SVM to PVM when SVM accept the irq
handle shared by PVM.

Change-Id: I49999c2d9494c68b1542680e521c012a25e3f6a8
Signed-off-by: default avatarMurali Nalajala <mnalajal@codeaurora.org>
parent c55f25cc
Loading
Loading
Loading
Loading
+16 −3
Original line number Diff line number Diff line
@@ -296,7 +296,9 @@ static int fts_ts_vm_handle_vm_hardware(struct fts_ts_data *fts_data)
	return rc;
}

static void fts_ts_vm_irq_on_lend_callback(void *data, enum hh_irq_label label)
static void fts_ts_vm_irq_on_lend_callback(void *data,
					unsigned long notif_type,
					enum hh_irq_label label)
{
	struct fts_ts_data *fts_data = data;
	struct irq_data *irq_data;
@@ -472,6 +474,10 @@ static void fts_ts_trusted_touch_vm_mode_disable(struct fts_ts_data *fts_data)
			pr_err("Failed to release irq rc:%d\n", rc);
		else
			atomic_set(&fts_data->vm_info->tvm_owns_irq, 0);

		rc = hh_irq_release_notify(fts_data->vm_info->irq_label);
		if (rc)
			pr_err("Failed to notify release irq rc:%d\n", rc);
	}
	atomic_set(&fts_data->trusted_touch_enabled, 0);
	reinit_completion(&fts_data->resource_checkpoint);
@@ -600,6 +606,7 @@ static void fts_ts_trusted_touch_complete(struct fts_ts_data *fts_data)
}

static void fts_ts_vm_irq_on_release_callback(void *data,
					unsigned long notif_type,
					enum hh_irq_label label)
{
	struct fts_ts_data *fts_data = data;
@@ -730,7 +737,7 @@ static int fts_ts_trusted_touch_vm_mode_enable(struct fts_ts_data *fts_data)
	}
	atomic_set(&vm_info->pvm_owns_iomem, 0);

	rc = hh_irq_lend(vm_info->irq_label, vm_info->vm_name,
	rc = hh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name,
		fts_data->irq, &fts_ts_vm_irq_on_release_callback, fts_data);
	if (rc) {
		pr_err("Failed to lend irq\n");
@@ -738,6 +745,12 @@ static int fts_ts_trusted_touch_vm_mode_enable(struct fts_ts_data *fts_data)
	}
	atomic_set(&vm_info->pvm_owns_irq, 0);

	rc = hh_irq_lend_notify(vm_info->irq_label);
	if (rc) {
		pr_err("Failed to notify irq\n");
		return -EINVAL;
	}

	reinit_completion(&fts_data->trusted_touch_powerdown);
	atomic_set(&fts_data->trusted_touch_enabled, 1);
	pr_debug("trusted touch enabled\n");
@@ -809,7 +822,7 @@ static int fts_ts_vm_init(struct fts_ts_data *fts_data)
		goto init_fail;
	}
	vm_info->mem_cookie = mem_cookie;
	rc = hh_irq_wait_for_lend(vm_info->irq_label, HH_PRIMARY_VM,
	rc = hh_irq_wait_for_lend_v2(vm_info->irq_label, HH_PRIMARY_VM,
			&fts_ts_vm_irq_on_lend_callback, fts_data);
	atomic_set(&vm_info->tvm_owns_irq, 0);
	atomic_set(&vm_info->tvm_owns_iomem, 0);
+18 −4
Original line number Diff line number Diff line
@@ -321,7 +321,9 @@ static int fts_vm_handle_vm_hardware(struct fts_ts_info *info)
	return rc;
}

static void fts_vm_irq_on_lend_callback(void *data, enum hh_irq_label label)
static void fts_vm_irq_on_lend_callback(void *data,
					unsigned long notif_type,
					enum hh_irq_label label)
{
	struct fts_ts_info *info = data;
	struct irq_data *irq_data;
@@ -497,6 +499,10 @@ static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info)
			pr_err("Failed to release irq rc:%d\n", rc);
		else
			atomic_set(&info->vm_info->tvm_owns_irq, 0);

		rc = hh_irq_release_notify(info->vm_info->irq_label);
		if (rc)
			pr_err("Failed to notify release irq rc:%d\n", rc);
	}
	atomic_set(&info->trusted_touch_enabled, 0);
	reinit_completion(&info->resource_checkpoint);
@@ -624,7 +630,9 @@ static void fts_trusted_touch_complete(struct fts_ts_info *info)
	}
}

static void fts_vm_irq_on_release_callback(void *data, enum hh_irq_label label)
static void fts_vm_irq_on_release_callback(void *data,
					unsigned long notif_type,
					enum hh_irq_label label)
{
	struct fts_ts_info *info = data;
	int rc = 0;
@@ -755,7 +763,7 @@ static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info)
	}
	atomic_set(&vm_info->pvm_owns_iomem, 0);

	rc = hh_irq_lend(vm_info->irq_label, vm_info->vm_name,
	rc = hh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name,
		info->client->irq, &fts_vm_irq_on_release_callback, info);
	if (rc) {
		pr_err("Failed to lend irq\n");
@@ -763,6 +771,12 @@ static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info)
	}
	atomic_set(&vm_info->pvm_owns_irq, 0);

	rc = hh_irq_lend_notify(vm_info->irq_label);
	if (rc) {
		pr_err("Failed to notify irq\n");
		return -EINVAL;
	}

	reinit_completion(&info->trusted_touch_powerdown);
	atomic_set(&info->trusted_touch_enabled, 1);
	pr_debug("trusted touch enabled\n");
@@ -833,7 +847,7 @@ static int fts_vm_init(struct fts_ts_info *info)
		goto init_fail;
	}
	vm_info->mem_cookie = mem_cookie;
	rc = hh_irq_wait_for_lend(vm_info->irq_label, HH_PRIMARY_VM,
	rc = hh_irq_wait_for_lend_v2(vm_info->irq_label, HH_PRIMARY_VM,
			&fts_vm_irq_on_lend_callback, info);
	atomic_set(&vm_info->tvm_owns_irq, 0);
	atomic_set(&vm_info->tvm_owns_iomem, 0);
+172 −47
Original line number Diff line number Diff line
@@ -17,22 +17,20 @@
struct hh_irq_entry {
	hh_vmid_t vmid;
	enum hh_vm_names vm_name;
	hh_irq_handle_fn_v2 v2_handle;
	hh_irq_handle_fn handle;
	void *data;

	enum {
		/* start state */
		HH_IRQ_STATE_NONE,
		/* NONE -> WAIT_RELEASE by hh_irq_lend */
		HH_IRQ_STATE_WAIT_RELEASE,
		/* NONE -> WAIT_LEND by hh_irq_wait_lend */

		HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT,
		HH_IRQ_STATE_WAIT_LEND,
		/* WAIT_RELEASE -> RELEASED by notifier */
		/* RELEASED -> NONE by hh_irq_reclaim */
		HH_IRQ_STATE_RELEASED,
		/* WAIT_LEND -> LENT by notifier */
		/* LENT -> NONE by hh_irq_release */
		HH_IRQ_STATE_LENT,

		/* notification states */
		HH_IRQ_STATE_RELEASED, /* svm -> pvm */
		HH_IRQ_STATE_ACCEPTED, /* svm -> pvm */
		HH_IRQ_STATE_LENT, /* pvm -> svm */
	} state;
	hh_virq_handle_t virq_handle;
};
@@ -40,40 +38,59 @@ struct hh_irq_entry {
static struct hh_irq_entry hh_irq_entries[HH_IRQ_LABEL_MAX];
static DEFINE_SPINLOCK(hh_irq_lend_lock);

static int hh_irq_released_nb_handler(struct notifier_block *this,
static int hh_irq_released_accepted_nb_handler(struct notifier_block *this,
				      unsigned long cmd, void *data)
{
	unsigned long flags;
	enum hh_irq_label label;
	struct hh_irq_entry *entry;
	struct hh_rm_notif_vm_irq_released_payload *released = data;
	struct hh_rm_notif_vm_irq_released_payload *released;
	struct hh_rm_notif_vm_irq_accepted_payload *accepted;

	if (cmd != HH_RM_NOTIF_VM_IRQ_RELEASED)
	if (cmd != HH_RM_NOTIF_VM_IRQ_RELEASED &&
			cmd != HH_RM_NOTIF_VM_IRQ_ACCEPTED)
		return NOTIFY_DONE;

	spin_lock_irqsave(&hh_irq_lend_lock, flags);
	for (label = 0; label < HH_IRQ_LABEL_MAX; label++) {
		entry = &hh_irq_entries[label];
		if (entry->state != HH_IRQ_STATE_WAIT_RELEASE)

		if (entry->state != HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT &&
					entry->state != HH_IRQ_STATE_ACCEPTED)
			continue;

		switch (cmd) {
		case HH_RM_NOTIF_VM_IRQ_RELEASED:
			released = data;
			if (released->virq_handle == entry->virq_handle) {
				entry->state = HH_IRQ_STATE_RELEASED;
				spin_unlock_irqrestore(&hh_irq_lend_lock,
									flags);
				entry->v2_handle(entry->data, cmd, label);
				return NOTIFY_OK;
			}

			entry->handle(entry->data, label);

			break;
		case HH_RM_NOTIF_VM_IRQ_ACCEPTED:
			accepted = data;
			if (accepted->virq_handle == entry->virq_handle) {
				entry->state = HH_IRQ_STATE_ACCEPTED;
				spin_unlock_irqrestore(&hh_irq_lend_lock,
									flags);
				entry->v2_handle(entry->data, cmd, label);
				return NOTIFY_OK;
			}

			break;
		}
	}
	spin_unlock_irqrestore(&hh_irq_lend_lock, flags);

	return NOTIFY_DONE;
}

static struct notifier_block hh_irq_released_nb = {
	.notifier_call = hh_irq_released_nb_handler,
static struct notifier_block hh_irq_released_accepted_nb = {
	.notifier_call = hh_irq_released_accepted_nb_handler,
};

static int hh_irq_lent_nb_handler(struct notifier_block *this,
@@ -112,7 +129,7 @@ static int hh_irq_lent_nb_handler(struct notifier_block *this,
			spin_unlock_irqrestore(&hh_irq_lend_lock,
					       flags);

			entry->handle(entry->data, label);
			entry->v2_handle(entry->data, cmd, label);

			return NOTIFY_OK;
		}
@@ -127,22 +144,24 @@ static struct notifier_block hh_irq_lent_nb = {
};

/**
 * hh_irq_lend: Lend a hardware interrupt to another VM
 * hh_irq_lend_v2: Lend a hardware interrupt to another VM
 * @label: vIRQ high-level label
 * @name: VM name to send interrupt to
 * @irq: Linux IRQ number to lend
 * @on_release: callback to invoke when other VM returns the
 *              interrupt
 * @data: Argument to pass to on_release
 * @cb_handle: callback to invoke when other VM release or accept the interrupt
 * @data: Argument to pass to cb_handle
 *
 * Returns 0 on success also the handle corresponding to Linux IRQ#.
 * Returns < 0 on error
 */
int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name,
		int irq, hh_irq_handle_fn on_release, void *data)
int hh_irq_lend_v2(enum hh_irq_label label, enum hh_vm_names name,
		int irq, hh_irq_handle_fn_v2 cb_handle, void *data)
{
	int ret, virq;
	unsigned long flags;
	struct hh_irq_entry *entry;

	if (label >= HH_IRQ_LABEL_MAX || !on_release)
	if (label >= HH_IRQ_LABEL_MAX || !cb_handle)
		return -EINVAL;

	entry = &hh_irq_entries[label];
@@ -163,16 +182,62 @@ int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name,
		return ret;
	}

	entry->handle = on_release;
	entry->v2_handle = cb_handle;
	entry->data = data;
	entry->state = HH_IRQ_STATE_WAIT_RELEASE;
	entry->state = HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT;
	spin_unlock_irqrestore(&hh_irq_lend_lock, flags);

	return hh_rm_vm_irq_lend_notify(entry->vmid, virq, label,
		&entry->virq_handle);
	return hh_rm_vm_irq_lend(entry->vmid, virq, label, &entry->virq_handle);
}
EXPORT_SYMBOL(hh_irq_lend_v2);

/**
 * hh_irq_lend: Lend a hardware interrupt to another VM
 * @label: vIRQ high-level label
 * @name: VM name to send interrupt to
 * @irq: Linux IRQ number to lend
 * @cb_handle: callback to invoke when other VM release or accept the interrupt
 * @data: Argument to pass to cb_handle
 *
 * Returns 0 on success also the handle corresponding to Linux IRQ#.
 * Returns < 0 on error
 */
int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name,
		int irq, hh_irq_handle_fn cb_handle, void *data)
{
	struct hh_irq_entry *entry;

	if (label >= HH_IRQ_LABEL_MAX || !cb_handle)
		return -EINVAL;

	entry = &hh_irq_entries[label];
	entry->handle = cb_handle;

	return 0;
}
EXPORT_SYMBOL(hh_irq_lend);

/**
 * hh_irq_lend_notify: Pass the irq handle to other VM for accept
 * @label: vIRQ high-level label
 *
 * Returns 0 on success, < 0 on error
 */
int hh_irq_lend_notify(enum hh_irq_label label)
{
	struct hh_irq_entry *entry;

	if (label >= HH_IRQ_LABEL_MAX)
		return -EINVAL;

	entry = &hh_irq_entries[label];
	if (entry->state == HH_IRQ_STATE_NONE)
		return -EINVAL;

	return hh_rm_vm_irq_lend_notify(entry->vmid, entry->virq_handle);
}
EXPORT_SYMBOL(hh_irq_lend_notify);

/**
 * hh_irq_reclaim: Reclaim a hardware interrupt after other VM
 * has released.
@@ -193,7 +258,8 @@ int hh_irq_reclaim(enum hh_irq_label label)

	entry = &hh_irq_entries[label];

	if (entry->state != HH_IRQ_STATE_RELEASED)
	if (entry->state != HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT &&
			(entry->state != HH_IRQ_STATE_RELEASED))
		return -EINVAL;

	ret = hh_rm_vm_irq_reclaim(entry->virq_handle);
@@ -204,15 +270,14 @@ int hh_irq_reclaim(enum hh_irq_label label)
EXPORT_SYMBOL(hh_irq_reclaim);

/**
 * hh_irq_wait_lend: Register to claim a lent interrupt from another
 * VM
 * hh_irq_wait_for_lend_v2: Register to claim a lent interrupt from another VM
 * @label: vIRQ high-level label
 * @name: Lender's VM name. If don't care, then use HH_VM_MAX
 * @on_lend: callback to invoke when other VM lends the interrupt
 * @data: Argument to pass to on_lend
 */
int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name,
			 hh_irq_handle_fn on_lend, void *data)
int hh_irq_wait_for_lend_v2(enum hh_irq_label label, enum hh_vm_names name,
			 hh_irq_handle_fn_v2 on_lend, void *data)
{
	unsigned long flags;
	struct hh_irq_entry *entry;
@@ -229,13 +294,27 @@ int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name,
	}

	entry->vm_name = name;
	entry->handle = on_lend;
	entry->v2_handle = on_lend;
	entry->data = data;
	entry->state = HH_IRQ_STATE_WAIT_LEND;
	spin_unlock_irqrestore(&hh_irq_lend_lock, flags);

	return 0;
}
EXPORT_SYMBOL(hh_irq_wait_for_lend_v2);

/**
 * hh_irq_wait_lend: Register to claim a lent interrupt from another VM
 * @label: vIRQ high-level label
 * @name: Lender's VM name. If don't care, then use HH_VM_MAX
 * @on_lend: callback to invoke when other VM lends the interrupt
 * @data: Argument to pass to on_lend
 */
int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name,
			 hh_irq_handle_fn on_lend, void *data)
{
	return 0;
}
EXPORT_SYMBOL(hh_irq_wait_for_lend);

/**
@@ -276,10 +355,38 @@ int hh_irq_accept(enum hh_irq_label label, int irq, int type)
	if (irq == -1)
		irq = hh_rm_virq_to_irq(virq, type);

	entry->state = HH_IRQ_STATE_ACCEPTED;
	return irq;
}
EXPORT_SYMBOL(hh_irq_accept);

/**
 * hh_irq_accept_notify: Notify the lend vm (pvm) that IRQ is accepted
 * @label: vIRQ high-level label
 * @irq: Linux IRQ# to associate vIRQ with. If don't care, use -1
 *
 * Returns the Linux IRQ# that vIRQ was registered to on success.
 * Returns <0 on error
 * This function is not thread-safe w.r.t. IRQ lend state. Do not race with
 * hh_irq_release or another hh_irq_accept with same label.
 */
int hh_irq_accept_notify(enum hh_irq_label label)
{
	struct hh_irq_entry *entry;

	if (label >= HH_IRQ_LABEL_MAX)
		return -EINVAL;

	entry = &hh_irq_entries[label];

	if (entry->state != HH_IRQ_STATE_ACCEPTED)
		return -EINVAL;

	return hh_rm_vm_irq_accept_notify(entry->vmid,
					  entry->virq_handle);
}
EXPORT_SYMBOL(hh_irq_accept_notify);

/**
 * hh_irq_release: Release a lent interrupt
 * @label: vIRQ high-level label
@@ -296,17 +403,34 @@ int hh_irq_release(enum hh_irq_label label)

	entry = &hh_irq_entries[label];

	if (entry->state != HH_IRQ_STATE_LENT)
	if (entry->state != HH_IRQ_STATE_ACCEPTED)
		return -EINVAL;

	ret = hh_rm_vm_irq_release_notify(entry->vmid,
					  entry->virq_handle);
	ret = hh_rm_vm_irq_release(entry->virq_handle);
	if (!ret)
		entry->state = HH_IRQ_STATE_WAIT_LEND;
	return ret;
}
EXPORT_SYMBOL(hh_irq_release);

int hh_irq_release_notify(enum hh_irq_label label)
{
	struct hh_irq_entry *entry;

	if (label >= HH_IRQ_LABEL_MAX)
		return -EINVAL;

	entry = &hh_irq_entries[label];

	if (entry->state != HH_IRQ_STATE_ACCEPTED &&
			entry->state != HH_IRQ_STATE_WAIT_LEND)
		return -EINVAL;

	return hh_rm_vm_irq_release_notify(entry->vmid,
					  entry->virq_handle);
}
EXPORT_SYMBOL(hh_irq_release_notify);

static int __init hh_irq_lend_init(void)
{
	int ret;
@@ -314,14 +438,15 @@ static int __init hh_irq_lend_init(void)
	ret = hh_rm_register_notifier(&hh_irq_lent_nb);
	if (ret)
		return ret;
	return hh_rm_register_notifier(&hh_irq_released_nb);

	return hh_rm_register_notifier(&hh_irq_released_accepted_nb);
}
module_init(hh_irq_lend_init);

static void hh_irq_lend_exit(void)
{
	hh_rm_unregister_notifier(&hh_irq_lent_nb);
	hh_rm_unregister_notifier(&hh_irq_released_nb);
	hh_rm_unregister_notifier(&hh_irq_released_accepted_nb);
}
module_exit(hh_irq_lend_exit);

+9 −0
Original line number Diff line number Diff line
@@ -218,6 +218,15 @@ static int hh_rm_process_notif(void *recv_buff, size_t recv_buff_size)
			goto err;
		}
		break;
	case HH_RM_NOTIF_VM_IRQ_ACCEPTED:
		if (recv_buff_size != sizeof(*hdr) +
			sizeof(struct hh_rm_notif_vm_irq_accepted_payload)) {
			pr_err("%s: Invalid size for VM_IRQ_ACCEPTED notif: %u\n",
				__func__, recv_buff_size - sizeof(*hdr));
			ret = -EINVAL;
			goto err;
		}
		break;
	case HH_RM_NOTIF_MEM_SHARED:
		if (recv_buff_size < sizeof(*hdr) +
			sizeof(struct hh_rm_notif_mem_shared_payload)) {
+1 −0
Original line number Diff line number Diff line
@@ -181,6 +181,7 @@ struct hh_vm_irq_lend_resp_payload {
/* Call: VM_IRQ_NOTIFY */
#define HH_VM_IRQ_NOTIFY_FLAGS_LENT	BIT(0)
#define HH_VM_IRQ_NOTIFY_FLAGS_RELEASED	BIT(1)
#define HH_VM_IRQ_NOTIFY_FLAGS_ACCEPTED	BIT(2)

/* Call: VM_IRQ_RELEASE */
struct hh_vm_irq_release_req_payload {
Loading