Basic state information is available via /sys/fs/ceph, including instances of the client, fsids, current monitor, mds and osd maps, and hooks to adjust debug levels. Signed-off-by: Sage Weil <sage@xxxxxxxxxxxx> --- fs/ceph/sysfs.c | 465 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 465 insertions(+), 0 deletions(-) create mode 100644 fs/ceph/sysfs.c diff --git a/fs/ceph/sysfs.c b/fs/ceph/sysfs.c new file mode 100644 index 0000000..fc107fd --- /dev/null +++ b/fs/ceph/sysfs.c @@ -0,0 +1,465 @@ +#include <linux/ctype.h> +#include <linux/kobject.h> + +#include "super.h" + +static struct kobject ceph_kobj; + +/* + * default kobject attribute operations. duplicated here from + * kobject.c + */ + +#define DEF_ATTR_OP(name) \ +static ssize_t name##_attr_show(struct kobject *kobj, struct attribute *attr,\ + char *buf) \ +{ \ + struct name##_attr *a = \ + container_of(attr, struct name##_attr, attr); \ + struct name *c = container_of(kobj, struct name, kobj); \ + ssize_t ret = -EIO; \ + \ + if (a->show) \ + ret = a->show(c, a, buf); \ + return ret; \ +} \ + \ +static ssize_t name##_attr_store(struct kobject *kobj, struct attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct name##_attr *a = container_of(attr, struct name##_attr, attr); \ + struct name *c = container_of(kobj, struct name, kobj); \ + ssize_t ret = -EIO; \ + \ + if (a->store) \ + ret = a->store(c, a, buf, count); \ + return ret; \ +} \ + \ +static struct sysfs_ops name##_sysfs_ops = { \ + .show = name##_attr_show, \ + .store = name##_attr_store, \ +}; \ + \ +static struct kobj_type name##_ops = { \ + .sysfs_ops = &name##_sysfs_ops, \ +}; + +#define ADD_ENTITY_ATTR(ent, a, n, m, sh, st) \ + ent->a.attr.name = n; \ + ent->a.attr.mode = m; \ + ent->a.show = sh; \ + ent->a.store = st; \ + ret = sysfs_create_file(&ent->kobj, &ent->a.attr); + + + +DEF_ATTR_OP(ceph_client) + + +/* + * per-client attributes + */ +#define to_client(c) container_of(c, struct ceph_client, kobj) + +static ssize_t fsid_show(struct ceph_client *client, + struct ceph_client_attr *attr, char *buf) +{ + return sprintf(buf, "%llx.%llx\n", + le64_to_cpu(__ceph_fsid_major(&client->monc.monmap->fsid)), + le64_to_cpu(__ceph_fsid_minor(&client->monc.monmap->fsid))); +} + +static ssize_t monmap_show(struct ceph_client *client, + struct ceph_client_attr *attr, char *buf) +{ + int i, pos; + + if (client->monc.monmap == NULL) + return 0; + + pos = sprintf(buf, "epoch %d\n", client->monc.monmap->epoch); + for (i = 0; i < client->monc.monmap->num_mon; i++) { + struct ceph_entity_inst *inst = + &client->monc.monmap->mon_inst[i]; + + if (pos > PAGE_SIZE - 128) + break; /* be conservative */ + pos += sprintf(buf + pos, "\t%s%d\t%u.%u.%u.%u:%u\n", + ENTITY_NAME(inst->name), + IPQUADPORT(inst->addr.ipaddr)); + } + return pos; +} + +static ssize_t mdsmap_show(struct ceph_client *client, + struct ceph_client_attr *attr, char *buf) +{ + int i, pos; + + if (client->mdsc.mdsmap == NULL) + return 0; + pos = sprintf(buf, "epoch %d\n", client->mdsc.mdsmap->m_epoch); + pos += sprintf(buf + pos, "root %d\n", client->mdsc.mdsmap->m_root); + pos += sprintf(buf + pos, "session_timeout %d\n", + client->mdsc.mdsmap->m_session_timeout); + pos += sprintf(buf + pos, "session_autoclose %d\n", + client->mdsc.mdsmap->m_session_autoclose); + for (i = 0; i < client->mdsc.mdsmap->m_max_mds; i++) { + struct ceph_entity_addr *addr = &client->mdsc.mdsmap->m_addr[i]; + int state = client->mdsc.mdsmap->m_state[i]; + + if (pos > PAGE_SIZE - 128) + break; /* be conservative */ + pos += sprintf(buf+pos, "\tmds%d\t%u.%u.%u.%u:%u\t(%s)\n", + i, + IPQUADPORT(addr->ipaddr), + ceph_mdsmap_state_str(state)); + } + return pos; +} + +static ssize_t osdmap_show(struct ceph_client *client, + struct ceph_client_attr *attr, char *buf) +{ + int i, pos; + + if (client->osdc.osdmap == NULL) + return 0; + pos = sprintf(buf, "epoch %d\n", client->osdc.osdmap->epoch); + pos += sprintf(buf + pos, "pg_num %d / %d\n", + client->osdc.osdmap->pg_num, + client->osdc.osdmap->pg_num_mask); + pos += sprintf(buf + pos, "lpg_num %d / %d\n", + client->osdc.osdmap->lpg_num, + client->osdc.osdmap->lpg_num_mask); + pos += sprintf(buf + pos, "flags%s%s\n", + (client->osdc.osdmap->flags & CEPH_OSDMAP_NEARFULL) ? + " NEARFULL":"", + (client->osdc.osdmap->flags & CEPH_OSDMAP_FULL) ? + " FULL":""); + for (i = 0; i < client->osdc.osdmap->max_osd; i++) { + struct ceph_entity_addr *addr = + &client->osdc.osdmap->osd_addr[i]; + int state = client->osdc.osdmap->osd_state[i]; + char sb[64]; + + if (pos > PAGE_SIZE - 128) + break; /* be conservative */ + pos += sprintf(buf + pos, + "\tosd%d\t%u.%u.%u.%u:%u\t%3d%%\t(%s)\n", + i, IPQUADPORT(addr->ipaddr), + ((client->osdc.osdmap->osd_weight[i]*100) >> 16), + ceph_osdmap_state_str(sb, sizeof(sb), state)); + } + return pos; +} + +static struct kobj_type entity_ops = { + .sysfs_ops = &ceph_client_sysfs_ops, +}; + + +int ceph_sysfs_client_init(struct ceph_client *client) +{ + int ret = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + ret = kobject_init_and_add(&client->kobj, &ceph_client_ops, + &ceph_kobj, "client%d", client->whoami); + if (ret) + goto out; + + ret = kobject_init_and_add(&client->mdsc.kobj, &entity_ops, + &client->kobj, "mdsc"); + if (ret) + goto out; + + ADD_ENTITY_ATTR(client, k_fsid, "fsid", 0400, fsid_show, NULL); + ADD_ENTITY_ATTR(client, k_monmap, "monmap", 0400, monmap_show, NULL); + ADD_ENTITY_ATTR(client, k_mdsmap, "mdsmap", 0400, mdsmap_show, NULL); + ADD_ENTITY_ATTR(client, k_osdmap, "osdmap", 0400, osdmap_show, NULL); + return 0; + +out: +#endif + return ret; +} + +void ceph_sysfs_client_cleanup(struct ceph_client *client) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + kobject_del(&client->mdsc.kobj); + kobject_del(&client->kobj); +#endif +} + +DEF_ATTR_OP(ceph_mds_request) + +static ssize_t req_mds_show(struct ceph_mds_request *req, + struct ceph_mds_request_attr *attr, char *buf) +{ + return sprintf(buf, "%u.%u.%u.%u:%u (%s%d)\n", + IPQUADPORT(req->r_request->hdr.dst.addr.ipaddr), + ENTITY_NAME(req->r_request->hdr.dst.name)); +} + +static ssize_t req_op_show(struct ceph_mds_request *req, + struct ceph_mds_request_attr *attr, char *buf) +{ + int pos = 0, pathlen; + u64 pathbase; + + char *path; + + pos += sprintf(buf, "%s", ceph_mds_op_name(req->r_op)); + + if (req->r_dentry) { + path = ceph_mdsc_build_path(req->r_dentry, &pathlen, &pathbase, -1); + if (path) { + pos += sprintf(buf+pos, " %s", path); + kfree(path); + } + } else if (req->r_path1) { + pos += sprintf(buf+pos, " %s", req->r_path1); + } + + if (req->r_old_dentry) { + path = ceph_mdsc_build_path(req->r_old_dentry, &pathlen, &pathbase, -1); + if (path) { + pos += sprintf(buf+pos, " %s", path); + kfree(path); + } + } else if (req->r_path2 && + req->r_op != CEPH_MDS_OP_FINDINODE) { + pos += sprintf(buf+pos, " %s", req->r_path2); + } + + pos += sprintf(buf+pos, "\n"); + + return pos; +} + +int ceph_sysfs_mds_req_init(struct ceph_mds_client *mdsc, struct ceph_mds_request *req) +{ + int ret = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + ret = kobject_init_and_add(&req->kobj, &ceph_mds_request_ops, + &mdsc->kobj, "%d", req->r_tid); + if (ret) + goto out; + + ADD_ENTITY_ATTR(req, k_mds, "mds", 0400, req_mds_show, NULL); + ADD_ENTITY_ATTR(req, k_op, "op", 0400, req_op_show, NULL); + + return 0; + +out: +#endif + return ret; +} + +void ceph_sysfs_mds_req_cleanup(struct ceph_mds_request *req) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + kobject_del(&req->kobj); +#endif +} + +/* + * ceph attrs + */ +struct ceph_attr { + struct attribute attr; + ssize_t (*show)(struct ceph_attr *, char *); + ssize_t (*store)(struct ceph_attr *, const char *, size_t); + int *val; +}; + +static ssize_t ceph_attr_show(struct kobject *ko, struct attribute *a, + char *buf) +{ + struct ceph_attr *ca = container_of(a, struct ceph_attr, attr); + return ca->show(ca, buf); +} + +static ssize_t ceph_attr_store(struct kobject *ko, struct attribute *a, + const char *buf, size_t len) +{ + struct ceph_attr *ca = container_of(a, struct ceph_attr, attr); + return ca->store(ca, buf, len); +} + +static struct sysfs_ops ceph_sysfs_ops = { + .show = ceph_attr_show, + .store = ceph_attr_store, +}; + +static struct kobj_type ceph_type = { + .sysfs_ops = &ceph_sysfs_ops, +}; + +/* + * simple int attrs (debug levels) + */ +static ssize_t int_attr_show(struct ceph_attr *a, char *buf) +{ + return sprintf(buf, "%d\n", *a->val); +} + +static ssize_t int_attr_store(struct ceph_attr *a, const char *buf, size_t len) +{ + if (sscanf(buf, "%d", a->val) < 1) + return 0; + return len; +} + +#define DECLARE_DEBUG_ATTR(_name) \ + static struct ceph_attr ceph_attr_##_name = { \ + .attr = { .name = __stringify(_name), .mode = 0600 }, \ + .show = int_attr_show, \ + .store = int_attr_store, \ + .val = &ceph_##_name, \ + }; + +DECLARE_DEBUG_ATTR(debug); +DECLARE_DEBUG_ATTR(debug_msgr); +DECLARE_DEBUG_ATTR(debug_console); + +/* + * ceph_debug_mask + */ +struct _debug_mask_name { + int mask; + char *name; +}; + +static struct _debug_mask_name _debug_mask_names[] = { + {DOUT_MASK_ADDR, "addr"}, + {DOUT_MASK_CAPS, "caps"}, + {DOUT_MASK_DIR, "dir"}, + {DOUT_MASK_EXPORT, "export"}, + {DOUT_MASK_FILE, "file"}, + {DOUT_MASK_INODE, "inode"}, + {DOUT_MASK_IOCTL, "ioctl"}, + {DOUT_MASK_MDSC, "mdsc"}, + {DOUT_MASK_MDSMAP, "mdsmap"}, + {DOUT_MASK_MSGR, "msgr"}, + {DOUT_MASK_MON, "mon"}, + {DOUT_MASK_OSDC, "osdc"}, + {DOUT_MASK_OSDMAP, "osdmap"}, + {DOUT_MASK_SNAP, "snap"}, + {DOUT_MASK_SUPER, "super"}, + {DOUT_MASK_PROTOCOL, "protocol"}, + {DOUT_MASK_PROC, "proc"}, + {DOUT_MASK_TOOLS, "tools"}, + {0, NULL} +}; + +static ssize_t debug_mask_show(struct ceph_attr *a, char *buf) +{ + int i = 0, pos; + + pos = sprintf(buf, "0x%x", ceph_debug_mask); + + while (_debug_mask_names[i].mask) { + if (ceph_debug_mask & _debug_mask_names[i].mask) + pos += sprintf(buf+pos, " %s", + _debug_mask_names[i].name); + i++; + } + pos += sprintf(buf+pos, "\n"); + return pos; +} + +static int get_debug_mask(const char *name, int len) +{ + int i = 0; + + while (_debug_mask_names[i].name) { + if (strncmp(_debug_mask_names[i].name, name, len) == 0) + return _debug_mask_names[i].mask; + i++; + } + return 0; +} + +static ssize_t debug_mask_store(struct ceph_attr *a, const char *buf, + size_t len) +{ + const char *next = buf, *tok; + + while (1) { + tok = next; + next = strpbrk(tok, " \t\r\n"); + if (!next) + break; + if (isdigit(*tok)) { + ceph_debug_mask = simple_strtol(tok, NULL, 0); + } else { + int remove = 0; + int mask; + + if (*tok == '-') { + remove = 1; + tok++; + } else if (*tok == '+') + tok++; + mask = get_debug_mask(tok, next-tok); + if (mask) { + if (remove) + ceph_debug_mask &= ~mask; + else + ceph_debug_mask |= mask; + } + } + next++; + } + + return len; +} + +static struct ceph_attr ceph_attr_debug_mask = { + .attr = { .name = "debug_mask", .mode = 0600 }, + .show = debug_mask_show, + .store = debug_mask_store, +}; + +int ceph_sysfs_init(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + int ret; + + ret = kobject_init_and_add(&ceph_kobj, &ceph_type, fs_kobj, "ceph"); + if (ret) + goto out; + ret = sysfs_create_file(&ceph_kobj, &ceph_attr_debug.attr); + if (ret) + goto out_del; + ret = sysfs_create_file(&ceph_kobj, &ceph_attr_debug_msgr.attr); + if (ret) + goto out_del; + ret = sysfs_create_file(&ceph_kobj, &ceph_attr_debug_console.attr); + if (ret) + goto out_del; + ret = sysfs_create_file(&ceph_kobj, &ceph_attr_debug_mask.attr); + if (ret) + goto out_del; + return 0; + +out_del: + kobject_del(&ceph_kobj); +out: + return ret; +#else + return 0; +#endif +} + +void ceph_sysfs_cleanup(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + kobject_del(&ceph_kobj); +#endif +} -- 1.5.6.5 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html