summaryrefslogtreecommitdiffstats
path: root/kernel/sysfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/sysfs.c')
-rw-r--r--kernel/sysfs.c460
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);
+}