19f532d00SPekka Enberg #include "kvm/disk-image.h" 286835cedSPrasad Joshi #include "kvm/qcow.h" 3f41a132bSSasha Levin #include "kvm/virtio-blk.h" 43b55dcdeSSasha Levin #include "kvm/kvm.h" 548427891SJean-Philippe Brucker #include "kvm/iovec.h" 6f41a132bSSasha Levin 79f9207c5SSasha Levin #include <linux/err.h> 852c22e6eSAndre Przywara #include <poll.h> 9f41a132bSSasha Levin 10aa400b00SPrasad Joshi int debug_iodelay; 11aa400b00SPrasad Joshi 123b55dcdeSSasha Levin static int disk_image__close(struct disk_image *disk); 133b55dcdeSSasha Levin 143b55dcdeSSasha Levin int disk_img_name_parser(const struct option *opt, const char *arg, int unset) 153b55dcdeSSasha Levin { 163b55dcdeSSasha Levin const char *cur; 173b55dcdeSSasha Levin char *sep; 183b55dcdeSSasha Levin struct kvm *kvm = opt->ptr; 193b55dcdeSSasha Levin 2039ab3a0bSAlexandru Elisei if (kvm->nr_disks >= MAX_DISK_IMAGES) 213b55dcdeSSasha Levin die("Currently only 4 images are supported"); 223b55dcdeSSasha Levin 2339ab3a0bSAlexandru Elisei kvm->cfg.disk_image[kvm->nr_disks].filename = arg; 243b55dcdeSSasha Levin cur = arg; 253b55dcdeSSasha Levin 263b55dcdeSSasha Levin if (strncmp(arg, "scsi:", 5) == 0) { 273b55dcdeSSasha Levin sep = strstr(arg, ":"); 2839ab3a0bSAlexandru Elisei kvm->cfg.disk_image[kvm->nr_disks].wwpn = sep + 1; 29*145a86feSJean-Philippe Brucker 30*145a86feSJean-Philippe Brucker /* Old invocation had two parameters. Ignore the second one. */ 313b55dcdeSSasha Levin sep = strstr(sep + 1, ":"); 323b55dcdeSSasha Levin if (sep) { 333b55dcdeSSasha Levin *sep = 0; 343b55dcdeSSasha Levin cur = sep + 1; 353b55dcdeSSasha Levin } 36*145a86feSJean-Philippe Brucker } 373b55dcdeSSasha Levin 383b55dcdeSSasha Levin do { 393b55dcdeSSasha Levin sep = strstr(cur, ","); 403b55dcdeSSasha Levin if (sep) { 413b55dcdeSSasha Levin if (strncmp(sep + 1, "ro", 2) == 0) 4239ab3a0bSAlexandru Elisei kvm->cfg.disk_image[kvm->nr_disks].readonly = true; 433b55dcdeSSasha Levin else if (strncmp(sep + 1, "direct", 6) == 0) 4439ab3a0bSAlexandru Elisei kvm->cfg.disk_image[kvm->nr_disks].direct = true; 453b55dcdeSSasha Levin *sep = 0; 463b55dcdeSSasha Levin cur = sep + 1; 473b55dcdeSSasha Levin } 483b55dcdeSSasha Levin } while (sep); 493b55dcdeSSasha Levin 5039ab3a0bSAlexandru Elisei kvm->nr_disks++; 513b55dcdeSSasha Levin 523b55dcdeSSasha Levin return 0; 533b55dcdeSSasha Levin } 543b55dcdeSSasha Levin 5554c84566SAsias He struct disk_image *disk_image__new(int fd, u64 size, 5654c84566SAsias He struct disk_image_operations *ops, 5754c84566SAsias He int use_mmap) 589f532d00SPekka Enberg { 5943835ac9SSasha Levin struct disk_image *disk; 609f9207c5SSasha Levin int r; 619f532d00SPekka Enberg 6243835ac9SSasha Levin disk = malloc(sizeof *disk); 6343835ac9SSasha Levin if (!disk) 649f9207c5SSasha Levin return ERR_PTR(-ENOMEM); 659f532d00SPekka Enberg 669f9207c5SSasha Levin *disk = (struct disk_image) { 679f9207c5SSasha Levin .fd = fd, 689f9207c5SSasha Levin .size = size, 699f9207c5SSasha Levin .ops = ops, 709f9207c5SSasha Levin }; 71f4ff38dfSPrasad Joshi 727d22135fSAsias He if (use_mmap == DISK_IMAGE_MMAP) { 737d22135fSAsias He /* 747d22135fSAsias He * The write to disk image will be discarded 757d22135fSAsias He */ 7637c34ca8SSasha Levin disk->priv = mmap(NULL, size, PROT_RW, MAP_PRIVATE | MAP_NORESERVE, fd, 0); 779d15c39aSSasha Levin if (disk->priv == MAP_FAILED) { 789f9207c5SSasha Levin r = -errno; 7930a9aa69SJean-Philippe Brucker goto err_free_disk; 809d15c39aSSasha Levin } 817d22135fSAsias He } 827d22135fSAsias He 8330a9aa69SJean-Philippe Brucker r = disk_aio_setup(disk); 8430a9aa69SJean-Philippe Brucker if (r) 8530a9aa69SJean-Philippe Brucker goto err_unmap_disk; 86f41a132bSSasha Levin 8743835ac9SSasha Levin return disk; 8830a9aa69SJean-Philippe Brucker 8930a9aa69SJean-Philippe Brucker err_unmap_disk: 9030a9aa69SJean-Philippe Brucker if (disk->priv) 9130a9aa69SJean-Philippe Brucker munmap(disk->priv, size); 9230a9aa69SJean-Philippe Brucker err_free_disk: 9330a9aa69SJean-Philippe Brucker free(disk); 9430a9aa69SJean-Philippe Brucker return ERR_PTR(r); 959f532d00SPekka Enberg } 969f532d00SPekka Enberg 973b55dcdeSSasha Levin static struct disk_image *disk_image__open(const char *filename, bool readonly, bool direct) 98499f3bedSPekka Enberg { 9943835ac9SSasha Levin struct disk_image *disk; 1006ec60cfdSPekka Enberg struct stat st; 1015236b505SAsias He int fd, flags; 1025236b505SAsias He 1035236b505SAsias He if (readonly) 1045236b505SAsias He flags = O_RDONLY; 1055236b505SAsias He else 1065236b505SAsias He flags = O_RDWR; 1075236b505SAsias He if (direct) 1085236b505SAsias He flags |= O_DIRECT; 109499f3bedSPekka Enberg 1106ec60cfdSPekka Enberg if (stat(filename, &st) < 0) 1119f9207c5SSasha Levin return ERR_PTR(-errno); 1126ec60cfdSPekka Enberg 113441767feSAsias He /* blk device ?*/ 1145236b505SAsias He disk = blkdev__probe(filename, flags, &st); 1155c5cae75SJean-Philippe Brucker if (!IS_ERR_OR_NULL(disk)) { 1165c5cae75SJean-Philippe Brucker disk->readonly = readonly; 117441767feSAsias He return disk; 1185c5cae75SJean-Philippe Brucker } 1196ec60cfdSPekka Enberg 1205236b505SAsias He fd = open(filename, flags); 121499f3bedSPekka Enberg if (fd < 0) 1229f9207c5SSasha Levin return ERR_PTR(fd); 123499f3bedSPekka Enberg 124441767feSAsias He /* qcow image ?*/ 12517f68274SPekka Enberg disk = qcow_probe(fd, true); 1269f9207c5SSasha Levin if (!IS_ERR_OR_NULL(disk)) { 12717f68274SPekka Enberg pr_warning("Forcing read-only support for QCOW"); 1285c5cae75SJean-Philippe Brucker disk->readonly = true; 12943835ac9SSasha Levin return disk; 13017f68274SPekka Enberg } 13186835cedSPrasad Joshi 132441767feSAsias He /* raw image ?*/ 13343835ac9SSasha Levin disk = raw_image__probe(fd, &st, readonly); 1345c5cae75SJean-Philippe Brucker if (!IS_ERR_OR_NULL(disk)) { 1355c5cae75SJean-Philippe Brucker disk->readonly = readonly; 13643835ac9SSasha Levin return disk; 1375c5cae75SJean-Philippe Brucker } 138499f3bedSPekka Enberg 139d6c58e5bSPrasad Joshi if (close(fd) < 0) 1404542f276SCyrill Gorcunov pr_warning("close() failed"); 141499f3bedSPekka Enberg 1429f9207c5SSasha Levin return ERR_PTR(-ENOSYS); 143499f3bedSPekka Enberg } 144499f3bedSPekka Enberg 1453b55dcdeSSasha Levin static struct disk_image **disk_image__open_all(struct kvm *kvm) 146c1ed214eSPrasad Joshi { 147c1ed214eSPrasad Joshi struct disk_image **disks; 14897f16d66SAsias He const char *filename; 149a67da3beSAsias He const char *wwpn; 15097f16d66SAsias He bool readonly; 1515236b505SAsias He bool direct; 1529f9207c5SSasha Levin void *err; 15397f16d66SAsias He int i; 1543b55dcdeSSasha Levin struct disk_image_params *params = (struct disk_image_params *)&kvm->cfg.disk_image; 15539ab3a0bSAlexandru Elisei int count = kvm->nr_disks; 156c1ed214eSPrasad Joshi 1579f9207c5SSasha Levin if (!count) 1589f9207c5SSasha Levin return ERR_PTR(-EINVAL); 1599f9207c5SSasha Levin if (count > MAX_DISK_IMAGES) 1609f9207c5SSasha Levin return ERR_PTR(-ENOSPC); 161c1ed214eSPrasad Joshi 162c1ed214eSPrasad Joshi disks = calloc(count, sizeof(*disks)); 163c1ed214eSPrasad Joshi if (!disks) 1649f9207c5SSasha Levin return ERR_PTR(-ENOMEM); 165c1ed214eSPrasad Joshi 166c1ed214eSPrasad Joshi for (i = 0; i < count; i++) { 16797f16d66SAsias He filename = params[i].filename; 16897f16d66SAsias He readonly = params[i].readonly; 1695236b505SAsias He direct = params[i].direct; 170a67da3beSAsias He wwpn = params[i].wwpn; 171a67da3beSAsias He 172a67da3beSAsias He if (wwpn) { 173a67da3beSAsias He disks[i] = malloc(sizeof(struct disk_image)); 174a67da3beSAsias He if (!disks[i]) 175a67da3beSAsias He return ERR_PTR(-ENOMEM); 176a67da3beSAsias He disks[i]->wwpn = wwpn; 177a67da3beSAsias He continue; 178a67da3beSAsias He } 179a67da3beSAsias He 18097f16d66SAsias He if (!filename) 181c1ed214eSPrasad Joshi continue; 182c1ed214eSPrasad Joshi 1835236b505SAsias He disks[i] = disk_image__open(filename, readonly, direct); 1849f9207c5SSasha Levin if (IS_ERR_OR_NULL(disks[i])) { 18597f16d66SAsias He pr_err("Loading disk image '%s' failed", filename); 1869f9207c5SSasha Levin err = disks[i]; 187c1ed214eSPrasad Joshi goto error; 188c1ed214eSPrasad Joshi } 1893b55dcdeSSasha Levin disks[i]->debug_iodelay = kvm->cfg.debug_iodelay; 190c1ed214eSPrasad Joshi } 191f41a132bSSasha Levin 192c1ed214eSPrasad Joshi return disks; 193c1ed214eSPrasad Joshi error: 194c1ed214eSPrasad Joshi for (i = 0; i < count; i++) 1959f9207c5SSasha Levin if (!IS_ERR_OR_NULL(disks[i])) 196c1ed214eSPrasad Joshi disk_image__close(disks[i]); 197c1ed214eSPrasad Joshi 198c1ed214eSPrasad Joshi free(disks); 1999f9207c5SSasha Levin return err; 200c1ed214eSPrasad Joshi } 201c1ed214eSPrasad Joshi 2022790307cSJean-Philippe Brucker int disk_image__wait(struct disk_image *disk) 2032790307cSJean-Philippe Brucker { 2042790307cSJean-Philippe Brucker if (disk->ops->wait) 2052790307cSJean-Philippe Brucker return disk->ops->wait(disk); 2062790307cSJean-Philippe Brucker 2072790307cSJean-Philippe Brucker return 0; 2082790307cSJean-Philippe Brucker } 2092790307cSJean-Philippe Brucker 210fda63751SAsias He int disk_image__flush(struct disk_image *disk) 211fda63751SAsias He { 212fda63751SAsias He if (disk->ops->flush) 213fda63751SAsias He return disk->ops->flush(disk); 214fda63751SAsias He 215fda63751SAsias He return fsync(disk->fd); 216fda63751SAsias He } 217fda63751SAsias He 2183b55dcdeSSasha Levin static int disk_image__close(struct disk_image *disk) 219499f3bedSPekka Enberg { 22082d94c50SIngo Molnar /* If there was no disk image then there's nothing to do: */ 22143835ac9SSasha Levin if (!disk) 22272133dd2SAsias He return 0; 22382d94c50SIngo Molnar 22430a9aa69SJean-Philippe Brucker disk_aio_destroy(disk); 22530a9aa69SJean-Philippe Brucker 22643835ac9SSasha Levin if (disk->ops->close) 22772133dd2SAsias He return disk->ops->close(disk); 228499f3bedSPekka Enberg 22943835ac9SSasha Levin if (close(disk->fd) < 0) 2304542f276SCyrill Gorcunov pr_warning("close() failed"); 231499f3bedSPekka Enberg 23243835ac9SSasha Levin free(disk); 23372133dd2SAsias He 23472133dd2SAsias He return 0; 235499f3bedSPekka Enberg } 23670b0d7b0SSasha Levin 2373b55dcdeSSasha Levin static int disk_image__close_all(struct disk_image **disks, int count) 2389df47d00SPrasad Joshi { 2399df47d00SPrasad Joshi while (count) 2409df47d00SPrasad Joshi disk_image__close(disks[--count]); 2419df47d00SPrasad Joshi 2429df47d00SPrasad Joshi free(disks); 2439f9207c5SSasha Levin 2449f9207c5SSasha Levin return 0; 2459df47d00SPrasad Joshi } 2469df47d00SPrasad Joshi 24797a90170SAsias He /* 24897a90170SAsias He * Fill iov with disk data, starting from sector 'sector'. 24997a90170SAsias He * Return amount of bytes read. 25097a90170SAsias He */ 25154c84566SAsias He ssize_t disk_image__read(struct disk_image *disk, u64 sector, 25254c84566SAsias He const struct iovec *iov, int iovcount, void *param) 25370b0d7b0SSasha Levin { 25497a90170SAsias He ssize_t total = 0; 25570b0d7b0SSasha Levin 256aa400b00SPrasad Joshi if (debug_iodelay) 257aa400b00SPrasad Joshi msleep(debug_iodelay); 258aa400b00SPrasad Joshi 259dcd3cd8eSAsias He if (disk->ops->read) { 260dcd3cd8eSAsias He total = disk->ops->read(disk, sector, iov, iovcount, param); 26197a90170SAsias He if (total < 0) { 2624542f276SCyrill Gorcunov pr_info("disk_image__read error: total=%ld\n", (long)total); 2639f9207c5SSasha Levin return total; 26497a90170SAsias He } 2652534c9b6SSasha Levin } 26697a90170SAsias He 267f41a132bSSasha Levin if (!disk->async && disk->disk_req_cb) 2688b52f877SSasha Levin disk->disk_req_cb(param, total); 2695af21162SSasha Levin 27097a90170SAsias He return total; 27170b0d7b0SSasha Levin } 27270b0d7b0SSasha Levin 27397a90170SAsias He /* 27497a90170SAsias He * Write iov to disk, starting from sector 'sector'. 27597a90170SAsias He * Return amount of bytes written. 27697a90170SAsias He */ 27754c84566SAsias He ssize_t disk_image__write(struct disk_image *disk, u64 sector, 27854c84566SAsias He const struct iovec *iov, int iovcount, void *param) 27970b0d7b0SSasha Levin { 28097a90170SAsias He ssize_t total = 0; 28170b0d7b0SSasha Levin 282aa400b00SPrasad Joshi if (debug_iodelay) 283aa400b00SPrasad Joshi msleep(debug_iodelay); 284aa400b00SPrasad Joshi 285dcd3cd8eSAsias He if (disk->ops->write) { 28697a90170SAsias He /* 28797a90170SAsias He * Try writev based operation first 28897a90170SAsias He */ 2895af21162SSasha Levin 290dcd3cd8eSAsias He total = disk->ops->write(disk, sector, iov, iovcount, param); 29197a90170SAsias He if (total < 0) { 2924542f276SCyrill Gorcunov pr_info("disk_image__write error: total=%ld\n", (long)total); 2939f9207c5SSasha Levin return total; 29497a90170SAsias He } 2952534c9b6SSasha Levin } else { 296b41ca15aSSasha Levin /* Do nothing */ 2972534c9b6SSasha Levin } 29870b0d7b0SSasha Levin 299f41a132bSSasha Levin if (!disk->async && disk->disk_req_cb) 3008b52f877SSasha Levin disk->disk_req_cb(param, total); 3015af21162SSasha Levin 30297a90170SAsias He return total; 30370b0d7b0SSasha Levin } 304ff6462e8SSasha Levin 30548427891SJean-Philippe Brucker ssize_t disk_image__get_serial(struct disk_image *disk, struct iovec *iov, 30648427891SJean-Philippe Brucker int iovcount, ssize_t len) 307ff6462e8SSasha Levin { 308ff6462e8SSasha Levin struct stat st; 30948427891SJean-Philippe Brucker void *buf; 3109f9207c5SSasha Levin int r; 311ff6462e8SSasha Levin 3129f9207c5SSasha Levin r = fstat(disk->fd, &st); 3139f9207c5SSasha Levin if (r) 3149f9207c5SSasha Levin return r; 315ff6462e8SSasha Levin 31648427891SJean-Philippe Brucker buf = malloc(len); 31748427891SJean-Philippe Brucker if (!buf) 31848427891SJean-Philippe Brucker return -ENOMEM; 31948427891SJean-Philippe Brucker 32048427891SJean-Philippe Brucker len = snprintf(buf, len, "%llu%llu%llu", 32169f50425SAndreas Herrmann (unsigned long long)st.st_dev, 32269f50425SAndreas Herrmann (unsigned long long)st.st_rdev, 32369f50425SAndreas Herrmann (unsigned long long)st.st_ino); 32448427891SJean-Philippe Brucker if (len < 0 || (size_t)len > iov_size(iov, iovcount)) { 32548427891SJean-Philippe Brucker free(buf); 32648427891SJean-Philippe Brucker return -ENOMEM; 32748427891SJean-Philippe Brucker } 32848427891SJean-Philippe Brucker 32948427891SJean-Philippe Brucker memcpy_toiovec(iov, buf, len); 33048427891SJean-Philippe Brucker free(buf); 33148427891SJean-Philippe Brucker return len; 332ff6462e8SSasha Levin } 3335af21162SSasha Levin 33454c84566SAsias He void disk_image__set_callback(struct disk_image *disk, 33554c84566SAsias He void (*disk_req_cb)(void *param, long len)) 3365af21162SSasha Levin { 3375af21162SSasha Levin disk->disk_req_cb = disk_req_cb; 3385af21162SSasha Levin } 3393b55dcdeSSasha Levin 3403b55dcdeSSasha Levin int disk_image__init(struct kvm *kvm) 3413b55dcdeSSasha Levin { 34239ab3a0bSAlexandru Elisei if (kvm->nr_disks) { 3433b55dcdeSSasha Levin kvm->disks = disk_image__open_all(kvm); 3443b55dcdeSSasha Levin if (IS_ERR(kvm->disks)) 3453b55dcdeSSasha Levin return PTR_ERR(kvm->disks); 3463b55dcdeSSasha Levin } 3473b55dcdeSSasha Levin 3483b55dcdeSSasha Levin return 0; 3493b55dcdeSSasha Levin } 35049a8afd1SSasha Levin dev_base_init(disk_image__init); 3513b55dcdeSSasha Levin 3523b55dcdeSSasha Levin int disk_image__exit(struct kvm *kvm) 3533b55dcdeSSasha Levin { 3543b55dcdeSSasha Levin return disk_image__close_all(kvm->disks, kvm->nr_disks); 3553b55dcdeSSasha Levin } 35649a8afd1SSasha Levin dev_base_exit(disk_image__exit); 357