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

Commit fdc33ffd authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "tty: hvc_haven: Use thread worker to send characters"

parents ce077090 f56b018e
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -31,14 +31,21 @@ static inline int _hh_hcall(const hh_hcall_fnid_t hcall_num,
	register uint64_t _x7 asm("x7") = args.arg7;

	asm volatile (
#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK)
		"str	x18, [%[_x18]]\n"
#endif
		"hvc	%[num]\n"
#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK)
		"ldr	x18, [%[_x18]]\n"
		"str	xzr, [%[_x18]]\n"
#endif
		: "+r"(_x0), "+r"(_x1), "+r"(_x2), "+r"(_x3), "+r"(_x4),
		  "+r"(_x5), "+r"(_x6), "+r"(_x7)
		: [num] "i" (hcall_num), [_x18] "r"(&_x18)
		: "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17",
#if !IS_ENABLED(CONFIG_SHADOW_CALL_STACK)
		  "x18",
#endif
		  "memory"
		);

+10 −3
Original line number Diff line number Diff line
@@ -786,13 +786,20 @@ static int mem_buf_msgq_recv_fn(void *unused)
	int ret;

	while (!kthread_should_stop()) {
		ret = hh_msgq_recv(mem_buf_hh_msgq_hdl, &buf, &size, 0);
		if (ret < 0)
		buf = kzalloc(HH_MSGQ_MAX_MSG_SIZE_BYTES, GFP_KERNEL);
		if (!buf)
			continue;

		ret = hh_msgq_recv(mem_buf_hh_msgq_hdl, buf,
					HH_MSGQ_MAX_MSG_SIZE_BYTES, &size, 0);
		if (ret < 0) {
			kfree(buf);
			pr_err_ratelimited("%s failed to receive message rc: %d\n",
					   __func__, ret);
		else
		} else {
			mem_buf_process_msg(buf, size);
		}
	}

	return 0;
}
+61 −14
Original line number Diff line number Diff line
@@ -12,21 +12,27 @@
#include <linux/notifier.h>
#include <linux/spinlock.h>
#include <linux/printk.h>
#include <linux/workqueue.h>

#include <linux/haven/hh_msgq.h>
#include <linux/haven/hh_common.h>
#include <linux/haven/hh_rm_drv.h>

#include "hvc_console.h"

#define HVC_HH_VTERM_COOKIE	0x474E5948
/* # of payload bytes that can fit in a 1-fragment CONSOLE_WRITE message */
#define HH_HVC_WRITE_MSG_SIZE	((1 * (HH_MSGQ_MAX_MSG_SIZE_BYTES - 8)) - 4)

struct hh_hvc_prv {
	struct hvc_struct *hvc;
	DECLARE_KFIFO(fifo, char, 1024);
	DECLARE_KFIFO(get_fifo, char, 1024);
	DECLARE_KFIFO(put_fifo, char, 1024);
	struct work_struct put_work;
};

static DEFINE_SPINLOCK(fifo_lock);
static struct hh_hvc_prv hh_hvc_data[2];
static struct hh_hvc_prv hh_hvc_data[HH_VM_MAX];

static inline int hh_vm_name_to_vtermno(enum hh_vm_names vmname)
{
@@ -50,11 +56,12 @@ static int hh_hvc_notify_console_chars(struct notifier_block *this,

	ret = hh_rm_get_vm_name(msg->vmid, &vm_name);
	if (ret) {
		pr_warn_ratelimited("don't know VMID %d\n", vm_name);
		pr_warn_ratelimited("don't know VMID %d ret: %d\n", msg->vmid,
				    ret);
		return NOTIFY_OK;
	}

	ret = kfifo_in_spinlocked(&hh_hvc_data[vm_name].fifo,
	ret = kfifo_in_spinlocked(&hh_hvc_data[vm_name].get_fifo,
				  msg->bytes, msg->num_bytes,
				  &fifo_lock);

@@ -69,6 +76,36 @@ static int hh_hvc_notify_console_chars(struct notifier_block *this,
	return NOTIFY_OK;
}

static void hh_hvc_put_work_fn(struct work_struct *ws)
{
	hh_vmid_t vmid;
	char buf[HH_HVC_WRITE_MSG_SIZE];
	int count, ret;
	struct hh_hvc_prv *prv = container_of(ws, struct hh_hvc_prv, put_work);
	enum hh_vm_names vm_name = vtermno_to_hh_vm_name(prv->hvc->vtermno);

	ret = hh_rm_get_vmid(vm_name, &vmid);
	if (ret) {
		pr_warn_once("hh_rm_get_vmid failed for %d: %d\n",
			     vm_name, ret);
		return;
	}

	while (!kfifo_is_empty(&prv->put_fifo)) {
		count = kfifo_out_spinlocked(&prv->put_fifo, buf, sizeof(buf),
					     &fifo_lock);
		if (count <= 0)
			continue;

		ret = hh_rm_console_write(vmid, buf, count);
		if (ret) {
			pr_warn_once("hh_rm_console_write failed for %d: %d\n",
				vm_name, ret);
			break;
		}
	}
}

static int hh_hvc_get_chars(uint32_t vtermno, char *buf, int count)
{
	int vm_name = vtermno_to_hh_vm_name(vtermno);
@@ -76,24 +113,22 @@ static int hh_hvc_get_chars(uint32_t vtermno, char *buf, int count)
	if (vm_name < 0 || vm_name >= HH_VM_MAX)
		return -EINVAL;

	return kfifo_out_spinlocked(&hh_hvc_data[vm_name].fifo,
	return kfifo_out_spinlocked(&hh_hvc_data[vm_name].get_fifo,
				    buf, count, &fifo_lock);
}

static int hh_hvc_put_chars(uint32_t vtermno, const char *buf, int count)
{
	int ret, vm_name = vtermno_to_hh_vm_name(vtermno);
	hh_vmid_t vmid;

	if (vm_name < 0 || vm_name >= HH_VM_MAX)
		return -EINVAL;

	ret = hh_rm_get_vmid(vm_name, &vmid);
	if (ret)
	ret = kfifo_in_spinlocked(&hh_hvc_data[vm_name].put_fifo,
				   buf, count, &fifo_lock);
	if (ret > 0)
		schedule_work(&hh_hvc_data[vm_name].put_work);
	return ret;


	return hh_rm_console_write(vmid, buf, count);
}

static int hh_hvc_flush(uint32_t vtermno, bool wait)
@@ -108,6 +143,11 @@ static int hh_hvc_flush(uint32_t vtermno, bool wait)
	if (ret)
		return ret;

	if (cancel_work_sync(&hh_hvc_data[vm_name].put_work)) {
		/* flush the fifo */
		hh_hvc_put_work_fn(&hh_hvc_data[vm_name].put_work);
	}

	return hh_rm_console_flush(vmid);
}

@@ -131,6 +171,11 @@ static void hh_hvc_notify_del(struct hvc_struct *hp, int vm_name)
	if (vm_name < 0 || vm_name >= HH_VM_MAX)
		return;

	if (cancel_work_sync(&hh_hvc_data[vm_name].put_work)) {
		/* flush the fifo */
		hh_hvc_put_work_fn(&hh_hvc_data[vm_name].put_work);
	}

	ret = hh_rm_get_vmid(vm_name, &vmid);
	if (ret)
		return;
@@ -140,7 +185,7 @@ static void hh_hvc_notify_del(struct hvc_struct *hp, int vm_name)
	if (ret)
		pr_err("Failed close VM%d console - %d\n", vm_name, ret);

	kfifo_reset(&hh_hvc_data[vm_name].fifo);
	kfifo_reset(&hh_hvc_data[vm_name].get_fifo);
}

static struct notifier_block hh_hvc_nb = {
@@ -175,7 +220,9 @@ static int __init hvc_hh_init(void)

	for (i = 0; i < HH_VM_MAX; i++) {
		prv = &hh_hvc_data[i];
		INIT_KFIFO(prv->fifo);
		INIT_KFIFO(prv->get_fifo);
		INIT_KFIFO(prv->put_fifo);
		INIT_WORK(&prv->put_work, hh_hvc_put_work_fn);
		prv->hvc = hvc_alloc(hh_vm_name_to_vtermno(i), i, &hh_hv_ops,
				     256);
		ret = PTR_ERR_OR_ZERO(prv->hvc);
+24 −30
Original line number Diff line number Diff line
@@ -71,7 +71,8 @@ static irqreturn_t hh_msgq_tx_isr(int irq, void *dev)
}

static int __hh_msgq_recv(struct hh_msgq_cap_table *cap_table_entry,
				void *buff, size_t *size, u64 rx_flags)
				void *buff, size_t buff_size,
				size_t *recv_size, u64 rx_flags)
{
	struct hh_hcall_msgq_recv_resp resp = {};
	unsigned long flags;
@@ -83,11 +84,11 @@ static int __hh_msgq_recv(struct hh_msgq_cap_table *cap_table_entry,

	spin_lock_irqsave(&cap_table_entry->rx_lock, flags);
	hh_ret = hh_hcall_msgq_recv(cap_table_entry->rx_cap_id, buff,
					HH_MSGQ_MAX_MSG_SIZE_BYTES, &resp);
					buff_size, &resp);

	switch (hh_ret) {
	case HH_ERROR_OK:
		*size = resp.recv_size;
		*recv_size = resp.recv_size;
		ret = 0;
		break;
	case HH_ERROR_MSGQUEUE_EMPTY:
@@ -110,30 +111,36 @@ static int __hh_msgq_recv(struct hh_msgq_cap_table *cap_table_entry,
/**
 * hh_msgq_recv: Receive a message from the client running on a different VM
 * @client_desc: The client descriptor that was obtained via hh_msgq_register()
 * @buff: Pointer to the buffer where the received data must be placed. Note
 *        that the caller is responsible to free the data contained in buff
 * @size: The size of the buffer received
 * @buff: Pointer to the buffer where the received data must be placed
 * @buff_size: The size of the buffer space available
 * @recv_size: The actual amount of data that is copied into buff
 * @flags: Optional flags to pass to receive the data. For the list of flags,
 *         see linux/haven/hh_msgq.h
 *
 * The function returns -EINVAL if the caller passes invalid arguments, -EAGAIN
 * The function returns 0 if the data is successfully received and recv_size
 * would contain the actual amount of data copied into buff.
 * It returns -EINVAL if the caller passes invalid arguments, -EAGAIN
 * if the message queue is not yet ready to communicate, and -EPERM if the
 * caller doesn't have permissions to receive the data. 0 is the data is
 * successfully received.
 * caller doesn't have permissions to receive the data. In all these failure
 * cases, recv_size is unmodified.
 *
 * Note: this function may sleep and should not be called from interrupt
 *       context
 */
int hh_msgq_recv(void *msgq_client_desc,
			void **buff, size_t *size, unsigned long flags)
			void *buff, size_t buff_size,
			size_t *recv_size, unsigned long flags)
{
	struct hh_msgq_desc *client_desc = msgq_client_desc;
	struct hh_msgq_cap_table *cap_table_entry;
	int ret;

	if (!client_desc || !(*buff) || !size)
	if (!client_desc || !buff || !buff_size || !recv_size)
		return -EINVAL;

	if (buff_size > HH_MSGQ_MAX_MSG_SIZE_BYTES)
		return -E2BIG;

	cap_table_entry = &hh_msgq_cap_table[client_desc->label];

	spin_lock(&cap_table_entry->cap_entry_lock);
@@ -170,31 +177,18 @@ int hh_msgq_recv(void *msgq_client_desc,

	spin_unlock(&cap_table_entry->cap_entry_lock);

	*buff = kzalloc(HH_MSGQ_MAX_MSG_SIZE_BYTES, GFP_KERNEL);
	if (!(*buff))
		return -ENOMEM;

	do {
		if (cap_table_entry->rx_empty && (flags & HH_MSGQ_NONBLOCK)) {
			ret = -EAGAIN;
			goto buff_free;
		}
		if (cap_table_entry->rx_empty && (flags & HH_MSGQ_NONBLOCK))
			return -EAGAIN;

		if (wait_event_interruptible(cap_table_entry->rx_wq,
					!cap_table_entry->rx_empty)) {
			ret = -ERESTARTSYS;
			goto buff_free;
		}
					!cap_table_entry->rx_empty))
			return -ERESTARTSYS;

		ret = __hh_msgq_recv(cap_table_entry, *buff, size, flags);
		ret = __hh_msgq_recv(cap_table_entry, buff, buff_size,
					recv_size, flags);
	} while (ret == -EAGAIN);

buff_free:
	if (ret < 0) {
		kfree(*buff);
		*buff = NULL;
	}

	return ret;

err:
+8 −1
Original line number Diff line number Diff line
@@ -417,6 +417,7 @@ static void hh_rm_process_recv_work(struct work_struct *work)
	 * to release the original packet that arrived.
	 */
	kfree(recv_buff);
	kfree(msgq_data);
}

static int hh_rm_recv_task_fn(void *data)
@@ -427,12 +428,18 @@ static int hh_rm_recv_task_fn(void *data)
	int ret;

	while (!kthread_should_stop()) {
		recv_buff = kzalloc(HH_MSGQ_MAX_MSG_SIZE_BYTES, GFP_KERNEL);
		if (!recv_buff)
			continue;

		/* Block until a new message is received */
		ret = hh_msgq_recv(hh_rm_msgq_desc, &recv_buff,
		ret = hh_msgq_recv(hh_rm_msgq_desc, recv_buff,
					HH_MSGQ_MAX_MSG_SIZE_BYTES,
					&recv_buff_size, 0);
		if (ret < 0) {
			pr_err("%s: Failed to receive the message: %d\n",
				__func__, ret);
			kfree(recv_buff);
			continue;
		} else if (recv_buff_size <= sizeof(struct hh_rm_rpc_hdr)) {
			pr_err("%s: Invalid message size received\n", __func__);
Loading