xref: /openbmc/linux/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
10c853c2cSMax Gurtovoy // SPDX-License-Identifier: GPL-2.0-only
20c853c2cSMax Gurtovoy /*
30c853c2cSMax Gurtovoy  * VDPA simulator for block device.
40c853c2cSMax Gurtovoy  *
50c853c2cSMax Gurtovoy  * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
67d189f61SStefano Garzarella  * Copyright (c) 2021, Red Hat Inc. All rights reserved.
70c853c2cSMax Gurtovoy  *
80c853c2cSMax Gurtovoy  */
90c853c2cSMax Gurtovoy 
100c853c2cSMax Gurtovoy #include <linux/init.h>
110c853c2cSMax Gurtovoy #include <linux/module.h>
120c853c2cSMax Gurtovoy #include <linux/device.h>
130c853c2cSMax Gurtovoy #include <linux/kernel.h>
140c853c2cSMax Gurtovoy #include <linux/blkdev.h>
150c853c2cSMax Gurtovoy #include <linux/vringh.h>
160c853c2cSMax Gurtovoy #include <linux/vdpa.h>
170c853c2cSMax Gurtovoy #include <uapi/linux/virtio_blk.h>
180c853c2cSMax Gurtovoy 
190c853c2cSMax Gurtovoy #include "vdpa_sim.h"
200c853c2cSMax Gurtovoy 
210c853c2cSMax Gurtovoy #define DRV_VERSION  "0.1"
220c853c2cSMax Gurtovoy #define DRV_AUTHOR   "Max Gurtovoy <mgurtovoy@nvidia.com>"
230c853c2cSMax Gurtovoy #define DRV_DESC     "vDPA Device Simulator for block device"
240c853c2cSMax Gurtovoy #define DRV_LICENSE  "GPL v2"
250c853c2cSMax Gurtovoy 
260c853c2cSMax Gurtovoy #define VDPASIM_BLK_FEATURES	(VDPASIM_FEATURES | \
27518083d2SStefano Garzarella 				 (1ULL << VIRTIO_BLK_F_FLUSH)    | \
280c853c2cSMax Gurtovoy 				 (1ULL << VIRTIO_BLK_F_SIZE_MAX) | \
290c853c2cSMax Gurtovoy 				 (1ULL << VIRTIO_BLK_F_SEG_MAX)  | \
300c853c2cSMax Gurtovoy 				 (1ULL << VIRTIO_BLK_F_BLK_SIZE) | \
310c853c2cSMax Gurtovoy 				 (1ULL << VIRTIO_BLK_F_TOPOLOGY) | \
324a44a5edSStefano Garzarella 				 (1ULL << VIRTIO_BLK_F_MQ)       | \
334a44a5edSStefano Garzarella 				 (1ULL << VIRTIO_BLK_F_DISCARD)  | \
344a44a5edSStefano Garzarella 				 (1ULL << VIRTIO_BLK_F_WRITE_ZEROES))
350c853c2cSMax Gurtovoy 
360c853c2cSMax Gurtovoy #define VDPASIM_BLK_CAPACITY	0x40000
370c853c2cSMax Gurtovoy #define VDPASIM_BLK_SIZE_MAX	0x1000
380c853c2cSMax Gurtovoy #define VDPASIM_BLK_SEG_MAX	32
394a44a5edSStefano Garzarella #define VDPASIM_BLK_DWZ_MAX_SECTORS UINT_MAX
4019cd4a54SStefano Garzarella 
4119cd4a54SStefano Garzarella /* 1 virtqueue, 1 address space, 1 virtqueue group */
420c853c2cSMax Gurtovoy #define VDPASIM_BLK_VQ_NUM	1
4319cd4a54SStefano Garzarella #define VDPASIM_BLK_AS_NUM	1
4419cd4a54SStefano Garzarella #define VDPASIM_BLK_GROUP_NUM	1
450c853c2cSMax Gurtovoy 
46112f23cdSStefano Garzarella struct vdpasim_blk {
47112f23cdSStefano Garzarella 	struct vdpasim vdpasim;
48112f23cdSStefano Garzarella 	void *buffer;
49abebb162SStefano Garzarella 	bool shared_backend;
50112f23cdSStefano Garzarella };
51112f23cdSStefano Garzarella 
sim_to_blk(struct vdpasim * vdpasim)52112f23cdSStefano Garzarella static struct vdpasim_blk *sim_to_blk(struct vdpasim *vdpasim)
53112f23cdSStefano Garzarella {
54112f23cdSStefano Garzarella 	return container_of(vdpasim, struct vdpasim_blk, vdpasim);
55112f23cdSStefano Garzarella }
56112f23cdSStefano Garzarella 
57e6fa6052SStefano Garzarella static char vdpasim_blk_id[VIRTIO_BLK_ID_BYTES] = "vdpa_blk_sim";
580c853c2cSMax Gurtovoy 
59abebb162SStefano Garzarella static bool shared_backend;
60abebb162SStefano Garzarella module_param(shared_backend, bool, 0444);
61abebb162SStefano Garzarella MODULE_PARM_DESC(shared_backend, "Enable the shared backend between virtio-blk devices");
62abebb162SStefano Garzarella 
63abebb162SStefano Garzarella static void *shared_buffer;
64abebb162SStefano Garzarella /* mutex to synchronize shared_buffer access */
65abebb162SStefano Garzarella static DEFINE_MUTEX(shared_buffer_mutex);
66abebb162SStefano Garzarella 
vdpasim_blk_buffer_lock(struct vdpasim_blk * blk)67abebb162SStefano Garzarella static void vdpasim_blk_buffer_lock(struct vdpasim_blk *blk)
68abebb162SStefano Garzarella {
69abebb162SStefano Garzarella 	if (blk->shared_backend)
70abebb162SStefano Garzarella 		mutex_lock(&shared_buffer_mutex);
71abebb162SStefano Garzarella }
72abebb162SStefano Garzarella 
vdpasim_blk_buffer_unlock(struct vdpasim_blk * blk)73abebb162SStefano Garzarella static void vdpasim_blk_buffer_unlock(struct vdpasim_blk *blk)
74abebb162SStefano Garzarella {
75abebb162SStefano Garzarella 	if (blk->shared_backend)
76abebb162SStefano Garzarella 		mutex_unlock(&shared_buffer_mutex);
77abebb162SStefano Garzarella }
78abebb162SStefano Garzarella 
vdpasim_blk_check_range(struct vdpasim * vdpasim,u64 start_sector,u64 num_sectors,u64 max_sectors)79ac926e1bSStefano Garzarella static bool vdpasim_blk_check_range(struct vdpasim *vdpasim, u64 start_sector,
80ac926e1bSStefano Garzarella 				    u64 num_sectors, u64 max_sectors)
817d189f61SStefano Garzarella {
82ac926e1bSStefano Garzarella 	if (start_sector > VDPASIM_BLK_CAPACITY) {
83ac926e1bSStefano Garzarella 		dev_dbg(&vdpasim->vdpa.dev,
84ac926e1bSStefano Garzarella 			"starting sector exceeds the capacity - start: 0x%llx capacity: 0x%x\n",
85ac926e1bSStefano Garzarella 			start_sector, VDPASIM_BLK_CAPACITY);
86ac926e1bSStefano Garzarella 	}
877d189f61SStefano Garzarella 
88ac926e1bSStefano Garzarella 	if (num_sectors > max_sectors) {
89ac926e1bSStefano Garzarella 		dev_dbg(&vdpasim->vdpa.dev,
90ac926e1bSStefano Garzarella 			"number of sectors exceeds the max allowed in a request - num: 0x%llx max: 0x%llx\n",
91ac926e1bSStefano Garzarella 			num_sectors, max_sectors);
927d189f61SStefano Garzarella 		return false;
93ac926e1bSStefano Garzarella 	}
947d189f61SStefano Garzarella 
95ac926e1bSStefano Garzarella 	if (num_sectors > VDPASIM_BLK_CAPACITY - start_sector) {
96ac926e1bSStefano Garzarella 		dev_dbg(&vdpasim->vdpa.dev,
97ac926e1bSStefano Garzarella 			"request exceeds the capacity - start: 0x%llx num: 0x%llx capacity: 0x%x\n",
98ac926e1bSStefano Garzarella 			start_sector, num_sectors, VDPASIM_BLK_CAPACITY);
997d189f61SStefano Garzarella 		return false;
100ac926e1bSStefano Garzarella 	}
1017d189f61SStefano Garzarella 
1027d189f61SStefano Garzarella 	return true;
1037d189f61SStefano Garzarella }
1047d189f61SStefano Garzarella 
1057d189f61SStefano Garzarella /* Returns 'true' if the request is handled (with or without an I/O error)
1067d189f61SStefano Garzarella  * and the status is correctly written in the last byte of the 'in iov',
1077d189f61SStefano Garzarella  * 'false' otherwise.
1087d189f61SStefano Garzarella  */
vdpasim_blk_handle_req(struct vdpasim * vdpasim,struct vdpasim_virtqueue * vq)1097d189f61SStefano Garzarella static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim,
1107d189f61SStefano Garzarella 				   struct vdpasim_virtqueue *vq)
1117d189f61SStefano Garzarella {
112112f23cdSStefano Garzarella 	struct vdpasim_blk *blk = sim_to_blk(vdpasim);
1137d189f61SStefano Garzarella 	size_t pushed = 0, to_pull, to_push;
1147d189f61SStefano Garzarella 	struct virtio_blk_outhdr hdr;
1158472019eSStefano Garzarella 	bool handled = false;
1167d189f61SStefano Garzarella 	ssize_t bytes;
1177d189f61SStefano Garzarella 	loff_t offset;
1187d189f61SStefano Garzarella 	u64 sector;
1197d189f61SStefano Garzarella 	u8 status;
1207d189f61SStefano Garzarella 	u32 type;
1217d189f61SStefano Garzarella 	int ret;
1227d189f61SStefano Garzarella 
1237d189f61SStefano Garzarella 	ret = vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, &vq->in_iov,
1247d189f61SStefano Garzarella 				   &vq->head, GFP_ATOMIC);
1257d189f61SStefano Garzarella 	if (ret != 1)
1267d189f61SStefano Garzarella 		return false;
1277d189f61SStefano Garzarella 
1287d189f61SStefano Garzarella 	if (vq->out_iov.used < 1 || vq->in_iov.used < 1) {
12942a84c09SStefano Garzarella 		dev_dbg(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n",
1307d189f61SStefano Garzarella 			vq->out_iov.used, vq->in_iov.used);
1318472019eSStefano Garzarella 		goto err;
1327d189f61SStefano Garzarella 	}
1337d189f61SStefano Garzarella 
1347d189f61SStefano Garzarella 	if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) {
13542a84c09SStefano Garzarella 		dev_dbg(&vdpasim->vdpa.dev, "request in header too short\n");
1368472019eSStefano Garzarella 		goto err;
1377d189f61SStefano Garzarella 	}
1387d189f61SStefano Garzarella 
1397d189f61SStefano Garzarella 	/* The last byte is the status and we checked if the last iov has
1407d189f61SStefano Garzarella 	 * enough room for it.
1417d189f61SStefano Garzarella 	 */
1427d189f61SStefano Garzarella 	to_push = vringh_kiov_length(&vq->in_iov) - 1;
1437d189f61SStefano Garzarella 
1447d189f61SStefano Garzarella 	to_pull = vringh_kiov_length(&vq->out_iov);
1457d189f61SStefano Garzarella 
1467d189f61SStefano Garzarella 	bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr,
1477d189f61SStefano Garzarella 				      sizeof(hdr));
1487d189f61SStefano Garzarella 	if (bytes != sizeof(hdr)) {
14942a84c09SStefano Garzarella 		dev_dbg(&vdpasim->vdpa.dev, "request out header too short\n");
1508472019eSStefano Garzarella 		goto err;
1517d189f61SStefano Garzarella 	}
1527d189f61SStefano Garzarella 
1537d189f61SStefano Garzarella 	to_pull -= bytes;
1547d189f61SStefano Garzarella 
1557d189f61SStefano Garzarella 	type = vdpasim32_to_cpu(vdpasim, hdr.type);
1567d189f61SStefano Garzarella 	sector = vdpasim64_to_cpu(vdpasim, hdr.sector);
1577d189f61SStefano Garzarella 	offset = sector << SECTOR_SHIFT;
1587d189f61SStefano Garzarella 	status = VIRTIO_BLK_S_OK;
1597d189f61SStefano Garzarella 
160b91cf6e9SStefano Garzarella 	if (type != VIRTIO_BLK_T_IN && type != VIRTIO_BLK_T_OUT &&
161b91cf6e9SStefano Garzarella 	    sector != 0) {
162b91cf6e9SStefano Garzarella 		dev_dbg(&vdpasim->vdpa.dev,
163b91cf6e9SStefano Garzarella 			"sector must be 0 for %u request - sector: 0x%llx\n",
164b91cf6e9SStefano Garzarella 			type, sector);
165b91cf6e9SStefano Garzarella 		status = VIRTIO_BLK_S_IOERR;
166b91cf6e9SStefano Garzarella 		goto err_status;
167b91cf6e9SStefano Garzarella 	}
168b91cf6e9SStefano Garzarella 
1697d189f61SStefano Garzarella 	switch (type) {
1707d189f61SStefano Garzarella 	case VIRTIO_BLK_T_IN:
171ac926e1bSStefano Garzarella 		if (!vdpasim_blk_check_range(vdpasim, sector,
172ac926e1bSStefano Garzarella 					     to_push >> SECTOR_SHIFT,
173ac926e1bSStefano Garzarella 					     VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)) {
1747d189f61SStefano Garzarella 			status = VIRTIO_BLK_S_IOERR;
1757d189f61SStefano Garzarella 			break;
1767d189f61SStefano Garzarella 		}
1777d189f61SStefano Garzarella 
178abebb162SStefano Garzarella 		vdpasim_blk_buffer_lock(blk);
1797d189f61SStefano Garzarella 		bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
180112f23cdSStefano Garzarella 					      blk->buffer + offset, to_push);
181abebb162SStefano Garzarella 		vdpasim_blk_buffer_unlock(blk);
1827d189f61SStefano Garzarella 		if (bytes < 0) {
18342a84c09SStefano Garzarella 			dev_dbg(&vdpasim->vdpa.dev,
1847d189f61SStefano Garzarella 				"vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
1857d189f61SStefano Garzarella 				bytes, offset, to_push);
1867d189f61SStefano Garzarella 			status = VIRTIO_BLK_S_IOERR;
1877d189f61SStefano Garzarella 			break;
1887d189f61SStefano Garzarella 		}
1897d189f61SStefano Garzarella 
1907d189f61SStefano Garzarella 		pushed += bytes;
1917d189f61SStefano Garzarella 		break;
1927d189f61SStefano Garzarella 
1937d189f61SStefano Garzarella 	case VIRTIO_BLK_T_OUT:
194ac926e1bSStefano Garzarella 		if (!vdpasim_blk_check_range(vdpasim, sector,
195ac926e1bSStefano Garzarella 					     to_pull >> SECTOR_SHIFT,
196ac926e1bSStefano Garzarella 					     VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)) {
1977d189f61SStefano Garzarella 			status = VIRTIO_BLK_S_IOERR;
1987d189f61SStefano Garzarella 			break;
1997d189f61SStefano Garzarella 		}
2007d189f61SStefano Garzarella 
201abebb162SStefano Garzarella 		vdpasim_blk_buffer_lock(blk);
2027d189f61SStefano Garzarella 		bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov,
203112f23cdSStefano Garzarella 					      blk->buffer + offset, to_pull);
204abebb162SStefano Garzarella 		vdpasim_blk_buffer_unlock(blk);
2057d189f61SStefano Garzarella 		if (bytes < 0) {
20642a84c09SStefano Garzarella 			dev_dbg(&vdpasim->vdpa.dev,
2077d189f61SStefano Garzarella 				"vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
2087d189f61SStefano Garzarella 				bytes, offset, to_pull);
2097d189f61SStefano Garzarella 			status = VIRTIO_BLK_S_IOERR;
2107d189f61SStefano Garzarella 			break;
2117d189f61SStefano Garzarella 		}
2127d189f61SStefano Garzarella 		break;
2137d189f61SStefano Garzarella 
214e6fa6052SStefano Garzarella 	case VIRTIO_BLK_T_GET_ID:
215e6fa6052SStefano Garzarella 		bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
216e6fa6052SStefano Garzarella 					      vdpasim_blk_id,
217e6fa6052SStefano Garzarella 					      VIRTIO_BLK_ID_BYTES);
218e6fa6052SStefano Garzarella 		if (bytes < 0) {
21942a84c09SStefano Garzarella 			dev_dbg(&vdpasim->vdpa.dev,
220e6fa6052SStefano Garzarella 				"vringh_iov_push_iotlb() error: %zd\n", bytes);
221e6fa6052SStefano Garzarella 			status = VIRTIO_BLK_S_IOERR;
222e6fa6052SStefano Garzarella 			break;
223e6fa6052SStefano Garzarella 		}
224e6fa6052SStefano Garzarella 
225e6fa6052SStefano Garzarella 		pushed += bytes;
226e6fa6052SStefano Garzarella 		break;
227e6fa6052SStefano Garzarella 
228518083d2SStefano Garzarella 	case VIRTIO_BLK_T_FLUSH:
229518083d2SStefano Garzarella 		/* nothing to do */
230518083d2SStefano Garzarella 		break;
231518083d2SStefano Garzarella 
2324a44a5edSStefano Garzarella 	case VIRTIO_BLK_T_DISCARD:
2334a44a5edSStefano Garzarella 	case VIRTIO_BLK_T_WRITE_ZEROES: {
2344a44a5edSStefano Garzarella 		struct virtio_blk_discard_write_zeroes range;
2354a44a5edSStefano Garzarella 		u32 num_sectors, flags;
2364a44a5edSStefano Garzarella 
2374a44a5edSStefano Garzarella 		if (to_pull != sizeof(range)) {
2384a44a5edSStefano Garzarella 			dev_dbg(&vdpasim->vdpa.dev,
2394a44a5edSStefano Garzarella 				"discard/write_zeroes header len: 0x%zx [expected: 0x%zx]\n",
2404a44a5edSStefano Garzarella 				to_pull, sizeof(range));
2414a44a5edSStefano Garzarella 			status = VIRTIO_BLK_S_IOERR;
2424a44a5edSStefano Garzarella 			break;
2434a44a5edSStefano Garzarella 		}
2444a44a5edSStefano Garzarella 
2454a44a5edSStefano Garzarella 		bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &range,
2464a44a5edSStefano Garzarella 					      to_pull);
2474a44a5edSStefano Garzarella 		if (bytes < 0) {
2484a44a5edSStefano Garzarella 			dev_dbg(&vdpasim->vdpa.dev,
2494a44a5edSStefano Garzarella 				"vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
2504a44a5edSStefano Garzarella 				bytes, offset, to_pull);
2514a44a5edSStefano Garzarella 			status = VIRTIO_BLK_S_IOERR;
2524a44a5edSStefano Garzarella 			break;
2534a44a5edSStefano Garzarella 		}
2544a44a5edSStefano Garzarella 
2554a44a5edSStefano Garzarella 		sector = le64_to_cpu(range.sector);
2564a44a5edSStefano Garzarella 		offset = sector << SECTOR_SHIFT;
2574a44a5edSStefano Garzarella 		num_sectors = le32_to_cpu(range.num_sectors);
2584a44a5edSStefano Garzarella 		flags = le32_to_cpu(range.flags);
2594a44a5edSStefano Garzarella 
2604a44a5edSStefano Garzarella 		if (type == VIRTIO_BLK_T_DISCARD && flags != 0) {
2614a44a5edSStefano Garzarella 			dev_dbg(&vdpasim->vdpa.dev,
2624a44a5edSStefano Garzarella 				"discard unexpected flags set - flags: 0x%x\n",
2634a44a5edSStefano Garzarella 				flags);
2644a44a5edSStefano Garzarella 			status = VIRTIO_BLK_S_UNSUPP;
2654a44a5edSStefano Garzarella 			break;
2664a44a5edSStefano Garzarella 		}
2674a44a5edSStefano Garzarella 
2684a44a5edSStefano Garzarella 		if (type == VIRTIO_BLK_T_WRITE_ZEROES &&
2694a44a5edSStefano Garzarella 		    flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
2704a44a5edSStefano Garzarella 			dev_dbg(&vdpasim->vdpa.dev,
2714a44a5edSStefano Garzarella 				"write_zeroes unexpected flags set - flags: 0x%x\n",
2724a44a5edSStefano Garzarella 				flags);
2734a44a5edSStefano Garzarella 			status = VIRTIO_BLK_S_UNSUPP;
2744a44a5edSStefano Garzarella 			break;
2754a44a5edSStefano Garzarella 		}
2764a44a5edSStefano Garzarella 
2774a44a5edSStefano Garzarella 		if (!vdpasim_blk_check_range(vdpasim, sector, num_sectors,
2784a44a5edSStefano Garzarella 					     VDPASIM_BLK_DWZ_MAX_SECTORS)) {
2794a44a5edSStefano Garzarella 			status = VIRTIO_BLK_S_IOERR;
2804a44a5edSStefano Garzarella 			break;
2814a44a5edSStefano Garzarella 		}
2824a44a5edSStefano Garzarella 
2834a44a5edSStefano Garzarella 		if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
284abebb162SStefano Garzarella 			vdpasim_blk_buffer_lock(blk);
285112f23cdSStefano Garzarella 			memset(blk->buffer + offset, 0,
2864a44a5edSStefano Garzarella 			       num_sectors << SECTOR_SHIFT);
287abebb162SStefano Garzarella 			vdpasim_blk_buffer_unlock(blk);
2884a44a5edSStefano Garzarella 		}
2894a44a5edSStefano Garzarella 
2904a44a5edSStefano Garzarella 		break;
2914a44a5edSStefano Garzarella 	}
2927d189f61SStefano Garzarella 	default:
29342a84c09SStefano Garzarella 		dev_dbg(&vdpasim->vdpa.dev,
2947d189f61SStefano Garzarella 			"Unsupported request type %d\n", type);
2957d189f61SStefano Garzarella 		status = VIRTIO_BLK_S_IOERR;
2967d189f61SStefano Garzarella 		break;
2977d189f61SStefano Garzarella 	}
2987d189f61SStefano Garzarella 
299b91cf6e9SStefano Garzarella err_status:
3007d189f61SStefano Garzarella 	/* If some operations fail, we need to skip the remaining bytes
3017d189f61SStefano Garzarella 	 * to put the status in the last byte
3027d189f61SStefano Garzarella 	 */
3037d189f61SStefano Garzarella 	if (to_push - pushed > 0)
3047d189f61SStefano Garzarella 		vringh_kiov_advance(&vq->in_iov, to_push - pushed);
3057d189f61SStefano Garzarella 
3067d189f61SStefano Garzarella 	/* Last byte is the status */
3077d189f61SStefano Garzarella 	bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1);
3087d189f61SStefano Garzarella 	if (bytes != 1)
3098472019eSStefano Garzarella 		goto err;
3107d189f61SStefano Garzarella 
3117d189f61SStefano Garzarella 	pushed += bytes;
3127d189f61SStefano Garzarella 
3137d189f61SStefano Garzarella 	/* Make sure data is wrote before advancing index */
3147d189f61SStefano Garzarella 	smp_wmb();
3157d189f61SStefano Garzarella 
3168472019eSStefano Garzarella 	handled = true;
3178472019eSStefano Garzarella 
3188472019eSStefano Garzarella err:
3197d189f61SStefano Garzarella 	vringh_complete_iotlb(&vq->vring, vq->head, pushed);
3207d189f61SStefano Garzarella 
3218472019eSStefano Garzarella 	return handled;
3227d189f61SStefano Garzarella }
3237d189f61SStefano Garzarella 
vdpasim_blk_work(struct vdpasim * vdpasim)324e2a4f808SStefano Garzarella static void vdpasim_blk_work(struct vdpasim *vdpasim)
3250c853c2cSMax Gurtovoy {
3269c4df090SStefano Garzarella 	bool reschedule = false;
3270c853c2cSMax Gurtovoy 	int i;
3280c853c2cSMax Gurtovoy 
329d7621c28SStefano Garzarella 	mutex_lock(&vdpasim->mutex);
3300c853c2cSMax Gurtovoy 
3310c853c2cSMax Gurtovoy 	if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
3320c853c2cSMax Gurtovoy 		goto out;
3330c853c2cSMax Gurtovoy 
3340c89e2a3SEugenio Pérez 	if (!vdpasim->running)
3350c89e2a3SEugenio Pérez 		goto out;
3360c89e2a3SEugenio Pérez 
3370c853c2cSMax Gurtovoy 	for (i = 0; i < VDPASIM_BLK_VQ_NUM; i++) {
3380c853c2cSMax Gurtovoy 		struct vdpasim_virtqueue *vq = &vdpasim->vqs[i];
3399c4df090SStefano Garzarella 		int reqs = 0;
3400c853c2cSMax Gurtovoy 
3410c853c2cSMax Gurtovoy 		if (!vq->ready)
3420c853c2cSMax Gurtovoy 			continue;
3430c853c2cSMax Gurtovoy 
3447d189f61SStefano Garzarella 		while (vdpasim_blk_handle_req(vdpasim, vq)) {
3450c853c2cSMax Gurtovoy 			/* Make sure used is visible before rasing the interrupt. */
3460c853c2cSMax Gurtovoy 			smp_wmb();
3470c853c2cSMax Gurtovoy 
3480c853c2cSMax Gurtovoy 			local_bh_disable();
3490c853c2cSMax Gurtovoy 			if (vringh_need_notify_iotlb(&vq->vring) > 0)
3500c853c2cSMax Gurtovoy 				vringh_notify(&vq->vring);
3510c853c2cSMax Gurtovoy 			local_bh_enable();
3529c4df090SStefano Garzarella 
3539c4df090SStefano Garzarella 			if (++reqs > 4) {
3549c4df090SStefano Garzarella 				reschedule = true;
3559c4df090SStefano Garzarella 				break;
3569c4df090SStefano Garzarella 			}
3570c853c2cSMax Gurtovoy 		}
3580c853c2cSMax Gurtovoy 	}
3590c853c2cSMax Gurtovoy out:
360d7621c28SStefano Garzarella 	mutex_unlock(&vdpasim->mutex);
3619c4df090SStefano Garzarella 
3629c4df090SStefano Garzarella 	if (reschedule)
363e2a4f808SStefano Garzarella 		vdpasim_schedule_work(vdpasim);
3640c853c2cSMax Gurtovoy }
3650c853c2cSMax Gurtovoy 
vdpasim_blk_get_config(struct vdpasim * vdpasim,void * config)3660c853c2cSMax Gurtovoy static void vdpasim_blk_get_config(struct vdpasim *vdpasim, void *config)
3670c853c2cSMax Gurtovoy {
3680c853c2cSMax Gurtovoy 	struct virtio_blk_config *blk_config = config;
3690c853c2cSMax Gurtovoy 
3700c853c2cSMax Gurtovoy 	memset(config, 0, sizeof(struct virtio_blk_config));
3710c853c2cSMax Gurtovoy 
3720c853c2cSMax Gurtovoy 	blk_config->capacity = cpu_to_vdpasim64(vdpasim, VDPASIM_BLK_CAPACITY);
3730c853c2cSMax Gurtovoy 	blk_config->size_max = cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_SIZE_MAX);
3740c853c2cSMax Gurtovoy 	blk_config->seg_max = cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_SEG_MAX);
3750c853c2cSMax Gurtovoy 	blk_config->num_queues = cpu_to_vdpasim16(vdpasim, VDPASIM_BLK_VQ_NUM);
3760c853c2cSMax Gurtovoy 	blk_config->min_io_size = cpu_to_vdpasim16(vdpasim, 1);
3770c853c2cSMax Gurtovoy 	blk_config->opt_io_size = cpu_to_vdpasim32(vdpasim, 1);
3780c853c2cSMax Gurtovoy 	blk_config->blk_size = cpu_to_vdpasim32(vdpasim, SECTOR_SIZE);
3794a44a5edSStefano Garzarella 	/* VIRTIO_BLK_F_DISCARD */
3804a44a5edSStefano Garzarella 	blk_config->discard_sector_alignment =
3814a44a5edSStefano Garzarella 		cpu_to_vdpasim32(vdpasim, SECTOR_SIZE);
3824a44a5edSStefano Garzarella 	blk_config->max_discard_sectors =
3834a44a5edSStefano Garzarella 		cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_DWZ_MAX_SECTORS);
3844a44a5edSStefano Garzarella 	blk_config->max_discard_seg = cpu_to_vdpasim32(vdpasim, 1);
3854a44a5edSStefano Garzarella 	/* VIRTIO_BLK_F_WRITE_ZEROES */
3864a44a5edSStefano Garzarella 	blk_config->max_write_zeroes_sectors =
3874a44a5edSStefano Garzarella 		cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_DWZ_MAX_SECTORS);
3884a44a5edSStefano Garzarella 	blk_config->max_write_zeroes_seg = cpu_to_vdpasim32(vdpasim, 1);
3894a44a5edSStefano Garzarella 
3900c853c2cSMax Gurtovoy }
3910c853c2cSMax Gurtovoy 
vdpasim_blk_free(struct vdpasim * vdpasim)392112f23cdSStefano Garzarella static void vdpasim_blk_free(struct vdpasim *vdpasim)
393112f23cdSStefano Garzarella {
394112f23cdSStefano Garzarella 	struct vdpasim_blk *blk = sim_to_blk(vdpasim);
395112f23cdSStefano Garzarella 
396abebb162SStefano Garzarella 	if (!blk->shared_backend)
397112f23cdSStefano Garzarella 		kvfree(blk->buffer);
398112f23cdSStefano Garzarella }
399112f23cdSStefano Garzarella 
vdpasim_blk_mgmtdev_release(struct device * dev)400899c4d18SStefano Garzarella static void vdpasim_blk_mgmtdev_release(struct device *dev)
401899c4d18SStefano Garzarella {
402899c4d18SStefano Garzarella }
403899c4d18SStefano Garzarella 
404899c4d18SStefano Garzarella static struct device vdpasim_blk_mgmtdev = {
405899c4d18SStefano Garzarella 	.init_name = "vdpasim_blk",
406899c4d18SStefano Garzarella 	.release = vdpasim_blk_mgmtdev_release,
407899c4d18SStefano Garzarella };
408899c4d18SStefano Garzarella 
vdpasim_blk_dev_add(struct vdpa_mgmt_dev * mdev,const char * name,const struct vdpa_dev_set_config * config)409d8ca2fa5SParav Pandit static int vdpasim_blk_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
410d8ca2fa5SParav Pandit 			       const struct vdpa_dev_set_config *config)
4110c853c2cSMax Gurtovoy {
4120c853c2cSMax Gurtovoy 	struct vdpasim_dev_attr dev_attr = {};
413112f23cdSStefano Garzarella 	struct vdpasim_blk *blk;
414899c4d18SStefano Garzarella 	struct vdpasim *simdev;
4150c853c2cSMax Gurtovoy 	int ret;
4160c853c2cSMax Gurtovoy 
417899c4d18SStefano Garzarella 	dev_attr.mgmt_dev = mdev;
418899c4d18SStefano Garzarella 	dev_attr.name = name;
4190c853c2cSMax Gurtovoy 	dev_attr.id = VIRTIO_ID_BLOCK;
4200c853c2cSMax Gurtovoy 	dev_attr.supported_features = VDPASIM_BLK_FEATURES;
4210c853c2cSMax Gurtovoy 	dev_attr.nvqs = VDPASIM_BLK_VQ_NUM;
42219cd4a54SStefano Garzarella 	dev_attr.ngroups = VDPASIM_BLK_GROUP_NUM;
42319cd4a54SStefano Garzarella 	dev_attr.nas = VDPASIM_BLK_AS_NUM;
424112f23cdSStefano Garzarella 	dev_attr.alloc_size = sizeof(struct vdpasim_blk);
4250c853c2cSMax Gurtovoy 	dev_attr.config_size = sizeof(struct virtio_blk_config);
4260c853c2cSMax Gurtovoy 	dev_attr.get_config = vdpasim_blk_get_config;
4270c853c2cSMax Gurtovoy 	dev_attr.work_fn = vdpasim_blk_work;
428112f23cdSStefano Garzarella 	dev_attr.free = vdpasim_blk_free;
4290c853c2cSMax Gurtovoy 
430477f7197SJason Wang 	simdev = vdpasim_create(&dev_attr, config);
431899c4d18SStefano Garzarella 	if (IS_ERR(simdev))
432899c4d18SStefano Garzarella 		return PTR_ERR(simdev);
4330c853c2cSMax Gurtovoy 
434112f23cdSStefano Garzarella 	blk = sim_to_blk(simdev);
435abebb162SStefano Garzarella 	blk->shared_backend = shared_backend;
436112f23cdSStefano Garzarella 
437abebb162SStefano Garzarella 	if (blk->shared_backend) {
438abebb162SStefano Garzarella 		blk->buffer = shared_buffer;
439abebb162SStefano Garzarella 	} else {
440*941fcbb0SStefano Garzarella 		blk->buffer = kvzalloc(VDPASIM_BLK_CAPACITY << SECTOR_SHIFT,
441112f23cdSStefano Garzarella 				       GFP_KERNEL);
442112f23cdSStefano Garzarella 		if (!blk->buffer) {
443112f23cdSStefano Garzarella 			ret = -ENOMEM;
444112f23cdSStefano Garzarella 			goto put_dev;
445112f23cdSStefano Garzarella 		}
446abebb162SStefano Garzarella 	}
447112f23cdSStefano Garzarella 
448899c4d18SStefano Garzarella 	ret = _vdpa_register_device(&simdev->vdpa, VDPASIM_BLK_VQ_NUM);
4490c853c2cSMax Gurtovoy 	if (ret)
4500c853c2cSMax Gurtovoy 		goto put_dev;
4510c853c2cSMax Gurtovoy 
4520c853c2cSMax Gurtovoy 	return 0;
4530c853c2cSMax Gurtovoy 
4540c853c2cSMax Gurtovoy put_dev:
455899c4d18SStefano Garzarella 	put_device(&simdev->vdpa.dev);
456899c4d18SStefano Garzarella 	return ret;
457899c4d18SStefano Garzarella }
458899c4d18SStefano Garzarella 
vdpasim_blk_dev_del(struct vdpa_mgmt_dev * mdev,struct vdpa_device * dev)459899c4d18SStefano Garzarella static void vdpasim_blk_dev_del(struct vdpa_mgmt_dev *mdev,
460899c4d18SStefano Garzarella 				struct vdpa_device *dev)
461899c4d18SStefano Garzarella {
462899c4d18SStefano Garzarella 	struct vdpasim *simdev = container_of(dev, struct vdpasim, vdpa);
463899c4d18SStefano Garzarella 
464899c4d18SStefano Garzarella 	_vdpa_unregister_device(&simdev->vdpa);
465899c4d18SStefano Garzarella }
466899c4d18SStefano Garzarella 
467899c4d18SStefano Garzarella static const struct vdpa_mgmtdev_ops vdpasim_blk_mgmtdev_ops = {
468899c4d18SStefano Garzarella 	.dev_add = vdpasim_blk_dev_add,
469899c4d18SStefano Garzarella 	.dev_del = vdpasim_blk_dev_del
470899c4d18SStefano Garzarella };
471899c4d18SStefano Garzarella 
472899c4d18SStefano Garzarella static struct virtio_device_id id_table[] = {
473899c4d18SStefano Garzarella 	{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
474899c4d18SStefano Garzarella 	{ 0 },
475899c4d18SStefano Garzarella };
476899c4d18SStefano Garzarella 
477899c4d18SStefano Garzarella static struct vdpa_mgmt_dev mgmt_dev = {
478899c4d18SStefano Garzarella 	.device = &vdpasim_blk_mgmtdev,
479899c4d18SStefano Garzarella 	.id_table = id_table,
480899c4d18SStefano Garzarella 	.ops = &vdpasim_blk_mgmtdev_ops,
481899c4d18SStefano Garzarella };
482899c4d18SStefano Garzarella 
vdpasim_blk_init(void)483899c4d18SStefano Garzarella static int __init vdpasim_blk_init(void)
484899c4d18SStefano Garzarella {
485899c4d18SStefano Garzarella 	int ret;
486899c4d18SStefano Garzarella 
487899c4d18SStefano Garzarella 	ret = device_register(&vdpasim_blk_mgmtdev);
488aeca7ff2Sruanjinjie 	if (ret) {
489aeca7ff2Sruanjinjie 		put_device(&vdpasim_blk_mgmtdev);
490899c4d18SStefano Garzarella 		return ret;
491aeca7ff2Sruanjinjie 	}
492899c4d18SStefano Garzarella 
493899c4d18SStefano Garzarella 	ret = vdpa_mgmtdev_register(&mgmt_dev);
494899c4d18SStefano Garzarella 	if (ret)
495899c4d18SStefano Garzarella 		goto parent_err;
496899c4d18SStefano Garzarella 
497abebb162SStefano Garzarella 	if (shared_backend) {
498*941fcbb0SStefano Garzarella 		shared_buffer = kvzalloc(VDPASIM_BLK_CAPACITY << SECTOR_SHIFT,
499abebb162SStefano Garzarella 					 GFP_KERNEL);
500abebb162SStefano Garzarella 		if (!shared_buffer) {
501abebb162SStefano Garzarella 			ret = -ENOMEM;
502d121df78SShawn.Shao 			goto mgmt_dev_err;
503abebb162SStefano Garzarella 		}
504abebb162SStefano Garzarella 	}
505abebb162SStefano Garzarella 
506899c4d18SStefano Garzarella 	return 0;
507d121df78SShawn.Shao mgmt_dev_err:
508d121df78SShawn.Shao 	vdpa_mgmtdev_unregister(&mgmt_dev);
509899c4d18SStefano Garzarella parent_err:
510899c4d18SStefano Garzarella 	device_unregister(&vdpasim_blk_mgmtdev);
5110c853c2cSMax Gurtovoy 	return ret;
5120c853c2cSMax Gurtovoy }
5130c853c2cSMax Gurtovoy 
vdpasim_blk_exit(void)5140c853c2cSMax Gurtovoy static void __exit vdpasim_blk_exit(void)
5150c853c2cSMax Gurtovoy {
516abebb162SStefano Garzarella 	kvfree(shared_buffer);
517899c4d18SStefano Garzarella 	vdpa_mgmtdev_unregister(&mgmt_dev);
518899c4d18SStefano Garzarella 	device_unregister(&vdpasim_blk_mgmtdev);
5190c853c2cSMax Gurtovoy }
5200c853c2cSMax Gurtovoy 
5210c853c2cSMax Gurtovoy module_init(vdpasim_blk_init)
5220c853c2cSMax Gurtovoy module_exit(vdpasim_blk_exit)
5230c853c2cSMax Gurtovoy 
5240c853c2cSMax Gurtovoy MODULE_VERSION(DRV_VERSION);
5250c853c2cSMax Gurtovoy MODULE_LICENSE(DRV_LICENSE);
5260c853c2cSMax Gurtovoy MODULE_AUTHOR(DRV_AUTHOR);
5270c853c2cSMax Gurtovoy MODULE_DESCRIPTION(DRV_DESC);
528