/* * 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 kobject_to_dev(kp) container_of(kp, dnbd2_device_t, kobj) #define kobject_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 = kobject_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 = kobject_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 = kobject_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 = kobject_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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) if (kobject_init_and_add(kobj, ktype, parent, name)) return -1; #else if (kobject_set_name(kobj, name)) return -1; if (kobject_register(kobj)) return -1; #endif return 0; } /* * Exported functions - see sysfs.h */ int start_sysfs(dnbd2_device_t *dev) { int i; char name[] = "server99"; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) if (setup_kobj(&dev->kobj, "config", get_disk(dev->disk), &device_ktype)) #else if (setup_kobj(&dev->kobj, "config", &dev->disk->dev.kobj, &device_ktype)) #endif return -1; for_each_server(i) { sprintf(name, "server%d", i); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) if (setup_kobj(&dev->servers[i].kobj, name, get_disk(dev->disk), &server_ktype)) #else if(setup_kobj(&dev-servers[i].kobj, name, &dev->disk->dev.kobj, &server_ktype)) #endif goto out; } return 0; out: while (i--) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) kobject_put(&dev->servers[i].kobj); #else kobject_unregister(&dev->servers[i].kobj); #endif return -1; } void stop_sysfs(dnbd2_device_t *dev) { int i; for_each_server(i) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) kobject_put(&dev->servers[i].kobj); kobject_put(&dev->kobj); #else kobject_unregister(&dev->servers[i].kobj); kobject_unregister(&dev->kobj); #endif }