vmspawn: heap-allocate each DriveInfo individually
Change DriveInfos from a contiguous array of DriveInfo structs to an array of pointers to individually heap-allocated entries. Make all DriveInfo string fields owned (strdup'd) and add drive_info_new()/ drive_info_free() constructors matching the cleanup pattern. This prepares for the block device hotplug work where drives need to be handed off to a hashmap via TAKE_PTR without copying. Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
+28
-10
@@ -25,19 +25,37 @@ DEFINE_PRIVATE_HASH_OPS_FULL(
|
||||
char, string_hash_func, string_compare_func, free,
|
||||
PendingJob, pending_job_free);
|
||||
|
||||
void drive_info_done(DriveInfo *info) {
|
||||
assert(info);
|
||||
info->serial = mfree(info->serial);
|
||||
info->node_name = mfree(info->node_name);
|
||||
info->pcie_port = mfree(info->pcie_port);
|
||||
info->fd = safe_close(info->fd);
|
||||
info->overlay_fd = safe_close(info->overlay_fd);
|
||||
DriveInfo* drive_info_new(void) {
|
||||
DriveInfo *d = new(DriveInfo, 1);
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
*d = (DriveInfo) {
|
||||
.fd = -EBADF,
|
||||
.overlay_fd = -EBADF,
|
||||
};
|
||||
return d;
|
||||
}
|
||||
|
||||
DriveInfo* drive_info_free(DriveInfo *d) {
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
free(d->path);
|
||||
free(d->format);
|
||||
free(d->disk_driver);
|
||||
free(d->serial);
|
||||
free(d->node_name);
|
||||
free(d->pcie_port);
|
||||
safe_close(d->fd);
|
||||
safe_close(d->overlay_fd);
|
||||
return mfree(d);
|
||||
}
|
||||
|
||||
void drive_infos_done(DriveInfos *infos) {
|
||||
assert(infos);
|
||||
FOREACH_ARRAY(d, infos->drives, infos->n_drives)
|
||||
drive_info_done(d);
|
||||
drive_info_free(*d);
|
||||
infos->drives = mfree(infos->drives);
|
||||
infos->n_drives = 0;
|
||||
infos->scsi_pcie_port = mfree(infos->scsi_pcie_port);
|
||||
@@ -748,7 +766,7 @@ static bool drives_need_scsi_controller(DriveInfos *drives) {
|
||||
assert(drives);
|
||||
|
||||
FOREACH_ARRAY(d, drives->drives, drives->n_drives)
|
||||
if (STR_IN_SET(d->disk_driver, "scsi-hd", "scsi-cd"))
|
||||
if (STR_IN_SET((*d)->disk_driver, "scsi-hd", "scsi-cd"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -792,7 +810,7 @@ int vmspawn_qmp_setup_drives(VmspawnQmpBridge *bridge, DriveInfos *drives) {
|
||||
}
|
||||
|
||||
FOREACH_ARRAY(d, drives->drives, drives->n_drives) {
|
||||
r = qmp_setup_drive(bridge, qmp, d);
|
||||
r = qmp_setup_drive(bridge, qmp, *d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
+15
-11
@@ -65,23 +65,27 @@ typedef enum QmpDriveFlags {
|
||||
QMP_DRIVE_DISCARD_NO_UNREF = 1u << 6, /* qcow2 only */
|
||||
} QmpDriveFlags;
|
||||
|
||||
/* Drive info for QMP-based drive setup */
|
||||
/* Drive info for QMP-based drive setup. All string fields are owned.
|
||||
* Each DriveInfo is individually heap-allocated so it can be handed off
|
||||
* to the block device registry via TAKE_PTR. */
|
||||
typedef struct DriveInfo {
|
||||
const char *path; /* kept for logging only — not passed to QEMU */
|
||||
const char *format; /* "raw" or "qcow2" */
|
||||
const char *disk_driver; /* "virtio-blk-pci", "scsi-hd", "scsi-cd", "nvme" */
|
||||
char *serial; /* owned */
|
||||
char *node_name; /* owned */
|
||||
char *pcie_port; /* owned: pcie-root-port id for device_add bus (NULL on non-PCIe) */
|
||||
int fd; /* pre-opened image fd (owned, -EBADF if unused) */
|
||||
int overlay_fd; /* pre-opened anonymous overlay fd for ephemeral (owned, -EBADF if unused) */
|
||||
char *path; /* original path (for logging; not passed to QEMU) */
|
||||
char *format; /* "raw" or "qcow2" */
|
||||
char *disk_driver; /* "virtio-blk-pci", "scsi-hd", "scsi-cd", "nvme" */
|
||||
char *serial;
|
||||
char *node_name;
|
||||
char *pcie_port; /* pcie-root-port id for device_add bus (NULL on non-PCIe) */
|
||||
int fd; /* pre-opened image fd (-EBADF if unused) */
|
||||
int overlay_fd; /* pre-opened anonymous overlay fd for ephemeral (-EBADF if unused) */
|
||||
QmpDriveFlags flags;
|
||||
} DriveInfo;
|
||||
|
||||
void drive_info_done(DriveInfo *info);
|
||||
DriveInfo* drive_info_new(void);
|
||||
DriveInfo* drive_info_free(DriveInfo *d);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DriveInfo *, drive_info_free);
|
||||
|
||||
typedef struct DriveInfos {
|
||||
DriveInfo *drives;
|
||||
DriveInfo **drives; /* array of individually heap-allocated entries */
|
||||
size_t n_drives;
|
||||
char *scsi_pcie_port; /* owned: pcie-root-port id for SCSI controller (NULL if no SCSI or non-PCIe) */
|
||||
} DriveInfos;
|
||||
|
||||
+37
-28
@@ -2303,6 +2303,8 @@ static int qemu_config_add_qmp_monitor(FILE *config_file, int bridge_fds[2], int
|
||||
}
|
||||
|
||||
static int resolve_disk_driver(DiskType dt, const char *filename, DriveInfo *info) {
|
||||
const char *driver;
|
||||
size_t serial_max;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
@@ -2310,34 +2312,34 @@ static int resolve_disk_driver(DiskType dt, const char *filename, DriveInfo *inf
|
||||
|
||||
switch (dt) {
|
||||
case DISK_TYPE_VIRTIO_BLK:
|
||||
info->disk_driver = "virtio-blk-pci";
|
||||
r = disk_serial(filename, DISK_SERIAL_MAX_LEN_VIRTIO_BLK, &info->serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
driver = "virtio-blk-pci";
|
||||
serial_max = DISK_SERIAL_MAX_LEN_VIRTIO_BLK;
|
||||
break;
|
||||
case DISK_TYPE_VIRTIO_SCSI:
|
||||
info->disk_driver = "scsi-hd";
|
||||
r = disk_serial(filename, DISK_SERIAL_MAX_LEN_SCSI, &info->serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
driver = "scsi-hd";
|
||||
serial_max = DISK_SERIAL_MAX_LEN_SCSI;
|
||||
break;
|
||||
case DISK_TYPE_NVME:
|
||||
info->disk_driver = "nvme";
|
||||
r = disk_serial(filename, DISK_SERIAL_MAX_LEN_NVME, &info->serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
driver = "nvme";
|
||||
serial_max = DISK_SERIAL_MAX_LEN_NVME;
|
||||
break;
|
||||
case DISK_TYPE_VIRTIO_SCSI_CDROM:
|
||||
info->disk_driver = "scsi-cd";
|
||||
driver = "scsi-cd";
|
||||
serial_max = DISK_SERIAL_MAX_LEN_SCSI;
|
||||
info->flags |= QMP_DRIVE_READ_ONLY;
|
||||
r = disk_serial(filename, DISK_SERIAL_MAX_LEN_SCSI, &info->serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
info->disk_driver = strdup(driver);
|
||||
if (!info->disk_driver)
|
||||
return log_oom();
|
||||
|
||||
r = disk_serial(filename, serial_max, &info->serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2355,8 +2357,9 @@ static int prepare_primary_drive(const char *runtime_dir, DriveInfos *drives) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract filename from path '%s': %m", arg_image);
|
||||
|
||||
DriveInfo *d = &drives->drives[drives->n_drives++];
|
||||
*d = (DriveInfo) { .fd = -EBADF, .overlay_fd = -EBADF };
|
||||
_cleanup_(drive_info_freep) DriveInfo *d = drive_info_new();
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
r = resolve_disk_driver(arg_image_disk_type, image_fn, d);
|
||||
if (r < 0)
|
||||
@@ -2376,10 +2379,10 @@ static int prepare_primary_drive(const char *runtime_dir, DriveInfos *drives) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Expected regular file or block device for image: %s", arg_image);
|
||||
|
||||
d->path = arg_image;
|
||||
d->format = image_format_to_string(arg_image_format);
|
||||
d->path = strdup(arg_image);
|
||||
d->format = strdup(ASSERT_PTR(image_format_to_string(arg_image_format)));
|
||||
d->node_name = strdup("vmspawn");
|
||||
if (!d->node_name)
|
||||
if (!d->path || !d->format || !d->node_name)
|
||||
return log_oom();
|
||||
d->fd = TAKE_FD(image_fd);
|
||||
if (S_ISBLK(st.st_mode))
|
||||
@@ -2406,6 +2409,7 @@ static int prepare_primary_drive(const char *runtime_dir, DriveInfos *drives) {
|
||||
d->flags |= QMP_DRIVE_NO_FLUSH;
|
||||
}
|
||||
|
||||
drives->drives[drives->n_drives++] = TAKE_PTR(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2423,8 +2427,9 @@ static int prepare_extra_drives(DriveInfos *drives) {
|
||||
|
||||
DiskType dt = drive->disk_type >= 0 ? drive->disk_type : arg_image_disk_type;
|
||||
|
||||
DriveInfo *d = &drives->drives[drives->n_drives++];
|
||||
*d = (DriveInfo) { .fd = -EBADF, .overlay_fd = -EBADF };
|
||||
_cleanup_(drive_info_freep) DriveInfo *d = drive_info_new();
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
r = resolve_disk_driver(dt, drive_fn, d);
|
||||
if (r < 0)
|
||||
@@ -2445,8 +2450,10 @@ static int prepare_extra_drives(DriveInfos *drives) {
|
||||
"Block device '%s' cannot be used with 'qcow2' format, only 'raw' is supported.",
|
||||
drive->path);
|
||||
|
||||
d->path = drive->path;
|
||||
d->format = image_format_to_string(drive->format);
|
||||
d->path = strdup(drive->path);
|
||||
d->format = strdup(ASSERT_PTR(image_format_to_string(drive->format)));
|
||||
if (!d->path || !d->format)
|
||||
return log_oom();
|
||||
d->fd = TAKE_FD(drive_fd);
|
||||
if (S_ISBLK(drive_st.st_mode))
|
||||
d->flags |= QMP_DRIVE_BLOCK_DEVICE;
|
||||
@@ -2454,6 +2461,8 @@ static int prepare_extra_drives(DriveInfos *drives) {
|
||||
|
||||
if (asprintf(&d->node_name, "vmspawn_extra_%zu", extra_idx++) < 0)
|
||||
return log_oom();
|
||||
|
||||
drives->drives[drives->n_drives++] = TAKE_PTR(d);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2477,11 +2486,11 @@ static int assign_pcie_ports(MachineConfig *c) {
|
||||
/* Drives: non-SCSI drives get individual ports, SCSI controller gets one port */
|
||||
bool need_scsi = false;
|
||||
FOREACH_ARRAY(d, drives->drives, drives->n_drives) {
|
||||
if (STR_IN_SET(d->disk_driver, "scsi-hd", "scsi-cd")) {
|
||||
if (STR_IN_SET((*d)->disk_driver, "scsi-hd", "scsi-cd")) {
|
||||
need_scsi = true;
|
||||
continue;
|
||||
}
|
||||
if (asprintf(&d->pcie_port, "vmspawn-pcieport-%zu", port++) < 0)
|
||||
if (asprintf(&(*d)->pcie_port, "vmspawn-pcieport-%zu", port++) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
if (need_scsi)
|
||||
@@ -2513,7 +2522,7 @@ static int prepare_device_info(const char *runtime_dir, MachineConfig *c) {
|
||||
|
||||
/* Build drive info for QMP-based setup. vmspawn opens all image files and
|
||||
* passes fds to QEMU via add-fd — QEMU never needs filesystem access. */
|
||||
drives->drives = new0(DriveInfo, 1 + arg_extra_drives.n_drives);
|
||||
drives->drives = new0(DriveInfo*, 1 + arg_extra_drives.n_drives);
|
||||
if (!drives->drives)
|
||||
return log_oom();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user