summaryrefslogblamecommitdiffstats
path: root/kernel/sysfs.c
blob: d23fbedc9cc1436b55511adf090001e98a507d29 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                    

                                                                  







































































































                                                                      
                                                   







                                                                  
                                                   









                                                                 
                                                         







                                                                  
                                                         












































































































































































































































































                                                                              


                                                            
                          


                                         

                                   
      











                                    





                                                                                  


                                             







                                                                   




                   




                                                          






                                    







                                                          
 
/*
 * 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
}