void
tapdisk_driver_stats(td_driver_t *driver, td_stats_t *st)
{
+ const disk_info_t *info;
+
tapdisk_stats_field(st, "type", "d", driver->type);
+ info = tapdisk_disk_types[driver->type];
+ tapdisk_stats_field(st, "name", "s", info->name);
+
if (driver->ops->td_stats) {
tapdisk_stats_field(st, "status", "{");
driver->ops->td_stats(driver, st);
tapdisk_stats_enter(st, '{');
tapdisk_stats_field(st, "name", "s", image->name);
+ tapdisk_stats_field(st, "hits", "[");
+ tapdisk_stats_val(st, "llu", image->stats.hits.rd);
+ tapdisk_stats_val(st, "llu", image->stats.hits.wr);
+ tapdisk_stats_leave(st, ']');
+
+ tapdisk_stats_field(st, "fail", "[");
+ tapdisk_stats_val(st, "llu", image->stats.fail.rd);
+ tapdisk_stats_val(st, "llu", image->stats.fail.wr);
+ tapdisk_stats_leave(st, ']');
+
tapdisk_stats_field(st, "driver", "{");
tapdisk_driver_stats(image->driver, st);
tapdisk_stats_leave(st, '}');
void *private;
struct list_head next;
+
+ /*
+ * Basic datapath statistics, in sectors read/written.
+ *
+ * hits: requests completed by this image.
+ * fail: requests completed with failure by this image.
+ *
+ * Not that we do not count e.g.
+ * miss: requests forwarded.
+ * total: requests processed by this image.
+ *
+ * This is because we'd have to compensate for restarts due to
+ * -EBUSY conditions. Those can be extrapolated by following
+ * the chain instead: sum(image[i].hits, i=0..) == vbd.secs;
+ */
+ struct {
+ td_sector_count_t hits;
+ td_sector_count_t fail;
+ } stats;
};
td_image_t *tapdisk_image_allocate(char *, int, int, td_flag_t, void *);
#include <stdio.h>
#include <stdarg.h>
+#include "tapdisk.h"
#include "tapdisk-stats.h"
+#define BUG_ON(_cond) if (_cond) { td_panic(); }
+
static void
-tapdisk_stats_vsprintf(td_stats_t *st,
+__stats_vsprintf(td_stats_t *st,
const char *fmt, va_list ap)
{
- st->pos += vsnprintf(st->pos, st->buf + st->size - st->pos, fmt, ap);
+ size_t size = st->buf + st->size - st->pos;
+ st->pos += vsnprintf(st->pos, size, fmt, ap);
}
static void __attribute__((format (printf, 2, 3)))
-tapdisk_stats_sprintf(td_stats_t *st,
+__stats_sprintf(td_stats_t *st,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- tapdisk_stats_vsprintf(st, fmt, ap);
+ __stats_vsprintf(st, fmt, ap);
va_end(ap);
}
-
-static inline void
-__tapdisk_stats_enter(td_stats_t *st, char t)
+static void
+__stats_enter(td_stats_t *st)
{
- tapdisk_stats_sprintf(st, "%c ", t);
-
st->depth++;
+ BUG_ON(st->depth > TD_STATS_MAX_DEPTH);
st->n_elem[st->depth] = 0;
}
-void
-tapdisk_stats_enter(td_stats_t *st, char t)
+static void
+__stats_leave(td_stats_t *st)
+{
+ st->depth--;
+}
+
+static void
+__stats_next(td_stats_t *st)
{
int n_elem;
n_elem = st->n_elem[st->depth];
if (n_elem > 0)
- tapdisk_stats_sprintf(st, ", ");
+ __stats_sprintf(st, ", ");
st->n_elem[st->depth]++;
+}
- __tapdisk_stats_enter(st, t);
+static void
+__tapdisk_stats_enter(td_stats_t *st, char t)
+{
+ __stats_sprintf(st, "%c ", t);
+ __stats_enter(st);
}
+void
+tapdisk_stats_enter(td_stats_t *st, char t)
+{
+ __stats_next(st);
+ __tapdisk_stats_enter(st, t);
+}
void
tapdisk_stats_leave(td_stats_t *st, char t)
{
- st->depth--;
+ __stats_leave(st);
+ __stats_sprintf(st, " %c", t);
+}
- tapdisk_stats_sprintf(st, " %c", t);
+static void
+tapdisk_stats_vval(td_stats_t *st, const char *conv, va_list ap)
+{
+ char t = conv[0], fmt[32];
+
+ __stats_next(st);
+
+ switch (t) {
+ case 's':
+ __stats_vsprintf(st, "\"%s\"", ap);
+ break;
+
+ default:
+ sprintf(fmt, "%%%s", conv);
+ __stats_vsprintf(st, fmt, ap);
+ break;
+ }
+}
+
+void
+tapdisk_stats_val(td_stats_t *st, const char *conv, ...)
+{
+ va_list ap;
+
+ va_start(ap, conv);
+ tapdisk_stats_vval(st, conv, ap);
+ va_end(ap);
}
void
{
va_list ap;
int n_elem;
- char fmt[32], t;
+ char t;
n_elem = st->n_elem[st->depth]++;
if (n_elem > 0)
- tapdisk_stats_sprintf(st, ", ");
+ __stats_sprintf(st, ", ");
- tapdisk_stats_sprintf(st, "\"%s\": ", key);
+ __stats_sprintf(st, "\"%s\": ", key);
if (!conv) {
- tapdisk_stats_sprintf(st, "null");
+ __stats_sprintf(st, "null");
return;
}
t = conv[0];
switch (t) {
- case 's':
- va_start(ap, conv);
- tapdisk_stats_vsprintf(st, "\"%s\"", ap);
- va_end(ap);
- break;
-
case '[':
case '{':
__tapdisk_stats_enter(st, t);
break;
-
default:
- sprintf(fmt, "%%%s", conv);
-
va_start(ap, conv);
- tapdisk_stats_vsprintf(st, fmt, ap);
+ __stats_enter(st);
+ tapdisk_stats_vval(st, conv, ap);
+ __stats_leave(st);
va_end(ap);
- break;
}
}
#include <string.h>
-#define TD_INFO_MAX_DEPTH 8
+#define TD_STATS_MAX_DEPTH 8
struct tapdisk_stats_ctx {
void *pos;
void *buf;
size_t size;
- int n_elem[TD_INFO_MAX_DEPTH];
+ int n_elem[TD_STATS_MAX_DEPTH];
int depth;
};
void tapdisk_stats_enter(td_stats_t *st, char t);
void tapdisk_stats_leave(td_stats_t *st, char t);
void tapdisk_stats_field(td_stats_t *st, const char *key, const char *conv, ...);
+void tapdisk_stats_val(td_stats_t *st, const char *conv, ...);
#endif /* _TAPDISK_STATS_H_ */
__tapdisk_vbd_complete_td_request(td_vbd_t *vbd, td_vbd_request_t *vreq,
td_request_t treq, int res)
{
+ td_image_t *image = treq.image, *prev, *tmp;
int err;
err = (res <= 0 ? res : -res);
vbd->secs_pending -= treq.secs;
vreq->secs_pending -= treq.secs;
+ if (err != -EBUSY) {
+ int write = treq.op == TD_OP_WRITE;
+ td_sector_count_add(&image->stats.hits, treq.secs, write);
+ if (err)
+ td_sector_count_add(&image->stats.fail,
+ treq.secs, write);
+ }
+
if (err) {
vreq->status = BLKIF_RSP_ERROR;
vreq->error = (vreq->error ? : err);
return err;
}
+static void
+tapdisk_vbd_count_new_request(td_vbd_t *vbd, td_vbd_request_t *vreq)
+{
+ blkif_request_t *req = &vreq->req;
+ struct blkif_request_segment *seg;
+ int write;
+
+ write = req->operation == BLKIF_OP_WRITE;
+
+ for (seg = &req->seg[0]; seg < &req->seg[req->nr_segments]; seg++) {
+ int secs = seg->last_sect - seg->first_sect + 1;
+ td_sector_count_add(&vbd->secs, secs, write);
+ }
+}
+
static int
tapdisk_vbd_issue_new_requests(td_vbd_t *vbd)
{
err = tapdisk_vbd_issue_request(vbd, vreq);
if (err)
return err;
+
+ tapdisk_vbd_count_new_request(vbd, vreq);
}
return 0;
tapdisk_stats_field(st, "name", "s", vbd->name);
tapdisk_stats_field(st, "minor", "d", vbd->minor);
- tapdisk_stats_field(st, "image", "[");
+ tapdisk_stats_field(st, "secs", "[");
+ tapdisk_stats_val(st, "llu", vbd->secs.rd);
+ tapdisk_stats_val(st, "llu", vbd->secs.wr);
+ tapdisk_stats_leave(st, ']');
+ tapdisk_stats_field(st, "images", "[");
tapdisk_vbd_for_each_image(vbd, image, next)
tapdisk_image_stats(image, st);
-
tapdisk_stats_leave(st, ']');
tapdisk_stats_leave(st, '}');
uint64_t secs_pending;
uint64_t retries;
uint64_t errors;
+ td_sector_count_t secs;
uint64_t kicks_in;
uint64_t kicks_out;
typedef struct td_request td_request_t;
typedef struct td_driver_handle td_driver_t;
typedef struct td_image_handle td_image_t;
+typedef struct td_sector_count td_sector_count_t;
/*
* Prototype of the callback to activate as requests complete.
void (*td_stats) (td_driver_t *, td_stats_t *);
};
+struct td_sector_count {
+ td_sector_t rd;
+ td_sector_t wr;
+};
+
+static inline void
+td_sector_count_add(td_sector_count_t *s, td_sector_t v, int write)
+{
+ if (write)
+ s->wr += v;
+ else
+ s->rd += v;
+}
+
void td_panic(void);
#endif