void ploop_copy_release(struct ploop_copy_handle *h) { if (h == NULL) return; if (h->fs_frozen) { (void)ioctl_device(h->mntfd, FITHAW, 0); h->fs_frozen = 0; } if (h->tracker_on) { (void)ioctl_device(h->devfd, PLOOP_IOC_TRACK_ABORT, 0); h->tracker_on = 0; } if (h->mntfd != -1) { close(h->mntfd); h->mntfd = -1; } if (h->devfd != -1) { close(h->devfd); h->devfd = -1; } if (h->idelta.fd != -1) close_delta(&h->idelta); }
static int ploop_balloon_relocation(int fd, struct ploop_balloon_ctl *b_ctl, const char *device) { int ret = -1; __u32 n_free_blocks = 0; __u32 freezed_a_h; struct freemap *freemap = NULL; struct freemap *rangemap = NULL; struct relocmap *relocmap = NULL; struct ploop_freeblks_ctl *freeblks = NULL; struct ploop_relocblks_ctl *relocblks = NULL;; __u32 *reverse_map = NULL; __u32 reverse_map_len; int top_level; struct delta delta = {}; freemap = freemap_alloc(128); rangemap = freemap_alloc(128); relocmap = relocmap_alloc(128); if (freemap == NULL || rangemap == NULL || relocmap == NULL) { ret = SYSEXIT_MALLOC; goto err; } top_level = b_ctl->level; freezed_a_h = b_ctl->alloc_head; if (b_ctl->mntn_type == PLOOP_MNTN_RELOC) goto reloc; if (b_ctl->mntn_type != PLOOP_MNTN_FBLOADED) { ploop_err(0, "Error: non-suitable mntn_type (%u)", b_ctl->mntn_type); ret = SYSEXIT_PROTOCOL; goto err; } ret = freeblks_alloc(&freeblks, 0); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_FBGET, freeblks); if (ret) goto err; if (freeblks->n_extents == 0) goto reloc; ret = freeblks_alloc(&freeblks, freeblks->n_extents); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_FBGET, freeblks); if (ret) goto err; ret = freeblks2freemap(freeblks, &freemap, &n_free_blocks); if (ret) goto err; ret = open_top_delta(device, &delta, &top_level); if (ret) goto err; reverse_map_len = delta.l2_size + delta.l2_size; reverse_map = alloc_reverse_map(reverse_map_len); if (reverse_map == NULL) { close_delta(&delta); ret = SYSEXIT_MALLOC; goto err; } ret = range_build(freezed_a_h, n_free_blocks, reverse_map, reverse_map_len, &delta, freemap, &rangemap, &relocmap); close_delta(&delta); if (ret) goto err; reloc: ret = relocmap2relocblks(relocmap, top_level, freezed_a_h, n_free_blocks, &relocblks); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_RELOCBLKS, relocblks); if (ret) goto err; ploop_log(0, "TRUNCATED: %u cluster-blocks (%llu bytes)", relocblks->alloc_head, (unsigned long long)(relocblks->alloc_head * S2B(delta.blocksize))); err: free(freemap); free(rangemap); free(relocmap); free(reverse_map); free(freeblks); free(relocblks); return ret; }
int ploop_balloon_change_size(const char *device, int balloonfd, off_t new_size) { int fd = -1; int ret; off_t old_size; __u32 dev_start; /* /sys/block/ploop0/ploop0p1/start */ __u32 n_free_blocks; __u32 freezed_a_h; struct ploop_balloon_ctl b_ctl; struct stat st; struct pfiemap *pfiemap = NULL; struct freemap *freemap = NULL; struct freemap *rangemap = NULL; struct relocmap *relocmap = NULL; struct ploop_freeblks_ctl *freeblks = NULL; struct ploop_relocblks_ctl *relocblks = NULL; __u32 *reverse_map = NULL; __u32 reverse_map_len; int top_level; struct delta delta = { .fd = -1 }; int entries_used; int drop_state = 0; if (fstat(balloonfd, &st)) { ploop_err(errno, "Can't get balloon file size"); return SYSEXIT_FSTAT; } old_size = st.st_size; new_size = (S2B(new_size) + st.st_blksize - 1) & ~(st.st_blksize - 1); ploop_log(0, "Changing balloon size old_size=%ld new_size=%ld", (long)old_size, (long)new_size); pfiemap = fiemap_alloc(128); freemap = freemap_alloc(128); rangemap = freemap_alloc(128); relocmap = relocmap_alloc(128); if (!pfiemap || !freemap || !rangemap || !relocmap) { ret = SYSEXIT_MALLOC; goto err; } fd = open_device(device); if (fd == -1) { ret = SYSEXIT_OPEN; goto err; } memset(&b_ctl, 0, sizeof(b_ctl)); if (old_size < new_size) b_ctl.inflate = 1; ret = ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); if (ret) goto err; drop_state = 1; if (old_size >= new_size) { ret = do_truncate(balloonfd, b_ctl.mntn_type, old_size, new_size); goto err; } if (dev_num2dev_start(device, st.st_dev, &dev_start)) { ploop_err(0, "Can't find out offset from start of ploop " "device (%s) to start of partition", device); ret = SYSEXIT_SYSFS; goto err; } ret = open_top_delta(device, &delta, &top_level); if (ret) goto err; ret = do_inflate(balloonfd, b_ctl.mntn_type, old_size, &new_size, &drop_state); if (ret) goto err; reverse_map_len = delta.l2_size + delta.l2_size; reverse_map = alloc_reverse_map(reverse_map_len); if (reverse_map == NULL) { ret = SYSEXIT_MALLOC; goto err; } ret = fiemap_get(balloonfd, S2B(dev_start), old_size, new_size, &pfiemap); if (ret) goto err; fiemap_adjust(pfiemap, delta.blocksize); ret = fiemap_build_rmap(pfiemap, reverse_map, reverse_map_len, &delta); if (ret) goto err; ret = rmap2freemap(reverse_map, 0, reverse_map_len, &freemap, &entries_used); if (ret) goto err; if (entries_used == 0) { drop_state = 1; ploop_log(0, "No unused cluster blocks found"); goto out; } ret = freemap2freeblks(freemap, top_level, &freeblks, &n_free_blocks); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_FREEBLKS, freeblks); if (ret) goto err; freezed_a_h = freeblks->alloc_head; if (freezed_a_h > reverse_map_len) { ploop_err(0, "Image corrupted: a_h=%u > rlen=%u", freezed_a_h, reverse_map_len); ret = SYSEXIT_PLOOPFMT; goto err; } ret = range_build(freezed_a_h, n_free_blocks, reverse_map, reverse_map_len, &delta, freemap, &rangemap, &relocmap); if (ret) goto err; ret = relocmap2relocblks(relocmap, top_level, freezed_a_h, n_free_blocks, &relocblks); if (ret) goto err; ret = ioctl_device(fd, PLOOP_IOC_RELOCBLKS, relocblks); if (ret) goto err; ploop_log(0, "TRUNCATED: %u cluster-blocks (%llu bytes)", relocblks->alloc_head, (unsigned long long)(relocblks->alloc_head * S2B(delta.blocksize))); out: ret = 0; err: if (drop_state) { memset(&b_ctl, 0, sizeof(b_ctl)); (void)ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); } if (fd != -1) close(fd); free(pfiemap); free(freemap); free(rangemap); free(relocmap); free(reverse_map); free(freeblks); free(relocblks); if (delta.fd != -1) close_delta(&delta); return ret; } int ploop_balloon_get_state(const char *device, __u32 *state) { int fd, ret; struct ploop_balloon_ctl b_ctl; fd = open_device(device); if (fd == -1) return SYSEXIT_OPEN; bzero(&b_ctl, sizeof(b_ctl)); b_ctl.keep_intact = 1; ret = ioctl_device(fd, PLOOP_IOC_BALLOON, &b_ctl); if (ret) goto err; *state = b_ctl.mntn_type; err: close(fd); return ret; }
int ploop_copy_send(struct ploop_copy_send_param *arg) { struct delta idelta = { .fd = -1 }; int tracker_on = 0; int fs_frozen = 0; int devfd = -1; int mntfd = -1; int ret = 0; char *send_from = NULL; char *format = NULL; void *iobuf[2] = {}; int blocksize; __u64 cluster; __u64 pos; __u64 iterpos; __u64 trackpos; __u64 trackend; __u64 xferred; int iter; struct ploop_track_extent e; int i; pthread_t send_th = 0; struct send_data sd = { .mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .cond_sent = PTHREAD_COND_INITIALIZER, }; if (!arg) return SYSEXIT_PARAM; sd.fd = arg->ofd; sd.is_pipe = is_fd_pipe(arg->ofd); if (sd.is_pipe < 0) { ploop_err(0, "Invalid output fd %d: must be a file, " "a pipe or a socket", arg->ofd); return SYSEXIT_PARAM; } if (arg->feedback_fd >= 0 && is_fd_pipe(arg->feedback_fd) != 1) { ploop_err(errno, "Invalid feedback fd %d: must be " "a pipe or a socket", arg->feedback_fd); return SYSEXIT_PARAM; } /* If data is to be send to stdout or stderr, * we have to disable logging to appropriate fd. * * As currently there's no way to disable just stderr, * so in this case we have to disable stdout as well. */ if (arg->ofd == STDOUT_FILENO) ploop_set_verbose_level(PLOOP_LOG_NOSTDOUT); else if (arg->ofd == STDERR_FILENO) ploop_set_verbose_level(PLOOP_LOG_NOCONSOLE); devfd = open(arg->device, O_RDONLY); if (devfd < 0) { ploop_err(errno, "Can't open device %s", arg->device); ret = SYSEXIT_DEVICE; goto done; } mntfd = open_mount_point(arg->device); if (mntfd < 0) { /* Error is printed by open_mount_point() */ ret = SYSEXIT_OPEN; goto done; } ret = get_image_info(arg->device, &send_from, &format, &blocksize); if (ret) goto done; cluster = S2B(blocksize); ret = SYSEXIT_MALLOC; for (i = 0; i < 2; i++) if (p_memalign(&iobuf[i], 4096, cluster)) goto done; ret = complete_running_operation(NULL, arg->device); if (ret) goto done; ret = ioctl_device(devfd, PLOOP_IOC_TRACK_INIT, &e); if (ret) goto done; tracker_on = 1; if (open_delta_simple(&idelta, send_from, O_RDONLY|O_DIRECT, OD_NOFLAGS)) { ret = SYSEXIT_OPEN; goto done; } ret = pthread_create(&send_th, NULL, send_thread, &sd); if (ret) { ploop_err(ret, "Can't create send thread"); ret = SYSEXIT_SYS; goto done; } ploop_log(-1, "Sending %s", send_from); trackend = e.end; for (pos = 0; pos < trackend; ) { int n; trackpos = pos + cluster; ret = ioctl_device(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos); if (ret) goto done; n = do_pread(cluster, pos); if (n == 0) /* EOF */ break; async_send(n, pos); pos += n; } /* First copy done */ iter = 1; iterpos = 0; xferred = 0; for (;;) { int err; err = ioctl(devfd, PLOOP_IOC_TRACK_READ, &e); if (err == 0) { //fprintf(stderr, "TRACK %llu-%llu\n", e.start, e.end); fflush(stdout); if (e.end > trackend) trackend = e.end; if (e.start < iterpos) iter++; iterpos = e.end; xferred += e.end - e.start; for (pos = e.start; pos < e.end; ) { int n; int copy = e.end - pos; if (copy > cluster) copy = cluster; if (pos + copy > trackpos) { trackpos = pos + copy; if (ioctl(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos)) { ploop_err(errno, "PLOOP_IOC_TRACK_SETPOS"); ret = SYSEXIT_DEVIOC; goto done; } } n = do_pread(copy, pos); if (n == 0) { ploop_err(0, "Unexpected EOF"); ret = SYSEXIT_READ; goto done; } async_send(n, pos); pos += n; } } else { if (errno == EAGAIN) /* no more dirty blocks */ break; ploop_err(errno, "PLOOP_IOC_TRACK_READ"); ret = SYSEXIT_DEVIOC; goto done; } if (iter > 10 || (iter > 1 && xferred > trackend)) break; } /* Live iterative transfers are done. Either we transferred * everything or iterations did not converge. In any case * now we must suspend VE disk activity. Now it is just * call of an external program (something sort of * "killall -9 writetest; sleep 1; umount /mnt2"), actual * implementation must be intergrated to vzctl/vzmigrate * and suspend VE with subsequent fsyncing FS. */ /* Send the sync command to receiving side. Since older ploop * might be present on the other side, we need to not break the * backward compatibility, so just send the first few (SYNC_MARK) * bytes of delta file contents. New ploop_receive() interprets * this as "sync me" command, while the old one just writes those * bytes which is useless but harmless. */ if (sd.is_pipe) { char buf[LEN_STATUS + 1] = {}; ret = do_pread(4096, 0); if (ret < SYNC_MARK) { ploop_err(errno, "Short read"); ret = SYSEXIT_READ; goto done; } TS("SEND 0 %d (sync)", SYNC_MARK); async_send(SYNC_MARK, 0); /* Now we should wait for the other side to finish syncing * before freezing the container, to optimize CT frozen time. */ if (arg->feedback_fd < 0) { /* No descriptor to receive a response back is given. * As ugly as it looks, let's just sleep for some time * hoping the other side will finish sync. */ TS("SLEEP 5"); sleep(5); goto sync_done; } /* Wait for feedback from the receiving side */ /* FIXME: use select/poll with a timeout */ if (read(arg->feedback_fd, buf, LEN_STATUS) != LEN_STATUS) { ploop_err(errno, "Can't read feedback"); ret = SYSEXIT_PROTOCOL; goto done; } if (strncmp(buf, STATUS_OK, LEN_STATUS) == 0) { goto sync_done; } else if (strncmp(buf, STATUS_FAIL, LEN_STATUS) == 0) { ploop_err(0, "Remote side reported sync failure"); ret = SYSEXIT_FSYNC; goto done; } else { ploop_err(0, "Got back feedback: %s", buf); ret = SYSEXIT_PROTOCOL; goto done; } } else { /* Writing to local file */ fdatasync(arg->ofd); } sync_done: /* Freeze the container */ TS("FLUSH"); ret = run_cmd(arg->flush_cmd); if (ret) goto done; /* Sync fs */ TS("SYNCFS"); if (sys_syncfs(mntfd)) { ploop_err(errno, "syncfs() failed"); ret = SYSEXIT_FSYNC; goto done; } /* Flush journal and freeze fs (this also clears the fs dirty bit) */ TS("FIFREEZE"); ret = ioctl_device(mntfd, FIFREEZE, 0); if (ret) goto done; fs_frozen = 1; TS("IOC_SYNC"); ret = ioctl_device(devfd, PLOOP_IOC_SYNC, 0); if (ret) goto done; iter = 1; iterpos = 0; for (;;) { int err; struct ploop_track_extent e; err = ioctl(devfd, PLOOP_IOC_TRACK_READ, &e); if (err == 0) { __u64 pos; //fprintf(stderr, "TRACK %llu-%llu\n", e.start, e.end); fflush(stdout); if (e.end > trackend) trackend = e.end; if (e.start < iterpos) iter++; iterpos = e.end; for (pos = e.start; pos < e.end; ) { int n; int copy = e.end - pos; if (copy > cluster) copy = cluster; if (pos + copy > trackpos) { trackpos = pos + copy; ret = ioctl(devfd, PLOOP_IOC_TRACK_SETPOS, &trackpos); if (ret) goto done; } TS("READ %llu %d", pos, copy); n = do_pread(copy, pos); if (n == 0) { ploop_err(0, "Unexpected EOF"); ret = SYSEXIT_READ; goto done; } TS("SEND %llu %d", pos, n); async_send(n, pos); pos += n; } } else { if (errno == EAGAIN) break; ploop_err(errno, "PLOOP_IOC_TRACK_READ"); ret = SYSEXIT_DEVIOC; goto done; } if (iter > 2) { ploop_err(0, "Too many iterations on frozen FS, aborting"); ret = SYSEXIT_LOOP; goto done; } } /* Must clear dirty flag on ploop1 image. */ if (strcmp(format, "ploop1") == 0) { int n; struct ploop_pvd_header *vh; TS("READ 0 4096"); n = do_pread(4096, 0); if (n < SECTOR_SIZE) { ploop_err(errno, "Short read"); ret = SYSEXIT_READ; goto done; } vh = iobuf[i]; vh->m_DiskInUse = 0; TS("SEND 0 %d (1st sector)", SECTOR_SIZE); async_send(SECTOR_SIZE, 0); } TS("IOCTL TRACK_STOP"); ret = ioctl(devfd, PLOOP_IOC_TRACK_STOP, 0); if (ret) goto done; tracker_on = 0; TS("SEND 0 0 (close)"); async_send(0, 0); pthread_join(send_th, NULL); send_th = 0; done: if (send_th) pthread_cancel(send_th); if (fs_frozen) (void)ioctl_device(mntfd, FITHAW, 0); if (tracker_on) (void)ioctl_device(devfd, PLOOP_IOC_TRACK_ABORT, 0); free(iobuf[0]); free(iobuf[1]); if (devfd >=0) close(devfd); if (mntfd >=0) close(mntfd); free(send_from); if (idelta.fd >= 0) close_delta(&idelta); TS("DONE"); return ret; } #undef do_pread #undef async_send /* Deprecated, please use ploop_copy_send() instead */ int ploop_send(const char *device, int ofd, const char *flush_cmd, int is_pipe) { struct ploop_copy_send_param s = { .device = device, .ofd = ofd, .flush_cmd = flush_cmd, }; return ploop_copy_send(&s); }