diff --git a/Makefile.am b/Makefile.am index 8cbf7401f..2dec2b436 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,6 +6,7 @@ SUBDIRS = lvm SUBDIRS += $(MAYBE_part) SUBDIRS += vhd +SUBDIRS += thin SUBDIRS += control SUBDIRS += drivers SUBDIRS += include diff --git a/configure.ac b/configure.ac index 5f7035415..69db1cc4e 100644 --- a/configure.ac +++ b/configure.ac @@ -80,5 +80,6 @@ control/Makefile drivers/Makefile include/Makefile tapback/Makefile +thin/Makefile ]) AC_OUTPUT diff --git a/control/tap-ctl-create.c b/control/tap-ctl-create.c index d9ae1e46e..16ad01d50 100644 --- a/control/tap-ctl-create.c +++ b/control/tap-ctl-create.c @@ -30,7 +30,7 @@ int tap_ctl_create(const char *params, char **devname, int flags, int parent_minor, - char *secondary, int timeout) + char *secondary, int timeout, int alloc_quantum) { int err, id, minor; @@ -49,7 +49,7 @@ tap_ctl_create(const char *params, char **devname, int flags, int parent_minor, goto destroy; err = tap_ctl_open(id, minor, params, flags, parent_minor, secondary, - timeout); + timeout, alloc_quantum); if (err) goto detach; diff --git a/control/tap-ctl-open.c b/control/tap-ctl-open.c index 3f207df33..d881416aa 100644 --- a/control/tap-ctl-open.c +++ b/control/tap-ctl-open.c @@ -30,7 +30,8 @@ int tap_ctl_open(const int id, const int minor, const char *params, int flags, - const int prt_minor, const char *secondary, int timeout) + const int prt_minor, const char *secondary, int timeout, + int alloc_quantum) { int err; tapdisk_message_t message; @@ -41,6 +42,7 @@ tap_ctl_open(const int id, const int minor, const char *params, int flags, message.u.params.devnum = minor; message.u.params.prt_devnum = prt_minor; message.u.params.req_timeout = timeout; + message.u.params.alloc_quantum = alloc_quantum; message.u.params.flags = flags; err = snprintf(message.u.params.path, diff --git a/control/tap-ctl.c b/control/tap-ctl.c index 90f9b9c7e..e774c8ad5 100644 --- a/control/tap-ctl.c +++ b/control/tap-ctl.c @@ -252,13 +252,16 @@ tap_cli_create_usage(FILE *stream) "[-r turn on read caching into leaf node] [-2 " "use secondary image (in mirror mode if no -s)] [-s " "fail over to the secondary image on ENOSPC] " - "[-t request timeout in seconds] [-D no O_DIRECT]\n"); + "[-t request timeout in seconds] [-D no O_DIRECT] " + "[-T enable thin provisioning] " + "[-q allocation quantum in MBytes]\n"); + } static int tap_cli_create(int argc, char **argv) { - int c, err, flags, prt_minor, timeout; + int c, err, flags, prt_minor, timeout, alloc_quantum; char *args, *devname, *secondary; args = NULL; @@ -267,9 +270,10 @@ tap_cli_create(int argc, char **argv) prt_minor = -1; flags = 0; timeout = 0; + alloc_quantum = 0; optind = 0; - while ((c = getopt(argc, argv, "a:RDd:e:r2:st:h")) != -1) { + while ((c = getopt(argc, argv, "a:RDd:e:r2:st:Tq:h")) != -1) { switch (c) { case 'a': args = optarg; @@ -300,6 +304,12 @@ tap_cli_create(int argc, char **argv) case 't': timeout = atoi(optarg); break; + case 'T': + flags |= TAPDISK_MESSAGE_FLAG_THIN; + break; + case 'q': + alloc_quantum = atoi(optarg); + break; case '?': goto usage; case 'h': @@ -312,7 +322,7 @@ tap_cli_create(int argc, char **argv) goto usage; err = tap_ctl_create(args, &devname, flags, prt_minor, secondary, - timeout); + timeout, alloc_quantum); if (!err) printf("%s\n", devname); @@ -717,14 +727,16 @@ tap_cli_open_usage(FILE *stream) "[-r turn on read caching into leaf node] [-2 " "use secondary image (in mirror mode if no -s)] [-s " "fail over to the secondary image on ENOSPC] " - "[-t request timeout in seconds] [-D no O_DIRECT]\n"); + "[-t request timeout in seconds] [-D no O_DIRECT] " + "[-T enable thin provisioning] " + "[-q allocation quantum in MBytes]\n"); } static int tap_cli_open(int argc, char **argv) { const char *args, *secondary; - int c, pid, minor, flags, prt_minor, timeout; + int c, pid, minor, flags, prt_minor, timeout, alloc_quantum; flags = 0; pid = -1; @@ -733,9 +745,11 @@ tap_cli_open(int argc, char **argv) timeout = 0; args = NULL; secondary = NULL; + alloc_quantum = 0; + optind = 0; - while ((c = getopt(argc, argv, "a:RDm:p:e:r2:st:h")) != -1) { + while ((c = getopt(argc, argv, "a:RDm:p:e:r2:st:Tq:h")) != -1) { switch (c) { case 'p': pid = atoi(optarg); @@ -769,6 +783,12 @@ tap_cli_open(int argc, char **argv) case 't': timeout = atoi(optarg); break; + case 'T': + flags |= TAPDISK_MESSAGE_FLAG_THIN; + break; + case 'q': + alloc_quantum = atoi(optarg); + break; case '?': goto usage; case 'h': @@ -781,7 +801,7 @@ tap_cli_open(int argc, char **argv) goto usage; return tap_ctl_open(pid, minor, args, flags, prt_minor, secondary, - timeout); + timeout, alloc_quantum); usage: tap_cli_open_usage(stderr); diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 4e46489d2..f647f0dea 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -100,6 +100,7 @@ libtapdisk_la_SOURCES += td-stats.c libtapdisk_la_SOURCES += td-stats.h libtapdisk_la_LIBADD = ../vhd/lib/libvhd.la +libtapdisk_la_LIBADD += ../thin/libtapdiskthin.la libtapdisk_la_LIBADD += -laio libtapdisk_la_LIBADD += -lxenctrl diff --git a/drivers/block-vhd.c b/drivers/block-vhd.c index 9cdc1a7e0..bc715e1cf 100644 --- a/drivers/block-vhd.c +++ b/drivers/block-vhd.c @@ -54,6 +54,7 @@ #include #include +#include #include "debug.h" #include "libvhd.h" #include "tapdisk.h" @@ -62,6 +63,8 @@ #include "tapdisk-disktype.h" #include "tapdisk-storage.h" +#include "payload.h" + unsigned int SPB; #define DEBUGGING 2 @@ -123,6 +126,7 @@ unsigned int SPB; #define VHD_FLAG_OPEN_PREALLOCATE 32 #define VHD_FLAG_OPEN_NO_O_DIRECT 64 #define VHD_FLAG_OPEN_LOCAL_CACHE 128 +#define VHD_FLAG_OPEN_THIN 256 #define VHD_FLAG_BAT_LOCKED 1 #define VHD_FLAG_BAT_WRITE_STARTED 2 @@ -140,7 +144,12 @@ unsigned int SPB; #define VHD_FLAG_TX_LIVE 1 #define VHD_FLAG_TX_UPDATE_BAT 2 -typedef uint8_t vhd_flag_t; +/*******THIN PARAMETERS******/ +#define THIN_RESIZE_MIN_INCREMENT 16777216L /* 16 MBs incremets */ +#define THIN_RESIZE_MAX_INCREMENT 1073741824L /* 1024 MBs incremets */ +#define THIN_RESIZE_DEF_INCREMENT 104857600L /* 100 MBs incremets */ + +typedef uint16_t vhd_flag_t; struct vhd_state; struct vhd_request; @@ -232,6 +241,25 @@ struct vhd_state { struct vhd_request *vreq_free[VHD_REQS_DATA]; struct vhd_request vreq_list[VHD_REQS_DATA]; + /* thin provisioning data */ + off64_t eof_bytes; + uint64_t req_bytes; + struct thin_conn_handle *ch; + /* bytes to increase the LV */ + int64_t alloc_quantum; /* bytes the LV will be + * increased every time */ + int64_t virt_bytes; /* virtual VHD size, will be + * set to 0 when the LV is + * increased at least as the + * virtual size */ + int64_t thin_warn_1; /* when available_bytes follow + * below this watermark, a new + * resize is requested */ + int64_t thin_warn_2; /* when available_bytes follow + * below this watermark, we + * start checking if resize + * has succeded */ + /* for redundant bitmap writes */ int padbm_size; char *padbm_buf; @@ -257,6 +285,7 @@ struct vhd_state { static void vhd_complete(void *, struct tiocb *, int); static void finish_data_transaction(struct vhd_state *, struct vhd_bitmap *); +static int vhd_thin_prepare(struct vhd_state *); static struct vhd_state *_vhd_master; static unsigned long _vhd_zsize; @@ -391,29 +420,41 @@ vhd_kill_footer(struct vhd_state *s) return 0; } +static void +update_next_db(struct vhd_state *s, uint64_t next_db) +{ + s->next_db = next_db; +} + + + static inline int find_next_free_block(struct vhd_state *s) { int err; off64_t eom; uint32_t i, entry; + uint64_t next_db; err = vhd_end_of_headers(&s->vhd, &eom); if (err) return err; - s->next_db = secs_round_up(eom); + update_next_db(s, secs_round_up(eom)); s->first_db = s->next_db; if ((s->first_db + s->bm_secs) % s->spp) s->first_db += (s->spp - ((s->first_db + s->bm_secs) % s->spp)); for (i = 0; i < s->bat.bat.entries; i++) { entry = bat_entry(s, i); - if (entry != DD_BLK_UNUSED && entry >= s->next_db) - s->next_db = (uint64_t)entry + (uint64_t)s->spb + if (entry != DD_BLK_UNUSED && entry >= s->next_db) { + next_db = (uint64_t)entry + (uint64_t)s->spb + (uint64_t)s->bm_secs; - if (s->next_db > UINT_MAX) - break; + update_next_db(s, next_db); + } + + if (s->next_db > UINT_MAX) + break; } return 0; @@ -642,6 +683,22 @@ vhd_log_open(struct vhd_state *s) allocated, full, s->next_db); } +static int +vhd_thin_prepare(struct vhd_state *s) +{ + if ((s->eof_bytes = lseek64(s->vhd.fd, 0, SEEK_END)) == -1) + return -errno; + s->req_bytes = 0; + s->virt_bytes = vhd_sectors_to_bytes(s->driver->info.size); + EPRINTF("Thin VHD virt_bytes = %ld", s->virt_bytes); + s->ch = thin_connection_create(); + if (s->ch == NULL) { + EPRINTF("thin connection creation has failed"); + return -1; + } + return 0; +} + static int __vhd_open(td_driver_t *driver, const char *name, vhd_flag_t flags) { @@ -696,6 +753,10 @@ __vhd_open(td_driver_t *driver, const char *name, vhd_flag_t flags) vhd_log_open(s); + if(!test_vhd_flag(flags, VHD_FLAG_OPEN_RDONLY)) { + update_next_db(s, s->next_db); + } + SPB = s->spb; s->vreq_free_count = VHD_REQS_DATA; @@ -706,6 +767,10 @@ __vhd_open(td_driver_t *driver, const char *name, vhd_flag_t flags) driver->info.sector_size = VHD_SECTOR_SIZE; driver->info.info = 0; + if(test_vhd_flag(flags, VHD_FLAG_OPEN_THIN)) { + vhd_thin_prepare(s); + } + DBG(TLOG_INFO, "vhd_open: done (sz:%"PRIu64", sct:%lu, inf:%u)\n", driver->info.size, driver->info.sector_size, driver->info.info); @@ -749,10 +814,17 @@ _vhd_open(td_driver_t *driver, const char *name, td_flag_t flags) VHD_FLAG_OPEN_NO_CACHE); if (flags & TD_OPEN_LOCAL_CACHE) vhd_flags |= VHD_FLAG_OPEN_LOCAL_CACHE; + if (flags & TD_OPEN_THIN) + if(!(flags & TD_OPEN_RDONLY)) + vhd_flags |= VHD_FLAG_OPEN_THIN; /* pre-allocate for all but NFS and LVM storage */ driver->storage = tapdisk_storage_type(name); + /* Disable thin provisioning if not LVM */ + if (driver->storage != TAPDISK_STORAGE_TYPE_LVM) + clear_vhd_flag(vhd_flags, VHD_FLAG_OPEN_THIN); + if (driver->storage != TAPDISK_STORAGE_TYPE_NFS && driver->storage != TAPDISK_STORAGE_TYPE_LVM) vhd_flags |= VHD_FLAG_OPEN_PREALLOCATE; @@ -820,6 +892,14 @@ _vhd_close(td_driver_t *driver) EPRINTF("writing %s batmap: %d\n", s->vhd.file, err); } + if (test_vhd_flag(s->flags, VHD_FLAG_OPEN_THIN)) { + if (s->ch) { + thin_connection_destroy(s->ch); + /* Let's set ch to NULL just in case */ + s->ch = NULL; + } + } + free: vhd_log_close(s); vhd_free_bat(s); @@ -1329,6 +1409,123 @@ aio_write(struct vhd_state *s, struct vhd_request *req, uint64_t offset) TRACE(s); } +static inline int +thin_check_warn_2(struct vhd_state *s, int64_t available_bytes) +{ + uint64_t phy_bytes; + int count; + + if (available_bytes < 0) { + count = 40; + } else { + count = 1; + } + while (count--) { + phy_bytes = lseek64(s->vhd.fd, 0, SEEK_END); + EPRINTF("thin_check_warn_2 phy_bytes = %ld", phy_bytes); + if (s->req_bytes != 0 && + s->req_bytes <= phy_bytes) { + /* Request is completed */ + EPRINTF("Request has been completed"); + s->eof_bytes = phy_bytes; + s->req_bytes = 0; + return 0; + } + if (count > 0) { + sleep(1); + } + } + + if (available_bytes < 0) { + /* We must fail with ENOSPC after we tried everything */ + EPRINTF("Returning -1, fail with ENOSPC"); + return -1; + } else { + return 0; + } +} + +static inline void +send_resize_request(struct vhd_state *s, struct payload *message) +{ + int err; + + init_payload(message); + strncpy(message->path, s->vhd.file, PAYLOAD_MAX_PATH_LENGTH); + if (s->virt_bytes < s->eof_bytes + s->alloc_quantum && + s->virt_bytes != 0) { + + /* This is so we do not waste too much space if we + * have a big quantum. For example if the virtual size + * of the VHD is 1000 MB and the alloc_quantum is 200MB, + * when the lv is 980MB and we need to resize again we + * go to 1180MB, meaning we are wasting around 180MB. + */ + + /* We extend once up to the virtual size and than start + * resizing at THIN_RESIZE_MIN_INCREMENT + * for the remaining metadata part of the VHD. + */ + message->req_size = s->virt_bytes; + s->virt_bytes = 0; + s->alloc_quantum = THIN_RESIZE_MIN_INCREMENT; + s->thin_warn_1 = s->alloc_quantum / 2; + s->thin_warn_2 = s->alloc_quantum / 4; + } else { + message->req_size = s->eof_bytes + s->alloc_quantum; + } + message->type = PAYLOAD_RESIZE; + + EPRINTF("sending resize request for %ld", message->req_size); + + err = thin_sync_send_and_receive(s->ch, message); + if (err) { + DBG(TLOG_WARN, "socket returned: %d\n", err); + } else if (message->err_code == THIN_ERR_CODE_SUCCESS) { + /* record that req_bytes request has been submitted*/ + s->req_bytes = message->req_size; + } else { + /* we will try to send the request again next time */ + DBG(TLOG_WARN, "failed reply: %d\n", message->err_code); + } +} + +/** + * @brief Execute checks related to thin provisioning + * + * This function is called every time there is a request for a new + * block in the VHD, but only if VHD_FLAG_OPEN_THIN is set. + * This is implementing the high level state machine and is using + * some other helper functions to do some specific bits. + * @param s Pointer to vhd_state structure + * @param needed_sectors Number of sectors needed + */ +static inline int +thin_provisioning_checks(struct vhd_state *s, uint64_t needed_sectors) +{ + int64_t available_bytes; + struct payload message; + int ret = 0; + + available_bytes = s->eof_bytes - vhd_sectors_to_bytes(needed_sectors); + EPRINTF("virt_bytes=%ld eof_bytes=%ld available_bytes=%ld", + s->virt_bytes, s->eof_bytes, available_bytes); + + if (available_bytes < s->thin_warn_1) { + /* s->req_bytes is indicating our state */ + if (s->req_bytes == 0) { + send_resize_request(s, &message); + } + + /* let's be more pedantic when we reach end of space */ + if (available_bytes < s->thin_warn_2) { + ret = thin_check_warn_2(s, available_bytes); + } + } + + return ret; +} + /** * Reserves a new extent. * @@ -1341,6 +1538,7 @@ static inline uint64_t reserve_new_block(struct vhd_state *s, uint32_t blk) { int gap = 0; + int ret; ASSERT(!test_vhd_flag(s->bat.status, VHD_FLAG_BAT_WRITE_STARTED)); @@ -1351,6 +1549,12 @@ reserve_new_block(struct vhd_state *s, uint32_t blk) if (s->next_db + gap > UINT_MAX) return (uint64_t)ENOSPC << 32; + if ((s->flags & VHD_FLAG_OPEN_THIN)) { + ret = thin_provisioning_checks(s, s->next_db + gap + s->spb + s->bm_secs); + if (ret < 0) + return (uint64_t)ENOSPC << 32; + } + s->bat.pbw_blk = blk; s->bat.pbw_offset = s->next_db + gap; @@ -1474,7 +1678,6 @@ update_bat(struct vhd_state *s, uint32_t blk) int err; uint64_t lb_end; struct vhd_bitmap *bm; - ASSERT(bat_entry(s, blk) == DD_BLK_UNUSED); if (bat_locked(s)) { @@ -1502,7 +1705,6 @@ update_bat(struct vhd_state *s, uint32_t blk) } schedule_zero_bm_write(s, bm, lb_end); set_vhd_flag(bm->tx.status, VHD_FLAG_TX_UPDATE_BAT); - return 0; } @@ -1537,7 +1739,7 @@ allocate_block(struct vhd_state *s, uint32_t blk) if (next_db > UINT_MAX) return -ENOSPC; - s->next_db = next_db; + update_next_db(s,next_db); s->bat.pbw_blk = blk; s->bat.pbw_offset = s->next_db; @@ -2146,7 +2348,7 @@ finish_bat_write(struct vhd_request *req) if (!req->error) { bat_entry(s, s->bat.pbw_blk) = s->bat.pbw_offset; - s->next_db = s->bat.pbw_offset + s->spb + s->bm_secs; + update_next_db(s, s->bat.pbw_offset + s->spb + s->bm_secs); } else tx->error = req->error; @@ -2473,12 +2675,40 @@ vhd_debug(td_driver_t *driver) */ } +int +vhd_set_quantum(td_driver_t *driver, int quantum_mb) +{ + struct vhd_state *s = (struct vhd_state *)driver->data; + int64_t quantum = quantum_mb * 1048576; + + /* This is always called in case of thin, and even if the + * tap-ctl open/create is not passed as parameter, + * s->alloc_quantum will still be set: + */ + + if (quantum < THIN_RESIZE_MIN_INCREMENT && quantum != 0) { + s->alloc_quantum = THIN_RESIZE_MIN_INCREMENT; + } else if (quantum > THIN_RESIZE_MAX_INCREMENT) { + s->alloc_quantum = THIN_RESIZE_MAX_INCREMENT; + } else { + if (quantum == 0) + s->alloc_quantum = THIN_RESIZE_DEF_INCREMENT; + else + s->alloc_quantum = quantum; + } + + s->thin_warn_1 = s->alloc_quantum / 2; + s->thin_warn_2 = s->alloc_quantum / 4; + return 0; +} + struct tap_disk tapdisk_vhd = { .disk_type = "tapdisk_vhd", .flags = 0, .private_data_size = sizeof(struct vhd_state), .td_open = _vhd_open, .td_close = _vhd_close, + .td_set_quantum = vhd_set_quantum, .td_queue_read = vhd_queue_read, .td_queue_write = vhd_queue_write, .td_get_parent_id = vhd_get_parent_id, diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c index 76970b69a..70bcb67e3 100644 --- a/drivers/tapdisk-control.c +++ b/drivers/tapdisk-control.c @@ -731,6 +731,8 @@ tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, flags |= TD_OPEN_REUSE_PARENT; if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_STANDBY) flags |= TD_OPEN_STANDBY; + if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_THIN) + flags |= TD_OPEN_THIN; if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SECONDARY) { char *name = strdup(request->u.params.secondary); if (!name) { @@ -746,6 +748,13 @@ tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, if (err) goto out; + if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_THIN) { + /* Save parameters in vbd for unpause */ + vbd->xlvhd_alloc_quantum = request->u.params.alloc_quantum; + /* Set allocation Quantum only to the leaf */ + tapdisk_vbd_set_quantum(vbd); + } + err = tapdisk_vbd_get_disk_info(vbd, &vbd->disk_info); if (err) { EPRINTF("VBD %d failed to get disk info: %s\n", vbd->uuid, diff --git a/drivers/tapdisk-interface.c b/drivers/tapdisk-interface.c index 661ebcbe0..96ef2dfb1 100644 --- a/drivers/tapdisk-interface.c +++ b/drivers/tapdisk-interface.c @@ -93,6 +93,21 @@ __td_open(td_image_t *image, td_disk_info_t *info) return 0; } +int +td_set_quantum(td_image_t *image, int quantum) +{ + td_driver_t *driver; + + driver = image->driver; + if (!driver) + return -EINVAL; + + if (driver->ops->td_set_quantum) + return driver->ops->td_set_quantum(driver, quantum); + + return -EINVAL; +} + int td_open(td_image_t *image) { diff --git a/drivers/tapdisk-interface.h b/drivers/tapdisk-interface.h index 7ec401888..458d74aa1 100644 --- a/drivers/tapdisk-interface.h +++ b/drivers/tapdisk-interface.h @@ -29,6 +29,7 @@ int td_load(td_image_t *); int td_close(td_image_t *); int td_get_parent_id(td_image_t *, td_disk_id_t *); int td_validate_parent(td_image_t *, td_image_t *); +int td_set_quantum(td_image_t *, int); void td_queue_write(td_image_t *, td_request_t); void td_queue_read(td_image_t *, td_request_t); diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c index 1df6eb0d2..995747005 100644 --- a/drivers/tapdisk-vbd.c +++ b/drivers/tapdisk-vbd.c @@ -633,6 +633,13 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d return err; } +int +tapdisk_vbd_set_quantum(td_vbd_t *vbd) +{ + return td_set_quantum(tapdisk_vbd_first_image(vbd), + vbd->xlvhd_alloc_quantum); +} + void tapdisk_vbd_detach(td_vbd_t *vbd) { @@ -936,6 +943,10 @@ tapdisk_vbd_resume(td_vbd_t *vbd, const char *name) for (i = 0; i < TD_VBD_EIO_RETRIES; i++) { err = tapdisk_vbd_open_vdi(vbd, name, vbd->flags | TD_OPEN_STRICT, -1); + if (vbd->flags & TD_OPEN_THIN) { + /* Set allocation Quantum only to the leaf */ + tapdisk_vbd_set_quantum(vbd); + } if (!err) break; diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h index 590452a9b..e439fe909 100644 --- a/drivers/tapdisk-vbd.h +++ b/drivers/tapdisk-vbd.h @@ -147,6 +147,7 @@ struct td_vbd_handle { struct td_vbd_rrd rrd; stats_t vdi_stats; + int xlvhd_alloc_quantum; }; #define tapdisk_vbd_for_each_request(vreq, tmp, list) \ @@ -186,6 +187,7 @@ int tapdisk_vbd_close(td_vbd_t *); */ int tapdisk_vbd_open_vdi(td_vbd_t * vbd, const char *params, td_flag_t flags, int prt_devnum); +int tapdisk_vbd_set_quantum(td_vbd_t *vbd); void tapdisk_vbd_close_vdi(td_vbd_t *); int tapdisk_vbd_attach(td_vbd_t *, const char *, int); diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h index bddedbfc7..40944a830 100644 --- a/drivers/tapdisk.h +++ b/drivers/tapdisk.h @@ -89,6 +89,7 @@ extern unsigned int PAGE_SHIFT; #define TD_OPEN_STANDBY 0x00800 #define TD_IGNORE_ENOSPC 0x01000 #define TD_OPEN_NO_O_DIRECT 0x02000 +#define TD_OPEN_THIN 0x04000 #define TD_CREATE_SPARSE 0x00001 #define TD_CREATE_MULTITYPE 0x00002 @@ -182,6 +183,7 @@ struct tap_disk { int private_data_size; int (*td_open) (td_driver_t *, const char *, td_flag_t); int (*td_close) (td_driver_t *); + int (*td_set_quantum) (td_driver_t *, int); int (*td_get_parent_id) (td_driver_t *, td_disk_id_t *); int (*td_validate_parent) (td_driver_t *, td_driver_t *, td_flag_t); void (*td_queue_read) (td_driver_t *, td_request_t); diff --git a/include/Makefile.am b/include/Makefile.am index 90ba1b45a..dfbf732f1 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -18,6 +18,9 @@ blktap_HEADERS += tapdisk-message.h blktap_HEADERS += tap-ctl.h blktap_HEADERS += debug.h blktap_HEADERS += util.h +blktap_HEADERS += payload.h +blktap_HEADERS += kpr_util.h +blktap_HEADERS += thin_log.h noinst_HEADERS = blktap.h noinst_HEADERS += compiler.h diff --git a/include/blktap.h b/include/blktap.h index 9c3437b82..e75406d40 100644 --- a/include/blktap.h +++ b/include/blktap.h @@ -27,5 +27,6 @@ #define BLKTAP2_RING_DEVICE BLKTAP2_DIRECTORY"/blktap" #define BLKTAP2_IO_DEVICE BLKTAP2_DIRECTORY"/tapdev" #define BLKTAP2_ENOSPC_SIGNAL_FILE "/var/run/tapdisk-enospc" +#define THIN_CONTROL_SOCKET "/var/run/thin-prov-ctl" #endif /* _TD_BLKTAP_H_ */ diff --git a/include/kpr_util.h b/include/kpr_util.h new file mode 100644 index 000000000..64ea35994 --- /dev/null +++ b/include/kpr_util.h @@ -0,0 +1,10 @@ +#ifndef _KPR_UTIL_H +#define _KPR_UTIL_H + +#include "payload.h" + +int kpr_split_lvm_path(const char *, char *, char *); +int kpr_tcp_create(uint16_t port); +int kpr_tcp_conn_tx_rx(const char *ip, uint16_t port, struct payload *); + +#endif /* KPR_UTIL_H */ diff --git a/include/payload.h b/include/payload.h new file mode 100644 index 000000000..3041b20ed --- /dev/null +++ b/include/payload.h @@ -0,0 +1,44 @@ +#ifndef _PAYLOAD_H +#define _PAYLOAD_H 1 +#include +#include +#include "tapdisk-message.h" + +#define THINPROVD_DIR "/var/run/thinprovd" +#define PAYLOAD_MAX_PATH_LENGTH TAPDISK_MESSAGE_MAX_PATH_LENGTH +#define IP_MAX_LEN 32 + +#define THIN_ERR_CODE_SUCCESS 0 +#define THIN_ERR_CODE_FAILURE 1 + +#define PAYLOAD_CB_NONE 0 +#define PAYLOAD_CB_SOCK 1 + +typedef enum { + PAYLOAD_RESIZE = 0, + PAYLOAD_CLI, + PAYLOAD_STATUS, + PAYLOAD_UNDEF +} payload_message_t; + +struct payload { + uint8_t type; + char path[PAYLOAD_MAX_PATH_LENGTH]; + uint64_t req_size; + uint8_t cb_type; + char cb_data[128]; + uint16_t err_code; + uint32_t reserved_clt; + uint32_t reserved_srv; + char id[128]; +}; + +struct thin_conn_handle; +int thin_sync_send_and_receive(struct thin_conn_handle *ch, + struct payload *message); +struct thin_conn_handle * thin_connection_create(void); +void thin_connection_destroy(struct thin_conn_handle *ch); +int init_payload(struct payload *); +void print_payload(struct payload *); + +#endif /* payload.h */ diff --git a/include/tap-ctl.h b/include/tap-ctl.h index fd502f00d..b4248ffa5 100644 --- a/include/tap-ctl.h +++ b/include/tap-ctl.h @@ -92,7 +92,8 @@ int tap_ctl_allocate(int *minor, char **devname); int tap_ctl_free(const int minor); int tap_ctl_create(const char *params, char **devname, int flags, - int prt_minor, char *secondary, int timeout); + int prt_minor, char *secondary, int timeout, + int alloc_quantum); int tap_ctl_destroy(const int id, const int minor, int force, struct timeval *timeout); @@ -103,7 +104,8 @@ int tap_ctl_attach(const int id, const int minor); int tap_ctl_detach(const int id, const int minor); int tap_ctl_open(const int id, const int minor, const char *params, int flags, - const int prt_minor, const char *secondary, int timeout); + const int prt_minor, const char *secondary, int timeout, + int alloc_quantum); int tap_ctl_close(const int id, const int minor, const int force, struct timeval *timeout); diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h index f4ea0e4a1..7d9bda77d 100644 --- a/include/tapdisk-message.h +++ b/include/tapdisk-message.h @@ -42,6 +42,7 @@ #define TAPDISK_MESSAGE_FLAG_SECONDARY 0x080 #define TAPDISK_MESSAGE_FLAG_STANDBY 0x100 #define TAPDISK_MESSAGE_FLAG_NO_O_DIRECT 0x200 +#define TAPDISK_MESSAGE_FLAG_THIN 0x400 typedef struct tapdisk_message tapdisk_message_t; typedef uint32_t tapdisk_message_flag_t; @@ -61,6 +62,7 @@ struct tapdisk_message_params { char path[TAPDISK_MESSAGE_MAX_PATH_LENGTH]; uint32_t prt_devnum; uint16_t req_timeout; + uint32_t alloc_quantum; char secondary[TAPDISK_MESSAGE_MAX_PATH_LENGTH]; }; diff --git a/include/thin_log.h b/include/thin_log.h new file mode 100644 index 000000000..361303a5c --- /dev/null +++ b/include/thin_log.h @@ -0,0 +1,36 @@ +#ifndef THIN_LOG +#define THIN_LOG + +/* + * This header file provides a logging API for thin provisioning. + * + * Things to note: + * + * 1) Messages over 1024 characters will be truncated by syslogd. + * + * 2) New line characters, '\n', DO NOT break lines; the octal ASCII + * code of the char is printed instead. For every new line you want, + * you have to call a variant of 'thin_log_X()' + */ + +enum THIN_LOG_LEVEL +{ + THIN_LOG_ERR, + THIN_LOG_INFO, + THIN_LOG_DBG +}; + +void thin_openlog (char *logname); +void thin_closelog(void); + +/* + * Use this function to specify the LOWEST logging level. + * Everything from this level and above will be logged. + */ +void thin_log_upto(enum THIN_LOG_LEVEL tpd_log_level); + +void thin_log_err (const char *message, ...); +void thin_log_info(const char *message, ...); +void thin_log_dbg (const char *message, ...); + +#endif /* THIN_LOG */ diff --git a/lvm/lvm-util.c b/lvm/lvm-util.c index 9e2bfda41..51c7cbb89 100644 --- a/lvm/lvm-util.c +++ b/lvm/lvm-util.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "lvm-util.h" @@ -96,6 +97,44 @@ lvm_parse_pv(struct vg *vg, const char *name, int pvs, uint64_t start) return 0; } +static int +lvm_create_cmd(char *out, const char *command, const char *vgname) +{ + char path[96] = "/var/run/nonpersistent/xenvm.d/"; + char vgs_opts[] = "vg_name,vg_extent_size,lv_count," + "pv_count,pv_name,pe_start"; + char lvs_opts[] = "lv_name,lv_size,segtype,seg_count," + "seg_start,seg_size,devices"; + int err = 0; + + if (strcmp(command, "vgs") && strcmp(command, "lvs")) { + err = EINVAL; + goto exit; + } + + /** + * 'vgname' is a string in the form: + * 'VG_XenStorage-85fcc87c-0167-acf1-da64-ed982543add8'. + */ + strcat(path, vgname); + + /* Construct the lvm command. */ + /* This file exists only if the SR is of type 'dynamic'. */ + strcpy(out, access(path, F_OK) ? "" : "/bin/xenvm "); + + strcat(out, command); + strcat(out, " "); + strcat(out, vgname); + strcat(out, " --noheadings --nosuffix --units=b --options="); + + strcat(out, strcmp(command, "vgs") ? lvs_opts : vgs_opts); + + strcat(out, " 2> /dev/null"); + +exit: + return -err; +} + static int lvm_open_vg(const char *vgname, struct vg *vg) { @@ -103,12 +142,16 @@ lvm_open_vg(const char *vgname, struct vg *vg) int i, err, pvs, lvs; char *cmd, pvname[256]; uint64_t size, pv_start; + char buf[MAX_NAME_SIZE + 256]; + + if ((err = lvm_create_cmd(buf, "vgs", vgname))) { + return err; + } memset(vg, 0, sizeof(*vg)); - err = asprintf(&cmd, "vgs %s --noheadings --nosuffix --units=b " - "--options=vg_name,vg_extent_size,lv_count,pv_count," - "pv_name,pe_start --unbuffered 2> /dev/null", vgname); + err = asprintf(&cmd, buf); + if (err == -1) return -ENOMEM; @@ -210,10 +253,14 @@ lvm_scan_lvs(struct vg *vg) char *cmd; FILE *scan; int i, err; + char buf[MAX_NAME_SIZE + 256]; + + if ((err = lvm_create_cmd(buf, "lvs", vg->name))) { + return err; + } + + err = asprintf(&cmd, buf); - err = asprintf(&cmd, "lvs %s --noheadings --nosuffix --units=b " - "--options=lv_name,lv_size,segtype,seg_count,seg_start," - "seg_size,devices --unbuffered 2> /dev/null", vg->name); if (err == -1) return -ENOMEM; diff --git a/mk/blktap.spec.in b/mk/blktap.spec.in index dbe4faacc..7766e56dd 100644 --- a/mk/blktap.spec.in +++ b/mk/blktap.spec.in @@ -61,12 +61,17 @@ mkdir -p %{buildroot}%{_localstatedir}/log/blktap %{_sbindir}/td-rated %{_sbindir}/part-util %{_sbindir}/vhdpartx +%{_sbindir}/thinprovd +%{_sbindir}/thin-cli +%{_sbindir}/xlvhd-resize +%{_sbindir}/xlvhd-refresh %{_libexecdir}/tapdisk %{_sysconfdir}/udev/rules.d/blktap.rules %{_sysconfdir}/rc.d/init.d/tapback %{_sysconfdir}/logrotate.d/blktap %{_sysconfdir}/xensource/bugtool/tapdisk-logs.xml %{_sysconfdir}/xensource/bugtool/tapdisk-logs/description.xml +%{_sysconfdir}/rc.d/init.d/thinprovd %{_localstatedir}/log/blktap %files devel @@ -79,5 +84,6 @@ mkdir -p %{buildroot}%{_localstatedir}/log/blktap %post [ ! -x /sbin/chkconfig ] || chkconfig --add tapback +[ ! -x /sbin/chkconfig ] || chkconfig --add thinprovd %changelog diff --git a/thin/Makefile.am b/thin/Makefile.am new file mode 100644 index 000000000..2442d30c1 --- /dev/null +++ b/thin/Makefile.am @@ -0,0 +1,36 @@ + +AM_CFLAGS = -Wall +AM_CFLAGS += -Werror +AM_CFLAGS += -pthread + +AM_CPPFLAGS = -D_GNU_SOURCE +AM_CPPFLAGS += -I$(top_srcdir)/include + +sbin_PROGRAMS = thinprovd +thinprovd_SOURCES = thinprovd.c +thinprovd_LDADD = libtapdiskthin.la + +sbin_PROGRAMS += thin-cli +thin_cli_SOURCES = thin_cli.c +thin_cli_LDADD = libtapdiskthin.la + +dist_sbin_SCRIPTS = xlvhd-resize +dist_sbin_SCRIPTS += xlvhd-refresh + +lib_LTLIBRARIES = libtapdiskthin.la + +libtapdiskthin_la_SOURCES = payload.c +libtapdiskthin_la_SOURCES += thin_client.c +libtapdiskthin_la_SOURCES += kpr_util.c +libtapdiskthin_la_SOURCES += thin_log.c + +libtapdiskthin_la_LDFLAGS = -version-info 1:1:1 + +# Have "exec" in name to ensure it's done trough the install-exec route +# (and before install-exec-local) +exec_init_ddir = $(sysconfdir)/rc.d/init.d +dist_exec_init_d_SCRIPTS = thinprovd.init + +install-exec-local: + cd ${DESTDIR}${exec_init_ddir} && mv thinprovd.init thinprovd + diff --git a/thin/kpr_util.c b/thin/kpr_util.c new file mode 100644 index 000000000..55c0663ec --- /dev/null +++ b/thin/kpr_util.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include +#include "payload.h" + +#define PFX_SIZE 5 +#define TCP_BACKLOG 10 + + +/** + * This function parse the given path and populate the other char arrays + * with volume group and logical volume names (if any). + * The accepted patter is the following + * /dev// where and can contain any + * char other than '/' and are interpreted, respectively as VG name + * and LV name. Any other pattern is wrong (/dev/mapper/ is accepted + * but it is not what you want..). + * + * @param[in] path is a NULL terminated char array containing the path to parse + * @param[out] vg if successful contains VG name. Caller must ensure allocated + * space is bigger than #path + * @param[out] lv if successful contains LV name. Caller must ensure allocated + * space is bigger than #path + * @return 0 if OK and 1 otherwise. On exit all the array are NULL terminated + */ +int +kpr_split_lvm_path(const char * path, char * vg, char * lv) +{ + static const char dev_pfx[PFX_SIZE] = "/dev/"; /* no \0 */ + bool flag = false; + + if( strncmp(dev_pfx, path, PFX_SIZE) ) { + fprintf(stderr, "Not a device pattern\n"); + return 1; + } + path += PFX_SIZE; + + /* Extract VG */ + for( ; *path; ++path, ++vg ) { + if( *path == '/' ) { + *vg = '\0'; + break; + } + *vg = *path; + flag = true; + } + + /* Check why and how the loop ended */ + if ( *path == '\0' || !flag ) { + /* terminate strings and error */ + *vg = '\0'; + *lv = '\0'; + return 1; + } + + /* Extract LV */ + ++path; /* skip slash */ + for( flag = false; *path; ++path, ++lv ) { + if( *path == '/' ) { + fprintf(stderr, "too many slashes\n"); + *lv = '\0'; + return 1; + } + *lv = *path; + flag = true; + } + *lv = '\0'; /* terminate string */ + + return flag ? 0 : 1; +} + + +/** + * Create, bind and listen to specified socket + * + * @param[in] port number + * @return file descriptor of socket or -1 + */ +int +kpr_tcp_create(uint16_t port) +{ + int sfd; + struct sockaddr_in s_addr; + + /* create tcp socket */ + sfd = socket(AF_INET, SOCK_STREAM, 0); + if (sfd == -1) { + fprintf(stderr, "tcp socket error"); + return -1; + } + + /* Build socket address, bind and listen */ + memset(&s_addr, 0, sizeof(s_addr)); + s_addr.sin_family = AF_INET; + s_addr.sin_port = htons(port); + s_addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(sfd, (struct sockaddr *) &s_addr, sizeof(s_addr)) == -1) { + fprintf(stderr, "bind error"); + goto fail; + } + + if (listen(sfd, TCP_BACKLOG) == -1) { + fprintf(stderr, "listen error"); + goto fail; + } + + return sfd; +fail: + close(sfd); + return -1; +} + + +int +kpr_tcp_conn_tx_rx(const char *ip, uint16_t port, struct payload * message) +{ + int sfd, len; + struct sockaddr_in s_addr; + struct in_addr ipaddr; + int ret = 1; + + if ( !inet_aton(ip, &ipaddr) ) { + return 0; + } + + len = sizeof(struct payload); + + /* create tcp socket */ + sfd = socket(AF_INET, SOCK_STREAM, 0); + if (sfd == -1) { + return 0; + } + + memset(&s_addr, 0, sizeof(s_addr)); + s_addr.sin_family = AF_INET; + s_addr.sin_port = htons(port); + s_addr.sin_addr = ipaddr; + + if ( connect(sfd, (struct sockaddr *) &s_addr, sizeof(s_addr)) ) { + fprintf(stderr,"Connect failed!\n"); + ret = 0; + goto end; + } + + /* TBD: very basic write, need a while loop */ + if (write(sfd, message, len) != len) { + fprintf(stderr,"Failed to write to socket!\n"); + ret = 0; + goto end; + } + + /* TBD: very basic read */ + if (read(sfd, message, len) != len) { + fprintf(stderr,"Failed to read from socket!\n"); + ret = 0; + goto end; + } + +end: + close(sfd); + return ret; /* Closes our socket; server sees EOF */ + +} diff --git a/thin/payload.c b/thin/payload.c new file mode 100644 index 000000000..e7f9287c7 --- /dev/null +++ b/thin/payload.c @@ -0,0 +1,20 @@ +#include +#include "payload.h" +#include "thin_log.h" + +int init_payload(struct payload *pload) +{ + memset(pload, 0, sizeof(struct payload)); + return 0; +} + +void print_payload(struct payload *pload) +{ + thin_log_info("payload data:\n"); + thin_log_info("type = %d\n", pload->type); + thin_log_info("path = %s\n", pload->path); + thin_log_info("requested size = %"PRIu64"\n", pload->req_size); + thin_log_info("cb_type = %d\n", pload->cb_type); + thin_log_info("err_code = %d\n", pload->err_code); + return; +} diff --git a/thin/thin_cli.c b/thin/thin_cli.c new file mode 100644 index 000000000..dec3ed19c --- /dev/null +++ b/thin/thin_cli.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "payload.h" + +static void usage(char *); + +int +main(int argc, char *argv[]) { + struct payload message; + struct thin_conn_handle *ch; + int arg; + int opt_idx = 0, flag = 1; + int ret; + char vg_name[256]; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + const struct option longopts[] = { + { "add", required_argument, NULL, 0 }, + { "del", required_argument, NULL, 0 }, + { 0, 0, 0, 0 } + }; + + init_payload(&message); + message.type = PAYLOAD_CLI; + + /* We expect at least one valid option and, if more, the others + are discarded + */ + while ((arg = getopt_long(argc, argv, "h", + longopts, &opt_idx)) != -1 && flag) { + switch(arg) { + case 0: + /* master: it is fine to have a string with trailing spaces */ + ret = snprintf(message.path, PAYLOAD_MAX_PATH_LENGTH, + "%s %s", longopts[opt_idx].name, optarg); + if (ret >= PAYLOAD_MAX_PATH_LENGTH) { + fprintf(stderr, "input too long\n"); + return 2; + } + flag = 0; + break; + case 's': + ret = snprintf(message.path, IP_MAX_LEN, "%s %s", + longopts[opt_idx].name, optarg); + if (ret >= IP_MAX_LEN) { + fprintf(stderr, "input too long\n"); + return 2; + } + flag = 0; + break; + case 'h': + usage(argv[0]); + return 0; + default: + usage(argv[0]); + return 1; + } + } + + /* there must be at least one valid option */ + if(flag) { + usage(argv[0]); + return 1; + } + + ch = thin_connection_create(); + if (ch == NULL) { + fprintf(stderr, "connection initialization failed," + " maybe thinprovd is not running?\n"); + return 1; + } + ret = thin_sync_send_and_receive(ch, &message); + if(ret) { + fprintf(stderr, "socket error (%d)\n", ret); + return -ret; + } + + thin_connection_destroy(ch); + + if(message.err_code == THIN_ERR_CODE_SUCCESS) { + /* The request has been successful, so we record it + * creating or deleting a VG file in THINPROVD_DIR. In + * this way if thinprovd will restart it will find all + * the VGs that had been added previously inside + * THINPROVD_DIR and it will we able to add them + * back. + */ + sprintf(vg_name, "%s/%s", THINPROVD_DIR, &message.path[4]); + if (strncmp("add ", message.path, 4) == 0) { + ret = open(vg_name, O_CREAT|O_RDONLY|O_EXCL, mode); + if (ret == -1) { + if (errno == 17) { + printf("%s already added\n", + &message.path[4]); + } else { + fprintf(stderr, "failed to create" + " %s errno=%d\n", + vg_name, errno); + } + } else { + printf("%s added\n", &message.path[4]); + } + close(ret); + } else { + ret = unlink(vg_name); + if (ret == -1) { + if (errno == 2) { + printf("%s already deleted\n", + &message.path[4]); + } else { + fprintf(stderr, "failed to unlink" + " %s errno=%d\n", + vg_name, errno); + } + } else { + printf("%s deleted\n", &message.path[4]); + } + } + return 0; + } else { + fprintf(stderr, "operation failed: err_code=%d\n", + message.err_code); + return message.err_code; + } +} + +static void +usage(char *prog_name) +{ + printf("usage: %s -h\n", prog_name); + printf("usage: %s --add \n", prog_name); + printf("usage: %s --del \n", prog_name); +} diff --git a/thin/thin_client.c b/thin/thin_client.c new file mode 100644 index 000000000..d17f7a6fe --- /dev/null +++ b/thin/thin_client.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "../drivers/tapdisk-log.h" +#include "blktap.h" +#include "payload.h" + +struct thin_conn_handle { + int sfd; +}; + +int thin_sync_send_and_receive(struct thin_conn_handle *ch, + struct payload *message) +{ + size_t len = sizeof(struct payload); + int ret; + + if (ch == NULL) { + return -1; + } + + /* Send messages to server */ +write: + ret = write(ch->sfd, message, len); + if (ret == -1 && errno == EINTR) + goto write; + if (ret != len) + return -errno; + + /* Wait for ACK packet */ +read: + ret = read(ch->sfd, message, len); + if (ret == -1 && errno == EINTR) + goto read; + if (ret != len) + return -errno; + + return 0; +} + +struct thin_conn_handle * +thin_connection_create(void) +{ + struct sockaddr_un svaddr, claddr; + struct thin_conn_handle *ch; + char client_sock_name[64]; + struct timeval timeout; + int ret; + + ch = malloc(sizeof(struct thin_conn_handle)); + if (ch == NULL) + goto out2; + + ch->sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (ch->sfd == -1) { + EPRINTF("Socket creation failed"); + goto out1; + } + + timeout.tv_sec = 10; + timeout.tv_usec = 0; + ret = setsockopt(ch->sfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(struct timeval)); + if (ret < 0) { + EPRINTF("Socket set timeout failed"); + goto out1; + } + + sprintf(client_sock_name, "td_thin_client_%d", getpid()); + + /* Construct address of the client*/ + memset(&claddr, 0, sizeof(struct sockaddr_un)); + claddr.sun_family = AF_UNIX; + strncpy(&claddr.sun_path[1], client_sock_name , strlen(client_sock_name)); + + if (bind(ch->sfd, (struct sockaddr *) &claddr, + sizeof(sa_family_t) + strlen(client_sock_name) + 1) == -1) { + EPRINTF("Bind has failed"); + goto out3; + } + + /* Construct address of server */ + memset(&svaddr, 0, sizeof(struct sockaddr_un)); + svaddr.sun_family = AF_UNIX; + strncpy(svaddr.sun_path, THIN_CONTROL_SOCKET, sizeof(svaddr.sun_path) - 1); + + /* Connect to the server */ + if (connect(ch->sfd, (struct sockaddr *) &svaddr, sizeof(svaddr)) == -1) { + EPRINTF("Connect has failed"); + goto out3; + } + + /* All went well, just return the opaque structure */ + return ch; + +out3: + close(ch->sfd); +out1: + free(ch); +out2: + return NULL; +} + +void +thin_connection_destroy(struct thin_conn_handle *ch) +{ + if (ch == NULL) { + EPRINTF("Was asked to destroy a NULL handle"); + return; + } + /* WARING: + * Close could fail, but not sure what to do if that happes + */ + close(ch->sfd); + free(ch); +} diff --git a/thin/thin_log.c b/thin/thin_log.c new file mode 100644 index 000000000..949f3cb59 --- /dev/null +++ b/thin/thin_log.c @@ -0,0 +1,87 @@ +/* This define is needed for vsyslog(). */ +#define _BSD_SOURCE + +#include "thin_log.h" + +#include +#include + +static int +thin_match_log_level(enum THIN_LOG_LEVEL tpd_log_level) +{ + int log_level; + + switch (tpd_log_level) { + case THIN_LOG_ERR: + log_level = LOG_EMERG; + break; + case THIN_LOG_INFO: + log_level = LOG_INFO; + break; + case THIN_LOG_DBG: + log_level = LOG_DEBUG; + break; + default: + log_level = LOG_INFO; + } + + return log_level; +} + +inline void +thin_openlog(char *logname) +{ + openlog(logname, LOG_CONS | LOG_PID, LOG_DAEMON); +} + +inline void +thin_closelog(void) +{ + closelog(); +} + +inline void +thin_log_upto(enum THIN_LOG_LEVEL tpd_log_level) +{ + setlogmask(LOG_UPTO(thin_match_log_level(tpd_log_level))); +} + +void +thin_log_err(const char *message, ...) +{ + va_list args; + + va_start(args, message); + + /* THIN_LOG_ERR == LOG_EMERG */ + vsyslog(LOG_EMERG, message, args); + + va_end(args); +} + +void +thin_log_info(const char *message, ...) + +{ + va_list args; + + va_start(args, message); + + /* THIN_LOG_INFO == LOG_INFO */ + vsyslog(LOG_INFO, message, args); + + va_end(args); +} + +void +thin_log_dbg(const char *message, ...) +{ + va_list args; + + va_start(args, message); + + /* THIN_LOG_DBG == LOG_DEBUG */ + vsyslog(LOG_DEBUG, message, args); + + va_end(args); +} diff --git a/thin/thinprovd.c b/thin/thinprovd.c new file mode 100644 index 000000000..af94f6be0 --- /dev/null +++ b/thin/thinprovd.c @@ -0,0 +1,885 @@ +#include +#include +#include +#include /* TCP accept client info */ +#include /* TCP accept client info */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* non POSIX */ +#include /* non POSIX */ +#include +#include +#include +#include "blktap.h" +#include "payload.h" +#include "thin_log.h" +#include "kpr_util.h" + +#define BACKLOG 5 +#define PORT_NO 7777 + +static inline int process_payload(struct payload *); +static void process_out_queue(void); +static inline int req_reply(struct payload *); +static int handle_resize(struct payload * buf); +static int handle_status(struct payload * buf); +static int handle_cli(struct payload *); +static void * worker_thread(void *); +static int slave_worker_hook(struct payload *); +static int increase_size(off64_t size, const char * path); +static void parse_cmdline(int, char **); +static int do_daemon(void); +static void split_command(char *, char **); +static int add_vg(char *vg); +static int del_vg(char *vg); +static int signal_set(int signo, void (*func) (int)); +static void clean_handler(int signo); + +#ifdef THIN_REFRESH_LVM +static int refresh_lvm(const char * path); +#endif /* THIN_REFRESH_LVM */ + +bool master; /* no need to be mutex-ed: main writes, workers read */ +pthread_mutex_t ip_mtx = PTHREAD_MUTEX_INITIALIZER; /* see above */ + +/* queue structures */ +SIMPLEQ_HEAD(sqhead, sq_entry); +struct kpr_queue { + struct sqhead qhead; + pthread_mutex_t mtx; + int efd; /* some queues are notified by eventfd */ +} *out_queue; +struct sq_entry { + struct payload data; + SIMPLEQ_ENTRY(sq_entry) entries; +}; + +/* thread structures */ +struct kpr_thread_info { + pthread_t thr_id; + struct kpr_queue *r_queue; + int (*hook)(struct payload *); + int (*net_hook)(struct payload *); + bool net; +}; + +/* list structures */ +LIST_HEAD(vg_list_head, vg_entry); +struct kpr_vg_list { + struct vg_list_head head; + pthread_mutex_t mtx; +} vg_pool; +struct vg_entry { + char name[PAYLOAD_MAX_PATH_LENGTH]; + struct kpr_thread_info thr; + struct kpr_queue *r_queue; + LIST_ENTRY(vg_entry) entries; +}; + +static struct vg_entry * vg_pool_find(char *, bool); +static struct vg_entry * vg_pool_find_and_remove(char *); + + +int daemonize; + + +static struct kpr_queue * +alloc_init_queue(void) +{ + struct kpr_queue *sqp; + + sqp = malloc(sizeof(*sqp)); + if (sqp) { + SIMPLEQ_INIT(&sqp->qhead); + if (pthread_mutex_init(&sqp->mtx, NULL) != 0) + goto out; + if ( (sqp->efd = eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK| + EFD_SEMAPHORE)) == -1 ) + goto out; + } + return sqp; + +out: + free(sqp); + return NULL; +} + +static void +free_queue(struct kpr_queue *sqp) +{ + if (sqp) { + close(sqp->efd); + free(sqp); + } +} + +static inline struct sq_entry * +get_req_from_queue(struct kpr_queue *q) +{ + struct sq_entry *req = NULL; + uint64_t ebuf; + + pthread_mutex_lock(&q->mtx); + if (!SIMPLEQ_EMPTY(&q->qhead)) { + /* pop from requests queue */ + req = SIMPLEQ_FIRST(&q->qhead); + SIMPLEQ_REMOVE_HEAD(&q->qhead, entries); + + } else { + /* clear the fd so we can go back to poll */ + eventfd_read(q->efd, &ebuf); + } + pthread_mutex_unlock(&q->mtx); + return req; +} + +static inline void +put_req_into_queue(struct kpr_queue *q, struct sq_entry *req ) +{ + bool notify_consumer; + + pthread_mutex_lock(&q->mtx); + if (SIMPLEQ_EMPTY(&q->qhead)) { + notify_consumer = true; + } else { + notify_consumer = false; + } + SIMPLEQ_INSERT_TAIL(&q->qhead, req, entries); + pthread_mutex_unlock(&q->mtx); + + /* Notify after releasing the mutex, since the receiver + * is probably in a poll, so when he gets notified the mutex + * is probably available */ + if (notify_consumer == true) { + eventfd_write(q->efd, 1); + } +} + +/** + * Signal handler to clean-up socket file on exit + * + * This is very basic and it assumes it is registered once the socket file + * is created, so no checks. + * FIXME: did not give much thought to its behaviour in multi-threaded env + * + * @param[in] signo the signal to handle + */ +static void clean_handler(int signo) +{ + unlink(THIN_CONTROL_SOCKET); + _exit(0); +} + + +/** + * Set a new signal handler for the specified signal + * + * @param[in] signo the signal to handle + * @return the same as sigaction + */ +static int +signal_set(int signo, void (*func) (int)) +{ + struct sigaction new_act; + struct sigaction old_act; + int r; + + new_act.sa_handler = func; + sigemptyset(&new_act.sa_mask); + new_act.sa_flags = 0; + + r = sigaction(signo, &new_act, &old_act); + if (r < 0) + thin_log_err("Signal %d: handler registration failed", + signo); + return r; +} + +static int +add_previously_added_vgs(void) +{ + DIR *dir; + struct dirent *ent; + int ret; + + if ((dir = opendir(THINPROVD_DIR)) != NULL) { + /* Wen need to call add_vg for every file in this + * directory excluding '.' and '..' since these files + * were added as a consequence of a successfull + * 'thin-cli --add ' command + */ + while ((ent = readdir(dir)) != NULL) { + if (ent->d_name[0] != '.') { + thin_log_info("adding VG %s\n", ent->d_name); + ret = add_vg(ent->d_name); + if (ret != 0) { + thin_log_info("failed to add VG %s\n", + ent->d_name); + } + } + } + closedir(dir); + } else { + /* could not open directory */ + thin_log_err("could not open %s\n", THINPROVD_DIR); + return errno; + } + return 0; +} + +int +main(int argc, char *argv[]) { + + struct pollfd fds[2]; + nfds_t maxfds = 2; + struct sockaddr_un sv_addr, cl_addr; + int sfd; + socklen_t len; + ssize_t ret; + int poll_ret; + struct payload buf; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + thin_openlog("THINPROVD"); + thin_log_upto(THIN_LOG_INFO); + + /* Init pool */ + LIST_INIT(&vg_pool.head); + if (pthread_mutex_init(&vg_pool.mtx, NULL) != 0) + return 1; + + /* Init default queues */ + out_queue = alloc_init_queue(); + if(!out_queue) + return 1; /*no free: return from main */ + + daemonize = 0; + + /* accept command line opts */ + parse_cmdline(argc, argv); + + /* daemonize if required */ + if (do_daemon() == -1) + return 1; /* can do better */ + + ret = mkdir(THINPROVD_DIR, mode); + if (ret == -1) { + if (errno == EEXIST) { + /* If there are some volume groups files in + * this directory, we need to add the + * corresponding VGs back. This is because + * some logic was able to successfully add + * them and is relying on that, so it is not + * going to do an other "add" to the newly + * started thinprovd. + */ + thin_log_info("adding previously added vgs\n"); + ret = add_previously_added_vgs(); + if (ret != 0) { + thin_log_info( + "failed to add previously added vgs\n"); + } + } else { + thin_log_err("failed to create %s errno=%d\n", + THINPROVD_DIR, errno); + return errno; + } + } + + sfd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (sfd == -1) + return -errno; + + memset(&sv_addr, 0, sizeof(struct sockaddr_un)); + sv_addr.sun_family = AF_UNIX; + strncpy(sv_addr.sun_path, THIN_CONTROL_SOCKET, sizeof(sv_addr.sun_path) - 1); + + if (bind(sfd, (struct sockaddr *) &sv_addr, sizeof(struct sockaddr_un)) == -1) { + thin_log_err("bind failed, %s", strerror(errno)); + return -errno; + } + + signal_set(SIGINT, clean_handler); + signal_set(SIGTERM, clean_handler); + + fds[0].fd = out_queue->efd; + fds[0].events = POLLIN; + fds[1].fd = sfd; + fds[1].events = POLLIN; + + for(;;) { + poll_ret = poll(fds, maxfds, -1); /* wait for ever */ + if ( poll_ret < 1 ) { /* 0 not expected */ + thin_log_info("poll returned %d, %s\n", + poll_ret, strerror(errno)); + continue; + } + + if (fds[0].revents) { + /* process out_queue until empty*/ + process_out_queue(); + } + + if (fds[1].revents) { +recv: + len = sizeof(struct sockaddr_un); + /* read from the control socket */ + ret = recvfrom(sfd, &buf, sizeof(buf), 0, + &cl_addr, &len); + if (ret == -1 && errno == EINTR) + goto recv; + if (ret != sizeof(buf)) { + thin_log_err("recvfrom returned %ld, %s\n", + (long)ret, strerror(errno)); + continue; + } + /* Packet of expected len arrived, process it*/ + process_payload(&buf); + + /* Send the acknowledge packet */ +send: + ret = sendto(sfd, &buf, ret, 0, &cl_addr, len); + if (ret == -1 && errno == EINTR) + goto send; + if(ret != sizeof(buf)) { + thin_log_err("sendto returned %ld, %s\n", + (long)ret, strerror(errno)); + } + } + } +} + +static void +process_out_queue(void) +{ + struct payload buf; + struct sq_entry *req; + + for (;;) { + req = get_req_from_queue(out_queue); + if (req == NULL) + break; + + /* Process the req */ + buf = req->data; + switch (buf.cb_type) { + case PAYLOAD_CB_NONE: + /* Just free the req, no async response + * was needed */ + thin_log_info("Processed CB_NONE req\n"); + break; + case PAYLOAD_CB_SOCK: + /* FIXME: + * We do not expect somebody to use this + * for now */ + thin_log_err("CB_SOCK not implemented yet\n"); + break; + default: + thin_log_err("cb_type unknown\n"); + } + free(req); + } +} + +static inline int +process_payload(struct payload * buf) +{ + int err; + + print_payload(buf); + err = req_reply(buf); + print_payload(buf); + thin_log_info("EOM\n\n"); + + return err; +} + +static int +req_reply(struct payload * buf) +{ + switch (buf->type) { + case PAYLOAD_RESIZE: + handle_resize(buf); + break; + case PAYLOAD_CLI: + handle_cli(buf); + break; + case PAYLOAD_STATUS: + handle_status(buf); + break; + default: + buf->type = PAYLOAD_UNDEF; + print_payload(buf); + } + return 0; +} + +static int +handle_resize(struct payload * buf) +{ + struct sq_entry *req; + struct vg_entry *vgentry; + struct kpr_queue *in_queue; + char vgname[PAYLOAD_MAX_PATH_LENGTH]; + char lvname[PAYLOAD_MAX_PATH_LENGTH]; + + if( kpr_split_lvm_path(buf->path, vgname, lvname) ) { + /* Fail request, malformed path */ + buf->err_code = THIN_ERR_CODE_FAILURE; + return 1; + } + + /* search we have a queue for it */ + vgentry = vg_pool_find(vgname, true); + if (vgentry) /* we do */ + in_queue = vgentry->r_queue; /* no lock (sure?) */ + else { + /* Fail request, vg unknown */ + buf->err_code = THIN_ERR_CODE_FAILURE; + return 1; + } + + req = malloc(sizeof(struct sq_entry)); + if(!req) + return 1; + + req->data = *buf; + buf->err_code = THIN_ERR_CODE_SUCCESS; + + put_req_into_queue(in_queue, req); + return 0; +} + +static int +handle_status(struct payload * buf) +{ + /* This is just returning SUCCESS for now */ + buf->err_code = THIN_ERR_CODE_SUCCESS; + return 0; +} + +static int +handle_cli(struct payload * buf) +{ + char command[PAYLOAD_MAX_PATH_LENGTH]; + char *cmd[2]; + int ret; + + /* we reuse the path field for CLI */ + strcpy(command, buf->path); + + split_command(command, cmd); + if(!cmd[0]) + return 1; + + if (!strcmp("add", cmd[0])) { + if(!cmd[1]) + return 1; + ret = add_vg(cmd[1]); + + } + else if (!strcmp("del", cmd[0])) { + if(!cmd[1]) + return 1; + ret = del_vg(cmd[1]); + } + else + ret = 1; + + if (ret) + buf->err_code = THIN_ERR_CODE_FAILURE; + else + buf->err_code = THIN_ERR_CODE_SUCCESS; + + return 0; +} + +static void * +worker_thread(void * ap) +{ + struct sq_entry * req; + struct payload * data; + struct kpr_thread_info *thr_arg; + struct kpr_queue *r_queue; + struct pollfd fds[1]; + int maxfds = 1; + int poll_ret; + int (*hook)(struct payload *); + + /* We must guarantee this structure is properly polulated or + check it and fail in case it is not. In the latter case + we need to check if the thread has returned. + */ + thr_arg = (struct kpr_thread_info *) ap; + r_queue = thr_arg->r_queue; + hook = thr_arg->hook; + + /* Register events for poll */ + fds[0].fd = r_queue->efd; + fds[0].events = POLLIN; + + for(;;) { + req = get_req_from_queue(r_queue); + if (req == NULL) { + /* wait until there is something in the queue */ + for (;;) { + /* wait for ever */ + poll_ret = poll(fds, maxfds, -1); + if ( poll_ret < 1 ) { /* 0 not expected */ + thin_log_info("poll returned %d, %s\n", + poll_ret, strerror(errno)); + continue; + } + if (fds[0].revents) + break; + } + /* try again to get a req after the poll */ + continue; + } + + data = &req->data; + /* For the time being we use PAYLOAD_UNDEF as a way + to notify threads to exit + */ + if (data->type == PAYLOAD_UNDEF) { + free(req); + thin_log_info("Thread cancellation received\n"); + return NULL; + } + + /* Execute worker-thread specific hook */ + hook(data); + + /* push to out queue */ + put_req_into_queue(out_queue, req); + } + return NULL; +} + +static int +slave_worker_hook(struct payload *data) +{ + int ret; + + /* Fulfil request */ + ret = increase_size(data->req_size, data->path); + if (ret == 0 || ret == 3) /* 3 means big enough */ + data->err_code = THIN_ERR_CODE_SUCCESS; + else + data->err_code = THIN_ERR_CODE_FAILURE; + thin_log_info("worker_thread: completed %s (%d)\n\n", + data->path, ret); + /* FIXME: + * Probably we do not need to call refresh_lvm, leaving the + * code here commented so we do not forget that before it was + * called from the slave as a result of a resize from the + * done from master */ +#ifdef THIN_REFRESH_LVM + refresh_lvm(data->path); +#endif /* THIN_REFRESH_LVM */ + return 0; +} + +/** + * @param size: current size to increase in bytes + * @param path: device full path + * @return command return code if command returned properly, -1 otherwise + */ +static int +increase_size(off64_t size, const char * path) +{ +#define NCHARS 16 + pid_t pid; + int status, num_read; + char ssize[NCHARS]; /* enough for G bytes */ + + /* prepare size for command line */ + num_read = snprintf(ssize, NCHARS, "%"PRIu64"b", size); + if (num_read >= NCHARS) + return -1; /* size too big */ + + switch (pid = fork()) { + case -1: + return -1; + case 0: /* child */ + execl("/usr/sbin/xlvhd-resize", "xlvhd-resize", ssize, + path, (char *)NULL); + _exit(127); /* TBD */ + default: /* parent */ + if (waitpid(pid, &status, 0) == -1) + return -1; + else if (WIFEXITED(status)) /* normal exit? */ + status = WEXITSTATUS(status); + else + return -1; + return status; + } +} + +#ifdef THIN_REFRESH_LVM +/** + * @param path: device full path + * @return command return code if command returned properly, -1 otherwise + */ +static int +refresh_lvm(const char * path) +{ + pid_t pid; + int status; + + switch (pid = fork()) { + case -1: + return -1; + case 0: /* child */ + execl("/usr/sbin/xlvhd-refresh", "xlvhd-refresh", path, + (char *)NULL); + _exit(127); /* TBD */ + default: /* parent */ + if (waitpid(pid, &status, 0) == -1) + return -1; + else if (WIFEXITED(status)) /* normal exit? */ + status = WEXITSTATUS(status); + else + return -1; + return status; + } +} +#endif /* THIN_REFRESH_LVM */ + +static void +parse_cmdline(int argc, char ** argv) +{ + int arg, fd_open = 0; + + while ((arg = getopt(argc, argv, "dfs:")) != EOF ) { + switch(arg) { + case 'd': /* daemonize and close fd */ + daemonize = 1; + break; + case 'f': /* if daemonized leave fd open */ + fd_open = 1; + break; + default: + break; + } + } + daemonize += daemonize ? fd_open : 0; + return; +} + + +static int +do_daemon() +{ + if (!daemonize) + return 0; + + return daemon(0, daemonize - 1); /* root dir and close if needed */ +} + + +static void +split_command(char *command, char **cmd_vec) +{ + char *token; + int i; + + token = strtok(command, " "); + for(i = 0; token && (i < 2); ++i) { + cmd_vec[i] = token; + token = strtok(NULL, " "); + } + + if (i < 2) + cmd_vec[1] = '\0'; + + return; +} + + +static int +add_vg(char *vg) +{ + struct vg_entry *p_vg; + + thin_log_info("CLI: add_vg for %s\n",vg); + + /* check we already have it */ + if(vg_pool_find(vg, true)) { + thin_log_info("%s already added\n", vg); + return 0; + } + + /* allocate and init vg_entry */ + p_vg = malloc(sizeof(*p_vg)); + if (!p_vg) { + thin_log_err("Failed to allocate vg_entry struct\n"); + return 1; + } + + /* We rely on CLI to avoid truncated name or non-NULL terminated + strings. Moreover, by dest is not smaller then src */ + strcpy(p_vg->name, vg); + + /* VG and thread specific thread allocated */ + p_vg->r_queue = alloc_init_queue(); + if(!p_vg->r_queue) { + thin_log_err("Failed worker queue creation for %s\n", + p_vg->name); + goto out; + } + + /* Prepare and start VG specific thread */ + p_vg->thr.r_queue = p_vg->r_queue; + p_vg->thr.hook = slave_worker_hook; + p_vg->thr.net_hook = NULL; + if (pthread_create(&p_vg->thr.thr_id, NULL, worker_thread, &p_vg->thr)) { + thin_log_err("Failed worker thread creation for %s\n", + p_vg->name); + goto out2; + } + + /* Everything ok. Add vg to pool */ + LIST_INSERT_HEAD(&vg_pool.head, p_vg, entries); + + thin_log_info("Successfully registered VG %s\n", p_vg->name); + return 0; +out2: + free(p_vg->r_queue); +out: + free(p_vg); + return 1; +} + + +static int +del_vg(char *vg) +{ + struct vg_entry *p_vg; + struct sq_entry *req; + int ret; + + thin_log_info("CLI: del_vg\n"); + + /* Once removed from the pool no new requests can be served + any more + */ + p_vg = vg_pool_find_and_remove(vg); + if(!p_vg) { + thin_log_info("Nothing removed\n"); + return 0; + } + + /* The thread is still able to crunch requests in its queue + so we "poison" the queue to stop the thread + */ + req = malloc(sizeof(*req)); + if(!req) { + /* FIXME: we are going to return but the vg is no more in the + pool while the thread is still running. + We are returning with a runnig thread, not able to receive new + requests and 2 memory leaks.. + */ + thin_log_err("Error with malloc!! Thread still running\n" + "and memory leaked\n"); + return 1; + } + init_payload(&req->data); + req->data.type = PAYLOAD_UNDEF; + /* Insert in queue */ + put_req_into_queue(p_vg->r_queue, req); + + /* Wait for thread to complete */ + ret = pthread_join(p_vg->thr.thr_id, NULL); + if (ret != 0) + thin_log_err("Problem joining thread..FIXME\n"); + + /* + * Thread is dead, let's free resources + */ + /* By design the queue must be empty but we check */ + if (!SIMPLEQ_EMPTY(&p_vg->r_queue->qhead)) + thin_log_err("queue not empty, memory leak! FIXME\n"); + free_queue(p_vg->r_queue); + free(p_vg); + + return 0; +} + +/** + * This function searches the vg_pool for an entry with a given VG name. + * If invoked with locking no mutexes must be hold + * + * @param vg_name name of the volume group to search for + * @param lock ask for function to take care of locking + * @return NULL if not in the pool or a pointer to the entry +*/ +static struct vg_entry * +vg_pool_find(char *vg_name, bool lock) +{ + struct vg_entry *entry, *ret; + ret = NULL; + + if(lock) + pthread_mutex_lock(&vg_pool.mtx); + LIST_FOREACH(entry, &vg_pool.head, entries) { + /* looking for exact match */ + if (strcmp(entry->name, vg_name) == 0) { + ret = entry; + break; + } + } + if(lock) + pthread_mutex_unlock(&vg_pool.mtx); + + return ret; +} + + +/** + * This function removes from vg_pool the entry named vg_name. + * The pool lock is automatic so make sure you are not holding + * any mutex + * + * @param vg_name name of the volume group to remove + * @return NULL if nothing is removed or a pointer to removed item +*/ +static struct vg_entry * +vg_pool_find_and_remove(char *vg_name) +{ + struct vg_entry *entry; + + pthread_mutex_lock(&vg_pool.mtx); + entry = vg_pool_find(vg_name, false); + if(!entry) { + pthread_mutex_unlock(&vg_pool.mtx); + return NULL; + } + LIST_REMOVE(entry, entries); + pthread_mutex_unlock(&vg_pool.mtx); + + return entry; +} + +#if 0 +/* Leaving this here in case will be usefull later */ +static struct sq_entry * +find_and_remove(struct sqhead * head, pid_t id) +{ + struct sq_entry * entry; + SIMPLEQ_FOREACH(entry, head, entries) { + if (entry->data.id == id) { + SIMPLEQ_REMOVE(head, entry, sq_entry, entries); + return entry; + } + } + /* No matches */ + return NULL; +} +#endif diff --git a/thin/thinprovd.init b/thin/thinprovd.init new file mode 100755 index 000000000..eb532ee38 --- /dev/null +++ b/thin/thinprovd.init @@ -0,0 +1,91 @@ +#!/bin/bash +# +# Copyright (C) Citrix Systems Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; version 2.1 only. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# /etc/rc.d/init.d/thinprovd +# +# Starts the thin provisioning daemon +# +# chkconfig: 2345 22 76 +# description: Manage the thin-provisioning daemon +# processname: thinprovd +# PID_FILE: /var/run/thinprovd.pid + +declare -r DAEMON=/usr/sbin/thinprovd +declare -r PROG=`basename $DAEMON` +declare -r PID_FILE=/var/run/${PROG}.pid + +# Source function library. +. /etc/init.d/functions + +RETVAL=0 + +start() { + test -x $DAEMON || exit 5 + if [ -f $PID_FILE ]; then + pid=`cat $PID_FILE` + echo "$PROG already running with pid $pid" + return 0 + fi + echo -n $"Starting $PROG daemon: " + $DAEMON -d + RETVAL=$? + if [ $RETVAL -eq 0 ]; then + i=`pidof $PROG` + echo $i > $PID_FILE + success + else + failure + fi + return $RETVAL +} + +stop() { + echo -n $"Stopping $PROG daemon: " + test -e $PID_FILE || exit 5 + PID=`cat $PID_FILE` + kill $PID + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f $PID_FILE + [ $RETVAL -eq 0 ] && success || failure + return $RETVAL +} + +restart() { + stop + start +} + +case "$1" in +start) + start + ;; +stop) + stop + ;; +restart) + restart + ;; +status) + status $PROG + RETVAL=$? + ;; +*) + echo $"Usage: $0 {start|stop|status|restart}" + RETVAL=3 +esac + +exit $RETVAL diff --git a/thin/xlvhd-refresh b/thin/xlvhd-refresh new file mode 100644 index 000000000..0d4124cbb --- /dev/null +++ b/thin/xlvhd-refresh @@ -0,0 +1,2 @@ +#!/bin/bash +true \ No newline at end of file diff --git a/thin/xlvhd-resize b/thin/xlvhd-resize new file mode 100644 index 000000000..ab5811e52 --- /dev/null +++ b/thin/xlvhd-resize @@ -0,0 +1,3 @@ +#!/bin/bash +logger -t xlvhd-resize ${1} ${2} +xenvm lvextend -L ${1} ${2} --live