static bool vdpasim_blk_handle_req()

in vdpa_sim/vdpa_sim_blk.c [61:195]


static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim,
				   struct vdpasim_virtqueue *vq)
{
	size_t pushed = 0, to_pull, to_push;
	struct virtio_blk_outhdr hdr;
	ssize_t bytes;
	loff_t offset;
	u64 sector;
	u8 status;
	u32 type;
	int ret;

	ret = vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, &vq->in_iov,
				   &vq->head, GFP_ATOMIC);
	if (ret != 1)
		return false;

	if (vq->out_iov.used < 1 || vq->in_iov.used < 1) {
		dev_err(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n",
			vq->out_iov.used, vq->in_iov.used);
		return false;
	}

	if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) {
		dev_err(&vdpasim->vdpa.dev, "request in header too short\n");
		return false;
	}

	/* The last byte is the status and we checked if the last iov has
	 * enough room for it.
	 */
	to_push = vringh_kiov_length(&vq->in_iov) - 1;

	to_pull = vringh_kiov_length(&vq->out_iov);

	bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr,
				      sizeof(hdr));
	if (bytes != sizeof(hdr)) {
		dev_err(&vdpasim->vdpa.dev, "request out header too short\n");
		return false;
	}

	to_pull -= bytes;

	type = vdpasim32_to_cpu(vdpasim, hdr.type);
	sector = vdpasim64_to_cpu(vdpasim, hdr.sector);
	offset = sector << SECTOR_SHIFT;
	status = VIRTIO_BLK_S_OK;

	switch (type) {
	case VIRTIO_BLK_T_IN:
		if (!vdpasim_blk_check_range(sector, to_push)) {
			dev_err(&vdpasim->vdpa.dev,
				"reading over the capacity - offset: 0x%llx len: 0x%zx\n",
				offset, to_push);
			status = VIRTIO_BLK_S_IOERR;
			break;
		}

		bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
					      vdpasim->buffer + offset,
					      to_push);
		if (bytes < 0) {
			dev_err(&vdpasim->vdpa.dev,
				"vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
				bytes, offset, to_push);
			status = VIRTIO_BLK_S_IOERR;
			break;
		}

		pushed += bytes;
		break;

	case VIRTIO_BLK_T_OUT:
		if (!vdpasim_blk_check_range(sector, to_pull)) {
			dev_err(&vdpasim->vdpa.dev,
				"writing over the capacity - offset: 0x%llx len: 0x%zx\n",
				offset, to_pull);
			status = VIRTIO_BLK_S_IOERR;
			break;
		}

		bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov,
					      vdpasim->buffer + offset,
					      to_pull);
		if (bytes < 0) {
			dev_err(&vdpasim->vdpa.dev,
				"vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
				bytes, offset, to_pull);
			status = VIRTIO_BLK_S_IOERR;
			break;
		}
		break;

	case VIRTIO_BLK_T_GET_ID:
		bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
					      vdpasim_blk_id,
					      VIRTIO_BLK_ID_BYTES);
		if (bytes < 0) {
			dev_err(&vdpasim->vdpa.dev,
				"vringh_iov_push_iotlb() error: %zd\n", bytes);
			status = VIRTIO_BLK_S_IOERR;
			break;
		}

		pushed += bytes;
		break;

	default:
		dev_warn(&vdpasim->vdpa.dev,
			 "Unsupported request type %d\n", type);
		status = VIRTIO_BLK_S_IOERR;
		break;
	}

	/* If some operations fail, we need to skip the remaining bytes
	 * to put the status in the last byte
	 */
	if (to_push - pushed > 0)
		vringh_kiov_advance(&vq->in_iov, to_push - pushed);

	/* Last byte is the status */
	bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1);
	if (bytes != 1)
		return false;

	pushed += bytes;

	/* Make sure data is wrote before advancing index */
	smp_wmb();

	vringh_complete_iotlb(&vq->vring, vq->head, pushed);

	return true;
}