38
#include "hw/virtio/virtio-blk-common.h"
39
#include "qemu/coroutine.h"
40
41
+/* Internal buffer size limit for zone report */
42
+#define VIRTIO_BLK_MAX_ZONES_PER_BATCH 4096
43
+
44
static void virtio_blk_ioeventfd_attach(VirtIOBlock *s);
45
46
static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,
450
return err_status;
451
}
452
453
+typedef struct {
454
+ unsigned int total_nr_zones; /* max zones to fill in this request */
455
+ unsigned int nr_zones_done; /* how many zones have been filled in */
456
+ int64_t iov_offset; /* current byte position in in_iov[] */
457
+ int64_t offset; /* current zone report disk offset */
458
+ unsigned int nr_zones; /* for zone report calls */
459
+ unsigned int zones_per_batch; /* size of zone report buffer */
460
+ BlockZoneDescriptor *zones; /* zone report buffer */
461
+} ZoneReportData;
462
+
463
typedef struct ZoneCmdData {
464
VirtIOBlockReq *req;
465
struct iovec *in_iov;
466
unsigned in_num;
467
union {
455
- struct {
456
- unsigned int nr_zones;
457
- BlockZoneDescriptor *zones;
458
- } zone_report_data;
468
+ ZoneReportData zone_report_data;
469
struct {
470
int64_t offset;
471
} zone_append_data;
522
static void virtio_blk_zone_report_complete(void *opaque, int ret)
523
{
524
ZoneCmdData *data = opaque;
525
+ ZoneReportData *zrd = &data->zone_report_data;
526
VirtIOBlockReq *req = data->req;
527
VirtIODevice *vdev = VIRTIO_DEVICE(req->dev);
528
struct iovec *in_iov = data->in_iov;
529
unsigned in_num = data->in_num;
519
- int64_t zrp_size, n, j = 0;
520
- int64_t nz = data->zone_report_data.nr_zones;
530
+ int64_t n;
531
+ unsigned nz = zrd->nr_zones;
532
int8_t err_status = VIRTIO_BLK_S_OK;
522
- struct virtio_blk_zone_report zrp_hdr = (struct virtio_blk_zone_report) {
523
- .nr_zones = cpu_to_le64(nz),
524
- };
533
+ struct virtio_blk_zone_report zrp_hdr = {};
534
535
trace_virtio_blk_zone_report_complete(vdev, req, nz, ret);
536
if (ret) {
538
goto out;
539
}
540
532
- zrp_size = sizeof(struct virtio_blk_zone_report)
533
- + sizeof(struct virtio_blk_zone_descriptor) * nz;
534
- n = iov_from_buf(in_iov, in_num, 0, &zrp_hdr, sizeof(zrp_hdr));
535
- if (n != sizeof(zrp_hdr)) {
536
- virtio_error(vdev, "Driver provided input buffer that is too small!");
537
- err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
538
- goto out;
539
- }
540
-
541
- for (size_t i = sizeof(zrp_hdr); i < zrp_size;
542
- i += sizeof(struct virtio_blk_zone_descriptor), ++j) {
541
+ for (unsigned j = 0; j < nz; j++) {
542
struct virtio_blk_zone_descriptor desc =
543
(struct virtio_blk_zone_descriptor) {
545
- .z_start = cpu_to_le64(data->zone_report_data.zones[j].start
544
+ .z_start = cpu_to_le64(zrd->zones[j].start
545
>> BDRV_SECTOR_BITS),
547
- .z_cap = cpu_to_le64(data->zone_report_data.zones[j].cap
546
+ .z_cap = cpu_to_le64(zrd->zones[j].cap
547
>> BDRV_SECTOR_BITS),
549
- .z_wp = cpu_to_le64(data->zone_report_data.zones[j].wp
548
+ .z_wp = cpu_to_le64(zrd->zones[j].wp
549
>> BDRV_SECTOR_BITS),
550
};
551
553
- switch (data->zone_report_data.zones[j].type) {
552
+ switch (zrd->zones[j].type) {
553
case BLK_ZT_CONV:
554
desc.z_type = VIRTIO_BLK_ZT_CONV;
555
break;
563
g_assert_not_reached();
564
}
565
567
- switch (data->zone_report_data.zones[j].state) {
566
+ switch (zrd->zones[j].state) {
567
case BLK_ZS_RDONLY:
568
desc.z_state = VIRTIO_BLK_ZS_RDONLY;
569
break;
593
}
594
595
/* TODO: it takes O(n^2) time complexity. Optimizations required. */
597
- n = iov_from_buf(in_iov, in_num, i, &desc, sizeof(desc));
596
+ n = iov_from_buf(in_iov, in_num, zrd->iov_offset, &desc, sizeof(desc));
597
if (n != sizeof(desc)) {
598
virtio_error(vdev, "Driver provided input buffer "
599
"for descriptors that is too small!");
600
err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
601
+ goto out;
602
}
603
+
604
+ zrd->iov_offset += sizeof(desc);
605
+ }
606
+
607
+ if (nz > 0) {
608
+ BlockZoneDescriptor *zone = &zrd->zones[nz - 1];
609
+ zrd->offset = zone->start + zone->length;
610
+ }
611
+
612
+ zrd->nr_zones_done += nz;
613
+
614
+ /* Call zone report again if the end hasn't been reached yet */
615
+ if (nz == zrd->zones_per_batch &&
616
+ zrd->nr_zones_done < zrd->total_nr_zones) {
617
+ zrd->nr_zones = MIN(zrd->zones_per_batch,
618
+ zrd->total_nr_zones - zrd->nr_zones_done);
619
+ blk_aio_zone_report(req->dev->blk, zrd->offset, &zrd->nr_zones,
620
+ zrd->zones, virtio_blk_zone_report_complete, data);
621
+ return;
622
+ }
623
+
624
+ /* Fill in header now that all zones have been reported */
625
+ zrp_hdr.nr_zones = cpu_to_le64(zrd->nr_zones_done);
626
+ n = iov_from_buf(in_iov, in_num, 0, &zrp_hdr, sizeof(zrp_hdr));
627
+ if (n != sizeof(zrp_hdr)) {
628
+ virtio_error(vdev, "Driver provided input buffer that is too small!");
629
+ err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
630
+ goto out;
631
}
632
633
out:
634
virtio_blk_req_complete(req, err_status);
635
g_free(req);
608
- g_free(data->zone_report_data.zones);
636
+ g_free(zrd->zones);
637
g_free(data);
638
}
639
645
VirtIODevice *vdev = VIRTIO_DEVICE(s);
646
unsigned int nr_zones;
647
ZoneCmdData *data;
620
- int64_t zone_size, offset;
648
+ ZoneReportData *zrd;
649
+ int64_t offset;
650
uint8_t err_status;
651
652
if (req->in_len < sizeof(struct virtio_blk_inhdr) +
668
trace_virtio_blk_handle_zone_report(vdev, req,
669
offset >> BDRV_SECTOR_BITS, nr_zones);
670
642
- zone_size = sizeof(BlockZoneDescriptor) * nr_zones;
671
data = g_malloc(sizeof(ZoneCmdData));
672
data->req = req;
673
data->in_iov = in_iov;
674
data->in_num = in_num;
647
- data->zone_report_data.nr_zones = nr_zones;
648
- data->zone_report_data.zones = g_malloc(zone_size),
675
650
- blk_aio_zone_report(s->blk, offset, &data->zone_report_data.nr_zones,
651
- data->zone_report_data.zones,
676
+ zrd = &data->zone_report_data;
677
+ zrd->total_nr_zones = nr_zones;
678
+ zrd->nr_zones_done = 0;
679
+ zrd->iov_offset = sizeof(struct virtio_blk_zone_report);
680
+ zrd->offset = offset;
681
+ zrd->zones_per_batch = MIN(nr_zones, VIRTIO_BLK_MAX_ZONES_PER_BATCH);
682
+ zrd->zones = g_malloc(zrd->zones_per_batch * sizeof(BlockZoneDescriptor));
683
+
684
+ zrd->nr_zones = zrd->zones_per_batch;
685
+ blk_aio_zone_report(s->blk, offset, &zrd->nr_zones, zrd->zones,
686
virtio_blk_zone_report_complete, data);
687
return;
688
out: