Skip to content

Commit

Permalink
CA-124991: Produce v1 RRDs for I/O errors and next VHD block
Browse files Browse the repository at this point in the history
Signed-off-by: Jon Ludlam <jonathan.ludlam@eu.citrix.com>
Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com>
Reviewed-by: Vineeth Thampi Raveendran <vineeth.thampi@citrix.com>

GitHub: closes #66 on xapi-project/blktap
  • Loading branch information
Thanos Makatos committed Feb 17, 2014
1 parent 57ee889 commit 4703674
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ libtapdisk_la_SOURCES += block-nbd.c

libtapdisk_la_LIBADD = ../vhd/lib/libvhd.la
libtapdisk_la_LIBADD += -laio
libtapdisk_la_LIBADD += -lcrypto

logrotatedir = $(sysconfdir)/logrotate.d
dist_logrotate_DATA = blktap
Expand Down
44 changes: 44 additions & 0 deletions drivers/tapdisk-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

#define SYSLOG_NAMES
#include <syslog.h>
#include <stdarg.h>

#include "tapdisk.h"
#include "tapdisk-log.h"
Expand All @@ -46,6 +47,13 @@

#define MIN(a,b) (((a) < (b)) ? (a) : (b))

#define ASSERT(_p) \
if (!(_p)) { \
EPRINTF("%s:%d: FAILED ASSERTION: '%s'\n", \
__FILE__, __LINE__, #_p); \
td_panic(); \
}

static int
tapdisk_syslog_facility_by_name(const char *name)
{
Expand Down Expand Up @@ -275,3 +283,39 @@ uint64_t ntohll(uint64_t a) {
#endif
#define htonll ntohll


/**
* Simplified version of snprintf that return 0 if everything has gone OK and
* +errno if not (including the buffer not being large enough to hold the
* string).
*/
int
tapdisk_snprintf(char *buf, int * const off, int * const size,
unsigned int depth, const char *format, ...) {

int err, i;
va_list ap;

ASSERT(buf);
ASSERT(off);
ASSERT(size);

for (i = 0; i < depth; i++) {
err = snprintf(buf + *off, *size, " ");
if (err < 0)
return errno;
*off += err;
*size -= err;
}

va_start(ap, format);
err = vsnprintf(buf + *off, *size, format, ap);
va_end(ap);
if (err >= *size)
return ENOBUFS;
else if (err < 0)
return errno;
*off += err;
*size -= err;
return 0;
}
9 changes: 9 additions & 0 deletions drivers/tapdisk-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,13 @@ int tapdisk_linux_version(void);
uint64_t ntohll(uint64_t);
#define htonll ntohll


/**
* Simplified version of snprintf that returns 0 if everything has gone OK and
* +errno if not (including the buffer not being large enough to hold the
* string).
*/
int
tapdisk_snprintf(char *buf, int * const off, int * const size,
unsigned int depth, const char *format, ...);
#endif
239 changes: 239 additions & 0 deletions drivers/tapdisk-vbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#include <libgen.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <openssl/md5.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "libvhd.h"
#include "tapdisk-blktap.h"
Expand All @@ -40,6 +43,13 @@
#include "tapdisk-stats.h"
#include "tapdisk-storage.h"
#include "tapdisk-nbdserver.h"
#include "tapdisk-utils.h"

/*
* FIXME blktap3 stores the page size in a global variable, use that once
* blktap3 gets merged
*/
#define RRD_SHM_SIZE 4096

#define DBG(_level, _f, _a...) tlog_write(_level, _f, ##_a)
#define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
Expand Down Expand Up @@ -127,9 +137,122 @@ tapdisk_vbd_validate_chain(td_vbd_t *vbd)
return tapdisk_image_validate_chain(&vbd->images);
}

static int
vbd_stats_destroy(td_vbd_t *vbd) {

int err = 0;

ASSERT(vbd);

if (vbd->rrd.mem) {
err = munmap(vbd->rrd.mem, RRD_SHM_SIZE);
if (err == -1) {
err = errno;
EPRINTF("failed to munmap %s: %s\n", vbd->rrd.path,
strerror(err));
goto out;
}
vbd->rrd.mem = NULL;
}

if (vbd->rrd.fd > 0) {
do {
err = close(vbd->rrd.fd);
if (err)
err = errno;
} while (err == EINTR);
if (err) {
EPRINTF("failed to close %s: %s\n", vbd->rrd.path, strerror(err));
goto out;
}
vbd->rrd.fd = 0;
}

if (vbd->rrd.path) {

err = unlink(vbd->rrd.path);
if (err == -1) {
err = errno;
if (err != ENOENT)
goto out;
}
free(vbd->rrd.path);
vbd->rrd.path = NULL;
}

out:
return -err;
}

static int
vbd_stats_create(td_vbd_t *vbd) {

int err;

ASSERT(vbd);

err = mkdir("/dev/shm/metrics", S_IRUSR | S_IWUSR);
if (err && errno != EEXIST)
goto out;

/*
* FIXME we use tap-<tapdisk PID>-<minor number> for now, might want to
* reconsider
*/
err = asprintf(&vbd->rrd.path, "/dev/shm/metrics/tap-%d-%d", getpid(),
vbd->uuid);
if (err == -1) {
err = errno;
vbd->rrd.path = NULL;
EPRINTF("failed to create metric file: %s\n", strerror(err));
goto out;
}
err = 0;

vbd->rrd.fd = open(vbd->rrd.path, O_CREAT | O_TRUNC | O_RDWR | O_EXCL,
S_IRUSR | S_IWUSR);
if (vbd->rrd.fd == -1) {
err = errno;
EPRINTF("failed to open %s: %s\n", vbd->rrd.path, strerror(err));
goto out;
}

err = ftruncate(vbd->rrd.fd, RRD_SHM_SIZE);
if (err == -1) {
err = errno;
EPRINTF("failed to truncate %s: %s\n", vbd->rrd.path, strerror(err));
goto out;
}

vbd->rrd.mem = mmap(NULL, RRD_SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
vbd->rrd.fd, 0);
if (vbd->rrd.mem == MAP_FAILED) {
err = errno;
EPRINTF("failed to mmap %s: %s\n", vbd->rrd.path, strerror(err));
goto out;
}

out:
if (err) {
int err2 = vbd_stats_destroy(vbd);
if (err2)
EPRINTF("failed to clean up failed RRD shared memory creation: "
"%s (error ignored)\n", strerror(-err2));
}
return -err;
}

void
tapdisk_vbd_close_vdi(td_vbd_t *vbd)
{
int err;

err = vbd_stats_destroy(vbd);
if (err) {
EPRINTF("failed to destroy RRD stats file: %s (error ignored)\n",
strerror(-err));
}

tapdisk_image_close_chain(&vbd->images);

if (vbd->secondary &&
Expand Down Expand Up @@ -493,6 +616,10 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d
}
}

err = vbd_stats_create(vbd);
if (err)
goto fail;

if (tmp != vbd->name)
free(tmp);

Expand Down Expand Up @@ -886,9 +1013,121 @@ tapdisk_vbd_check_queue_state(td_vbd_t *vbd)

}

static inline int
tapdisk_vbd_produce_rrds(td_vbd_t *vbd) {

td_image_t *leaf;
int off = 0, size = 0;
int err;
int i, j;
char *buf;
int json_str_len_off, md5sum_str_len_off, json_data_off, json_data_len;
const int json_str_len = 8 + 1, md5sum_str_len = 32 + 1;
char tmp[md5sum_str_len + 1];
time_t t;
MD5_CTX md5_ctx;
unsigned char md5_out[MD5_DIGEST_LENGTH];

ASSERT(vbd);

buf = vbd->rrd.mem;

/*
* If no VDI has been opened yet there's nothing to report.
*/
if (!buf)
return 0;

/*
* Produce RRDs every five seconds.
*/
t = time(NULL);
if (t - vbd->rrd.last < 5)
return 0;
vbd->rrd.last = t;

size = RRD_SHM_SIZE - off;
err = tapdisk_snprintf(buf, &off, &size, 0, "DATASOURCES\n");
if (err)
return err;

/*
* reserve space for JSON string length
*/
json_str_len_off = off;
off += json_str_len, size -= json_str_len;

/*
* reserve space for MD5 sum of JSON string
*/
md5sum_str_len_off = off;
off += md5sum_str_len, size -= md5sum_str_len;

json_data_off = off;
err = tapdisk_snprintf(buf, &off, &size, 0, "{\n");
err += tapdisk_snprintf(buf, &off, &size, 1, "\"timestamp\": %lu,\n",
time(NULL));
err += tapdisk_snprintf(buf, &off, &size, 1, "\"datasources\": {\n");
if (err)
return err;

leaf = tapdisk_vbd_first_image(vbd);

/*
* XXX We're only reporting RRDs for leaves. We could traverse the list
* of parent and report RRDs for each one of them, if there is something
* to report. However, for internal VHD files there's nothing to report
* so that would end up in a useless traverse of the list. We could address
* this issue by keeping a list of images that do have an RRD callback.
*/
if (leaf && leaf->driver->ops->td_rrd) {
err = leaf->driver->ops->td_rrd(leaf->driver, buf, &off, &size);
if (err)
return err;
err = tapdisk_snprintf(buf, &off, &size, 0, ",\n");
if (err)
return err;
}

err += tapdisk_snprintf(buf, &off, &size, 2, "\"io_errors\": {\n");
err += tapdisk_snprintf(buf, &off, &size, 3,
"\"description\": \"Number of I/O errors\",\n");
err += tapdisk_snprintf(buf, &off, &size, 3, "\"owner\": \"host\",\n");
err += tapdisk_snprintf(buf, &off, &size, 3, "\"type\": "
"\"absolute\",\n");
err += tapdisk_snprintf(buf, &off, &size, 3, "\"units\": \"units\",\n");
err += tapdisk_snprintf(buf, &off, &size, 3, "\"min\": \"0.00\",\n");
err += tapdisk_snprintf(buf, &off, &size, 3, "\"max\": \"inf\",\n");
err += tapdisk_snprintf(buf, &off, &size, 3, "\"value\": \"%llu\",\n",
vbd->errors);
err += tapdisk_snprintf(buf, &off, &size, 3, "\"value_type\": \"float\"\n");
err += tapdisk_snprintf(buf, &off, &size, 2, "}\n");
err += tapdisk_snprintf(buf, &off, &size, 1, "}\n");
err += tapdisk_snprintf(buf, &off, &size, 0, "}\n");
if (err)
return err;

json_data_len = off - json_str_len;
sprintf(tmp, "%08x\n", json_data_len);
strncpy(buf + json_str_len_off, tmp, json_str_len);

MD5_Init(&md5_ctx);
MD5_Update(&md5_ctx, buf + json_data_off, json_data_len);
MD5_Final(md5_out, &md5_ctx);
for (i = 0, j = 0; i < MD5_DIGEST_LENGTH; i++)
j += sprintf(buf + md5sum_str_len_off + j, "%02x", md5_out[i]);
buf[(md5sum_str_len_off + j)] = '\n';

memset(buf + off, '\0', size - off);
return msync(buf, RRD_SHM_SIZE, MS_ASYNC);
}

void
tapdisk_vbd_check_state(td_vbd_t *vbd)
{

tapdisk_vbd_produce_rrds(vbd);

tapdisk_vbd_check_queue_state(vbd);

if (td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED))
Expand Down

0 comments on commit 4703674

Please sign in to comment.