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/

Reply via email to