162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 2023 Advanced Micro Devices, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/anon_inodes.h>
562306a36Sopenharmony_ci#include <linux/file.h>
662306a36Sopenharmony_ci#include <linux/fs.h>
762306a36Sopenharmony_ci#include <linux/highmem.h>
862306a36Sopenharmony_ci#include <linux/vfio.h>
962306a36Sopenharmony_ci#include <linux/vfio_pci_core.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "vfio_dev.h"
1262306a36Sopenharmony_ci#include "cmds.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic struct pds_vfio_lm_file *
1562306a36Sopenharmony_cipds_vfio_get_lm_file(const struct file_operations *fops, int flags, u64 size)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct pds_vfio_lm_file *lm_file = NULL;
1862306a36Sopenharmony_ci	unsigned long long npages;
1962306a36Sopenharmony_ci	struct page **pages;
2062306a36Sopenharmony_ci	void *page_mem;
2162306a36Sopenharmony_ci	const void *p;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (!size)
2462306a36Sopenharmony_ci		return NULL;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	/* Alloc file structure */
2762306a36Sopenharmony_ci	lm_file = kzalloc(sizeof(*lm_file), GFP_KERNEL);
2862306a36Sopenharmony_ci	if (!lm_file)
2962306a36Sopenharmony_ci		return NULL;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/* Create file */
3262306a36Sopenharmony_ci	lm_file->filep =
3362306a36Sopenharmony_ci		anon_inode_getfile("pds_vfio_lm", fops, lm_file, flags);
3462306a36Sopenharmony_ci	if (IS_ERR(lm_file->filep))
3562306a36Sopenharmony_ci		goto out_free_file;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	stream_open(lm_file->filep->f_inode, lm_file->filep);
3862306a36Sopenharmony_ci	mutex_init(&lm_file->lock);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/* prevent file from being released before we are done with it */
4162306a36Sopenharmony_ci	get_file(lm_file->filep);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Allocate memory for file pages */
4462306a36Sopenharmony_ci	npages = DIV_ROUND_UP_ULL(size, PAGE_SIZE);
4562306a36Sopenharmony_ci	pages = kmalloc_array(npages, sizeof(*pages), GFP_KERNEL);
4662306a36Sopenharmony_ci	if (!pages)
4762306a36Sopenharmony_ci		goto out_put_file;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	page_mem = kvzalloc(ALIGN(size, PAGE_SIZE), GFP_KERNEL);
5062306a36Sopenharmony_ci	if (!page_mem)
5162306a36Sopenharmony_ci		goto out_free_pages_array;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	p = page_mem - offset_in_page(page_mem);
5462306a36Sopenharmony_ci	for (unsigned long long i = 0; i < npages; i++) {
5562306a36Sopenharmony_ci		if (is_vmalloc_addr(p))
5662306a36Sopenharmony_ci			pages[i] = vmalloc_to_page(p);
5762306a36Sopenharmony_ci		else
5862306a36Sopenharmony_ci			pages[i] = kmap_to_page((void *)p);
5962306a36Sopenharmony_ci		if (!pages[i])
6062306a36Sopenharmony_ci			goto out_free_page_mem;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		p += PAGE_SIZE;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/* Create scatterlist of file pages to use for DMA mapping later */
6662306a36Sopenharmony_ci	if (sg_alloc_table_from_pages(&lm_file->sg_table, pages, npages, 0,
6762306a36Sopenharmony_ci				      size, GFP_KERNEL))
6862306a36Sopenharmony_ci		goto out_free_page_mem;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	lm_file->size = size;
7162306a36Sopenharmony_ci	lm_file->pages = pages;
7262306a36Sopenharmony_ci	lm_file->npages = npages;
7362306a36Sopenharmony_ci	lm_file->page_mem = page_mem;
7462306a36Sopenharmony_ci	lm_file->alloc_size = npages * PAGE_SIZE;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return lm_file;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciout_free_page_mem:
7962306a36Sopenharmony_ci	kvfree(page_mem);
8062306a36Sopenharmony_ciout_free_pages_array:
8162306a36Sopenharmony_ci	kfree(pages);
8262306a36Sopenharmony_ciout_put_file:
8362306a36Sopenharmony_ci	fput(lm_file->filep);
8462306a36Sopenharmony_ci	mutex_destroy(&lm_file->lock);
8562306a36Sopenharmony_ciout_free_file:
8662306a36Sopenharmony_ci	kfree(lm_file);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return NULL;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void pds_vfio_put_lm_file(struct pds_vfio_lm_file *lm_file)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	mutex_lock(&lm_file->lock);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	lm_file->size = 0;
9662306a36Sopenharmony_ci	lm_file->alloc_size = 0;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Free scatter list of file pages */
9962306a36Sopenharmony_ci	sg_free_table(&lm_file->sg_table);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	kvfree(lm_file->page_mem);
10262306a36Sopenharmony_ci	lm_file->page_mem = NULL;
10362306a36Sopenharmony_ci	kfree(lm_file->pages);
10462306a36Sopenharmony_ci	lm_file->pages = NULL;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	mutex_unlock(&lm_file->lock);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* allow file to be released since we are done with it */
10962306a36Sopenharmony_ci	fput(lm_file->filep);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid pds_vfio_put_save_file(struct pds_vfio_pci_device *pds_vfio)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (!pds_vfio->save_file)
11562306a36Sopenharmony_ci		return;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	pds_vfio_put_lm_file(pds_vfio->save_file);
11862306a36Sopenharmony_ci	pds_vfio->save_file = NULL;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid pds_vfio_put_restore_file(struct pds_vfio_pci_device *pds_vfio)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	if (!pds_vfio->restore_file)
12462306a36Sopenharmony_ci		return;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	pds_vfio_put_lm_file(pds_vfio->restore_file);
12762306a36Sopenharmony_ci	pds_vfio->restore_file = NULL;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic struct page *pds_vfio_get_file_page(struct pds_vfio_lm_file *lm_file,
13162306a36Sopenharmony_ci					   unsigned long offset)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned long cur_offset = 0;
13462306a36Sopenharmony_ci	struct scatterlist *sg;
13562306a36Sopenharmony_ci	unsigned int i;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* All accesses are sequential */
13862306a36Sopenharmony_ci	if (offset < lm_file->last_offset || !lm_file->last_offset_sg) {
13962306a36Sopenharmony_ci		lm_file->last_offset = 0;
14062306a36Sopenharmony_ci		lm_file->last_offset_sg = lm_file->sg_table.sgl;
14162306a36Sopenharmony_ci		lm_file->sg_last_entry = 0;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	cur_offset = lm_file->last_offset;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for_each_sg(lm_file->last_offset_sg, sg,
14762306a36Sopenharmony_ci		    lm_file->sg_table.orig_nents - lm_file->sg_last_entry, i) {
14862306a36Sopenharmony_ci		if (offset < sg->length + cur_offset) {
14962306a36Sopenharmony_ci			lm_file->last_offset_sg = sg;
15062306a36Sopenharmony_ci			lm_file->sg_last_entry += i;
15162306a36Sopenharmony_ci			lm_file->last_offset = cur_offset;
15262306a36Sopenharmony_ci			return nth_page(sg_page(sg),
15362306a36Sopenharmony_ci					(offset - cur_offset) / PAGE_SIZE);
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci		cur_offset += sg->length;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return NULL;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int pds_vfio_release_file(struct inode *inode, struct file *filp)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct pds_vfio_lm_file *lm_file = filp->private_data;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	mutex_lock(&lm_file->lock);
16662306a36Sopenharmony_ci	lm_file->filep->f_pos = 0;
16762306a36Sopenharmony_ci	lm_file->size = 0;
16862306a36Sopenharmony_ci	mutex_unlock(&lm_file->lock);
16962306a36Sopenharmony_ci	mutex_destroy(&lm_file->lock);
17062306a36Sopenharmony_ci	kfree(lm_file);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic ssize_t pds_vfio_save_read(struct file *filp, char __user *buf,
17662306a36Sopenharmony_ci				  size_t len, loff_t *pos)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct pds_vfio_lm_file *lm_file = filp->private_data;
17962306a36Sopenharmony_ci	ssize_t done = 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (pos)
18262306a36Sopenharmony_ci		return -ESPIPE;
18362306a36Sopenharmony_ci	pos = &filp->f_pos;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	mutex_lock(&lm_file->lock);
18662306a36Sopenharmony_ci	if (*pos > lm_file->size) {
18762306a36Sopenharmony_ci		done = -EINVAL;
18862306a36Sopenharmony_ci		goto out_unlock;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	len = min_t(size_t, lm_file->size - *pos, len);
19262306a36Sopenharmony_ci	while (len) {
19362306a36Sopenharmony_ci		size_t page_offset;
19462306a36Sopenharmony_ci		struct page *page;
19562306a36Sopenharmony_ci		size_t page_len;
19662306a36Sopenharmony_ci		u8 *from_buff;
19762306a36Sopenharmony_ci		int err;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		page_offset = (*pos) % PAGE_SIZE;
20062306a36Sopenharmony_ci		page = pds_vfio_get_file_page(lm_file, *pos - page_offset);
20162306a36Sopenharmony_ci		if (!page) {
20262306a36Sopenharmony_ci			if (done == 0)
20362306a36Sopenharmony_ci				done = -EINVAL;
20462306a36Sopenharmony_ci			goto out_unlock;
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		page_len = min_t(size_t, len, PAGE_SIZE - page_offset);
20862306a36Sopenharmony_ci		from_buff = kmap_local_page(page);
20962306a36Sopenharmony_ci		err = copy_to_user(buf, from_buff + page_offset, page_len);
21062306a36Sopenharmony_ci		kunmap_local(from_buff);
21162306a36Sopenharmony_ci		if (err) {
21262306a36Sopenharmony_ci			done = -EFAULT;
21362306a36Sopenharmony_ci			goto out_unlock;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci		*pos += page_len;
21662306a36Sopenharmony_ci		len -= page_len;
21762306a36Sopenharmony_ci		done += page_len;
21862306a36Sopenharmony_ci		buf += page_len;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ciout_unlock:
22262306a36Sopenharmony_ci	mutex_unlock(&lm_file->lock);
22362306a36Sopenharmony_ci	return done;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic const struct file_operations pds_vfio_save_fops = {
22762306a36Sopenharmony_ci	.owner = THIS_MODULE,
22862306a36Sopenharmony_ci	.read = pds_vfio_save_read,
22962306a36Sopenharmony_ci	.release = pds_vfio_release_file,
23062306a36Sopenharmony_ci	.llseek = no_llseek,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int pds_vfio_get_save_file(struct pds_vfio_pci_device *pds_vfio)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct device *dev = &pds_vfio->vfio_coredev.pdev->dev;
23662306a36Sopenharmony_ci	struct pds_vfio_lm_file *lm_file;
23762306a36Sopenharmony_ci	u64 size;
23862306a36Sopenharmony_ci	int err;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* Get live migration state size in this state */
24162306a36Sopenharmony_ci	err = pds_vfio_get_lm_state_size_cmd(pds_vfio, &size);
24262306a36Sopenharmony_ci	if (err) {
24362306a36Sopenharmony_ci		dev_err(dev, "failed to get save status: %pe\n", ERR_PTR(err));
24462306a36Sopenharmony_ci		return err;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	dev_dbg(dev, "save status, size = %lld\n", size);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (!size) {
25062306a36Sopenharmony_ci		dev_err(dev, "invalid state size\n");
25162306a36Sopenharmony_ci		return -EIO;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	lm_file = pds_vfio_get_lm_file(&pds_vfio_save_fops, O_RDONLY, size);
25562306a36Sopenharmony_ci	if (!lm_file) {
25662306a36Sopenharmony_ci		dev_err(dev, "failed to create save file\n");
25762306a36Sopenharmony_ci		return -ENOENT;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	dev_dbg(dev, "size = %lld, alloc_size = %lld, npages = %lld\n",
26162306a36Sopenharmony_ci		lm_file->size, lm_file->alloc_size, lm_file->npages);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	pds_vfio->save_file = lm_file;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic ssize_t pds_vfio_restore_write(struct file *filp, const char __user *buf,
26962306a36Sopenharmony_ci				      size_t len, loff_t *pos)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct pds_vfio_lm_file *lm_file = filp->private_data;
27262306a36Sopenharmony_ci	loff_t requested_length;
27362306a36Sopenharmony_ci	ssize_t done = 0;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (pos)
27662306a36Sopenharmony_ci		return -ESPIPE;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	pos = &filp->f_pos;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (*pos < 0 ||
28162306a36Sopenharmony_ci	    check_add_overflow((loff_t)len, *pos, &requested_length))
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	mutex_lock(&lm_file->lock);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	while (len) {
28762306a36Sopenharmony_ci		size_t page_offset;
28862306a36Sopenharmony_ci		struct page *page;
28962306a36Sopenharmony_ci		size_t page_len;
29062306a36Sopenharmony_ci		u8 *to_buff;
29162306a36Sopenharmony_ci		int err;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		page_offset = (*pos) % PAGE_SIZE;
29462306a36Sopenharmony_ci		page = pds_vfio_get_file_page(lm_file, *pos - page_offset);
29562306a36Sopenharmony_ci		if (!page) {
29662306a36Sopenharmony_ci			if (done == 0)
29762306a36Sopenharmony_ci				done = -EINVAL;
29862306a36Sopenharmony_ci			goto out_unlock;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		page_len = min_t(size_t, len, PAGE_SIZE - page_offset);
30262306a36Sopenharmony_ci		to_buff = kmap_local_page(page);
30362306a36Sopenharmony_ci		err = copy_from_user(to_buff + page_offset, buf, page_len);
30462306a36Sopenharmony_ci		kunmap_local(to_buff);
30562306a36Sopenharmony_ci		if (err) {
30662306a36Sopenharmony_ci			done = -EFAULT;
30762306a36Sopenharmony_ci			goto out_unlock;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci		*pos += page_len;
31062306a36Sopenharmony_ci		len -= page_len;
31162306a36Sopenharmony_ci		done += page_len;
31262306a36Sopenharmony_ci		buf += page_len;
31362306a36Sopenharmony_ci		lm_file->size += page_len;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ciout_unlock:
31662306a36Sopenharmony_ci	mutex_unlock(&lm_file->lock);
31762306a36Sopenharmony_ci	return done;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic const struct file_operations pds_vfio_restore_fops = {
32162306a36Sopenharmony_ci	.owner = THIS_MODULE,
32262306a36Sopenharmony_ci	.write = pds_vfio_restore_write,
32362306a36Sopenharmony_ci	.release = pds_vfio_release_file,
32462306a36Sopenharmony_ci	.llseek = no_llseek,
32562306a36Sopenharmony_ci};
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int pds_vfio_get_restore_file(struct pds_vfio_pci_device *pds_vfio)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct device *dev = &pds_vfio->vfio_coredev.pdev->dev;
33062306a36Sopenharmony_ci	struct pds_vfio_lm_file *lm_file;
33162306a36Sopenharmony_ci	u64 size;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	size = sizeof(union pds_lm_dev_state);
33462306a36Sopenharmony_ci	dev_dbg(dev, "restore status, size = %lld\n", size);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (!size) {
33762306a36Sopenharmony_ci		dev_err(dev, "invalid state size");
33862306a36Sopenharmony_ci		return -EIO;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	lm_file = pds_vfio_get_lm_file(&pds_vfio_restore_fops, O_WRONLY, size);
34262306a36Sopenharmony_ci	if (!lm_file) {
34362306a36Sopenharmony_ci		dev_err(dev, "failed to create restore file");
34462306a36Sopenharmony_ci		return -ENOENT;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	pds_vfio->restore_file = lm_file;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistruct file *
35262306a36Sopenharmony_cipds_vfio_step_device_state_locked(struct pds_vfio_pci_device *pds_vfio,
35362306a36Sopenharmony_ci				  enum vfio_device_mig_state next)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	enum vfio_device_mig_state cur = pds_vfio->state;
35662306a36Sopenharmony_ci	int err;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (cur == VFIO_DEVICE_STATE_STOP && next == VFIO_DEVICE_STATE_STOP_COPY) {
35962306a36Sopenharmony_ci		err = pds_vfio_get_save_file(pds_vfio);
36062306a36Sopenharmony_ci		if (err)
36162306a36Sopenharmony_ci			return ERR_PTR(err);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		err = pds_vfio_get_lm_state_cmd(pds_vfio);
36462306a36Sopenharmony_ci		if (err) {
36562306a36Sopenharmony_ci			pds_vfio_put_save_file(pds_vfio);
36662306a36Sopenharmony_ci			return ERR_PTR(err);
36762306a36Sopenharmony_ci		}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		return pds_vfio->save_file->filep;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (cur == VFIO_DEVICE_STATE_STOP_COPY && next == VFIO_DEVICE_STATE_STOP) {
37362306a36Sopenharmony_ci		pds_vfio_put_save_file(pds_vfio);
37462306a36Sopenharmony_ci		pds_vfio_dirty_disable(pds_vfio, true);
37562306a36Sopenharmony_ci		return NULL;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (cur == VFIO_DEVICE_STATE_STOP && next == VFIO_DEVICE_STATE_RESUMING) {
37962306a36Sopenharmony_ci		err = pds_vfio_get_restore_file(pds_vfio);
38062306a36Sopenharmony_ci		if (err)
38162306a36Sopenharmony_ci			return ERR_PTR(err);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		return pds_vfio->restore_file->filep;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (cur == VFIO_DEVICE_STATE_RESUMING && next == VFIO_DEVICE_STATE_STOP) {
38762306a36Sopenharmony_ci		err = pds_vfio_set_lm_state_cmd(pds_vfio);
38862306a36Sopenharmony_ci		if (err)
38962306a36Sopenharmony_ci			return ERR_PTR(err);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		pds_vfio_put_restore_file(pds_vfio);
39262306a36Sopenharmony_ci		return NULL;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (cur == VFIO_DEVICE_STATE_RUNNING && next == VFIO_DEVICE_STATE_RUNNING_P2P) {
39662306a36Sopenharmony_ci		pds_vfio_send_host_vf_lm_status_cmd(pds_vfio,
39762306a36Sopenharmony_ci						    PDS_LM_STA_IN_PROGRESS);
39862306a36Sopenharmony_ci		err = pds_vfio_suspend_device_cmd(pds_vfio,
39962306a36Sopenharmony_ci						  PDS_LM_SUSPEND_RESUME_TYPE_P2P);
40062306a36Sopenharmony_ci		if (err)
40162306a36Sopenharmony_ci			return ERR_PTR(err);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		return NULL;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (cur == VFIO_DEVICE_STATE_RUNNING_P2P && next == VFIO_DEVICE_STATE_RUNNING) {
40762306a36Sopenharmony_ci		err = pds_vfio_resume_device_cmd(pds_vfio,
40862306a36Sopenharmony_ci						 PDS_LM_SUSPEND_RESUME_TYPE_FULL);
40962306a36Sopenharmony_ci		if (err)
41062306a36Sopenharmony_ci			return ERR_PTR(err);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		pds_vfio_send_host_vf_lm_status_cmd(pds_vfio, PDS_LM_STA_NONE);
41362306a36Sopenharmony_ci		return NULL;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (cur == VFIO_DEVICE_STATE_STOP && next == VFIO_DEVICE_STATE_RUNNING_P2P) {
41762306a36Sopenharmony_ci		err = pds_vfio_resume_device_cmd(pds_vfio,
41862306a36Sopenharmony_ci						 PDS_LM_SUSPEND_RESUME_TYPE_P2P);
41962306a36Sopenharmony_ci		if (err)
42062306a36Sopenharmony_ci			return ERR_PTR(err);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		return NULL;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (cur == VFIO_DEVICE_STATE_RUNNING_P2P && next == VFIO_DEVICE_STATE_STOP) {
42662306a36Sopenharmony_ci		err = pds_vfio_suspend_device_cmd(pds_vfio,
42762306a36Sopenharmony_ci						  PDS_LM_SUSPEND_RESUME_TYPE_FULL);
42862306a36Sopenharmony_ci		if (err)
42962306a36Sopenharmony_ci			return ERR_PTR(err);
43062306a36Sopenharmony_ci		return NULL;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
43462306a36Sopenharmony_ci}
435