diff options
Diffstat (limited to 'kernel/sysfs.c')
-rw-r--r-- | kernel/sysfs.c | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/kernel/sysfs.c b/kernel/sysfs.c new file mode 100644 index 0000000..802e9d2 --- /dev/null +++ b/kernel/sysfs.c @@ -0,0 +1,460 @@ +/* + * kernel/sysfs.c + */ + + +#include "dnbd2.h" +#include "misc.h" +#include "sysfs.h" +#include "devices.h" +#include "servers.h" + + +#define RW 0644 +#define RO 0444 + +#define kobj_to_dev(kp) container_of(kp, dnbd2_device_t, kobj) +#define kobj_to_srv(kp) container_of(kp, struct srv_info, kobj) + +#define attr_to_srvattr(ap) container_of(ap, struct server_attr, attr) +#define attr_to_devattr(ap) container_of(ap, struct device_attr, attr) + +#define DEV_ATTR_RW(_name) \ +static struct device_attr _name = \ +__ATTR(_name, RW, show_##_name, store_##_name) + +#define SRV_ATTR_RW(_name) \ +static struct server_attr _name = \ +__ATTR(_name, RW, show_##_name, store_##_name) + +#define DEV_ATTR_RO(_name) \ +static struct device_attr _name = \ +__ATTR(_name, RO, show_##_name, NULL) + +#define SRV_ATTR_RO(_name) \ +static struct server_attr _name = \ +__ATTR(_name, RO, show_##_name, NULL) + + +struct device_attr { + struct attribute attr; + ssize_t (*show)(char *, dnbd2_device_t *); + ssize_t (*store)(const char *, size_t, dnbd2_device_t *); +}; + +struct server_attr { + struct attribute attr; + ssize_t (*show)(char *, struct srv_info *); + ssize_t (*store)(const char *, size_t, struct srv_info *); +}; + + +void release(struct kobject *kobj) {} + +ssize_t show_running(char *, dnbd2_device_t *); +ssize_t store_running(const char *, size_t, dnbd2_device_t *); +ssize_t show_to_percent(char *, dnbd2_device_t *); +ssize_t store_to_percent(const char *, size_t, dnbd2_device_t *); +ssize_t show_to_jiffies(char *, dnbd2_device_t *); +ssize_t store_to_jiffies(const char *, size_t, dnbd2_device_t *); +ssize_t show_vid(char *, dnbd2_device_t *); +ssize_t store_vid(const char *, size_t, dnbd2_device_t *); +ssize_t show_rid(char *, dnbd2_device_t *); +ssize_t store_rid(const char *, size_t, dnbd2_device_t *); +ssize_t show_pending_reqs(char *, dnbd2_device_t *); +ssize_t show_emergency(char *, dnbd2_device_t *); + +ssize_t show_sock(char *, struct srv_info *); +ssize_t store_sock(const char *, size_t, struct srv_info *); +ssize_t show_active(char *, struct srv_info *); +ssize_t store_active(const char *, size_t, struct srv_info *); +ssize_t show_rtt(char *, struct srv_info *); +ssize_t show_retries(char *, struct srv_info *); +ssize_t show_last_reply(char *, struct srv_info *); + + +/* device attributes */ +DEV_ATTR_RW(running); +DEV_ATTR_RW(to_percent); +DEV_ATTR_RW(to_jiffies); +DEV_ATTR_RW(vid); +DEV_ATTR_RW(rid); +DEV_ATTR_RO(pending_reqs); +DEV_ATTR_RO(emergency); + +static struct attribute *device_attrs[] = { + &running.attr, + &to_percent.attr, + &to_jiffies.attr, + &vid.attr, + &rid.attr, + &pending_reqs.attr, + &emergency.attr, + NULL, +}; + +/* server attributes */ +SRV_ATTR_RW(sock); +SRV_ATTR_RW(active); +SRV_ATTR_RO(rtt); +SRV_ATTR_RO(retries); +SRV_ATTR_RO(last_reply); + +struct attribute *server_attrs[] = { + &sock.attr, + &active.attr, + &rtt.attr, + &retries.attr, + &last_reply.attr, + NULL, +}; + + +/* + * Wrapper functions for show/store, one pair for device attributes + * and one pair for server attributes. Each attribute has its own + * specific function(s). + */ +ssize_t device_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct device_attr *device_attr = attr_to_devattr(attr); + dnbd2_device_t *dev = kobj_to_dev(kobj); + return device_attr->show(buf, dev); +} + +ssize_t device_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct device_attr *device_attr = attr_to_devattr(attr); + dnbd2_device_t *dev = kobj_to_dev(kobj); + down(&dev->config_mutex); + ret = device_attr->store(buf, count, dev); + up(&dev->config_mutex); + return ret; +} + +ssize_t server_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct server_attr *server_attr = attr_to_srvattr(attr); + struct srv_info *srv_info = kobj_to_srv(kobj); + return server_attr->show(buf, srv_info); +} + +ssize_t server_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct server_attr *server_attr = attr_to_srvattr(attr); + struct srv_info *srv_info = kobj_to_srv(kobj); + down(&srv_info->dev->config_mutex); + ret = server_attr->store(buf, count, srv_info); + up(&srv_info->dev->config_mutex); + return ret; +} + + +struct sysfs_ops device_ops = { + .show = device_show, + .store = device_store, +}; + +struct sysfs_ops server_ops = { + .show = server_show, + .store = server_store, +}; + +struct kobj_type device_ktype = { + .default_attrs = device_attrs, + .sysfs_ops = &device_ops, + .release = release, +}; + +struct kobj_type server_ktype = { + .default_attrs = server_attrs, + .sysfs_ops = &server_ops, + .release = release, +}; + + +/* + * RW device attribute functions. + */ +ssize_t show_running(char *buf, dnbd2_device_t *dev) +{ + return sprintf(buf, "%d\n", dev->running); +} + +ssize_t store_running(const char *buf, size_t count, dnbd2_device_t *dev) +{ + sector_t capacity = 0; + struct srv_info *srv_info; + int i, running, ret = sscanf(buf, "%d", &running); + + if (ret != 1) + return -EINVAL; + + if (!dev->running) { + if (running != 1 || !dev->vid || !dev->rid) + return -EINVAL; + if (atomic_read(&dev->refcnt) > 0) + return -EBUSY; + + for_each_server(i) { + srv_info = &dev->servers[i]; + if (!srv_info->sock) + continue; + capacity = srv_get_capacity(srv_info); + if (capacity) { + set_capacity(dev->disk, capacity); + dev->active_server = srv_info; + break; + } + } + if (!capacity) { + p("Could not contact any servers.\n"); + return -EHOSTUNREACH; + } + for_each_server(i) { + dev->emerg_list[i].ip = dev->servers[i].ip; + dev->emerg_list[i].port = dev->servers[i].port; + } + printk(LOG "Device capacity = " SECT_PRECISION " KB\n", + capacity * SECTOR_SIZE / 1024); + dev->running = 1; + __module_get(THIS_MODULE); + } else { + if (running != 0) + return -EINVAL; + if (atomic_read(&dev->refcnt) > 0) + return -EBUSY; + + /* Stop device. */ + dev->running = 0; + set_capacity(dev->disk, 0); + dev->active_server = NULL; + module_put(THIS_MODULE); + } + + return count; +} + +ssize_t show_vid(char *buf, dnbd2_device_t *dev) +{ + return sprintf(buf, "%hu\n", dev->vid); +} + +ssize_t store_vid(const char *buf, size_t count, dnbd2_device_t *dev) +{ + uint16_t vid; + + if (dev->running) + return -EBUSY; + if (sscanf(buf, "%hu", &vid) != 1) + return -EINVAL; + + dev->vid = vid; + return count; +} + +ssize_t show_rid(char *buf, dnbd2_device_t *dev) +{ + return sprintf(buf, "%hu\n", dev->rid); +} + +ssize_t store_rid(const char *buf, size_t count, dnbd2_device_t *dev) +{ + uint16_t rid; + + if (dev->running) + return -EBUSY; + if (sscanf(buf, "%hu", &rid) != 1) + return -EINVAL; + + dev->rid = rid; + return count; +} + +ssize_t show_to_percent(char *buf, dnbd2_device_t *dev) +{ + return sprintf(buf, "%d\n", dev->to_percent); +} + +ssize_t store_to_percent(const char *buf, size_t count, dnbd2_device_t *dev) +{ + if (sscanf(buf, "%d", &dev->to_percent) == 1) + return count; + return -EINVAL; +} + +ssize_t show_to_jiffies(char *buf, dnbd2_device_t *dev) +{ + return sprintf(buf, "%hu\n", dev->to_jiffies); +} + +ssize_t store_to_jiffies(const char *buf, size_t count, dnbd2_device_t *dev) +{ + if (sscanf(buf, "%hu", &dev->to_jiffies) == 1) + return count; + return -EINVAL; +} + + +/* + * RO device attribute functions. + */ +ssize_t show_pending_reqs(char *buf, dnbd2_device_t *dev) +{ + return sprintf(buf, "%lu\n", dev->pending_reqs); +} + +ssize_t show_emergency(char *buf, dnbd2_device_t *dev) +{ + return sprintf(buf, "%d\n", dev->emergency); +} + + +/* + * RW server attribute functions. + */ +ssize_t show_sock(char *buf, struct srv_info *srv_info) +{ + return sprintf(buf, "%s %hu\n", + inet_ntoa(srv_info->ip), + ntohs(srv_info->port)); +} + +ssize_t store_sock(const char *buf, size_t count, struct srv_info *srv_info) +{ + char ip[sizeof "aaa.bbb.ccc.ddd 12345"]; + uint16_t port; + dnbd2_server_t server; + dnbd2_device_t *dev = srv_info->dev; + + if (count > sizeof "aaa.bbb.ccc.ddd 12345") + return -EINVAL; + if (sscanf(buf, "%s %hu", ip, &port) != 2) + return -EINVAL; + server.ip = in_aton(ip); + server.port = htons(port); + + down(&dev->servers_mutex); + if (dev->running && dev->active_server == srv_info) { + up(&dev->servers_mutex); + return -EBUSY; + } + if (srv_info->sock) + del_server(srv_info); + if (server.ip && server.port && add_server(server, srv_info)) { + up(&dev->servers_mutex); + return -EINVAL; + } + up(&dev->servers_mutex); + + return count; +} + +ssize_t show_active(char *buf, struct srv_info *srv_info) +{ + if (!srv_info->sock) + return sprintf(buf, "0\n"); + if (srv_info->dev->active_server == srv_info) + return sprintf(buf, "1\n"); + return sprintf(buf, "0\n"); +} + +ssize_t store_active(const char *buf, size_t count, struct srv_info *srv_info) +{ + dnbd2_device_t *dev = srv_info->dev; + int active; + + if (sscanf(buf, "%d", &active) != 1 || active != 1) + return -EINVAL; + + down(&dev->servers_mutex); + if (!dev->running) { + up(&dev->servers_mutex); + return -EINVAL; + } + if (!srv_info->sock || dev->active_server == srv_info) { + up(&dev->servers_mutex); + return -EINVAL; + } + dev->active_server = srv_info; + up(&dev->servers_mutex); + + return count; +} + + +/* + * RO server attribute functions. + */ +ssize_t show_rtt(char *buf, struct srv_info *srv_info) +{ + return sprintf(buf, "%hu %lu %hu\n", + srv_info->min, + srv_info->srtt >> SRTT_SHIFT, + srv_info->max); +} + +ssize_t show_retries(char *buf, struct srv_info *srv_info) +{ + return sprintf(buf, "%lu\n", srv_info->retries); +} + +ssize_t show_last_reply(char *buf, struct srv_info *srv_info) +{ + return sprintf(buf, "%lu\n", srv_info->last_reply); +} + + +/* Helper for start_sysfs. */ +int setup_kobj(struct kobject *kobj, char *name, struct kobject *parent, + struct kobj_type *ktype) +{ + memset(kobj, 0, sizeof(struct kobject)); + kobj->parent = parent; + kobj->ktype = ktype; + if (kobject_set_name(kobj, name)) + return -1; + if (kobject_register(kobj)) + return -1; + return 0; +} + + +/* + * Exported functions - see sysfs.h + */ +int start_sysfs(dnbd2_device_t *dev) +{ + int i; + char name[] = "server99"; + + if (setup_kobj(&dev->kobj, "config", &dev->disk->kobj, &device_ktype)) + return -1; + + for_each_server(i) { + sprintf(name, "server%d", i); + if (setup_kobj(&dev->servers[i].kobj, name, + &dev->disk->kobj, &server_ktype)) + goto out; + } + return 0; + + out: + while (i--) + kobject_unregister(&dev->servers[i].kobj); + return -1; +} + +void stop_sysfs(dnbd2_device_t *dev) +{ + int i; + for_each_server(i) + kobject_unregister(&dev->servers[i].kobj); + kobject_unregister(&dev->kobj); +} |