block/export: port virtio-blk discard/write zeroes input validation
Validate discard/write zeroes the same way we do for virtio-blk. Some of these checks are mandated by the VIRTIO specification, others are internal to QEMU. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Message-Id: <20210223144653.811468-11-stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
e44362ce31
commit
db4eadf9f1
@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
|
VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
|
||||||
|
VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768,
|
||||||
|
VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768,
|
||||||
};
|
};
|
||||||
struct virtio_blk_inhdr {
|
struct virtio_blk_inhdr {
|
||||||
unsigned char status;
|
unsigned char status;
|
||||||
@ -65,30 +67,102 @@ static void vu_blk_req_complete(VuBlkReq *req)
|
|||||||
free(req);
|
free(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
uint64_t nb_sectors = size >> BDRV_SECTOR_BITS;
|
||||||
|
uint64_t total_sectors;
|
||||||
|
|
||||||
|
if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
blk_get_geometry(vexp->export.blk, &total_sectors);
|
||||||
|
if (sector > total_sectors || nb_sectors > total_sectors - sector) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int coroutine_fn
|
static int coroutine_fn
|
||||||
vu_blk_discard_write_zeroes(BlockBackend *blk, struct iovec *iov,
|
vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov,
|
||||||
uint32_t iovcnt, uint32_t type)
|
uint32_t iovcnt, uint32_t type)
|
||||||
{
|
{
|
||||||
|
BlockBackend *blk = vexp->export.blk;
|
||||||
struct virtio_blk_discard_write_zeroes desc;
|
struct virtio_blk_discard_write_zeroes desc;
|
||||||
ssize_t size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
|
ssize_t size;
|
||||||
|
uint64_t sector;
|
||||||
|
uint32_t num_sectors;
|
||||||
|
uint32_t max_sectors;
|
||||||
|
uint32_t flags;
|
||||||
|
int bytes;
|
||||||
|
|
||||||
|
/* Only one desc is currently supported */
|
||||||
|
if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) {
|
||||||
|
return VIRTIO_BLK_S_UNSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
|
||||||
if (unlikely(size != sizeof(desc))) {
|
if (unlikely(size != sizeof(desc))) {
|
||||||
error_report("Invalid size %zd, expect %zu", size, sizeof(desc));
|
error_report("Invalid size %zd, expected %zu", size, sizeof(desc));
|
||||||
return -EINVAL;
|
return VIRTIO_BLK_S_IOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t range[2] = { le64_to_cpu(desc.sector) << 9,
|
sector = le64_to_cpu(desc.sector);
|
||||||
le32_to_cpu(desc.num_sectors) << 9 };
|
num_sectors = le32_to_cpu(desc.num_sectors);
|
||||||
if (type == VIRTIO_BLK_T_DISCARD) {
|
flags = le32_to_cpu(desc.flags);
|
||||||
if (blk_co_pdiscard(blk, range[0], range[1]) == 0) {
|
max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ?
|
||||||
return 0;
|
VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS :
|
||||||
|
VHOST_USER_BLK_MAX_DISCARD_SECTORS;
|
||||||
|
|
||||||
|
/* This check ensures that 'bytes' fits in an int */
|
||||||
|
if (unlikely(num_sectors > max_sectors)) {
|
||||||
|
return VIRTIO_BLK_S_IOERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS;
|
||||||
|
|
||||||
|
if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) {
|
||||||
|
return VIRTIO_BLK_S_IOERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
|
||||||
|
* and write zeroes commands if any unknown flag is set.
|
||||||
|
*/
|
||||||
|
if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
|
||||||
|
return VIRTIO_BLK_S_UNSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
|
||||||
|
int blk_flags = 0;
|
||||||
|
|
||||||
|
if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
|
||||||
|
blk_flags |= BDRV_REQ_MAY_UNMAP;
|
||||||
}
|
}
|
||||||
} else if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
|
|
||||||
if (blk_co_pwrite_zeroes(blk, range[0], range[1], 0) == 0) {
|
if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS,
|
||||||
return 0;
|
bytes, blk_flags) == 0) {
|
||||||
|
return VIRTIO_BLK_S_OK;
|
||||||
|
}
|
||||||
|
} else if (type == VIRTIO_BLK_T_DISCARD) {
|
||||||
|
/*
|
||||||
|
* The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
|
||||||
|
* discard commands if the unmap flag is set.
|
||||||
|
*/
|
||||||
|
if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
|
||||||
|
return VIRTIO_BLK_S_UNSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS,
|
||||||
|
bytes) == 0) {
|
||||||
|
return VIRTIO_BLK_S_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return VIRTIO_BLK_S_IOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
|
static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
|
||||||
@ -177,19 +251,13 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
|
|||||||
}
|
}
|
||||||
case VIRTIO_BLK_T_DISCARD:
|
case VIRTIO_BLK_T_DISCARD:
|
||||||
case VIRTIO_BLK_T_WRITE_ZEROES: {
|
case VIRTIO_BLK_T_WRITE_ZEROES: {
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (!vexp->writable) {
|
if (!vexp->writable) {
|
||||||
req->in->status = VIRTIO_BLK_S_IOERR;
|
req->in->status = VIRTIO_BLK_S_IOERR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = vu_blk_discard_write_zeroes(blk, &elem->out_sg[1], out_num, type);
|
req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num,
|
||||||
if (rc == 0) {
|
type);
|
||||||
req->in->status = VIRTIO_BLK_S_OK;
|
|
||||||
} else {
|
|
||||||
req->in->status = VIRTIO_BLK_S_IOERR;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -362,11 +430,13 @@ vu_blk_initialize_config(BlockDriverState *bs,
|
|||||||
config->min_io_size = cpu_to_le16(1);
|
config->min_io_size = cpu_to_le16(1);
|
||||||
config->opt_io_size = cpu_to_le32(1);
|
config->opt_io_size = cpu_to_le32(1);
|
||||||
config->num_queues = cpu_to_le16(num_queues);
|
config->num_queues = cpu_to_le16(num_queues);
|
||||||
config->max_discard_sectors = cpu_to_le32(32768);
|
config->max_discard_sectors =
|
||||||
|
cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS);
|
||||||
config->max_discard_seg = cpu_to_le32(1);
|
config->max_discard_seg = cpu_to_le32(1);
|
||||||
config->discard_sector_alignment =
|
config->discard_sector_alignment =
|
||||||
cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
|
cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
|
||||||
config->max_write_zeroes_sectors = cpu_to_le32(32768);
|
config->max_write_zeroes_sectors
|
||||||
|
= cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS);
|
||||||
config->max_write_zeroes_seg = cpu_to_le32(1);
|
config->max_write_zeroes_seg = cpu_to_le32(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user