162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2023 Advanced Micro Devices, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/io.h> 562306a36Sopenharmony_ci#include <linux/types.h> 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/pds/pds_common.h> 962306a36Sopenharmony_ci#include <linux/pds/pds_core_if.h> 1062306a36Sopenharmony_ci#include <linux/pds/pds_adminq.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "vfio_dev.h" 1362306a36Sopenharmony_ci#include "cmds.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define SUSPEND_TIMEOUT_S 5 1662306a36Sopenharmony_ci#define SUSPEND_CHECK_INTERVAL_MS 1 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic int pds_vfio_client_adminq_cmd(struct pds_vfio_pci_device *pds_vfio, 1962306a36Sopenharmony_ci union pds_core_adminq_cmd *req, 2062306a36Sopenharmony_ci union pds_core_adminq_comp *resp, 2162306a36Sopenharmony_ci bool fast_poll) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio); 2462306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = {}; 2562306a36Sopenharmony_ci struct pdsc *pdsc; 2662306a36Sopenharmony_ci int err; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci /* Wrap the client request */ 2962306a36Sopenharmony_ci cmd.client_request.opcode = PDS_AQ_CMD_CLIENT_CMD; 3062306a36Sopenharmony_ci cmd.client_request.client_id = cpu_to_le16(pds_vfio->client_id); 3162306a36Sopenharmony_ci memcpy(cmd.client_request.client_cmd, req, 3262306a36Sopenharmony_ci sizeof(cmd.client_request.client_cmd)); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci pdsc = pdsc_get_pf_struct(pdev); 3562306a36Sopenharmony_ci if (IS_ERR(pdsc)) 3662306a36Sopenharmony_ci return PTR_ERR(pdsc); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci err = pdsc_adminq_post(pdsc, &cmd, resp, fast_poll); 3962306a36Sopenharmony_ci if (err && err != -EAGAIN) 4062306a36Sopenharmony_ci dev_err(pds_vfio_to_dev(pds_vfio), 4162306a36Sopenharmony_ci "client admin cmd failed: %pe\n", ERR_PTR(err)); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return err; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciint pds_vfio_register_client_cmd(struct pds_vfio_pci_device *pds_vfio) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio); 4962306a36Sopenharmony_ci char devname[PDS_DEVNAME_LEN]; 5062306a36Sopenharmony_ci struct pdsc *pdsc; 5162306a36Sopenharmony_ci int ci; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci snprintf(devname, sizeof(devname), "%s.%d-%u", PDS_VFIO_LM_DEV_NAME, 5462306a36Sopenharmony_ci pci_domain_nr(pdev->bus), 5562306a36Sopenharmony_ci PCI_DEVID(pdev->bus->number, pdev->devfn)); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci pdsc = pdsc_get_pf_struct(pdev); 5862306a36Sopenharmony_ci if (IS_ERR(pdsc)) 5962306a36Sopenharmony_ci return PTR_ERR(pdsc); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ci = pds_client_register(pdsc, devname); 6262306a36Sopenharmony_ci if (ci < 0) 6362306a36Sopenharmony_ci return ci; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci pds_vfio->client_id = ci; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_civoid pds_vfio_unregister_client_cmd(struct pds_vfio_pci_device *pds_vfio) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio); 7362306a36Sopenharmony_ci struct pdsc *pdsc; 7462306a36Sopenharmony_ci int err; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci pdsc = pdsc_get_pf_struct(pdev); 7762306a36Sopenharmony_ci if (IS_ERR(pdsc)) 7862306a36Sopenharmony_ci return; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci err = pds_client_unregister(pdsc, pds_vfio->client_id); 8162306a36Sopenharmony_ci if (err) 8262306a36Sopenharmony_ci dev_err(&pdev->dev, "unregister from DSC failed: %pe\n", 8362306a36Sopenharmony_ci ERR_PTR(err)); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci pds_vfio->client_id = 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int 8962306a36Sopenharmony_cipds_vfio_suspend_wait_device_cmd(struct pds_vfio_pci_device *pds_vfio, u8 type) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 9262306a36Sopenharmony_ci .lm_suspend_status = { 9362306a36Sopenharmony_ci .opcode = PDS_LM_CMD_SUSPEND_STATUS, 9462306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 9562306a36Sopenharmony_ci .type = type, 9662306a36Sopenharmony_ci }, 9762306a36Sopenharmony_ci }; 9862306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 9962306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 10062306a36Sopenharmony_ci unsigned long time_limit; 10162306a36Sopenharmony_ci unsigned long time_start; 10262306a36Sopenharmony_ci unsigned long time_done; 10362306a36Sopenharmony_ci int err; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci time_start = jiffies; 10662306a36Sopenharmony_ci time_limit = time_start + HZ * SUSPEND_TIMEOUT_S; 10762306a36Sopenharmony_ci do { 10862306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, true); 10962306a36Sopenharmony_ci if (err != -EAGAIN) 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci msleep(SUSPEND_CHECK_INTERVAL_MS); 11362306a36Sopenharmony_ci } while (time_before(jiffies, time_limit)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci time_done = jiffies; 11662306a36Sopenharmony_ci dev_dbg(dev, "%s: vf%u: Suspend comp received in %d msecs\n", __func__, 11762306a36Sopenharmony_ci pds_vfio->vf_id, jiffies_to_msecs(time_done - time_start)); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Check the results */ 12062306a36Sopenharmony_ci if (time_after_eq(time_done, time_limit)) { 12162306a36Sopenharmony_ci dev_err(dev, "%s: vf%u: Suspend comp timeout\n", __func__, 12262306a36Sopenharmony_ci pds_vfio->vf_id); 12362306a36Sopenharmony_ci err = -ETIMEDOUT; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return err; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciint pds_vfio_suspend_device_cmd(struct pds_vfio_pci_device *pds_vfio, u8 type) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 13262306a36Sopenharmony_ci .lm_suspend = { 13362306a36Sopenharmony_ci .opcode = PDS_LM_CMD_SUSPEND, 13462306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 13562306a36Sopenharmony_ci .type = type, 13662306a36Sopenharmony_ci }, 13762306a36Sopenharmony_ci }; 13862306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 13962306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 14062306a36Sopenharmony_ci int err; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci dev_dbg(dev, "vf%u: Suspend device\n", pds_vfio->vf_id); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * The initial suspend request to the firmware starts the device suspend 14662306a36Sopenharmony_ci * operation and the firmware returns success if it's started 14762306a36Sopenharmony_ci * successfully. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, true); 15062306a36Sopenharmony_ci if (err) { 15162306a36Sopenharmony_ci dev_err(dev, "vf%u: Suspend failed: %pe\n", pds_vfio->vf_id, 15262306a36Sopenharmony_ci ERR_PTR(err)); 15362306a36Sopenharmony_ci return err; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * The subsequent suspend status request(s) check if the firmware has 15862306a36Sopenharmony_ci * completed the device suspend process. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci return pds_vfio_suspend_wait_device_cmd(pds_vfio, type); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciint pds_vfio_resume_device_cmd(struct pds_vfio_pci_device *pds_vfio, u8 type) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 16662306a36Sopenharmony_ci .lm_resume = { 16762306a36Sopenharmony_ci .opcode = PDS_LM_CMD_RESUME, 16862306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 16962306a36Sopenharmony_ci .type = type, 17062306a36Sopenharmony_ci }, 17162306a36Sopenharmony_ci }; 17262306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 17362306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci dev_dbg(dev, "vf%u: Resume device\n", pds_vfio->vf_id); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, true); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciint pds_vfio_get_lm_state_size_cmd(struct pds_vfio_pci_device *pds_vfio, u64 *size) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 18362306a36Sopenharmony_ci .lm_state_size = { 18462306a36Sopenharmony_ci .opcode = PDS_LM_CMD_STATE_SIZE, 18562306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 18662306a36Sopenharmony_ci }, 18762306a36Sopenharmony_ci }; 18862306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 18962306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 19062306a36Sopenharmony_ci int err; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci dev_dbg(dev, "vf%u: Get migration status\n", pds_vfio->vf_id); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false); 19562306a36Sopenharmony_ci if (err) 19662306a36Sopenharmony_ci return err; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci *size = le64_to_cpu(comp.lm_state_size.size); 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int pds_vfio_dma_map_lm_file(struct device *dev, 20362306a36Sopenharmony_ci enum dma_data_direction dir, 20462306a36Sopenharmony_ci struct pds_vfio_lm_file *lm_file) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct pds_lm_sg_elem *sgl, *sge; 20762306a36Sopenharmony_ci struct scatterlist *sg; 20862306a36Sopenharmony_ci dma_addr_t sgl_addr; 20962306a36Sopenharmony_ci size_t sgl_size; 21062306a36Sopenharmony_ci int err; 21162306a36Sopenharmony_ci int i; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (!lm_file) 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* dma map file pages */ 21762306a36Sopenharmony_ci err = dma_map_sgtable(dev, &lm_file->sg_table, dir, 0); 21862306a36Sopenharmony_ci if (err) 21962306a36Sopenharmony_ci return err; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci lm_file->num_sge = lm_file->sg_table.nents; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* alloc sgl */ 22462306a36Sopenharmony_ci sgl_size = lm_file->num_sge * sizeof(struct pds_lm_sg_elem); 22562306a36Sopenharmony_ci sgl = kzalloc(sgl_size, GFP_KERNEL); 22662306a36Sopenharmony_ci if (!sgl) { 22762306a36Sopenharmony_ci err = -ENOMEM; 22862306a36Sopenharmony_ci goto out_unmap_sgtable; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* fill sgl */ 23262306a36Sopenharmony_ci sge = sgl; 23362306a36Sopenharmony_ci for_each_sgtable_dma_sg(&lm_file->sg_table, sg, i) { 23462306a36Sopenharmony_ci sge->addr = cpu_to_le64(sg_dma_address(sg)); 23562306a36Sopenharmony_ci sge->len = cpu_to_le32(sg_dma_len(sg)); 23662306a36Sopenharmony_ci dev_dbg(dev, "addr = %llx, len = %u\n", sge->addr, sge->len); 23762306a36Sopenharmony_ci sge++; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci sgl_addr = dma_map_single(dev, sgl, sgl_size, DMA_TO_DEVICE); 24162306a36Sopenharmony_ci if (dma_mapping_error(dev, sgl_addr)) { 24262306a36Sopenharmony_ci err = -EIO; 24362306a36Sopenharmony_ci goto out_free_sgl; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci lm_file->sgl = sgl; 24762306a36Sopenharmony_ci lm_file->sgl_addr = sgl_addr; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ciout_free_sgl: 25262306a36Sopenharmony_ci kfree(sgl); 25362306a36Sopenharmony_ciout_unmap_sgtable: 25462306a36Sopenharmony_ci lm_file->num_sge = 0; 25562306a36Sopenharmony_ci dma_unmap_sgtable(dev, &lm_file->sg_table, dir, 0); 25662306a36Sopenharmony_ci return err; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void pds_vfio_dma_unmap_lm_file(struct device *dev, 26062306a36Sopenharmony_ci enum dma_data_direction dir, 26162306a36Sopenharmony_ci struct pds_vfio_lm_file *lm_file) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci if (!lm_file) 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* free sgl */ 26762306a36Sopenharmony_ci if (lm_file->sgl) { 26862306a36Sopenharmony_ci dma_unmap_single(dev, lm_file->sgl_addr, 26962306a36Sopenharmony_ci lm_file->num_sge * sizeof(*lm_file->sgl), 27062306a36Sopenharmony_ci DMA_TO_DEVICE); 27162306a36Sopenharmony_ci kfree(lm_file->sgl); 27262306a36Sopenharmony_ci lm_file->sgl = NULL; 27362306a36Sopenharmony_ci lm_file->sgl_addr = DMA_MAPPING_ERROR; 27462306a36Sopenharmony_ci lm_file->num_sge = 0; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* dma unmap file pages */ 27862306a36Sopenharmony_ci dma_unmap_sgtable(dev, &lm_file->sg_table, dir, 0); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciint pds_vfio_get_lm_state_cmd(struct pds_vfio_pci_device *pds_vfio) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 28462306a36Sopenharmony_ci .lm_save = { 28562306a36Sopenharmony_ci .opcode = PDS_LM_CMD_SAVE, 28662306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 28762306a36Sopenharmony_ci }, 28862306a36Sopenharmony_ci }; 28962306a36Sopenharmony_ci struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio); 29062306a36Sopenharmony_ci struct device *pdsc_dev = &pci_physfn(pdev)->dev; 29162306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 29262306a36Sopenharmony_ci struct pds_vfio_lm_file *lm_file; 29362306a36Sopenharmony_ci int err; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "vf%u: Get migration state\n", pds_vfio->vf_id); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci lm_file = pds_vfio->save_file; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci err = pds_vfio_dma_map_lm_file(pdsc_dev, DMA_FROM_DEVICE, lm_file); 30062306a36Sopenharmony_ci if (err) { 30162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to map save migration file: %pe\n", 30262306a36Sopenharmony_ci ERR_PTR(err)); 30362306a36Sopenharmony_ci return err; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci cmd.lm_save.sgl_addr = cpu_to_le64(lm_file->sgl_addr); 30762306a36Sopenharmony_ci cmd.lm_save.num_sge = cpu_to_le32(lm_file->num_sge); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false); 31062306a36Sopenharmony_ci if (err) 31162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get migration state: %pe\n", 31262306a36Sopenharmony_ci ERR_PTR(err)); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci pds_vfio_dma_unmap_lm_file(pdsc_dev, DMA_FROM_DEVICE, lm_file); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return err; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciint pds_vfio_set_lm_state_cmd(struct pds_vfio_pci_device *pds_vfio) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 32262306a36Sopenharmony_ci .lm_restore = { 32362306a36Sopenharmony_ci .opcode = PDS_LM_CMD_RESTORE, 32462306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 32562306a36Sopenharmony_ci }, 32662306a36Sopenharmony_ci }; 32762306a36Sopenharmony_ci struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio); 32862306a36Sopenharmony_ci struct device *pdsc_dev = &pci_physfn(pdev)->dev; 32962306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 33062306a36Sopenharmony_ci struct pds_vfio_lm_file *lm_file; 33162306a36Sopenharmony_ci int err; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "vf%u: Set migration state\n", pds_vfio->vf_id); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci lm_file = pds_vfio->restore_file; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci err = pds_vfio_dma_map_lm_file(pdsc_dev, DMA_TO_DEVICE, lm_file); 33862306a36Sopenharmony_ci if (err) { 33962306a36Sopenharmony_ci dev_err(&pdev->dev, 34062306a36Sopenharmony_ci "failed to map restore migration file: %pe\n", 34162306a36Sopenharmony_ci ERR_PTR(err)); 34262306a36Sopenharmony_ci return err; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci cmd.lm_restore.sgl_addr = cpu_to_le64(lm_file->sgl_addr); 34662306a36Sopenharmony_ci cmd.lm_restore.num_sge = cpu_to_le32(lm_file->num_sge); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false); 34962306a36Sopenharmony_ci if (err) 35062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to set migration state: %pe\n", 35162306a36Sopenharmony_ci ERR_PTR(err)); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci pds_vfio_dma_unmap_lm_file(pdsc_dev, DMA_TO_DEVICE, lm_file); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return err; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_civoid pds_vfio_send_host_vf_lm_status_cmd(struct pds_vfio_pci_device *pds_vfio, 35962306a36Sopenharmony_ci enum pds_lm_host_vf_status vf_status) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 36262306a36Sopenharmony_ci .lm_host_vf_status = { 36362306a36Sopenharmony_ci .opcode = PDS_LM_CMD_HOST_VF_STATUS, 36462306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 36562306a36Sopenharmony_ci .status = vf_status, 36662306a36Sopenharmony_ci }, 36762306a36Sopenharmony_ci }; 36862306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 36962306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 37062306a36Sopenharmony_ci int err; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci dev_dbg(dev, "vf%u: Set host VF LM status: %u", pds_vfio->vf_id, 37362306a36Sopenharmony_ci vf_status); 37462306a36Sopenharmony_ci if (vf_status != PDS_LM_STA_IN_PROGRESS && 37562306a36Sopenharmony_ci vf_status != PDS_LM_STA_NONE) { 37662306a36Sopenharmony_ci dev_warn(dev, "Invalid host VF migration status, %d\n", 37762306a36Sopenharmony_ci vf_status); 37862306a36Sopenharmony_ci return; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false); 38262306a36Sopenharmony_ci if (err) 38362306a36Sopenharmony_ci dev_warn(dev, "failed to send host VF migration status: %pe\n", 38462306a36Sopenharmony_ci ERR_PTR(err)); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ciint pds_vfio_dirty_status_cmd(struct pds_vfio_pci_device *pds_vfio, 38862306a36Sopenharmony_ci u64 regions_dma, u8 *max_regions, u8 *num_regions) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 39162306a36Sopenharmony_ci .lm_dirty_status = { 39262306a36Sopenharmony_ci .opcode = PDS_LM_CMD_DIRTY_STATUS, 39362306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 39462306a36Sopenharmony_ci }, 39562306a36Sopenharmony_ci }; 39662306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 39762306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 39862306a36Sopenharmony_ci int err; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci dev_dbg(dev, "vf%u: Dirty status\n", pds_vfio->vf_id); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci cmd.lm_dirty_status.regions_dma = cpu_to_le64(regions_dma); 40362306a36Sopenharmony_ci cmd.lm_dirty_status.max_regions = *max_regions; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false); 40662306a36Sopenharmony_ci if (err) { 40762306a36Sopenharmony_ci dev_err(dev, "failed to get dirty status: %pe\n", ERR_PTR(err)); 40862306a36Sopenharmony_ci return err; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* only support seq_ack approach for now */ 41262306a36Sopenharmony_ci if (!(le32_to_cpu(comp.lm_dirty_status.bmp_type_mask) & 41362306a36Sopenharmony_ci BIT(PDS_LM_DIRTY_BMP_TYPE_SEQ_ACK))) { 41462306a36Sopenharmony_ci dev_err(dev, "Dirty bitmap tracking SEQ_ACK not supported\n"); 41562306a36Sopenharmony_ci return -EOPNOTSUPP; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci *num_regions = comp.lm_dirty_status.num_regions; 41962306a36Sopenharmony_ci *max_regions = comp.lm_dirty_status.max_regions; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci dev_dbg(dev, 42262306a36Sopenharmony_ci "Page Tracking Status command successful, max_regions: %d, num_regions: %d, bmp_type: %s\n", 42362306a36Sopenharmony_ci *max_regions, *num_regions, "PDS_LM_DIRTY_BMP_TYPE_SEQ_ACK"); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciint pds_vfio_dirty_enable_cmd(struct pds_vfio_pci_device *pds_vfio, 42962306a36Sopenharmony_ci u64 regions_dma, u8 num_regions) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 43262306a36Sopenharmony_ci .lm_dirty_enable = { 43362306a36Sopenharmony_ci .opcode = PDS_LM_CMD_DIRTY_ENABLE, 43462306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 43562306a36Sopenharmony_ci .regions_dma = cpu_to_le64(regions_dma), 43662306a36Sopenharmony_ci .bmp_type = PDS_LM_DIRTY_BMP_TYPE_SEQ_ACK, 43762306a36Sopenharmony_ci .num_regions = num_regions, 43862306a36Sopenharmony_ci }, 43962306a36Sopenharmony_ci }; 44062306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 44162306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 44262306a36Sopenharmony_ci int err; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false); 44562306a36Sopenharmony_ci if (err) { 44662306a36Sopenharmony_ci dev_err(dev, "failed dirty tracking enable: %pe\n", 44762306a36Sopenharmony_ci ERR_PTR(err)); 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciint pds_vfio_dirty_disable_cmd(struct pds_vfio_pci_device *pds_vfio) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 45762306a36Sopenharmony_ci .lm_dirty_disable = { 45862306a36Sopenharmony_ci .opcode = PDS_LM_CMD_DIRTY_DISABLE, 45962306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 46062306a36Sopenharmony_ci }, 46162306a36Sopenharmony_ci }; 46262306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 46362306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 46462306a36Sopenharmony_ci int err; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false); 46762306a36Sopenharmony_ci if (err || comp.lm_dirty_status.num_regions != 0) { 46862306a36Sopenharmony_ci /* in case num_regions is still non-zero after disable */ 46962306a36Sopenharmony_ci err = err ? err : -EIO; 47062306a36Sopenharmony_ci dev_err(dev, 47162306a36Sopenharmony_ci "failed dirty tracking disable: %pe, num_regions %d\n", 47262306a36Sopenharmony_ci ERR_PTR(err), comp.lm_dirty_status.num_regions); 47362306a36Sopenharmony_ci return err; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ciint pds_vfio_dirty_seq_ack_cmd(struct pds_vfio_pci_device *pds_vfio, 48062306a36Sopenharmony_ci u64 sgl_dma, u16 num_sge, u32 offset, 48162306a36Sopenharmony_ci u32 total_len, bool read_seq) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci const char *cmd_type_str = read_seq ? "read_seq" : "write_ack"; 48462306a36Sopenharmony_ci union pds_core_adminq_cmd cmd = { 48562306a36Sopenharmony_ci .lm_dirty_seq_ack = { 48662306a36Sopenharmony_ci .vf_id = cpu_to_le16(pds_vfio->vf_id), 48762306a36Sopenharmony_ci .len_bytes = cpu_to_le32(total_len), 48862306a36Sopenharmony_ci .off_bytes = cpu_to_le32(offset), 48962306a36Sopenharmony_ci .sgl_addr = cpu_to_le64(sgl_dma), 49062306a36Sopenharmony_ci .num_sge = cpu_to_le16(num_sge), 49162306a36Sopenharmony_ci }, 49262306a36Sopenharmony_ci }; 49362306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 49462306a36Sopenharmony_ci union pds_core_adminq_comp comp = {}; 49562306a36Sopenharmony_ci int err; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (read_seq) 49862306a36Sopenharmony_ci cmd.lm_dirty_seq_ack.opcode = PDS_LM_CMD_DIRTY_READ_SEQ; 49962306a36Sopenharmony_ci else 50062306a36Sopenharmony_ci cmd.lm_dirty_seq_ack.opcode = PDS_LM_CMD_DIRTY_WRITE_ACK; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false); 50362306a36Sopenharmony_ci if (err) { 50462306a36Sopenharmony_ci dev_err(dev, "failed cmd Page Tracking %s: %pe\n", cmd_type_str, 50562306a36Sopenharmony_ci ERR_PTR(err)); 50662306a36Sopenharmony_ci return err; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 511