xref: /kernel/linux/linux-6.6/drivers/virt/acrn/ioreq.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ACRN_HSM: Handle I/O requests
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2020 Intel Corporation. All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors:
862306a36Sopenharmony_ci *	Jason Chen CJ <jason.cj.chen@intel.com>
962306a36Sopenharmony_ci *	Fengwei Yin <fengwei.yin@intel.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/kthread.h>
1562306a36Sopenharmony_ci#include <linux/mm.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/acrn.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "acrn_drv.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic void ioreq_pause(void);
2362306a36Sopenharmony_cistatic void ioreq_resume(void);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void ioreq_dispatcher(struct work_struct *work);
2662306a36Sopenharmony_cistatic struct workqueue_struct *ioreq_wq;
2762306a36Sopenharmony_cistatic DECLARE_WORK(ioreq_work, ioreq_dispatcher);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic inline bool has_pending_request(struct acrn_ioreq_client *client)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	return !bitmap_empty(client->ioreqs_map, ACRN_IO_REQUEST_MAX);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline bool is_destroying(struct acrn_ioreq_client *client)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	return test_bit(ACRN_IOREQ_CLIENT_DESTROYING, &client->flags);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int ioreq_complete_request(struct acrn_vm *vm, u16 vcpu,
4062306a36Sopenharmony_ci				  struct acrn_io_request *acrn_req)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	bool polling_mode;
4362306a36Sopenharmony_ci	int ret = 0;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	polling_mode = acrn_req->completion_polling;
4662306a36Sopenharmony_ci	/* Add barrier() to make sure the writes are done before completion */
4762306a36Sopenharmony_ci	smp_store_release(&acrn_req->processed, ACRN_IOREQ_STATE_COMPLETE);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * To fulfill the requirement of real-time in several industry
5162306a36Sopenharmony_ci	 * scenarios, like automotive, ACRN can run under the partition mode,
5262306a36Sopenharmony_ci	 * in which User VMs and Service VM are bound to dedicated CPU cores.
5362306a36Sopenharmony_ci	 * Polling mode of handling the I/O request is introduced to achieve a
5462306a36Sopenharmony_ci	 * faster I/O request handling. In polling mode, the hypervisor polls
5562306a36Sopenharmony_ci	 * I/O request's completion. Once an I/O request is marked as
5662306a36Sopenharmony_ci	 * ACRN_IOREQ_STATE_COMPLETE, hypervisor resumes from the polling point
5762306a36Sopenharmony_ci	 * to continue the I/O request flow. Thus, the completion notification
5862306a36Sopenharmony_ci	 * from HSM of I/O request is not needed.  Please note,
5962306a36Sopenharmony_ci	 * completion_polling needs to be read before the I/O request being
6062306a36Sopenharmony_ci	 * marked as ACRN_IOREQ_STATE_COMPLETE to avoid racing with the
6162306a36Sopenharmony_ci	 * hypervisor.
6262306a36Sopenharmony_ci	 */
6362306a36Sopenharmony_ci	if (!polling_mode) {
6462306a36Sopenharmony_ci		ret = hcall_notify_req_finish(vm->vmid, vcpu);
6562306a36Sopenharmony_ci		if (ret < 0)
6662306a36Sopenharmony_ci			dev_err(acrn_dev.this_device,
6762306a36Sopenharmony_ci				"Notify I/O request finished failed!\n");
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return ret;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int acrn_ioreq_complete_request(struct acrn_ioreq_client *client,
7462306a36Sopenharmony_ci				       u16 vcpu,
7562306a36Sopenharmony_ci				       struct acrn_io_request *acrn_req)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int ret;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (vcpu >= client->vm->vcpu_num)
8062306a36Sopenharmony_ci		return -EINVAL;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	clear_bit(vcpu, client->ioreqs_map);
8362306a36Sopenharmony_ci	if (!acrn_req) {
8462306a36Sopenharmony_ci		acrn_req = (struct acrn_io_request *)client->vm->ioreq_buf;
8562306a36Sopenharmony_ci		acrn_req += vcpu;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	ret = ioreq_complete_request(client->vm, vcpu, acrn_req);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return ret;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciint acrn_ioreq_request_default_complete(struct acrn_vm *vm, u16 vcpu)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int ret = 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	spin_lock_bh(&vm->ioreq_clients_lock);
9862306a36Sopenharmony_ci	if (vm->default_client)
9962306a36Sopenharmony_ci		ret = acrn_ioreq_complete_request(vm->default_client,
10062306a36Sopenharmony_ci						  vcpu, NULL);
10162306a36Sopenharmony_ci	spin_unlock_bh(&vm->ioreq_clients_lock);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return ret;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/**
10762306a36Sopenharmony_ci * acrn_ioreq_range_add() - Add an iorange monitored by an ioreq client
10862306a36Sopenharmony_ci * @client:	The ioreq client
10962306a36Sopenharmony_ci * @type:	Type (ACRN_IOREQ_TYPE_MMIO or ACRN_IOREQ_TYPE_PORTIO)
11062306a36Sopenharmony_ci * @start:	Start address of iorange
11162306a36Sopenharmony_ci * @end:	End address of iorange
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * Return: 0 on success, <0 on error
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ciint acrn_ioreq_range_add(struct acrn_ioreq_client *client,
11662306a36Sopenharmony_ci			 u32 type, u64 start, u64 end)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct acrn_ioreq_range *range;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (end < start) {
12162306a36Sopenharmony_ci		dev_err(acrn_dev.this_device,
12262306a36Sopenharmony_ci			"Invalid IO range [0x%llx,0x%llx]\n", start, end);
12362306a36Sopenharmony_ci		return -EINVAL;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	range = kzalloc(sizeof(*range), GFP_KERNEL);
12762306a36Sopenharmony_ci	if (!range)
12862306a36Sopenharmony_ci		return -ENOMEM;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	range->type = type;
13162306a36Sopenharmony_ci	range->start = start;
13262306a36Sopenharmony_ci	range->end = end;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	write_lock_bh(&client->range_lock);
13562306a36Sopenharmony_ci	list_add(&range->list, &client->range_list);
13662306a36Sopenharmony_ci	write_unlock_bh(&client->range_lock);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/**
14262306a36Sopenharmony_ci * acrn_ioreq_range_del() - Del an iorange monitored by an ioreq client
14362306a36Sopenharmony_ci * @client:	The ioreq client
14462306a36Sopenharmony_ci * @type:	Type (ACRN_IOREQ_TYPE_MMIO or ACRN_IOREQ_TYPE_PORTIO)
14562306a36Sopenharmony_ci * @start:	Start address of iorange
14662306a36Sopenharmony_ci * @end:	End address of iorange
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_civoid acrn_ioreq_range_del(struct acrn_ioreq_client *client,
14962306a36Sopenharmony_ci			  u32 type, u64 start, u64 end)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct acrn_ioreq_range *range;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	write_lock_bh(&client->range_lock);
15462306a36Sopenharmony_ci	list_for_each_entry(range, &client->range_list, list) {
15562306a36Sopenharmony_ci		if (type == range->type &&
15662306a36Sopenharmony_ci		    start == range->start &&
15762306a36Sopenharmony_ci		    end == range->end) {
15862306a36Sopenharmony_ci			list_del(&range->list);
15962306a36Sopenharmony_ci			kfree(range);
16062306a36Sopenharmony_ci			break;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	write_unlock_bh(&client->range_lock);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/*
16762306a36Sopenharmony_ci * ioreq_task() is the execution entity of handler thread of an I/O client.
16862306a36Sopenharmony_ci * The handler callback of the I/O client is called within the handler thread.
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic int ioreq_task(void *data)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct acrn_ioreq_client *client = data;
17362306a36Sopenharmony_ci	struct acrn_io_request *req;
17462306a36Sopenharmony_ci	unsigned long *ioreqs_map;
17562306a36Sopenharmony_ci	int vcpu, ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/*
17862306a36Sopenharmony_ci	 * Lockless access to ioreqs_map is safe, because
17962306a36Sopenharmony_ci	 * 1) set_bit() and clear_bit() are atomic operations.
18062306a36Sopenharmony_ci	 * 2) I/O requests arrives serialized. The access flow of ioreqs_map is:
18162306a36Sopenharmony_ci	 *	set_bit() - in ioreq_work handler
18262306a36Sopenharmony_ci	 *	Handler callback handles corresponding I/O request
18362306a36Sopenharmony_ci	 *	clear_bit() - in handler thread (include ACRN userspace)
18462306a36Sopenharmony_ci	 *	Mark corresponding I/O request completed
18562306a36Sopenharmony_ci	 *	Loop again if a new I/O request occurs
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci	ioreqs_map = client->ioreqs_map;
18862306a36Sopenharmony_ci	while (!kthread_should_stop()) {
18962306a36Sopenharmony_ci		acrn_ioreq_client_wait(client);
19062306a36Sopenharmony_ci		while (has_pending_request(client)) {
19162306a36Sopenharmony_ci			vcpu = find_first_bit(ioreqs_map, client->vm->vcpu_num);
19262306a36Sopenharmony_ci			req = client->vm->ioreq_buf->req_slot + vcpu;
19362306a36Sopenharmony_ci			ret = client->handler(client, req);
19462306a36Sopenharmony_ci			if (ret < 0) {
19562306a36Sopenharmony_ci				dev_err(acrn_dev.this_device,
19662306a36Sopenharmony_ci					"IO handle failure: %d\n", ret);
19762306a36Sopenharmony_ci				break;
19862306a36Sopenharmony_ci			}
19962306a36Sopenharmony_ci			acrn_ioreq_complete_request(client, vcpu, req);
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/*
20762306a36Sopenharmony_ci * For the non-default I/O clients, give them chance to complete the current
20862306a36Sopenharmony_ci * I/O requests if there are any. For the default I/O client, it is safe to
20962306a36Sopenharmony_ci * clear all pending I/O requests because the clearing request is from ACRN
21062306a36Sopenharmony_ci * userspace.
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_civoid acrn_ioreq_request_clear(struct acrn_vm *vm)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct acrn_ioreq_client *client;
21562306a36Sopenharmony_ci	bool has_pending = false;
21662306a36Sopenharmony_ci	unsigned long vcpu;
21762306a36Sopenharmony_ci	int retry = 10;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/*
22062306a36Sopenharmony_ci	 * IO requests of this VM will be completed directly in
22162306a36Sopenharmony_ci	 * acrn_ioreq_dispatch if ACRN_VM_FLAG_CLEARING_IOREQ flag is set.
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	set_bit(ACRN_VM_FLAG_CLEARING_IOREQ, &vm->flags);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/*
22662306a36Sopenharmony_ci	 * acrn_ioreq_request_clear is only called in VM reset case. Simply
22762306a36Sopenharmony_ci	 * wait 100ms in total for the IO requests' completion.
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci	do {
23062306a36Sopenharmony_ci		spin_lock_bh(&vm->ioreq_clients_lock);
23162306a36Sopenharmony_ci		list_for_each_entry(client, &vm->ioreq_clients, list) {
23262306a36Sopenharmony_ci			has_pending = has_pending_request(client);
23362306a36Sopenharmony_ci			if (has_pending)
23462306a36Sopenharmony_ci				break;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci		spin_unlock_bh(&vm->ioreq_clients_lock);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		if (has_pending)
23962306a36Sopenharmony_ci			schedule_timeout_interruptible(HZ / 100);
24062306a36Sopenharmony_ci	} while (has_pending && --retry > 0);
24162306a36Sopenharmony_ci	if (retry == 0)
24262306a36Sopenharmony_ci		dev_warn(acrn_dev.this_device,
24362306a36Sopenharmony_ci			 "%s cannot flush pending request!\n", client->name);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Clear all ioreqs belonging to the default client */
24662306a36Sopenharmony_ci	spin_lock_bh(&vm->ioreq_clients_lock);
24762306a36Sopenharmony_ci	client = vm->default_client;
24862306a36Sopenharmony_ci	if (client) {
24962306a36Sopenharmony_ci		for_each_set_bit(vcpu, client->ioreqs_map, ACRN_IO_REQUEST_MAX)
25062306a36Sopenharmony_ci			acrn_ioreq_complete_request(client, vcpu, NULL);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	spin_unlock_bh(&vm->ioreq_clients_lock);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Clear ACRN_VM_FLAG_CLEARING_IOREQ flag after the clearing */
25562306a36Sopenharmony_ci	clear_bit(ACRN_VM_FLAG_CLEARING_IOREQ, &vm->flags);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciint acrn_ioreq_client_wait(struct acrn_ioreq_client *client)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	if (client->is_default) {
26162306a36Sopenharmony_ci		/*
26262306a36Sopenharmony_ci		 * In the default client, a user space thread waits on the
26362306a36Sopenharmony_ci		 * waitqueue. The is_destroying() check is used to notify user
26462306a36Sopenharmony_ci		 * space the client is going to be destroyed.
26562306a36Sopenharmony_ci		 */
26662306a36Sopenharmony_ci		wait_event_interruptible(client->wq,
26762306a36Sopenharmony_ci					 has_pending_request(client) ||
26862306a36Sopenharmony_ci					 is_destroying(client));
26962306a36Sopenharmony_ci		if (is_destroying(client))
27062306a36Sopenharmony_ci			return -ENODEV;
27162306a36Sopenharmony_ci	} else {
27262306a36Sopenharmony_ci		wait_event_interruptible(client->wq,
27362306a36Sopenharmony_ci					 has_pending_request(client) ||
27462306a36Sopenharmony_ci					 kthread_should_stop());
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic bool is_cfg_addr(struct acrn_io_request *req)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	return ((req->type == ACRN_IOREQ_TYPE_PORTIO) &&
28362306a36Sopenharmony_ci		(req->reqs.pio_request.address == 0xcf8));
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic bool is_cfg_data(struct acrn_io_request *req)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	return ((req->type == ACRN_IOREQ_TYPE_PORTIO) &&
28962306a36Sopenharmony_ci		((req->reqs.pio_request.address >= 0xcfc) &&
29062306a36Sopenharmony_ci		 (req->reqs.pio_request.address < (0xcfc + 4))));
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/* The low 8-bit of supported pci_reg addr.*/
29462306a36Sopenharmony_ci#define PCI_LOWREG_MASK  0xFC
29562306a36Sopenharmony_ci/* The high 4-bit of supported pci_reg addr */
29662306a36Sopenharmony_ci#define PCI_HIGHREG_MASK 0xF00
29762306a36Sopenharmony_ci/* Max number of supported functions */
29862306a36Sopenharmony_ci#define PCI_FUNCMAX	7
29962306a36Sopenharmony_ci/* Max number of supported slots */
30062306a36Sopenharmony_ci#define PCI_SLOTMAX	31
30162306a36Sopenharmony_ci/* Max number of supported buses */
30262306a36Sopenharmony_ci#define PCI_BUSMAX	255
30362306a36Sopenharmony_ci#define CONF1_ENABLE	0x80000000UL
30462306a36Sopenharmony_ci/*
30562306a36Sopenharmony_ci * A PCI configuration space access via PIO 0xCF8 and 0xCFC normally has two
30662306a36Sopenharmony_ci * following steps:
30762306a36Sopenharmony_ci *   1) writes address into 0xCF8 port
30862306a36Sopenharmony_ci *   2) accesses data in/from 0xCFC
30962306a36Sopenharmony_ci * This function combines such paired PCI configuration space I/O requests into
31062306a36Sopenharmony_ci * one ACRN_IOREQ_TYPE_PCICFG type I/O request and continues the processing.
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_cistatic bool handle_cf8cfc(struct acrn_vm *vm,
31362306a36Sopenharmony_ci			  struct acrn_io_request *req, u16 vcpu)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	int offset, pci_cfg_addr, pci_reg;
31662306a36Sopenharmony_ci	bool is_handled = false;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (is_cfg_addr(req)) {
31962306a36Sopenharmony_ci		WARN_ON(req->reqs.pio_request.size != 4);
32062306a36Sopenharmony_ci		if (req->reqs.pio_request.direction == ACRN_IOREQ_DIR_WRITE)
32162306a36Sopenharmony_ci			vm->pci_conf_addr = req->reqs.pio_request.value;
32262306a36Sopenharmony_ci		else
32362306a36Sopenharmony_ci			req->reqs.pio_request.value = vm->pci_conf_addr;
32462306a36Sopenharmony_ci		is_handled = true;
32562306a36Sopenharmony_ci	} else if (is_cfg_data(req)) {
32662306a36Sopenharmony_ci		if (!(vm->pci_conf_addr & CONF1_ENABLE)) {
32762306a36Sopenharmony_ci			if (req->reqs.pio_request.direction ==
32862306a36Sopenharmony_ci					ACRN_IOREQ_DIR_READ)
32962306a36Sopenharmony_ci				req->reqs.pio_request.value = 0xffffffff;
33062306a36Sopenharmony_ci			is_handled = true;
33162306a36Sopenharmony_ci		} else {
33262306a36Sopenharmony_ci			offset = req->reqs.pio_request.address - 0xcfc;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci			req->type = ACRN_IOREQ_TYPE_PCICFG;
33562306a36Sopenharmony_ci			pci_cfg_addr = vm->pci_conf_addr;
33662306a36Sopenharmony_ci			req->reqs.pci_request.bus =
33762306a36Sopenharmony_ci					(pci_cfg_addr >> 16) & PCI_BUSMAX;
33862306a36Sopenharmony_ci			req->reqs.pci_request.dev =
33962306a36Sopenharmony_ci					(pci_cfg_addr >> 11) & PCI_SLOTMAX;
34062306a36Sopenharmony_ci			req->reqs.pci_request.func =
34162306a36Sopenharmony_ci					(pci_cfg_addr >> 8) & PCI_FUNCMAX;
34262306a36Sopenharmony_ci			pci_reg = (pci_cfg_addr & PCI_LOWREG_MASK) +
34362306a36Sopenharmony_ci				   ((pci_cfg_addr >> 16) & PCI_HIGHREG_MASK);
34462306a36Sopenharmony_ci			req->reqs.pci_request.reg = pci_reg + offset;
34562306a36Sopenharmony_ci		}
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (is_handled)
34962306a36Sopenharmony_ci		ioreq_complete_request(vm, vcpu, req);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return is_handled;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic bool acrn_in_range(struct acrn_ioreq_range *range,
35562306a36Sopenharmony_ci		     struct acrn_io_request *req)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	bool ret = false;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (range->type == req->type) {
36062306a36Sopenharmony_ci		switch (req->type) {
36162306a36Sopenharmony_ci		case ACRN_IOREQ_TYPE_MMIO:
36262306a36Sopenharmony_ci			if (req->reqs.mmio_request.address >= range->start &&
36362306a36Sopenharmony_ci			    (req->reqs.mmio_request.address +
36462306a36Sopenharmony_ci			     req->reqs.mmio_request.size - 1) <= range->end)
36562306a36Sopenharmony_ci				ret = true;
36662306a36Sopenharmony_ci			break;
36762306a36Sopenharmony_ci		case ACRN_IOREQ_TYPE_PORTIO:
36862306a36Sopenharmony_ci			if (req->reqs.pio_request.address >= range->start &&
36962306a36Sopenharmony_ci			    (req->reqs.pio_request.address +
37062306a36Sopenharmony_ci			     req->reqs.pio_request.size - 1) <= range->end)
37162306a36Sopenharmony_ci				ret = true;
37262306a36Sopenharmony_ci			break;
37362306a36Sopenharmony_ci		default:
37462306a36Sopenharmony_ci			break;
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return ret;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic struct acrn_ioreq_client *find_ioreq_client(struct acrn_vm *vm,
38262306a36Sopenharmony_ci						   struct acrn_io_request *req)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct acrn_ioreq_client *client, *found = NULL;
38562306a36Sopenharmony_ci	struct acrn_ioreq_range *range;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	lockdep_assert_held(&vm->ioreq_clients_lock);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	list_for_each_entry(client, &vm->ioreq_clients, list) {
39062306a36Sopenharmony_ci		read_lock_bh(&client->range_lock);
39162306a36Sopenharmony_ci		list_for_each_entry(range, &client->range_list, list) {
39262306a36Sopenharmony_ci			if (acrn_in_range(range, req)) {
39362306a36Sopenharmony_ci				found = client;
39462306a36Sopenharmony_ci				break;
39562306a36Sopenharmony_ci			}
39662306a36Sopenharmony_ci		}
39762306a36Sopenharmony_ci		read_unlock_bh(&client->range_lock);
39862306a36Sopenharmony_ci		if (found)
39962306a36Sopenharmony_ci			break;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	return found ? found : vm->default_client;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/**
40562306a36Sopenharmony_ci * acrn_ioreq_client_create() - Create an ioreq client
40662306a36Sopenharmony_ci * @vm:		The VM that this client belongs to
40762306a36Sopenharmony_ci * @handler:	The ioreq_handler of ioreq client acrn_hsm will create a kernel
40862306a36Sopenharmony_ci *		thread and call the handler to handle I/O requests.
40962306a36Sopenharmony_ci * @priv:	Private data for the handler
41062306a36Sopenharmony_ci * @is_default:	If it is the default client
41162306a36Sopenharmony_ci * @name:	The name of ioreq client
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * Return: acrn_ioreq_client pointer on success, NULL on error
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_cistruct acrn_ioreq_client *acrn_ioreq_client_create(struct acrn_vm *vm,
41662306a36Sopenharmony_ci						   ioreq_handler_t handler,
41762306a36Sopenharmony_ci						   void *priv, bool is_default,
41862306a36Sopenharmony_ci						   const char *name)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct acrn_ioreq_client *client;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (!handler && !is_default) {
42362306a36Sopenharmony_ci		dev_dbg(acrn_dev.this_device,
42462306a36Sopenharmony_ci			"Cannot create non-default client w/o handler!\n");
42562306a36Sopenharmony_ci		return NULL;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci	client = kzalloc(sizeof(*client), GFP_KERNEL);
42862306a36Sopenharmony_ci	if (!client)
42962306a36Sopenharmony_ci		return NULL;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	client->handler = handler;
43262306a36Sopenharmony_ci	client->vm = vm;
43362306a36Sopenharmony_ci	client->priv = priv;
43462306a36Sopenharmony_ci	client->is_default = is_default;
43562306a36Sopenharmony_ci	if (name)
43662306a36Sopenharmony_ci		strncpy(client->name, name, sizeof(client->name) - 1);
43762306a36Sopenharmony_ci	rwlock_init(&client->range_lock);
43862306a36Sopenharmony_ci	INIT_LIST_HEAD(&client->range_list);
43962306a36Sopenharmony_ci	init_waitqueue_head(&client->wq);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (client->handler) {
44262306a36Sopenharmony_ci		client->thread = kthread_run(ioreq_task, client, "VM%u-%s",
44362306a36Sopenharmony_ci					     client->vm->vmid, client->name);
44462306a36Sopenharmony_ci		if (IS_ERR(client->thread)) {
44562306a36Sopenharmony_ci			kfree(client);
44662306a36Sopenharmony_ci			return NULL;
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	spin_lock_bh(&vm->ioreq_clients_lock);
45162306a36Sopenharmony_ci	if (is_default)
45262306a36Sopenharmony_ci		vm->default_client = client;
45362306a36Sopenharmony_ci	else
45462306a36Sopenharmony_ci		list_add(&client->list, &vm->ioreq_clients);
45562306a36Sopenharmony_ci	spin_unlock_bh(&vm->ioreq_clients_lock);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	dev_dbg(acrn_dev.this_device, "Created ioreq client %s.\n", name);
45862306a36Sopenharmony_ci	return client;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/**
46262306a36Sopenharmony_ci * acrn_ioreq_client_destroy() - Destroy an ioreq client
46362306a36Sopenharmony_ci * @client:	The ioreq client
46462306a36Sopenharmony_ci */
46562306a36Sopenharmony_civoid acrn_ioreq_client_destroy(struct acrn_ioreq_client *client)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct acrn_ioreq_range *range, *next;
46862306a36Sopenharmony_ci	struct acrn_vm *vm = client->vm;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	dev_dbg(acrn_dev.this_device,
47162306a36Sopenharmony_ci		"Destroy ioreq client %s.\n", client->name);
47262306a36Sopenharmony_ci	ioreq_pause();
47362306a36Sopenharmony_ci	set_bit(ACRN_IOREQ_CLIENT_DESTROYING, &client->flags);
47462306a36Sopenharmony_ci	if (client->is_default)
47562306a36Sopenharmony_ci		wake_up_interruptible(&client->wq);
47662306a36Sopenharmony_ci	else
47762306a36Sopenharmony_ci		kthread_stop(client->thread);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	spin_lock_bh(&vm->ioreq_clients_lock);
48062306a36Sopenharmony_ci	if (client->is_default)
48162306a36Sopenharmony_ci		vm->default_client = NULL;
48262306a36Sopenharmony_ci	else
48362306a36Sopenharmony_ci		list_del(&client->list);
48462306a36Sopenharmony_ci	spin_unlock_bh(&vm->ioreq_clients_lock);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	write_lock_bh(&client->range_lock);
48762306a36Sopenharmony_ci	list_for_each_entry_safe(range, next, &client->range_list, list) {
48862306a36Sopenharmony_ci		list_del(&range->list);
48962306a36Sopenharmony_ci		kfree(range);
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci	write_unlock_bh(&client->range_lock);
49262306a36Sopenharmony_ci	kfree(client);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	ioreq_resume();
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int acrn_ioreq_dispatch(struct acrn_vm *vm)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct acrn_ioreq_client *client;
50062306a36Sopenharmony_ci	struct acrn_io_request *req;
50162306a36Sopenharmony_ci	int i;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	for (i = 0; i < vm->vcpu_num; i++) {
50462306a36Sopenharmony_ci		req = vm->ioreq_buf->req_slot + i;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		/* barrier the read of processed of acrn_io_request */
50762306a36Sopenharmony_ci		if (smp_load_acquire(&req->processed) ==
50862306a36Sopenharmony_ci				     ACRN_IOREQ_STATE_PENDING) {
50962306a36Sopenharmony_ci			/* Complete the IO request directly in clearing stage */
51062306a36Sopenharmony_ci			if (test_bit(ACRN_VM_FLAG_CLEARING_IOREQ, &vm->flags)) {
51162306a36Sopenharmony_ci				ioreq_complete_request(vm, i, req);
51262306a36Sopenharmony_ci				continue;
51362306a36Sopenharmony_ci			}
51462306a36Sopenharmony_ci			if (handle_cf8cfc(vm, req, i))
51562306a36Sopenharmony_ci				continue;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci			spin_lock_bh(&vm->ioreq_clients_lock);
51862306a36Sopenharmony_ci			client = find_ioreq_client(vm, req);
51962306a36Sopenharmony_ci			if (!client) {
52062306a36Sopenharmony_ci				dev_err(acrn_dev.this_device,
52162306a36Sopenharmony_ci					"Failed to find ioreq client!\n");
52262306a36Sopenharmony_ci				spin_unlock_bh(&vm->ioreq_clients_lock);
52362306a36Sopenharmony_ci				return -EINVAL;
52462306a36Sopenharmony_ci			}
52562306a36Sopenharmony_ci			if (!client->is_default)
52662306a36Sopenharmony_ci				req->kernel_handled = 1;
52762306a36Sopenharmony_ci			else
52862306a36Sopenharmony_ci				req->kernel_handled = 0;
52962306a36Sopenharmony_ci			/*
53062306a36Sopenharmony_ci			 * Add barrier() to make sure the writes are done
53162306a36Sopenharmony_ci			 * before setting ACRN_IOREQ_STATE_PROCESSING
53262306a36Sopenharmony_ci			 */
53362306a36Sopenharmony_ci			smp_store_release(&req->processed,
53462306a36Sopenharmony_ci					  ACRN_IOREQ_STATE_PROCESSING);
53562306a36Sopenharmony_ci			set_bit(i, client->ioreqs_map);
53662306a36Sopenharmony_ci			wake_up_interruptible(&client->wq);
53762306a36Sopenharmony_ci			spin_unlock_bh(&vm->ioreq_clients_lock);
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	return 0;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic void ioreq_dispatcher(struct work_struct *work)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct acrn_vm *vm;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	read_lock(&acrn_vm_list_lock);
54962306a36Sopenharmony_ci	list_for_each_entry(vm, &acrn_vm_list, list) {
55062306a36Sopenharmony_ci		if (!vm->ioreq_buf)
55162306a36Sopenharmony_ci			break;
55262306a36Sopenharmony_ci		acrn_ioreq_dispatch(vm);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci	read_unlock(&acrn_vm_list_lock);
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic void ioreq_intr_handler(void)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	queue_work(ioreq_wq, &ioreq_work);
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void ioreq_pause(void)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	/* Flush and unarm the handler to ensure no I/O requests pending */
56562306a36Sopenharmony_ci	acrn_remove_intr_handler();
56662306a36Sopenharmony_ci	drain_workqueue(ioreq_wq);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic void ioreq_resume(void)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	/* Schedule after enabling in case other clients miss interrupt */
57262306a36Sopenharmony_ci	acrn_setup_intr_handler(ioreq_intr_handler);
57362306a36Sopenharmony_ci	queue_work(ioreq_wq, &ioreq_work);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ciint acrn_ioreq_intr_setup(void)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	acrn_setup_intr_handler(ioreq_intr_handler);
57962306a36Sopenharmony_ci	ioreq_wq = alloc_ordered_workqueue("ioreq_wq",
58062306a36Sopenharmony_ci					   WQ_HIGHPRI | WQ_MEM_RECLAIM);
58162306a36Sopenharmony_ci	if (!ioreq_wq) {
58262306a36Sopenharmony_ci		dev_err(acrn_dev.this_device, "Failed to alloc workqueue!\n");
58362306a36Sopenharmony_ci		acrn_remove_intr_handler();
58462306a36Sopenharmony_ci		return -ENOMEM;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_civoid acrn_ioreq_intr_remove(void)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	if (ioreq_wq)
59262306a36Sopenharmony_ci		destroy_workqueue(ioreq_wq);
59362306a36Sopenharmony_ci	acrn_remove_intr_handler();
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ciint acrn_ioreq_init(struct acrn_vm *vm, u64 buf_vma)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct acrn_ioreq_buffer *set_buffer;
59962306a36Sopenharmony_ci	struct page *page;
60062306a36Sopenharmony_ci	int ret;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (vm->ioreq_buf)
60362306a36Sopenharmony_ci		return -EEXIST;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	set_buffer = kzalloc(sizeof(*set_buffer), GFP_KERNEL);
60662306a36Sopenharmony_ci	if (!set_buffer)
60762306a36Sopenharmony_ci		return -ENOMEM;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	ret = pin_user_pages_fast(buf_vma, 1,
61062306a36Sopenharmony_ci				  FOLL_WRITE | FOLL_LONGTERM, &page);
61162306a36Sopenharmony_ci	if (unlikely(ret != 1) || !page) {
61262306a36Sopenharmony_ci		dev_err(acrn_dev.this_device, "Failed to pin ioreq page!\n");
61362306a36Sopenharmony_ci		ret = -EFAULT;
61462306a36Sopenharmony_ci		goto free_buf;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	vm->ioreq_buf = page_address(page);
61862306a36Sopenharmony_ci	vm->ioreq_page = page;
61962306a36Sopenharmony_ci	set_buffer->ioreq_buf = page_to_phys(page);
62062306a36Sopenharmony_ci	ret = hcall_set_ioreq_buffer(vm->vmid, virt_to_phys(set_buffer));
62162306a36Sopenharmony_ci	if (ret < 0) {
62262306a36Sopenharmony_ci		dev_err(acrn_dev.this_device, "Failed to init ioreq buffer!\n");
62362306a36Sopenharmony_ci		unpin_user_page(page);
62462306a36Sopenharmony_ci		vm->ioreq_buf = NULL;
62562306a36Sopenharmony_ci		goto free_buf;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	dev_dbg(acrn_dev.this_device,
62962306a36Sopenharmony_ci		"Init ioreq buffer %pK!\n", vm->ioreq_buf);
63062306a36Sopenharmony_ci	ret = 0;
63162306a36Sopenharmony_cifree_buf:
63262306a36Sopenharmony_ci	kfree(set_buffer);
63362306a36Sopenharmony_ci	return ret;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_civoid acrn_ioreq_deinit(struct acrn_vm *vm)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct acrn_ioreq_client *client, *next;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	dev_dbg(acrn_dev.this_device,
64162306a36Sopenharmony_ci		"Deinit ioreq buffer %pK!\n", vm->ioreq_buf);
64262306a36Sopenharmony_ci	/* Destroy all clients belonging to this VM */
64362306a36Sopenharmony_ci	list_for_each_entry_safe(client, next, &vm->ioreq_clients, list)
64462306a36Sopenharmony_ci		acrn_ioreq_client_destroy(client);
64562306a36Sopenharmony_ci	if (vm->default_client)
64662306a36Sopenharmony_ci		acrn_ioreq_client_destroy(vm->default_client);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (vm->ioreq_buf && vm->ioreq_page) {
64962306a36Sopenharmony_ci		unpin_user_page(vm->ioreq_page);
65062306a36Sopenharmony_ci		vm->ioreq_buf = NULL;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci}
653