RE: [PATCH v8 8/9] hisi_acc_vfio_pci: Add support for VFIO live migration
From: Shameerali Kolothum Thodi
Date: Fri Mar 04 2022 - 03:48:43 EST
Hi Alex,
> -----Original Message-----
> From: Shameerali Kolothum Thodi
> Sent: 03 March 2022 23:02
> To: kvm@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx;
> linux-crypto@xxxxxxxxxxxxxxx
> Cc: linux-pci@xxxxxxxxxxxxxxx; alex.williamson@xxxxxxxxxx; jgg@xxxxxxxxxx;
> cohuck@xxxxxxxxxx; mgurtovoy@xxxxxxxxxx; yishaih@xxxxxxxxxx; Linuxarm
> <linuxarm@xxxxxxxxxx>; liulongfang <liulongfang@xxxxxxxxxx>; Zengtao (B)
> <prime.zeng@xxxxxxxxxxxxx>; Jonathan Cameron
> <jonathan.cameron@xxxxxxxxxx>; Wangzhou (B) <wangzhou1@xxxxxxxxxxxxx>
> Subject: [PATCH v8 8/9] hisi_acc_vfio_pci: Add support for VFIO live migration
>
> From: Longfang Liu <liulongfang@xxxxxxxxxx>
>
> VMs assigned with HiSilicon ACC VF devices can now perform live migration if
> the VF devices are bind to the hisi_acc_vfio_pci driver.
>
> Signed-off-by: Longfang Liu <liulongfang@xxxxxxxxxx>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@xxxxxxxxxx>
[...]
> +
> +static int vf_qm_check_match(struct hisi_acc_vf_core_device *hisi_acc_vdev,
> + struct hisi_acc_vf_migration_file *migf) {
> + struct acc_vf_data *vf_data = &migf->vf_data;
> + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm;
> + struct hisi_qm *pf_qm = &hisi_acc_vdev->vf_qm;
Oops, the above has to be,
struct hisi_qm *pf_qm = hisi_acc_vdev->pf_qm;
This was actually fixed in v6, but now that I rebased mainly to v5, missed it.
Please let me know if you want a re-spin with the above fix(in case there are no further
comments) or this is something you can take care.
Thanks,
Shameer
> + struct device *dev = &vf_qm->pdev->dev;
> + u32 que_iso_state;
> + int ret;
> +
> + if (migf->total_length < QM_MATCH_SIZE)
> + return -EINVAL;
> +
> + if (vf_data->acc_magic != ACC_DEV_MAGIC) {
> + dev_err(dev, "failed to match ACC_DEV_MAGIC\n");
> + return -EINVAL;
> + }
> +
> + if (vf_data->dev_id != hisi_acc_vdev->vf_dev->device) {
> + dev_err(dev, "failed to match VF devices\n");
> + return -EINVAL;
> + }
> +
> + /* vf qp num check */
> + ret = qm_get_vft(vf_qm, &vf_qm->qp_base);
> + if (ret <= 0) {
> + dev_err(dev, "failed to get vft qp nums\n");
> + return -EINVAL;
> + }
> +
> + if (ret != vf_data->qp_num) {
> + dev_err(dev, "failed to match VF qp num\n");
> + return -EINVAL;
> + }
> +
> + vf_qm->qp_num = ret;
> +
> + /* vf isolation state check */
> + ret = qm_read_reg(pf_qm, QM_QUE_ISO_CFG_V, &que_iso_state, 1);
> + if (ret) {
> + dev_err(dev, "failed to read QM_QUE_ISO_CFG_V\n");
> + return ret;
> + }
> +
> + if (vf_data->que_iso_cfg != que_iso_state) {
> + dev_err(dev, "failed to match isolation state\n");
> + return ret;
> + }
> +
> + ret = qm_write_reg(vf_qm, QM_VF_STATE, &vf_data->vf_qm_state, 1);
> + if (ret) {
> + dev_err(dev, "failed to write QM_VF_STATE\n");
> + return ret;
> + }
> +
> + hisi_acc_vdev->vf_qm_state = vf_data->vf_qm_state;
> + return 0;
> +}
> +
> +static int vf_qm_get_match_data(struct hisi_acc_vf_core_device
> *hisi_acc_vdev,
> + struct acc_vf_data *vf_data)
> +{
> + struct hisi_qm *pf_qm = hisi_acc_vdev->pf_qm;
> + struct device *dev = &pf_qm->pdev->dev;
> + int vf_id = hisi_acc_vdev->vf_id;
> + int ret;
> +
> + vf_data->acc_magic = ACC_DEV_MAGIC;
> + /* save device id */
> + vf_data->dev_id = hisi_acc_vdev->vf_dev->device;
> +
> + /* vf qp num save from PF */
> + ret = pf_qm_get_qp_num(pf_qm, vf_id, &vf_data->qp_base);
> + if (ret <= 0) {
> + dev_err(dev, "failed to get vft qp nums!\n");
> + return -EINVAL;
> + }
> +
> + vf_data->qp_num = ret;
> +
> + /* VF isolation state save from PF */
> + ret = qm_read_reg(pf_qm, QM_QUE_ISO_CFG_V, &vf_data->que_iso_cfg,
> 1);
> + if (ret) {
> + dev_err(dev, "failed to read QM_QUE_ISO_CFG_V!\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int vf_qm_load_data(struct hisi_acc_vf_core_device *hisi_acc_vdev,
> + struct hisi_acc_vf_migration_file *migf) {
> + struct hisi_qm *qm = &hisi_acc_vdev->vf_qm;
> + struct device *dev = &qm->pdev->dev;
> + struct acc_vf_data *vf_data = &migf->vf_data;
> + int ret;
> +
> + /* Return if only match data was transferred */
> + if (migf->total_length == QM_MATCH_SIZE)
> + return 0;
> +
> + if (migf->total_length < sizeof(struct acc_vf_data))
> + return -EINVAL;
> +
> + qm->eqe_dma = vf_data->eqe_dma;
> + qm->aeqe_dma = vf_data->aeqe_dma;
> + qm->sqc_dma = vf_data->sqc_dma;
> + qm->cqc_dma = vf_data->cqc_dma;
> +
> + qm->qp_base = vf_data->qp_base;
> + qm->qp_num = vf_data->qp_num;
> +
> + ret = qm_rw_regs_write(qm, vf_data);
> + if (ret) {
> + dev_err(dev, "Set VF regs failed\n");
> + return ret;
> + }
> +
> + ret = hisi_qm_mb(qm, QM_MB_CMD_SQC_BT, qm->sqc_dma, 0, 0);
> + if (ret) {
> + dev_err(dev, "Set sqc failed\n");
> + return ret;
> + }
> +
> + ret = hisi_qm_mb(qm, QM_MB_CMD_CQC_BT, qm->cqc_dma, 0, 0);
> + if (ret) {
> + dev_err(dev, "Set cqc failed\n");
> + return ret;
> + }
> +
> + qm_dev_cmd_init(qm);
> + return 0;
> +}
> +
> +static int vf_qm_state_save(struct hisi_acc_vf_core_device *hisi_acc_vdev,
> + struct hisi_acc_vf_migration_file *migf) {
> + struct acc_vf_data *vf_data = &migf->vf_data;
> + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm;
> + struct device *dev = &vf_qm->pdev->dev;
> + int ret;
> +
> + ret = vf_qm_get_match_data(hisi_acc_vdev, vf_data);
> + if (ret)
> + return ret;
> +
> + if (unlikely(qm_wait_dev_not_ready(vf_qm))) {
> + /* Update state and return with match data */
> + vf_data->vf_qm_state = QM_NOT_READY;
> + hisi_acc_vdev->vf_qm_state = vf_data->vf_qm_state;
> + migf->total_length = QM_MATCH_SIZE;
> + return 0;
> + }
> +
> + vf_data->vf_qm_state = QM_READY;
> + hisi_acc_vdev->vf_qm_state = vf_data->vf_qm_state;
> +
> + ret = vf_qm_cache_wb(vf_qm);
> + if (ret) {
> + dev_err(dev, "failed to writeback QM Cache!\n");
> + return ret;
> + }
> +
> + ret = qm_rw_regs_read(vf_qm, vf_data);
> + if (ret)
> + return -EINVAL;
> +
> + /* Every reg is 32 bit, the dma address is 64 bit. */
> + vf_data->eqe_dma = vf_data->qm_eqc_dw[2];
> + vf_data->eqe_dma <<= QM_XQC_ADDR_OFFSET;
> + vf_data->eqe_dma |= vf_data->qm_eqc_dw[1];
> + vf_data->aeqe_dma = vf_data->qm_aeqc_dw[2];
> + vf_data->aeqe_dma <<= QM_XQC_ADDR_OFFSET;
> + vf_data->aeqe_dma |= vf_data->qm_aeqc_dw[1];
> +
> + /* Through SQC_BT/CQC_BT to get sqc and cqc address */
> + ret = qm_get_sqc(vf_qm, &vf_data->sqc_dma);
> + if (ret) {
> + dev_err(dev, "failed to read SQC addr!\n");
> + return -EINVAL;
> + }
> +
> + ret = qm_get_cqc(vf_qm, &vf_data->cqc_dma);
> + if (ret) {
> + dev_err(dev, "failed to read CQC addr!\n");
> + return -EINVAL;
> + }
> +
> + migf->total_length = sizeof(struct acc_vf_data);
> + return 0;
> +}
> +
> +static void hisi_acc_vf_disable_fd(struct hisi_acc_vf_migration_file
> +*migf) {
> + mutex_lock(&migf->lock);
> + migf->disabled = true;
> + migf->total_length = 0;
> + migf->filp->f_pos = 0;
> + mutex_unlock(&migf->lock);
> +}
> +
> +static void hisi_acc_vf_disable_fds(struct hisi_acc_vf_core_device
> +*hisi_acc_vdev) {
> + if (hisi_acc_vdev->resuming_migf) {
> + hisi_acc_vf_disable_fd(hisi_acc_vdev->resuming_migf);
> + fput(hisi_acc_vdev->resuming_migf->filp);
> + hisi_acc_vdev->resuming_migf = NULL;
> + }
> +
> + if (hisi_acc_vdev->saving_migf) {
> + hisi_acc_vf_disable_fd(hisi_acc_vdev->saving_migf);
> + fput(hisi_acc_vdev->saving_migf->filp);
> + hisi_acc_vdev->saving_migf = NULL;
> + }
> +}
> +
> +static void hisi_acc_vf_start_device(struct hisi_acc_vf_core_device
> +*hisi_acc_vdev) {
> + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm;
> +
> + if (hisi_acc_vdev->vf_qm_state != QM_READY)
> + return;
> +
> + vf_qm_fun_reset(hisi_acc_vdev, vf_qm); }
> +
> +static int hisi_acc_vf_load_state(struct hisi_acc_vf_core_device
> +*hisi_acc_vdev) {
> + struct device *dev = &hisi_acc_vdev->vf_dev->dev;
> + struct hisi_acc_vf_migration_file *migf = hisi_acc_vdev->resuming_migf;
> + int ret;
> +
> + /* Check dev compatibility */
> + ret = vf_qm_check_match(hisi_acc_vdev, migf);
> + if (ret) {
> + dev_err(dev, "failed to match the VF!\n");
> + return ret;
> + }
> + /* Recover data to VF */
> + ret = vf_qm_load_data(hisi_acc_vdev, migf);
> + if (ret) {
> + dev_err(dev, "failed to recover the VF!\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int hisi_acc_vf_release_file(struct inode *inode, struct file
> +*filp) {
> + struct hisi_acc_vf_migration_file *migf = filp->private_data;
> +
> + hisi_acc_vf_disable_fd(migf);
> + mutex_destroy(&migf->lock);
> + kfree(migf);
> + return 0;
> +}
> +
> +static ssize_t hisi_acc_vf_resume_write(struct file *filp, const char __user
> *buf,
> + size_t len, loff_t *pos)
> +{
> + struct hisi_acc_vf_migration_file *migf = filp->private_data;
> + loff_t requested_length;
> + ssize_t done = 0;
> + int ret;
> +
> + if (pos)
> + return -ESPIPE;
> + pos = &filp->f_pos;
> +
> + if (*pos < 0 ||
> + check_add_overflow((loff_t)len, *pos, &requested_length))
> + return -EINVAL;
> +
> + if (requested_length > sizeof(struct acc_vf_data))
> + return -ENOMEM;
> +
> + mutex_lock(&migf->lock);
> + if (migf->disabled) {
> + done = -ENODEV;
> + goto out_unlock;
> + }
> +
> + ret = copy_from_user(&migf->vf_data, buf, len);
> + if (ret) {
> + done = -EFAULT;
> + goto out_unlock;
> + }
> + *pos += len;
> + done = len;
> + migf->total_length += len;
> +out_unlock:
> + mutex_unlock(&migf->lock);
> + return done;
> +}
> +
> +static const struct file_operations hisi_acc_vf_resume_fops = {
> + .owner = THIS_MODULE,
> + .write = hisi_acc_vf_resume_write,
> + .release = hisi_acc_vf_release_file,
> + .llseek = no_llseek,
> +};
> +
> +static struct hisi_acc_vf_migration_file *
> +hisi_acc_vf_pci_resume(struct hisi_acc_vf_core_device *hisi_acc_vdev) {
> + struct hisi_acc_vf_migration_file *migf;
> +
> + migf = kzalloc(sizeof(*migf), GFP_KERNEL);
> + if (!migf)
> + return ERR_PTR(-ENOMEM);
> +
> + migf->filp = anon_inode_getfile("hisi_acc_vf_mig",
> &hisi_acc_vf_resume_fops, migf,
> + O_WRONLY);
> + if (IS_ERR(migf->filp)) {
> + int err = PTR_ERR(migf->filp);
> +
> + kfree(migf);
> + return ERR_PTR(err);
> + }
> +
> + stream_open(migf->filp->f_inode, migf->filp);
> + mutex_init(&migf->lock);
> + return migf;
> +}
> +
> +static ssize_t hisi_acc_vf_save_read(struct file *filp, char __user *buf, size_t
> len,
> + loff_t *pos)
> +{
> + struct hisi_acc_vf_migration_file *migf = filp->private_data;
> + ssize_t done = 0;
> + int ret;
> +
> + if (pos)
> + return -ESPIPE;
> + pos = &filp->f_pos;
> +
> + mutex_lock(&migf->lock);
> + if (*pos > migf->total_length) {
> + done = -EINVAL;
> + goto out_unlock;
> + }
> +
> + if (migf->disabled) {
> + done = -ENODEV;
> + goto out_unlock;
> + }
> +
> + len = min_t(size_t, migf->total_length - *pos, len);
> + if (len) {
> + ret = copy_to_user(buf, &migf->vf_data, len);
> + if (ret) {
> + done = -EFAULT;
> + goto out_unlock;
> + }
> + *pos += len;
> + done = len;
> + }
> +out_unlock:
> + mutex_unlock(&migf->lock);
> + return done;
> +}
> +
> +static const struct file_operations hisi_acc_vf_save_fops = {
> + .owner = THIS_MODULE,
> + .read = hisi_acc_vf_save_read,
> + .release = hisi_acc_vf_release_file,
> + .llseek = no_llseek,
> +};
> +
> +static struct hisi_acc_vf_migration_file * hisi_acc_vf_stop_copy(struct
> +hisi_acc_vf_core_device *hisi_acc_vdev) {
> + struct hisi_acc_vf_migration_file *migf;
> + int ret;
> +
> + migf = kzalloc(sizeof(*migf), GFP_KERNEL);
> + if (!migf)
> + return ERR_PTR(-ENOMEM);
> +
> + migf->filp = anon_inode_getfile("hisi_acc_vf_mig",
> &hisi_acc_vf_save_fops, migf,
> + O_RDONLY);
> + if (IS_ERR(migf->filp)) {
> + int err = PTR_ERR(migf->filp);
> +
> + kfree(migf);
> + return ERR_PTR(err);
> + }
> +
> + stream_open(migf->filp->f_inode, migf->filp);
> + mutex_init(&migf->lock);
> +
> + ret = vf_qm_state_save(hisi_acc_vdev, migf);
> + if (ret) {
> + fput(migf->filp);
> + return ERR_PTR(ret);
> + }
> +
> + return migf;
> +}
> +
> +static int hisi_acc_vf_stop_device(struct hisi_acc_vf_core_device
> +*hisi_acc_vdev) {
> + struct device *dev = &hisi_acc_vdev->vf_dev->dev;
> + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm;
> + int ret;
> +
> + ret = vf_qm_func_stop(vf_qm);
> + if (ret) {
> + dev_err(dev, "failed to stop QM VF function!\n");
> + return ret;
> + }
> +
> + ret = qm_check_int_state(hisi_acc_vdev);
> + if (ret) {
> + dev_err(dev, "failed to check QM INT state!\n");
> + return ret;
> + }
> + return 0;
> +}
> +
> +static struct file *
> +hisi_acc_vf_set_device_state(struct hisi_acc_vf_core_device *hisi_acc_vdev,
> + u32 new)
> +{
> + u32 cur = hisi_acc_vdev->mig_state;
> + int ret;
> +
> + if (cur == VFIO_DEVICE_STATE_RUNNING && new ==
> VFIO_DEVICE_STATE_STOP) {
> + ret = hisi_acc_vf_stop_device(hisi_acc_vdev);
> + if (ret)
> + return ERR_PTR(ret);
> + return NULL;
> + }
> +
> + if (cur == VFIO_DEVICE_STATE_STOP && new ==
> VFIO_DEVICE_STATE_STOP_COPY) {
> + struct hisi_acc_vf_migration_file *migf;
> +
> + migf = hisi_acc_vf_stop_copy(hisi_acc_vdev);
> + if (IS_ERR(migf))
> + return ERR_CAST(migf);
> + get_file(migf->filp);
> + hisi_acc_vdev->saving_migf = migf;
> + return migf->filp;
> + }
> +
> + if ((cur == VFIO_DEVICE_STATE_STOP_COPY && new ==
> VFIO_DEVICE_STATE_STOP)) {
> + hisi_acc_vf_disable_fds(hisi_acc_vdev);
> + return NULL;
> + }
> +
> + if (cur == VFIO_DEVICE_STATE_STOP && new ==
> VFIO_DEVICE_STATE_RESUMING) {
> + struct hisi_acc_vf_migration_file *migf;
> +
> + migf = hisi_acc_vf_pci_resume(hisi_acc_vdev);
> + if (IS_ERR(migf))
> + return ERR_CAST(migf);
> + get_file(migf->filp);
> + hisi_acc_vdev->resuming_migf = migf;
> + return migf->filp;
> + }
> +
> + if (cur == VFIO_DEVICE_STATE_RESUMING && new ==
> VFIO_DEVICE_STATE_STOP) {
> + ret = hisi_acc_vf_load_state(hisi_acc_vdev);
> + if (ret)
> + return ERR_PTR(ret);
> + hisi_acc_vf_disable_fds(hisi_acc_vdev);
> + return NULL;
> + }
> +
> + if (cur == VFIO_DEVICE_STATE_STOP && new ==
> VFIO_DEVICE_STATE_RUNNING) {
> + hisi_acc_vf_start_device(hisi_acc_vdev);
> + return NULL;
> + }
> +
> + /*
> + * vfio_mig_get_next_state() does not use arcs other than the above
> + */
> + WARN_ON(true);
> + return ERR_PTR(-EINVAL);
> +}
> +
> +static struct file *
> +hisi_acc_vfio_pci_set_device_state(struct vfio_device *vdev,
> + enum vfio_device_mig_state new_state) {
> + struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(vdev,
> + struct hisi_acc_vf_core_device, core_device.vdev);
> + enum vfio_device_mig_state next_state;
> + struct file *res = NULL;
> + int ret;
> +
> + mutex_lock(&hisi_acc_vdev->state_mutex);
> + while (new_state != hisi_acc_vdev->mig_state) {
> + ret = vfio_mig_get_next_state(vdev,
> + hisi_acc_vdev->mig_state,
> + new_state, &next_state);
> + if (ret) {
> + res = ERR_PTR(-EINVAL);
> + break;
> + }
> +
> + res = hisi_acc_vf_set_device_state(hisi_acc_vdev, next_state);
> + if (IS_ERR(res))
> + break;
> + hisi_acc_vdev->mig_state = next_state;
> + if (WARN_ON(res && new_state != hisi_acc_vdev->mig_state)) {
> + fput(res);
> + res = ERR_PTR(-EINVAL);
> + break;
> + }
> + }
> + mutex_unlock(&hisi_acc_vdev->state_mutex);
> + return res;
> +}
> +
> +static int
> +hisi_acc_vfio_pci_get_device_state(struct vfio_device *vdev,
> + enum vfio_device_mig_state *curr_state) {
> + struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(vdev,
> + struct hisi_acc_vf_core_device, core_device.vdev);
> +
> + mutex_lock(&hisi_acc_vdev->state_mutex);
> + *curr_state = hisi_acc_vdev->mig_state;
> + mutex_unlock(&hisi_acc_vdev->state_mutex);
> + return 0;
> +}
> +
> +static int hisi_acc_vf_qm_init(struct hisi_acc_vf_core_device
> +*hisi_acc_vdev) {
> + struct vfio_pci_core_device *vdev = &hisi_acc_vdev->core_device;
> + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm;
> + struct pci_dev *vf_dev = vdev->pdev;
> +
> + /*
> + * ACC VF dev BAR2 region consists of both functional register space
> + * and migration control register space. For migration to work, we
> + * need access to both. Hence, we map the entire BAR2 region here.
> + * But from a security point of view, we restrict access to the
> + * migration control space from Guest(Please see mmap/ioctl/read/write
> + * override functions).
> + *
> + * Also the HiSilicon ACC VF devices supported by this driver on
> + * HiSilicon hardware platforms are integrated end point devices
> + * and has no capability to perform PCIe P2P.
> + */
> +
> + vf_qm->io_base =
> + ioremap(pci_resource_start(vf_dev, VFIO_PCI_BAR2_REGION_INDEX),
> + pci_resource_len(vf_dev, VFIO_PCI_BAR2_REGION_INDEX));
> + if (!vf_qm->io_base)
> + return -EIO;
> +
> + vf_qm->fun_type = QM_HW_VF;
> + vf_qm->pdev = vf_dev;
> + mutex_init(&vf_qm->mailbox_lock);
> +
> + return 0;
> +}
> +
> +static struct hisi_qm *hisi_acc_get_pf_qm(struct pci_dev *pdev) {
> + struct hisi_qm *pf_qm;
> + struct pci_driver *pf_driver;
> +
> + if (!pdev->is_virtfn)
> + return NULL;
> +
> + switch (pdev->device) {
> + case PCI_DEVICE_ID_HUAWEI_SEC_VF:
> + pf_driver = hisi_sec_get_pf_driver();
> + break;
> + case PCI_DEVICE_ID_HUAWEI_HPRE_VF:
> + pf_driver = hisi_hpre_get_pf_driver();
> + break;
> + case PCI_DEVICE_ID_HUAWEI_ZIP_VF:
> + pf_driver = hisi_zip_get_pf_driver();
> + break;
> + default:
> + return NULL;
> + }
> +
> + if (!pf_driver)
> + return NULL;
> +
> + pf_qm = pci_iov_get_pf_drvdata(pdev, pf_driver);
> +
> + return !IS_ERR(pf_qm) ? pf_qm : NULL;
> +}
>
> static int hisi_acc_pci_rw_access_check(struct vfio_device *core_vdev,
> size_t count, loff_t *ppos,
> @@ -128,23 +1115,42 @@ static long hisi_acc_vfio_pci_ioctl(struct
> vfio_device *core_vdev, unsigned int
>
> static int hisi_acc_vfio_pci_open_device(struct vfio_device *core_vdev) {
> - struct vfio_pci_core_device *vdev =
> - container_of(core_vdev, struct vfio_pci_core_device, vdev);
> + struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(core_vdev,
> + struct hisi_acc_vf_core_device, core_device.vdev);
> + struct vfio_pci_core_device *vdev = &hisi_acc_vdev->core_device;
> int ret;
>
> ret = vfio_pci_core_enable(vdev);
> if (ret)
> return ret;
>
> - vfio_pci_core_finish_enable(vdev);
> + if (core_vdev->ops->migration_set_state) {
> + ret = hisi_acc_vf_qm_init(hisi_acc_vdev);
> + if (ret) {
> + vfio_pci_core_disable(vdev);
> + return ret;
> + }
> + hisi_acc_vdev->mig_state = VFIO_DEVICE_STATE_RUNNING;
> + }
>
> + vfio_pci_core_finish_enable(vdev);
> return 0;
> }
>
> +static void hisi_acc_vfio_pci_close_device(struct vfio_device
> +*core_vdev) {
> + struct hisi_acc_vf_core_device *hisi_acc_vdev = container_of(core_vdev,
> + struct hisi_acc_vf_core_device, core_device.vdev);
> + struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm;
> +
> + iounmap(vf_qm->io_base);
> + vfio_pci_core_close_device(core_vdev);
> +}
> +
> static const struct vfio_device_ops hisi_acc_vfio_pci_migrn_ops = {
> .name = "hisi-acc-vfio-pci-migration",
> .open_device = hisi_acc_vfio_pci_open_device,
> - .close_device = vfio_pci_core_close_device,
> + .close_device = hisi_acc_vfio_pci_close_device,
> .ioctl = hisi_acc_vfio_pci_ioctl,
> .device_feature = vfio_pci_core_ioctl_feature,
> .read = hisi_acc_vfio_pci_read,
> @@ -152,6 +1158,8 @@ static const struct vfio_device_ops
> hisi_acc_vfio_pci_migrn_ops = {
> .mmap = hisi_acc_vfio_pci_mmap,
> .request = vfio_pci_core_request,
> .match = vfio_pci_core_match,
> + .migration_set_state = hisi_acc_vfio_pci_set_device_state,
> + .migration_get_state = hisi_acc_vfio_pci_get_device_state,
> };
>
> static const struct vfio_device_ops hisi_acc_vfio_pci_ops = { @@ -167,38
> +1175,72 @@ static const struct vfio_device_ops hisi_acc_vfio_pci_ops = {
> .match = vfio_pci_core_match,
> };
>
> +static int
> +hisi_acc_vfio_pci_migrn_init(struct hisi_acc_vf_core_device *hisi_acc_vdev,
> + struct pci_dev *pdev, struct hisi_qm *pf_qm) {
> + int vf_id;
> +
> + vf_id = pci_iov_vf_id(pdev);
> + if (vf_id < 0)
> + return vf_id;
> +
> + hisi_acc_vdev->vf_id = vf_id + 1;
> + hisi_acc_vdev->core_device.vdev.migration_flags =
> + VFIO_MIGRATION_STOP_COPY;
> + hisi_acc_vdev->pf_qm = pf_qm;
> + hisi_acc_vdev->vf_dev = pdev;
> + mutex_init(&hisi_acc_vdev->state_mutex);
> +
> + return 0;
> +}
> +
> static int hisi_acc_vfio_pci_probe(struct pci_dev *pdev, const struct
> pci_device_id *id) {
> - struct vfio_pci_core_device *vdev;
> + struct hisi_acc_vf_core_device *hisi_acc_vdev;
> + struct hisi_qm *pf_qm;
> int ret;
>
> - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
> - if (!vdev)
> + hisi_acc_vdev = kzalloc(sizeof(*hisi_acc_vdev), GFP_KERNEL);
> + if (!hisi_acc_vdev)
> return -ENOMEM;
>
> - vfio_pci_core_init_device(vdev, pdev, &hisi_acc_vfio_pci_ops);
> + pf_qm = hisi_acc_get_pf_qm(pdev);
> + if (pf_qm && pf_qm->ver >= QM_HW_V3) {
> + ret = hisi_acc_vfio_pci_migrn_init(hisi_acc_vdev, pdev, pf_qm);
> + if (!ret) {
> + vfio_pci_core_init_device(&hisi_acc_vdev->core_device, pdev,
> + &hisi_acc_vfio_pci_migrn_ops);
> + } else {
> + pci_warn(pdev, "migration support failed, continue with generic
> interface\n");
> + vfio_pci_core_init_device(&hisi_acc_vdev->core_device, pdev,
> + &hisi_acc_vfio_pci_ops);
> + }
> + } else {
> + vfio_pci_core_init_device(&hisi_acc_vdev->core_device, pdev,
> + &hisi_acc_vfio_pci_ops);
> + }
>
> - ret = vfio_pci_core_register_device(vdev);
> + ret = vfio_pci_core_register_device(&hisi_acc_vdev->core_device);
> if (ret)
> goto out_free;
>
> - dev_set_drvdata(&pdev->dev, vdev);
> -
> + dev_set_drvdata(&pdev->dev, hisi_acc_vdev);
> return 0;
>
> out_free:
> - vfio_pci_core_uninit_device(vdev);
> - kfree(vdev);
> + vfio_pci_core_uninit_device(&hisi_acc_vdev->core_device);
> + kfree(hisi_acc_vdev);
> return ret;
> }
>
> static void hisi_acc_vfio_pci_remove(struct pci_dev *pdev) {
> - struct vfio_pci_core_device *vdev = dev_get_drvdata(&pdev->dev);
> + struct hisi_acc_vf_core_device *hisi_acc_vdev =
> +dev_get_drvdata(&pdev->dev);
>
> - vfio_pci_core_unregister_device(vdev);
> - vfio_pci_core_uninit_device(vdev);
> - kfree(vdev);
> + vfio_pci_core_unregister_device(&hisi_acc_vdev->core_device);
> + vfio_pci_core_uninit_device(&hisi_acc_vdev->core_device);
> + kfree(hisi_acc_vdev);
> }
>
> static const struct pci_device_id hisi_acc_vfio_pci_table[] = { @@ -223,4
> +1265,4 @@ module_pci_driver(hisi_acc_vfio_pci_driver);
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Liu Longfang <liulongfang@xxxxxxxxxx>");
> MODULE_AUTHOR("Shameer Kolothum
> <shameerali.kolothum.thodi@xxxxxxxxxx>");
> -MODULE_DESCRIPTION("HiSilicon VFIO PCI - Generic VFIO PCI driver for
> HiSilicon ACC device family");
> +MODULE_DESCRIPTION("HiSilicon VFIO PCI - VFIO PCI driver with live
> +migration support for HiSilicon ACC device family");
> diff --git a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h
> b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h
> new file mode 100644
> index 000000000000..1c7d75408790
> --- /dev/null
> +++ b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h
> @@ -0,0 +1,114 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2021 HiSilicon Ltd. */
> +
> +#ifndef HISI_ACC_VFIO_PCI_H
> +#define HISI_ACC_VFIO_PCI_H
> +
> +#include <linux/hisi_acc_qm.h>
> +
> +#define MB_POLL_PERIOD_US 10
> +#define MB_POLL_TIMEOUT_US 1000
> +#define QM_CACHE_WB_START 0x204
> +#define QM_CACHE_WB_DONE 0x208
> +#define QM_MB_CMD_PAUSE_QM 0xe
> +#define QM_ABNORMAL_INT_STATUS 0x100008
> +#define QM_IFC_INT_STATUS 0x0028
> +#define SEC_CORE_INT_STATUS 0x301008
> +#define HPRE_HAC_INT_STATUS 0x301800
> +#define HZIP_CORE_INT_STATUS 0x3010AC
> +#define QM_QUE_ISO_CFG 0x301154
> +
> +#define QM_VFT_CFG_RDY 0x10006c
> +#define QM_VFT_CFG_OP_WR 0x100058
> +#define QM_VFT_CFG_TYPE 0x10005c
> +#define QM_VFT_CFG 0x100060
> +#define QM_VFT_CFG_OP_ENABLE 0x100054
> +#define QM_VFT_CFG_DATA_L 0x100064
> +#define QM_VFT_CFG_DATA_H 0x100068
> +
> +#define ERROR_CHECK_TIMEOUT 100
> +#define CHECK_DELAY_TIME 100
> +
> +#define QM_SQC_VFT_BASE_SHIFT_V2 28
> +#define QM_SQC_VFT_BASE_MASK_V2 GENMASK(15, 0)
> +#define QM_SQC_VFT_NUM_SHIFT_V2 45
> +#define QM_SQC_VFT_NUM_MASK_V2 GENMASK(9, 0)
> +
> +/* RW regs */
> +#define QM_REGS_MAX_LEN 7
> +#define QM_REG_ADDR_OFFSET 0x0004
> +
> +#define QM_XQC_ADDR_OFFSET 32U
> +#define QM_VF_AEQ_INT_MASK 0x0004
> +#define QM_VF_EQ_INT_MASK 0x000c
> +#define QM_IFC_INT_SOURCE_V 0x0020
> +#define QM_IFC_INT_MASK 0x0024
> +#define QM_IFC_INT_SET_V 0x002c
> +#define QM_QUE_ISO_CFG_V 0x0030
> +#define QM_PAGE_SIZE 0x0034
> +
> +#define QM_EQC_DW0 0X8000
> +#define QM_AEQC_DW0 0X8020
> +
> +struct acc_vf_data {
> +#define QM_MATCH_SIZE offsetofend(struct acc_vf_data, qm_rsv_state)
> + /* QM match information */
> +#define ACC_DEV_MAGIC 0XCDCDCDCDFEEDAACC
> + u64 acc_magic;
> + u32 qp_num;
> + u32 dev_id;
> + u32 que_iso_cfg;
> + u32 qp_base;
> + u32 vf_qm_state;
> + /* QM reserved match information */
> + u32 qm_rsv_state[3];
> +
> + /* QM RW regs */
> + u32 aeq_int_mask;
> + u32 eq_int_mask;
> + u32 ifc_int_source;
> + u32 ifc_int_mask;
> + u32 ifc_int_set;
> + u32 page_size;
> +
> + /* QM_EQC_DW has 7 regs */
> + u32 qm_eqc_dw[7];
> +
> + /* QM_AEQC_DW has 7 regs */
> + u32 qm_aeqc_dw[7];
> +
> + /* QM reserved 5 regs */
> + u32 qm_rsv_regs[5];
> + u32 padding;
> + /* qm memory init information */
> + u64 eqe_dma;
> + u64 aeqe_dma;
> + u64 sqc_dma;
> + u64 cqc_dma;
> +};
> +
> +struct hisi_acc_vf_migration_file {
> + struct file *filp;
> + struct mutex lock;
> + bool disabled;
> +
> + struct acc_vf_data vf_data;
> + size_t total_length;
> +};
> +
> +struct hisi_acc_vf_core_device {
> + struct vfio_pci_core_device core_device;
> + /* for migration state */
> + struct mutex state_mutex;
> + enum vfio_device_mig_state mig_state;
> + struct pci_dev *pf_dev;
> + struct pci_dev *vf_dev;
> + struct hisi_qm *pf_qm;
> + struct hisi_qm vf_qm;
> + u32 vf_qm_state;
> + int vf_id;
> +
> + struct hisi_acc_vf_migration_file *resuming_migf;
> + struct hisi_acc_vf_migration_file *saving_migf; }; #endif /*
> +HISI_ACC_VFIO_PCI_H */
> --
> 2.25.1