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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <[email protected]>

---
 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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux