[PATCH 7/10] 9p: sysfs support for in-kenel servers

From: Latchesar Ionkov
Date: Fri Nov 02 2007 - 13:05:41 EST


Sysfs support for 9P servers.
Every server type is represented as a directory in /sys/fs/9p/srv. Initially
there is a single file in the directory -- 'clone'. Reading from the clone
file creates a new instance of the file server and returns its id. Each
instance is represented as a directory in /sys/fs/9p/srv/<file server>/. The
file server instance can be controlled by the 'ctl' file in its directory.
Currently the ctl file accepts the following commands:

listen-add <trans> <options>
listen-del <trans> <options>
destroy

Example:
echo 'listen-add tcp port=564' > /sys/fs/9p/srv/ramfs/0/ctl

Signed-off-by: Latchesar Ionkov <lucho@xxxxxxxxxx>

---
include/net/9p/srv.h | 24 +++-
net/9p/srv.c | 386 +++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 405 insertions(+), 5 deletions(-)

diff --git a/include/net/9p/srv.h b/include/net/9p/srv.h
index 72d011e..a81ae8f 100644
--- a/include/net/9p/srv.h
+++ b/include/net/9p/srv.h
@@ -69,11 +69,17 @@ struct p9srv {
void (*wstat)(struct p9srv_req *);

/* implementation specific */
+ int id;
atomic_t refcount;
spinlock_t lock; /* covers all srv fields */
+ unsigned long status;
struct list_head conn_list; /* all connections for srv */
struct list_head req_list; /* requests not yet worked on */
struct list_head workreq_list; /* requests being worked on */
+ struct kobject kobj;
+ struct list_head srv_list; /* all servers of a type */
+ struct list_head lis_list; /* all listeners for the srv */
+ struct work_struct shutwork;
};

struct p9srv_conn {
@@ -90,6 +96,16 @@ struct p9srv_conn {
wait_queue_head_t reset_wqueue;
};

+struct p9srv_type {
+ spinlock_t lock;
+ char *name;
+ int nextid;
+ struct kobject kobj;
+ struct list_head srv_list; /* all servers of a type */
+ struct list_head stype_list; /* all server types */
+ struct p9srv* (*create)(void);
+};
+
enum {
/* connection status values */
Reset = 1, /* resetting */
@@ -149,11 +165,11 @@ extern char *Enotfound;
extern char *Eopen;
extern char *Eexist;
extern char *Enotempty;
-
+extern struct kset p9srv_subsys;

struct p9srv *p9srv_srv_create(void);
void p9srv_srv_stop(struct p9srv *srv);
-void p9srv_srv_destroy(struct p9srv *srv);
+void p9srv_srv_shutdown(struct p9srv *srv);
void p9srv_srv_start(struct p9srv *srv);
void p9srv_respond(struct p9srv_req *req, struct p9_fcall *rc);
void p9srv_respond_error(struct p9srv_req *req, char *ename, int ecode);
@@ -165,5 +181,9 @@ void p9srv_conn_destroy(struct p9srv_conn *conn);
struct p9srv_fid *p9srv_fid_find(struct p9srv_fidpool *fp, u32 fid);
void p9srv_fid_incref(struct p9srv_fid *fid);
void p9srv_fid_decref(struct p9srv_fid *fid);
+struct p9srv_type *p9srv_type_register(char *name, struct p9srv *(*create)(void));
+void p9srv_type_unregister(char *name);
+int p9srv_listener_add(struct p9srv *, char *, char *);
+int p9srv_listener_del(struct p9srv *, char *, char *);

#endif
diff --git a/net/9p/srv.c b/net/9p/srv.c
index 855ac70..e0c359b 100644
--- a/net/9p/srv.c
+++ b/net/9p/srv.c
@@ -37,6 +37,18 @@ struct p9srv_fidpool {
struct hlist_head hash[FID_HTABLE_SIZE];
};

+struct p9srv_listener {
+ struct p9_trans_listener *listener;
+ char *name;
+ char *options;
+ struct list_head lis_list;
+};
+
+enum {
+ /* p9srv status */
+ Shutdown = 1,
+};
+
enum {
/* p9srv_req status */
Respond = 1,
@@ -45,6 +57,8 @@ enum {
};

struct workqueue_struct *p9srv_wq;
+static DEFINE_SPINLOCK(p9srv_type_lock);
+static LIST_HEAD(p9srv_type_list);

char *Eunknownfid = "unknown fid";
EXPORT_SYMBOL(Eunknownfid);
@@ -78,9 +92,12 @@ char *Eexist = "file or directory already exists";
EXPORT_SYMBOL(Eexist);
char *Enotempty = "directory not empty";
EXPORT_SYMBOL(Enotempty);
+decl_subsys_name(p9srv, srv, NULL, NULL);
+EXPORT_SYMBOL(p9srv_subsys);

static void p9srv_srv_ref(struct p9srv *srv);
static void p9srv_srv_unref(struct p9srv *srv);
+static void p9srv_shut_work(struct work_struct *work);
static void p9srv_conn_reset(struct p9srv_conn *conn, struct p9srv_req *vreq);
static void p9srv_conn_respond(struct p9srv_req *req);
static struct p9srv_fidpool *p9srv_fidpool_create(void);
@@ -109,6 +126,14 @@ static void p9srv_stat(struct p9srv_req *);
static void p9srv_wstat(struct p9srv_req *);
static void p9srv_preq_work(struct work_struct *work);
static void p9srv_conn_request(struct p9_trans *, struct p9_trans_req *req);
+static void p9srv_type_release(struct kobject *);
+static ssize_t p9srv_type_show(struct kobject *, struct attribute *, char *);
+static ssize_t p9srv_type_store(struct kobject *, struct attribute *,
+ const char *, size_t);
+static void p9srv_srv_release(struct kobject *);
+static ssize_t p9srv_srv_show(struct kobject *, struct attribute *, char *);
+static ssize_t p9srv_srv_store(struct kobject *, struct attribute *,
+ const char *, size_t);

static void (*p9srv_fcall[])(struct p9srv_req *) = {
p9srv_version, /* Tversion */
@@ -144,6 +169,36 @@ static void (*p9srv_fcall_post[])(struct p9srv_req *) = {
NULL, /* Twstat */
};

+static struct sysfs_ops p9srv_type_ops = {
+ .show = p9srv_type_show,
+ .store = p9srv_type_store,
+};
+
+static struct kobj_type p9srv_type_ktype = {
+ .release = p9srv_type_release,
+ .sysfs_ops = &p9srv_type_ops,
+};
+
+static struct attribute p9srv_clone_attr = {
+ .name = "clone",
+ .mode = 0444,
+};
+
+static struct sysfs_ops p9srv_srv_ops = {
+ .show = p9srv_srv_show,
+ .store = p9srv_srv_store,
+};
+
+static struct kobj_type p9srv_srv_ktype = {
+ .release = p9srv_srv_release,
+ .sysfs_ops = &p9srv_srv_ops,
+};
+
+static struct attribute p9srv_ctl_attr = {
+ .name = "ctl",
+ .mode = 0644,
+};
+
struct p9srv *p9srv_srv_create(void)
{
struct p9srv *srv;
@@ -156,10 +211,14 @@ struct p9srv *p9srv_srv_create(void)

memset(srv, 0, sizeof(struct p9srv));
spin_lock_init(&srv->lock);
+ srv->id = -1;
atomic_set(&srv->refcount, 1);
+ srv->status = 0;
INIT_LIST_HEAD(&srv->conn_list);
INIT_LIST_HEAD(&srv->req_list);
INIT_LIST_HEAD(&srv->workreq_list);
+ INIT_LIST_HEAD(&srv->lis_list);
+ INIT_WORK(&srv->shutwork, p9srv_shut_work);

/* the values below may be overwritten by the creator */
srv->msize = 65536 + P9_IOHDRSZ;
@@ -171,12 +230,32 @@ struct p9srv *p9srv_srv_create(void)
}
EXPORT_SYMBOL(p9srv_srv_create);

-void p9srv_srv_destroy(struct p9srv *srv)
+void p9srv_srv_shutdown(struct p9srv *srv)
{
- P9_DPRINTK(P9SRV_DEBUG_SRV, "%p\n", srv);
+ struct p9srv_listener *slis, *stmp;
+ LIST_HEAD(lis_list);
+
+ if (test_and_set_bit(Shutdown, &srv->status))
+ return;
+
+ P9_DPRINTK(P9SRV_DEBUG_SRV, "srv %p\n", srv);
+ spin_lock(&srv->lock);
+ list_for_each_entry_safe(slis, stmp, &srv->lis_list, lis_list) {
+ list_move(&slis->lis_list, &lis_list);
+ }
+ spin_unlock(&srv->lock);
+
+ list_for_each_entry_safe(slis, stmp, &lis_list, lis_list) {
+ list_del(&slis->lis_list);
+ (*slis->listener->destroy)(slis->listener);
+ kfree(slis);
+ }
+
+ sysfs_remove_file(&srv->kobj, &p9srv_ctl_attr);
+ kobject_unregister(&srv->kobj);
p9srv_srv_unref(srv);
}
-EXPORT_SYMBOL(p9srv_srv_destroy);
+EXPORT_SYMBOL(p9srv_srv_shutdown);

void p9srv_srv_ref(struct p9srv *srv)
{
@@ -200,6 +279,14 @@ void p9srv_srv_unref(struct p9srv *srv)
}
}

+static void p9srv_shut_work(struct work_struct *work)
+{
+ struct p9srv *srv;
+
+ srv = container_of(work, struct p9srv, shutwork);
+ p9srv_srv_shutdown(srv);
+}
+
void p9srv_srv_start(struct p9srv *srv)
{
P9_DPRINTK(P9SRV_DEBUG_SRV, "%p\n", srv);
@@ -366,6 +453,289 @@ void p9srv_req_unref(struct p9srv_req *req)
}
EXPORT_SYMBOL(p9srv_req_unref);

+struct p9srv_type *p9srv_type_register(char *name,
+ struct p9srv *(*create)(void))
+{
+ int err;
+ struct p9srv_type *ret, *stype;
+
+ ret = kzalloc(sizeof(*ret), GFP_KERNEL);
+ if (!ret)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&ret->lock);
+ ret->name = name;
+ ret->nextid = 0;
+ INIT_LIST_HEAD(&ret->srv_list);
+ INIT_LIST_HEAD(&ret->stype_list);
+ ret->create = create;
+
+ spin_lock(&p9srv_type_lock);
+ list_for_each_entry(stype, &p9srv_type_list, stype_list) {
+ if (strcmp(name, stype->name) == 0) {
+ spin_unlock(&p9srv_type_lock);
+ kfree(ret);
+ return ERR_PTR(-EEXIST);
+ }
+ }
+
+ list_add_tail(&ret->stype_list, &p9srv_type_list);
+ spin_unlock(&p9srv_type_lock);
+
+ kobject_init(&ret->kobj);
+ kobject_set_name(&ret->kobj, "%s", name);
+ ret->kobj.parent = &p9srv_subsys.kobj;
+ ret->kobj.ktype = &p9srv_type_ktype;
+ err = kobject_add(&ret->kobj);
+ if (err)
+ goto error;
+
+ err = sysfs_create_file(&ret->kobj, &p9srv_clone_attr);
+ if (err)
+ goto error;
+
+ return ret;
+
+error:
+ kobject_unregister(&ret->kobj);
+ /* kfree ??? */
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(p9srv_type_register);
+
+void p9srv_type_unregister(char *name)
+{
+ struct p9srv_type *stype;
+
+ spin_lock(&p9srv_type_lock);
+ list_for_each_entry(stype, &p9srv_type_list, stype_list) {
+ if (strcmp(name, stype->name) == 0) {
+ list_del(&stype->stype_list);
+ spin_unlock(&p9srv_type_lock);
+ sysfs_remove_file(&stype->kobj, &p9srv_clone_attr);
+ kobject_unregister(&stype->kobj);
+ /* kfree ??? */
+ return;
+ }
+ }
+ spin_unlock(&p9srv_type_lock);
+}
+EXPORT_SYMBOL(p9srv_type_unregister);
+
+static void p9srv_type_release(struct kobject *kobj)
+{
+}
+
+static ssize_t p9srv_type_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ int err;
+ struct p9srv_type *stype;
+ struct p9srv *srv;
+
+ stype = container_of(kobj, struct p9srv_type, kobj);
+ srv = (*stype->create)();
+ if (IS_ERR(srv))
+ return PTR_ERR(srv);
+
+ spin_lock(&stype->lock);
+ srv->id = stype->nextid++;
+ list_add_tail(&srv->srv_list, &stype->srv_list);
+ spin_unlock(&stype->lock);
+
+ kobject_init(&srv->kobj);
+ kobject_set_name(&srv->kobj, "%d", srv->id);
+ srv->kobj.parent = &stype->kobj;
+ srv->kobj.ktype = &p9srv_srv_ktype;
+ err = kobject_add(&srv->kobj);
+ if (err)
+ goto error;
+
+ err = sysfs_create_file(&srv->kobj, &p9srv_ctl_attr);
+ if (err)
+ goto error;
+
+ return snprintf(buf, PAGE_SIZE, "%d", srv->id);
+
+error:
+ spin_lock(&stype->lock);
+ list_del(&srv->srv_list);
+ spin_unlock(&stype->lock);
+ kobject_unregister(&srv->kobj);
+ /* kfree ??? */
+ return err;
+}
+
+static ssize_t p9srv_type_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t buflen)
+{
+ return -EPERM;
+}
+
+static void p9srv_srv_release(struct kobject *kobj)
+{
+}
+
+static ssize_t p9srv_srv_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct p9srv *srv;
+
+ srv = container_of(kobj, struct p9srv, kobj);
+ return snprintf(buf, PAGE_SIZE, "%d\n", srv->id);
+}
+
+static ssize_t p9srv_srv_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t buflen)
+{
+ int n, err;
+ char *args[7];
+ char *p, *ep;
+ struct p9srv *srv;
+
+ srv = container_of(kobj, struct p9srv, kobj);
+ if (test_bit(Shutdown, &srv->status))
+ return -ENOENT;
+
+ p = (char *) buf;
+ ep = (char *) buf + buflen - 1;
+ while (*ep == '\n')
+ ep--;
+
+ ep++;
+ *ep = '\0';
+ P9_DPRINTK(P9SRV_DEBUG_SRV, "buf '%s'\n", p);
+ for(n = 0; n < ARRAY_SIZE(args) && p < ep; n++) {
+ args[n] = p;
+ p = strchr(p, ' ');
+ if (!p)
+ break;
+
+ *p = '\0';
+ p++;
+ }
+
+ while (*args[n] == '\0')
+ n--;
+
+ n++;
+ if (n == 0)
+ return -EINVAL;
+
+ if (strcmp(args[0], "listen-add") == 0) {
+ if (n == 2)
+ args[2] = NULL;
+ else if (n < 2 || n > 3)
+ return -EINVAL;
+
+ err = p9srv_listener_add(srv, args[1], args[2]);
+ if (err)
+ return err;
+ } else if (strcmp(args[0], "listen-del") == 0) {
+ if (n == 2)
+ args[2] = NULL;
+ else if (n < 2 || n > 3)
+ return -EINVAL;
+
+ err = p9srv_listener_del(srv, args[1], args[2]);
+ if (err)
+ return err;
+ } else if (strcmp(args[0], "shutdown") == 0) {
+ /* we can't call p9srv_shutdown directly because
+ kobject_unregister will wait for the kobject
+ that we hold to be freed (and that won't happen) */
+ queue_work(p9srv_wq, &srv->shutwork);
+ } else {
+ return -EINVAL;
+ }
+
+ return buflen;
+}
+
+static void p9srv_newtrans(struct p9_trans_listener *l, struct p9_trans *trans)
+{
+ struct p9srv *srv;
+
+ srv = l->aux;
+ if (!trans) {
+ P9_DPRINTK(P9SRV_DEBUG_SRV, "listener err %d\n", l->err);
+ return;
+ }
+
+ if (test_bit(Shutdown, &srv->status)) {
+ (*trans->destroy)(trans);
+ return;
+ }
+
+ p9srv_conn_create(srv, trans);
+}
+
+
+int p9srv_listener_add(struct p9srv *srv, char *lname, char *options)
+{
+ substring_t tname;
+ struct p9_trans_module *tmod;
+ struct p9_trans_listener *lis;
+ struct p9srv_listener *slis;
+
+ P9_DPRINTK(P9SRV_DEBUG_SRV, "listener '%s' options '%s'\n",
+ lname, options);
+ tname.from = lname;
+ tname.to = lname + strlen(lname);
+ tmod = v9fs_match_trans(&tname);
+ if (!tmod)
+ return -ENOENT;
+
+ lis = tmod->create_listener(options);
+ if (IS_ERR(lis))
+ return PTR_ERR(lis);
+
+ lis->newtrans = p9srv_newtrans;
+ lis->aux = srv;
+
+ slis = kmalloc(sizeof(*slis) + strlen(lname) + strlen(options) + 2,
+ GFP_KERNEL);
+ if (!slis) {
+ (*lis->destroy)(lis);
+ return -ENOMEM;
+ }
+
+ slis->listener = lis;
+ slis->name = (char *) slis + sizeof(*slis);
+ slis->options = slis->name + strlen(lname) + 1;
+ strcpy(slis->name, lname);
+ strcpy(slis->options, options);
+ INIT_LIST_HEAD(&slis->lis_list);
+ spin_lock(&srv->lock);
+ list_add_tail(&slis->lis_list, &srv->lis_list);
+ spin_unlock(&srv->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(p9srv_listener_add);
+
+int p9srv_listener_del(struct p9srv *srv, char *lname, char *options)
+{
+ struct p9srv_listener *slis;
+
+ P9_DPRINTK(P9SRV_DEBUG_SRV, "listener '%s' options '%s'\n",
+ lname, options);
+ spin_lock(&srv->lock);
+ list_for_each_entry(slis, &srv->lis_list, lis_list) {
+ if (!strcmp(lname, slis->name) &&
+ !strcmp(options, slis->options)) {
+ list_del(&slis->lis_list);
+ spin_unlock(&srv->lock);
+ (*slis->listener->destroy)(slis->listener);
+ kfree(slis);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(p9srv_listener_del);
+
static void p9srv_version(struct p9srv_req *req)
{
int msize;
@@ -1258,17 +1628,27 @@ void p9srv_conn_respond(struct p9srv_req *req)

static int __init p9srv_init(void)
{
+ int err;
+
p9srv_wq = create_workqueue("9psrv");
if (!p9srv_wq) {
printk(KERN_WARNING "9psrv: creating workqueue failed\n");
return -ENOMEM;
}

+ kobj_set_kset_s(&p9srv_subsys, p9_subsys);
+ err = subsystem_register(&p9srv_subsys);
+ if (err) {
+ destroy_workqueue(p9srv_wq);
+ return err;
+ }
+
return 0;
}

static void __exit p9srv_exit(void)
{
+ subsystem_unregister(&p9srv_subsys);
destroy_workqueue(p9srv_wq);
p9srv_wq = NULL;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/