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