/** * \file sysfs.c * License details are found in the file LICENSE. * \brief * sysfs framework, IHK-Master side * \author Gou Nakamura \par * Copyright (C) 2015 - 2016 RIKEN AICS */ /* * HISTORY: */ #include #include #include #include #include #include "mcctrl.h" #include "sysfs_msg.h" #define dprintk(...) do { if (0) printk(__VA_ARGS__); } while (0) #define wprintk(...) do { if (1) printk(KERN_WARNING __VA_ARGS__); } while (0) #define eprintk(...) do { if (1) printk(KERN_ERR __VA_ARGS__); } while (0) enum { /* sysfsm_node.type */ SNT_FILE = 1, SNT_DIR = 2, SNT_LINK = 3, }; struct sysfsm_node { int8_t type; int8_t padding[7]; char *name; struct sysfsm_node *parent; struct sysfsm_data *sdp; struct list_head chain; union { /* SNT_DIR */ struct { struct kobject kobj; struct list_head children; }; /* SNT_FILE */ struct { struct attribute attr; struct sysfsm_ops *server_ops; long client_ops; long client_instance; }; }; }; /* struct sysfsm_node */ struct sysfs_work { void *os; int msg; int err; long arg1; long arg2; struct work_struct work; }; /* struct sysfs_work */ static struct sysfs_ops the_ops; static struct kobj_type the_ktype; static struct sysfsm_ops remote_ops; static struct sysfsm_ops local_ops; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0) static inline int bitmap_scnprintf(char *buf, unsigned buflen, const unsigned long *maskp, int nmaskbits) { return scnprintf(buf, buflen, "%*pb\n", nmaskbits, maskp); } /* bitmap_scnprintf() */ static inline int bitmap_scnlistprintf(char *buf, unsigned buflen, const unsigned long *maskp, int nmaskbits) { return scnprintf(buf, buflen, "%*pbl\n", nmaskbits, maskp); } /* bitmap_scnlistprintf() */ #endif static ssize_t remote_show(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { int error; struct semaphore *held_sem = NULL; struct ikc_scd_packet packet; struct sysfsm_node *np = instance; ssize_t ssize = -EIO; struct sysfsm_data *sdp; struct sysfsm_req *req; dprintk("mcctrl:remote_show(%p,%p,%p,%#lx)\n", ops, instance, buf, bufsize); sdp = np->sdp; req = &sdp->sysfs_req; if (!sysfs_inited(sdp)) { /* emulate EOF */ error = 0; ssize = 0; eprintk("mcctrl:remote_show:not initialized. %d\n", error); goto out; } error = down_interruptible(&sdp->sysfs_io_sem); if (error) { eprintk("mcctrl:remote_show:down failed. %d\n", error); goto out; } held_sem = &sdp->sysfs_io_sem; /* for the case that last wait_event_interruptible() was interrupted */ error = wait_event_interruptible(req->wq, !req->busy); if (error) { eprintk("mcctrl:remote_show:wait_event_interruptible0 failed. %d\n", error); error = -EINTR; goto out; } packet.msg = SCD_MSG_SYSFS_REQ_SHOW; packet.sysfs_arg1 = (long)np; packet.sysfs_arg2 = (long)np->client_ops; packet.sysfs_arg3 = (long)np->client_instance; req->busy = 1; #define SYSFS_MCK_CPU 0 error = mcctrl_ikc_send(sdp->sysfs_os, SYSFS_MCK_CPU, &packet); if (error) { eprintk("mcctrl:remote_show:mcctrl_ikc_send failed. %d\n", error); goto out; } error = wait_event_interruptible(req->wq, !req->busy); if (error) { eprintk("mcctrl:remote_show:wait_event_interruptible failed. %d\n", error); error = -EINTR; goto out; } ssize = req->lresult; if (ssize < 0) { error = ssize; eprintk("mcctrl:remote_show:SCD_MSG_SYSFS_REQ_SHOW failed. %d\n", error); goto out; } if (ssize > 0) { memcpy(buf, sdp->sysfs_buf, ssize); } error = 0; out: if (held_sem) { up(held_sem); } if (error) { eprintk("mcctrl:remote_show(%p,%p,%p,%#lx): %d\n", ops, instance, buf, bufsize, error); ssize = error; } dprintk("mcctrl:remote_show(%p,%p,%p,%#lx): %ld %d\n", ops, instance, buf, bufsize, ssize, error); return ssize; } /* remote_show() */ static ssize_t remote_store(struct sysfsm_ops *ops, void *instance, const void *buf, size_t bufsize) { int error; struct semaphore *held_sem = NULL; struct ikc_scd_packet packet; struct sysfsm_node *np = instance; ssize_t ssize = -EIO; struct sysfsm_data *sdp; struct sysfsm_req *req; dprintk("mcctrl:remote_store(%p,%p,%p,%#lx)\n", ops, instance, buf, bufsize); sdp = np->sdp; req = &sdp->sysfs_req; if (!sysfs_inited(sdp)) { /* emulate EOF */ error = -ENOSPC; eprintk("mcctrl:remote_store:not initialized. %d\n", error); goto out; } error = down_interruptible(&sdp->sysfs_io_sem); if (error) { eprintk("mcctrl:remote_store:down failed. %d\n", error); goto out; } held_sem = &sdp->sysfs_io_sem; /* for the case that last wait_event_interruptible() was interrupted */ error = wait_event_interruptible(req->wq, !req->busy); if (error) { eprintk("mcctrl:remote_store:wait_event_interruptible0 failed. %d\n", error); error = -EINTR; goto out; } if (bufsize > sdp->sysfs_bufsize) { error = -ENOSPC; eprintk("mcctrl:remote_store:too large size %#lx. %d\n", bufsize, error); goto out; } memcpy(sdp->sysfs_buf, buf, bufsize); packet.msg = SCD_MSG_SYSFS_REQ_STORE; packet.sysfs_arg1 = (long)np; packet.sysfs_arg2 = (long)np->client_ops; packet.sysfs_arg3 = (long)np->client_instance; packet.err = bufsize; req->busy = 1; #define SYSFS_MCK_CPU 0 error = mcctrl_ikc_send(sdp->sysfs_os, SYSFS_MCK_CPU, &packet); if (error) { eprintk("mcctrl:remote_store:mcctrl_ikc_send failed. %d\n", error); goto out; } error = wait_event_interruptible(req->wq, !req->busy); if (error) { eprintk("mcctrl:remote_store:wait_event_interruptible failed. %d\n", error); error = -EINTR; goto out; } ssize = req->lresult; if (ssize < 0) { error = ssize; eprintk("mcctrl:remote_store:SCD_MSG_SYSFS_REQ_STORE failed. %d\n", error); goto out; } error = 0; out: if (held_sem) { up(held_sem); } if (error) { eprintk("mcctrl:remote_store(%p,%p,%p,%#lx): %d\n", ops, instance, buf, bufsize, error); ssize = error; } dprintk("mcctrl:remote_store(%p,%p,%p,%#lx): %ld %d\n", ops, instance, buf, bufsize, ssize, error); return ssize; } /* remote_store() */ static int release_i(struct sysfsm_node *np) { int error; struct sysfsm_data *sdp; BUG_ON(!np); dprintk("mcctrl:release_i(%p %s)\n", np, np->name); sdp = np->sdp; if (np->type != SNT_DIR) { if (np->server_ops && np->server_ops->release) { (*np->server_ops->release)(np->server_ops, np); } } kfree(np->name); kfree(np); error = 0; #if 0 out: #endif if (error) { eprintk("mcctrl:release_i(%p %s): %d\n", np, np->name, error); } dprintk("mcctrl:release_i(%p): %d\n", np, error); return error; } /* release_i() */ static void remote_release(struct sysfsm_ops *ops, void *instance) { int error; struct sysfsm_node *np = instance; struct semaphore *held_sem = NULL; struct sysfsm_data *sdp; struct sysfsm_req *req; struct ikc_scd_packet packet; dprintk("mcctrl:remote_release(%p,%p)\n", ops, instance); sdp = np->sdp; req = &sdp->sysfs_req; if ((np->type == SNT_FILE) && np->client_ops && sysfs_inited(sdp)) { error = down_interruptible(&sdp->sysfs_io_sem); if (error) { eprintk("mcctrl:remote_release:down failed. %d\n", error); goto out; } held_sem = &sdp->sysfs_io_sem; /* for the case that last wait_event_interruptible() was interrupted */ error = wait_event_interruptible(req->wq, !req->busy); if (error) { eprintk("mcctrl:remote_release:wait_event_interruptible0 failed. %d\n", error); error = -EINTR; goto out; } packet.msg = SCD_MSG_SYSFS_REQ_RELEASE; packet.sysfs_arg1 = (long)np; packet.sysfs_arg2 = np->client_ops; packet.sysfs_arg3 = np->client_instance; req->busy = 1; #define SYSFS_MCK_CPU 0 error = mcctrl_ikc_send(sdp->sysfs_os, SYSFS_MCK_CPU, &packet); if (error) { eprintk("mcctrl:remote_release:mcctrl_ikc_send failed. %d\n", error); goto out; } error = wait_event_interruptible(req->wq, !req->busy); if (error) { eprintk("mcctrl:remote_release:wait_event_interruptible failed. %d\n", error); error = -EINTR; goto out; } } error = 0; out: if (held_sem) { up(held_sem); } if (error) { eprintk("mcctrl:remote_release(%p,%p): %d\n", ops, instance, error); } dprintk("mcctrl:remote_release(%p,%p): %d\n", ops, instance, error); return; } /* remote_release() */ static struct sysfsm_node * lookup_i(struct sysfsm_node *dirp, const char *name) { int error; struct sysfsm_node *np; BUG_ON(!dirp); BUG_ON(!name); dprintk("mcctrl:lookup_i(%s,%s)\n", dirp->name, name); if (dirp->type != SNT_DIR) { error = -ENOTDIR; eprintk("mcctrl:lookup_i:not a directory. %d\n", error); goto out; } if (name[0] == '\0') { error = -ENOENT; eprintk("mcctrl:lookup_i:null component. %d\n", error); goto out; } list_for_each_entry(np, &dirp->children, chain) { if (!strcmp(np->name, name)) { /* found */ error = 0; goto out; } } /* this is usual when called from create_i(), mkdir_i() and symlink_i(). */ error = ENOENT; /* positive value means suppressing error message */ out: if (error) { if (error < 0) { eprintk("mcctrl:lookup_i(%s,%s): %d\n", dirp->name, name, error); } else { error = -error; } np = ERR_PTR(error); } dprintk("mcctrl:lookup_i(%s,%s): %p %d\n", dirp->name, name, np, error); return np; } /* lookup_i() */ static struct sysfsm_node * create_i(struct sysfsm_node *parent, const char *name, mode_t mode, struct sysfsm_ops *server_ops, long client_ops, long client_instance) { int error; struct sysfsm_node *np = NULL; struct sysfsm_data *sdp; BUG_ON(!parent); BUG_ON(!name); dprintk("mcctrl:create_i(%s,%s,%#o,%#lx,%#lx)\n", parent->name, name, mode, client_ops, client_instance); sdp = parent->sdp; if (parent == sdp->sysfs_root) { error = -EPERM; eprintk("mcctrl:create_i:root dir. %d\n", error); goto out; } if (parent->type != SNT_DIR) { error = -ENOTDIR; eprintk("mcctrl:create_i:not a directory. %d\n", error); goto out; } if (name[0] == '\0') { error = -EINVAL; eprintk("mcctrl:create_i:null filename. %d\n", error); goto out; } np = lookup_i(parent, name); if (!IS_ERR(np)) { error = -EEXIST; eprintk("mcctrl:create_i:already exist. %d\n", error); np = NULL; goto out; } np = kzalloc(sizeof(*np), GFP_KERNEL); if (!np) { error = -ENOMEM; eprintk("mcctrl:create_i:kzalloc failed. %d\n", error); goto out; } np->type = SNT_FILE; np->name = kstrdup(name, GFP_KERNEL); np->parent = parent; np->sdp = sdp; INIT_LIST_HEAD(&np->chain); np->attr.name = np->name; np->attr.mode = mode; np->server_ops = server_ops; np->client_ops = client_ops; np->client_instance = client_instance; if (!np->name) { error = -ENOMEM; eprintk("mcctrl:create_i:kstrdup failed. %d\n", error); goto out; } error = sysfs_create_file(&parent->kobj, &np->attr); if (error) { eprintk("mcctrl:create_i:sysfs_create_file failed. %d\n", error); goto out; } list_add(&np->chain, &parent->children); error = 0; out: if (error) { if (np) { kfree(np->name); kfree(np); } np = ERR_PTR(error); eprintk("mcctrl:create_i(%s,%s,%#o,%#lx,%#lx) : %d\n", parent->name, name, mode, client_ops, client_instance, error); } dprintk("mcctrl:create_i(%s,%s,%#o,%#lx,%#lx) : %p %d\n", parent->name, name, mode, client_ops, client_instance, np, error); return np; } /* create_i() */ static struct sysfsm_node * mkdir_i(struct sysfsm_node *parent, const char *name) { int error; struct sysfsm_node *np = NULL; struct kobject *parent_kobj; struct sysfsm_data *sdp; BUG_ON(!parent); BUG_ON(!name); dprintk("mcctrl:mkdir_i(%s,%s)\n", parent->name, name); sdp = parent->sdp; if ((parent == sdp->sysfs_root) && strcmp(name, "sys")) { error = -EPERM; eprintk("mcctrl:mkdir_i:root dir. %d\n", error); goto out; } if (parent->type != SNT_DIR) { error = -ENOTDIR; eprintk("mcctrl:mkdir_i:not a directory. %d\n", error); goto out; } if (name[0] == '\0') { error = -EINVAL; eprintk("mcctrl:mkdir_i:null dirname. %d\n", error); goto out; } np = lookup_i(parent, name); if (!IS_ERR(np)) { error = -EEXIST; eprintk("mcctrl:mkdir_i:already exist. %d\n", error); np = NULL; goto out; } np = kzalloc(sizeof(*np), GFP_KERNEL); if (!np) { error = -ENOMEM; eprintk("mcctrl:mkdir_i:kzalloc failed. %d\n", error); goto out; } np->type = SNT_DIR; np->name = kstrdup(name, GFP_KERNEL); np->parent = parent; np->sdp = sdp; INIT_LIST_HEAD(&np->chain); INIT_LIST_HEAD(&np->children); if (!np->name) { error = -ENOMEM; eprintk("mcctrl:mkdir_i:kstrdup failed. %d\n", error); goto out; } parent_kobj = &parent->kobj; if (parent == sdp->sysfs_root) { parent_kobj = sdp->sysfs_kobj; } error = kobject_init_and_add(&np->kobj, &the_ktype, parent_kobj, np->name); if (error) { eprintk("mcctrl:mkdir_i:kobject_init_and_add failed. %d\n", error); goto out; } list_add(&np->chain, &parent->children); error = 0; out: if (error) { if (np) { kfree(np->name); kfree(np); } np = ERR_PTR(error); eprintk("mcctrl:mkdir_i(%s,%s): %d\n", parent->name, name, error); } dprintk("mcctrl:mkdir_i(%s,%s): %p %d\n", parent->name, name, np, error); return np; } /* mkdir_i() */ static struct sysfsm_node * symlink_i(struct sysfsm_node *target, struct sysfsm_node *parent, const char *name) { int error; struct sysfsm_node *np = NULL; struct sysfsm_data *sdp; BUG_ON(!target); BUG_ON(!parent); BUG_ON(!name); dprintk("mcctrl:symlink_i(%s,%s,%s)\n", target->name, parent->name, name); sdp = parent->sdp; if (target->type != SNT_DIR) { error = -EINVAL; eprintk("mcctrl:symlink_i:target isn't a directory. %d\n", error); goto out; } if (parent == sdp->sysfs_root) { error = -EPERM; eprintk("mcctrl:symlink_i:root directory. %d\n", error); goto out; } if (parent->type != SNT_DIR) { error = -ENOTDIR; eprintk("mcctrl:symlink_i:parent isn't a directory. %d\n", error); goto out; } if (name[0] == '\0') { error = -EINVAL; eprintk("mcctrl:symlink_i:null linkname. %d\n", error); goto out; } np = lookup_i(parent, name); if (!IS_ERR(np)) { error = -EEXIST; eprintk("mcctrl:symlink_i:already exist. %d\n", error); np = NULL; goto out; } np = kzalloc(sizeof(*np), GFP_KERNEL); if (!np) { error = -ENOMEM; eprintk("mcctrl:symlink_i:kzalloc failed. %d\n", error); goto out; } np->type = SNT_LINK; np->name = kstrdup(name, GFP_KERNEL); np->parent = parent; np->sdp = sdp; INIT_LIST_HEAD(&np->chain); if (!np->name) { error = -ENOMEM; eprintk("mcctrl:symlink_i:kstrdup failed. %d\n", error); goto out; } error = sysfs_create_link(&parent->kobj, &target->kobj, name); if (error) { eprintk("mcctrl:symlink_i:sysfs_create_link failed. %d\n", error); goto out; } list_add(&np->chain, &parent->children); error = 0; out: if (error) { if (np) { kfree(np->name); kfree(np); } np = ERR_PTR(error); eprintk("mcctrl:symlink_i(%s,%s,%s): %d\n", target->name, parent->name, name, error); } dprintk("mcctrl:symlink_i(%s,%s,%s): %p %d\n", target->name, parent->name, name, np, error); return np; } /* symlink_i() */ static int unlink_i(struct sysfsm_node *np) { int error; struct sysfsm_data *sdp; BUG_ON(!np); dprintk("mcctrl:unlink_i(%s)\n", np->name); sdp = np->sdp; if ((np == sdp->sysfs_root) || (np->parent == sdp->sysfs_root)) { error = -EPERM; eprintk("mcctrl:unlink_i:protected directory. %d\n", error); goto out; } if ((np->type == SNT_DIR) && !list_empty(&np->children)) { error = -ENOTEMPTY; eprintk("mcctrl:unlink_i:not empty dir. %d\n", error); goto out; } list_del(&np->chain); if (np->type == SNT_FILE) { sysfs_remove_file(&np->parent->kobj, &np->attr); } else if (np->type == SNT_DIR) { if (np->parent != np) { kobject_del(&np->kobj); } } else if (np->type == SNT_LINK) { sysfs_remove_link(&np->parent->kobj, np->name); } else { BUG(); } error = release_i(np); if (error) { eprintk("mcctrl:unlink_i:release_i failed. %d\n", error); goto out; } error = 0; out: if (error) { eprintk("mcctrl:unlink_i(%s): %d\n", np->name, error); } dprintk("mcctrl:unlink_i(%s): %d\n", (!error)?NULL:np->name, error); return error; } /* unlink_i() */ static int remove(struct sysfsm_node *target) { int error; struct sysfsm_node *np; struct sysfsm_node *next_np; BUG_ON(!target); dprintk("mcctrl:remove(%s)\n", target->name); for (np = target; np; np = next_np) { while ((np->type == SNT_DIR) && !list_empty(&np->children)) { np = list_first_entry(&np->children, struct sysfsm_node, chain); } next_np = np->parent; if (np == target) { next_np = NULL; } error = unlink_i(np); if (error) { eprintk("mcctrl:remove:unlink_i(%s) failed. %d\n", np->name, error); goto out; } } error = 0; out: if (error) { eprintk("mcctrl:remove(%s): %d\n", target->name, error); } dprintk("mcctrl:remove(%s): %d\n", (!error)?NULL:target->name, error); return error; } /* remove() */ static struct sysfsm_node * lookup(struct sysfsm_node *from, char *path) { int error; struct sysfsm_node *dirp; struct sysfsm_node *np; char *p; char *name; BUG_ON(!from); BUG_ON(!path); dprintk("mcctrl:lookup(%s,%s)\n", from->name, path); dirp = from; np = from; p = path; while (!!(name = strsep(&p, "/"))) { if (!*name) { continue; } np = lookup_i(dirp, name); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:lookup:lookup_i(%s,%s) failed. %d\n", dirp->name, name, error); goto out; } dirp = np; } error = 0; out: if (error) { np = ERR_PTR(error); eprintk("mcctrl:lookup(%s,%s): %d\n", from->name, path, error); } dprintk("mcctrl:lookup(%s,%s): %p %d\n", from->name, path, np, error); return np; } /* lookup() */ static struct sysfsm_node * dig(struct sysfsm_node *from, char *path) { int error; struct sysfsm_node *dirp; char *p; char *name; struct sysfsm_node *np; BUG_ON(!from); BUG_ON(!path); dprintk("mcctrl:dig(%s,%s)\n", from->name, path); dirp = from; p = path; while (!!(name = strsep(&p, "/"))) { if (!*name) { continue; } np = lookup_i(dirp, name); if (IS_ERR(np)) { error = PTR_ERR(np); if (error != -ENOENT) { eprintk("mcctrl:dig:lookup_i(%s,%s) failed. %d\n", dirp->name, name, error); goto out; } np = mkdir_i(dirp, name); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:dig:mkdir_i(%s,%s) failed. %d\n", dirp->name, name, error); goto out; } } dirp = np; } if (dirp->type != SNT_DIR) { error = -ENOTDIR; eprintk("mcctrl:dig:%s:not a directory. %d\n", dirp->name, error); goto out; } error = 0; out: if (error) { dirp = ERR_PTR(error); eprintk("mcctrl:dig(%s): %d\n", from->name, error); } dprintk("mcctrl:dig(%s): %p %d\n", from->name, dirp, error); return dirp; } /* dig() */ static void cleanup_ancestor(struct sysfsm_node *target) { int error; struct sysfsm_node *np; struct sysfsm_node *next_np; BUG_ON(!target); dprintk("mcctrl:cleanup_ancestor(%p {%s})\n", target, target->name); error = 0; for (np = target; !error; np = next_np) { next_np = np->parent; if ((np == np->sdp->sysfs_root) || (np->parent == np->sdp->sysfs_root) || !list_empty(&np->children)) { break; } error = unlink_i(np); } dprintk("mcctrl:cleanup_ancestor(%p):\n", target); return; } /* cleanup_ancestor() */ static struct sysfsm_node * sysfsm_create(struct sysfsm_data *sdp, const char *path0, mode_t mode, struct sysfsm_ops *server_ops, long client_ops, long client_instance) { int error; char *path = NULL; struct semaphore *held_sem = NULL; struct sysfsm_node *dirp; char *name; struct sysfsm_node *np = ERR_PTR(-EIO); BUG_ON(!sdp); dprintk("mcctrl:sysfsm_create(%p,%s,%#o,%#lx,%#lx)\n", sdp, path0, mode, client_ops, client_instance); path = kstrdup(path0, GFP_KERNEL); if (!path) { error = -ENOMEM; eprintk("mcctrl:sysfsm_create:kstrdup failed. %d\n", error); goto out; } error = down_interruptible(&sdp->sysfs_tree_sem); if (error) { eprintk("mcctrl:sysfsm_create:down failed. %d\n", error); goto out; } held_sem = &sdp->sysfs_tree_sem; dirp = sdp->sysfs_root; name = strrchr(path, '/'); if (!name) { name = path; } else { *name = '\0'; ++name; dirp = dig(dirp, path); if (IS_ERR(dirp)) { error = PTR_ERR(dirp); eprintk("mcctrl:sysfsm_create:dig failed. %d\n", error); goto out; } } np = create_i(dirp, name, mode, server_ops, client_ops, client_instance); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_create:create_i(%s,%s) failed. %d\n", dirp->name, name, error); goto out; } error = 0; out: if (held_sem) { up(held_sem); } kfree(path); if (error) { np = ERR_PTR(error); eprintk("mcctrl:sysfsm_create(%p,%s,%#o,%#lx,%#lx): %d\n", sdp, path0, mode, client_ops, client_instance, error); } dprintk("mcctrl:sysfsm_create(%p,%s,%#o,%#lx,%#lx): %p %d\n", sdp, path0, mode, client_ops, client_instance, np, error); return np; } /* sysfsm_create() */ struct sysfsm_node * sysfsm_mkdir(struct sysfsm_data *sdp, const char *path0) { int error; char *path = NULL; struct semaphore *held_sem = NULL; struct sysfsm_node *dirp; char *name; struct sysfsm_node *np = ERR_PTR(-EIO); BUG_ON(!sdp); dprintk("mcctrl:sysfsm_mkdir(%p,%s)\n", sdp, path0); path = kstrdup(path0, GFP_KERNEL); if (!path) { error = -ENOMEM; eprintk("mcctrl:sysfsm_mkdir:kstrdup failed. %d\n", error); goto out; } error = down_interruptible(&sdp->sysfs_tree_sem); if (error) { eprintk("mcctrl:sysfsm_mkdir:down failed. %d\n", error); goto out; } held_sem = &sdp->sysfs_tree_sem; dirp = sdp->sysfs_root; name = strrchr(path, '/'); if (!name) { name = path; } else { *name = '\0'; ++name; dirp = dig(dirp, path); if (IS_ERR(dirp)) { error = PTR_ERR(dirp); eprintk("mcctrl:sysfsm_mkdir:dig failed. %d\n", error); goto out; } } np = mkdir_i(dirp, name); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_mkdir:mkdir_i failed. %d\n", error); goto out; } error = 0; out: if (held_sem) { up(held_sem); } kfree(path); if (error) { np = ERR_PTR(error); eprintk("mcctrl:sysfsm_mkdir(%p,%s): %d\n", sdp, path0, error); } dprintk("mcctrl:sysfsm_mkdir(%p,%s): %p %d\n", sdp, path0, np, error); return np; } /* sysfsm_mkdir() */ struct sysfsm_node * sysfsm_symlink(struct sysfsm_data *sdp, struct sysfsm_node *target, const char *path0) { int error; char *path = NULL; struct semaphore *held_sem = NULL; char *name; struct sysfsm_node *dirp; struct sysfsm_node *np = ERR_PTR(-EIO); BUG_ON(!sdp); BUG_ON(!target); dprintk("mcctrl:sysfsm_symlink(%p,%s,%s)\n", sdp, target->name, path0); path = kstrdup(path0, GFP_KERNEL); if (!path) { error = -ENOMEM; eprintk("mcctrl:sysfsm_symlink:kstrdup failed. %d\n", error); goto out; } error = down_interruptible(&sdp->sysfs_tree_sem); if (error) { eprintk("mcctrl:sysfsm_symlink:down failed. %d\n", error); goto out; } held_sem = &sdp->sysfs_tree_sem; dirp = sdp->sysfs_root; name = strrchr(path, '/'); if (!name) { name = path; } else { *name = '\0'; ++name; dirp = dig(dirp, path); if (IS_ERR(dirp)) { error = PTR_ERR(dirp); eprintk("mcctrl:sysfsm_symlink:dig failed. %d\n", error); goto out; } } np = symlink_i(target, dirp, name); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_symlink:symlink_i(%s,%s,%s) failed. %d\n", target->name, dirp->name, name, error); goto out; } error = 0; out: if (held_sem) { up(held_sem); } kfree(path); if (error) { np = ERR_PTR(error); eprintk("mcctrl:sysfsm_symlink(%p,%s,%s): %d\n", sdp, target->name, path0, error); } dprintk("mcctrl:sysfsm_symlink(%p,%s,%s): %p %d\n", sdp, target->name, path0, np, error); return np; } /* sysfsm_symlink() */ static struct sysfsm_node * sysfsm_lookup(struct sysfsm_data *sdp, const char *path0) { int error; char *path = NULL; struct semaphore *held_sem = NULL; struct sysfsm_node *np; BUG_ON(!sdp); dprintk("mcctrl:sysfsm_lookup(%p,%s)\n", sdp, path0); path = kstrdup(path0, GFP_KERNEL); if (!path) { error = -ENOMEM; eprintk("mcctrl:sysfsm_lookup:kstrdup failed. %d\n", error); goto out; } error = down_interruptible(&sdp->sysfs_tree_sem); if (error) { eprintk("mcctrl:sysfsm_lookup:down failed. %d\n", error); goto out; } held_sem = &sdp->sysfs_tree_sem; np = lookup(sdp->sysfs_root, path); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_lookup:lookup failed. %d\n", error); goto out; } error = 0; out: if (held_sem) { up(held_sem); } kfree(path); if (error) { np = ERR_PTR(error); eprintk("mcctrl:sysfsm_lookup(%p,%s): %d\n", sdp, path0, error); } dprintk("mcctrl:sysfsm_lookup(%p,%s): %p %d\n", sdp, path0, np, error); return np; } /* sysfsm_lookup() */ static int sysfsm_unlink(struct sysfsm_data *sdp, const char *path0, int flags) { int error; char *path = NULL; struct semaphore *held_sem = NULL; struct sysfsm_node *dirp; struct sysfsm_node *np; BUG_ON(!sdp); dprintk("mcctrl:sysfsm_unlink(%p,%s,%#x)\n", sdp, path0, flags); path = kstrdup(path0, GFP_KERNEL); if (!path) { error = -ENOMEM; eprintk("mcctrl:sysfsm_unlink:kstrdup failed. %d\n", error); goto out; } error = down_interruptible(&sdp->sysfs_tree_sem); if (error) { eprintk("mcctrl:sysfsm_unlink:down failed. %d\n", error); goto out; } held_sem = &sdp->sysfs_tree_sem; np = lookup(sdp->sysfs_root, path); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_unlink:lookup failed. %d\n", error); goto out; } dirp = np->parent; error = remove(np); if (error) { eprintk("mcctrl:sysfsm_unlink:remove failed. %d\n", error); goto out; } if (!(flags & SYSFS_UNLINK_KEEP_ANCESTOR)) { cleanup_ancestor(dirp); } error = 0; out: if (held_sem) { up(held_sem); } kfree(path); if (error) { eprintk("mcctrl:sysfsm_unlink(%p,%s,%#x): %d\n", sdp, path0, flags, error); } dprintk("mcctrl:sysfsm_unlink(%p,%s,%#x): %d\n", sdp, path0, flags, error); return error; } /* sysfsm_unlink() */ void sysfsm_cleanup(ihk_os_t os) { int error; ihk_device_t dev = ihk_os_to_dev(os); struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); struct sysfsm_data *sdp; struct sysfsm_node *np; if (!udp) { printk("%s: WARNING: no mcctrl_usrdata found\n", __FUNCTION__); return; } sdp = &udp->sysfsm_data; dprintk("mcctrl:sysfsm_cleanup(%p)\n", os); if (sdp->sysfs_buf) { ihk_device_unmap_virtual(dev, sdp->sysfs_buf, sdp->sysfs_bufsize); ihk_device_unmap_memory(dev, sdp->sysfs_buf_pa, sdp->sysfs_bufsize); sdp->sysfs_buf = NULL; sdp->sysfs_buf_pa = 0; sdp->sysfs_buf_rpa = 0; } np = sdp->sysfs_root; sdp->sysfs_root = NULL; if (np) { error = remove(np); if (error) { wprintk("mcctrl:sysfsm_cleanup:remove failed. %d\n", error); /* through */ } } dprintk("mcctrl:sysfsm_cleanup(%p):\n", os); return; } /* sysfsm_cleanup() */ int sysfsm_setup(ihk_os_t os, void *buf, long buf_pa, size_t bufsize) { int error; struct device *dev = ihk_os_get_linux_device(os); struct sysfsm_node *np = NULL; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); struct sysfsm_data *sdp = &udp->sysfsm_data; struct sysfsm_req *req = &sdp->sysfs_req; dprintk("mcctrl:sysfsm_setup(%p)\n", os); req->busy = 0; init_waitqueue_head(&req->wq); np = kzalloc(sizeof(*np), GFP_KERNEL); if (!np) { error = -ENOMEM; eprintk("mcctrl:sysfsm_setup:kzalloc failed. %d\n", error); goto out; } np->type = SNT_DIR; np->name = kstrdup("(the_root)", GFP_KERNEL); np->parent = np; np->sdp = sdp; INIT_LIST_HEAD(&np->chain); INIT_LIST_HEAD(&np->children); if (!np->name) { error = -ENOMEM; eprintk("mcctrl:sysfsm_setup:kstrdup failed. %d\n", error); goto out; } sdp->sysfs_os = os; sdp->sysfs_kobj = &dev->kobj; sema_init(&sdp->sysfs_io_sem, 1); sema_init(&sdp->sysfs_tree_sem, 1); sdp->sysfs_root = np; np = NULL; np = mkdir_i(sdp->sysfs_root, "sys"); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_setup:mkdir_i failed. %d\n", error); goto out; } sdp->sysfs_bufsize = bufsize; sdp->sysfs_buf_pa = buf_pa; wmb(); sdp->sysfs_buf = buf; setup_sysfs_files(os); error = 0; out: if (error) { if (np) { kfree(np->name); kfree(np); } sysfsm_cleanup(os); eprintk("mcctrl:sysfsm_setup(%p): %d\n", os, error); } dprintk("mcctrl:sysfsm_setup(%p): %d\n", os, error); return error; } /* sysfsm_setup() */ /*********************************************************************** * remote snooping */ struct remote_snooping_param { ihk_device_t dev; int nbits; int size; long phys; void *ptr; }; static void cleanup_special_remote_create(struct sysfsm_ops *ops, void *instance) { struct sysfsm_node *np = instance; struct remote_snooping_param *param = (void *)np->client_instance; ihk_device_unmap_virtual(param->dev, param->ptr, param->size); ihk_device_unmap_memory(param->dev, param->phys, param->size); kfree(param); return; } /* cleanup_special_remote_create() */ /**** remote int ****/ static ssize_t snooping_remote_show_d32(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct remote_snooping_param *p = (void *)np->client_instance; return sprintf(buf, "%d\n", *(int *)p->ptr); } /* snooping_remote_show_d32() */ static struct sysfsm_ops snooping_remote_ops_d32 = { .show = &snooping_remote_show_d32, .release = &cleanup_special_remote_create, }; /**** remote long ****/ static ssize_t snooping_remote_show_d64(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct remote_snooping_param *p = (void *)np->client_instance; return sprintf(buf, "%ld\n", *(long *)p->ptr); } /* snooping_remote_show_d64() */ static struct sysfsm_ops snooping_remote_ops_d64 = { .show = &snooping_remote_show_d64, .release = &cleanup_special_remote_create, }; /**** remote unsigned int ****/ static ssize_t snooping_remote_show_u32(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct remote_snooping_param *p = (void *)np->client_instance; return sprintf(buf, "%u\n", *(unsigned *)p->ptr); } /* snooping_remote_show_u32() */ static struct sysfsm_ops snooping_remote_ops_u32 = { .show = &snooping_remote_show_u32, .release = &cleanup_special_remote_create, }; /**** remote unsigned long ****/ static ssize_t snooping_remote_show_u64(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct remote_snooping_param *p = (void *)np->client_instance; return sprintf(buf, "%lu\n", *(unsigned long *)p->ptr); } /* snooping_remote_show_u64() */ static struct sysfsm_ops snooping_remote_ops_u64 = { .show = &snooping_remote_show_u64, .release = &cleanup_special_remote_create, }; /**** remote string ****/ static ssize_t snooping_remote_show_s(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct remote_snooping_param *p = (void *)np->client_instance; return sprintf(buf, "%.*s\n", (int)p->size, (char *)p->ptr); } /* snooping_remote_show_s() */ static struct sysfsm_ops snooping_remote_ops_s = { .show = &snooping_remote_show_s, .release = &cleanup_special_remote_create, }; /**** remote list ****/ static ssize_t snooping_remote_show_pbl(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct remote_snooping_param *p = (void *)np->client_instance; return bitmap_scnlistprintf(buf, bufsize, p->ptr, p->nbits); } /* snooping_remote_show_pbl() */ static struct sysfsm_ops snooping_remote_ops_pbl = { .show = &snooping_remote_show_pbl, .release = &cleanup_special_remote_create, }; /**** remote map ****/ static ssize_t snooping_remote_show_pb(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct remote_snooping_param *p = (void *)np->client_instance; return bitmap_scnprintf(buf, bufsize, p->ptr, p->nbits); } /* snooping_remote_show_pb() */ static struct sysfsm_ops snooping_remote_ops_pb = { .show = &snooping_remote_show_pb, .release = &cleanup_special_remote_create, }; /**** remote K unsigned int ****/ static ssize_t snooping_remote_show_u32K(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct remote_snooping_param *p = (void *)np->client_instance; return sprintf(buf, "%uK\n", (*(unsigned *)p->ptr >> 10)); } /* snooping_remote_show_u32K() */ static struct sysfsm_ops snooping_remote_ops_u32K = { .show = &snooping_remote_show_u32K, .release = &cleanup_special_remote_create, }; struct sysfsm_ops * const remote_snooping_ops_table[] = { [(long)SYSFS_SNOOPING_OPS_d32] = &snooping_remote_ops_d32, [(long)SYSFS_SNOOPING_OPS_d64] = &snooping_remote_ops_d64, [(long)SYSFS_SNOOPING_OPS_u32] = &snooping_remote_ops_u32, [(long)SYSFS_SNOOPING_OPS_u64] = &snooping_remote_ops_u64, [(long)SYSFS_SNOOPING_OPS_s] = &snooping_remote_ops_s, [(long)SYSFS_SNOOPING_OPS_pbl] = &snooping_remote_ops_pbl, [(long)SYSFS_SNOOPING_OPS_pb] = &snooping_remote_ops_pb, [(long)SYSFS_SNOOPING_OPS_u32K] = &snooping_remote_ops_u32K, }; static int setup_special_remote_create(ihk_device_t dev, const struct sysfs_req_create_param *param, struct sysfsm_ops **mopsp, long *cinstancep) { int error; struct remote_snooping_param *rsp = NULL; long phys = -1; struct sysfsm_bitmap_param *pbp = NULL; long rpa; switch (param->client_ops) { case (long)SYSFS_SNOOPING_OPS_d32: case (long)SYSFS_SNOOPING_OPS_d64: case (long)SYSFS_SNOOPING_OPS_u32: case (long)SYSFS_SNOOPING_OPS_u64: case (long)SYSFS_SNOOPING_OPS_s: case (long)SYSFS_SNOOPING_OPS_pbl: case (long)SYSFS_SNOOPING_OPS_pb: case (long)SYSFS_SNOOPING_OPS_u32K: break; default: eprintk("mcctrl:setup_special_remote_create:unknown ops %#lx\n", param->client_ops); return -EINVAL; } rsp = kmalloc(sizeof(*rsp), GFP_KERNEL); if (!rsp) { eprintk("mcctrl:setup_special_remote_create:kmalloc failed.\n"); return -ENOMEM; } switch (param->client_ops) { case (long)SYSFS_SNOOPING_OPS_s: case (long)SYSFS_SNOOPING_OPS_pbl: case (long)SYSFS_SNOOPING_OPS_pb: phys = ihk_device_map_memory(dev, *cinstancep, sizeof(*pbp)); pbp = ihk_device_map_virtual(dev, phys, sizeof(*pbp), NULL, 0); break; } rsp->dev = dev; switch (param->client_ops) { case (long)SYSFS_SNOOPING_OPS_d32: rsp->size = sizeof(int); rpa = *cinstancep; break; case (long)SYSFS_SNOOPING_OPS_d64: rsp->size = sizeof(long); rpa = *cinstancep; break; case (long)SYSFS_SNOOPING_OPS_u32: case (long)SYSFS_SNOOPING_OPS_u32K: rsp->size = sizeof(unsigned); rpa = *cinstancep; break; case (long)SYSFS_SNOOPING_OPS_u64: rsp->size = sizeof(unsigned long); rpa = *cinstancep; break; case (long)SYSFS_SNOOPING_OPS_s: case (long)SYSFS_SNOOPING_OPS_pbl: case (long)SYSFS_SNOOPING_OPS_pb: rsp->nbits = pbp->nbits; rsp->size = (rsp->nbits + 7) / 8; /* how many bytes */ rpa = (long)pbp->ptr; break; default: BUG(); } rsp->phys = ihk_device_map_memory(dev, rpa, rsp->size); rsp->ptr = ihk_device_map_virtual(dev, rsp->phys, rsp->size, NULL, 0); error = 0; *mopsp = remote_snooping_ops_table[param->client_ops]; *cinstancep = (long)rsp; rsp = NULL; #if 0 out: #endif if (pbp) { ihk_device_unmap_virtual(dev, pbp, sizeof(*pbp)); ihk_device_unmap_memory(dev, phys, sizeof(*pbp)); } if (rsp) { kfree(rsp); } return error; } /* setup_special_remote_create() */ static void sysfsm_req_setup(void *os, long param_rpa) { int error; ihk_device_t dev = ihk_os_to_dev(os); long param_pa; struct sysfs_req_setup_param *param; long buf_pa; void *buf; param_pa = ihk_device_map_memory(dev, param_rpa, sizeof(*param)); param = ihk_device_map_virtual(dev, param_pa, sizeof(*param), NULL, 0); buf_pa = ihk_device_map_memory(dev, param->buf_rpa, param->bufsize); buf = ihk_device_map_virtual(dev, buf_pa, param->bufsize, NULL, 0); error = sysfsm_setup(os, buf, buf_pa, param->bufsize); param->error = error; wmb(); param->busy = 0; if (error) { ihk_device_unmap_virtual(dev, buf, param->bufsize); ihk_device_unmap_memory(dev, buf_pa, param->bufsize); } ihk_device_unmap_virtual(dev, param, sizeof(*param)); ihk_device_unmap_memory(dev, param_pa, sizeof(*param)); return; } /* sysfsm_req_setup() */ static void sysfsm_req_create(void *os, long param_rpa) { int error; ihk_device_t dev = ihk_os_to_dev(os); long param_pa; struct sysfs_req_create_param *param; struct sysfsm_node *np; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); struct sysfsm_ops *ops; long cinstance; param_pa = ihk_device_map_memory(dev, param_rpa, sizeof(*param)); param = ihk_device_map_virtual(dev, param_pa, sizeof(*param), NULL, 0); ops = &remote_ops; cinstance = param->client_instance; if (is_special_sysfs_ops((void *)param->client_ops)) { error = setup_special_remote_create(dev, param, &ops, &cinstance); if (error) { goto out; } } np = sysfsm_create(&udp->sysfsm_data, param->path, param->mode, ops, param->client_ops, cinstance); if (IS_ERR(np)) { error = PTR_ERR(np); if (is_special_sysfs_ops((void *)param->client_ops)) { cleanup_special_remote_create(ops, (void *)cinstance); } goto out; } error = 0; out: param->error = error; wmb(); param->busy = 0; ihk_device_unmap_virtual(dev, param, sizeof(*param)); ihk_device_unmap_memory(dev, param_pa, sizeof(*param)); return; } /* sysfsm_req_create() */ static void sysfsm_req_mkdir(void *os, long param_rpa) { int error; ihk_device_t dev = ihk_os_to_dev(os); long param_pa; struct sysfs_req_mkdir_param *param; struct sysfsm_node *np; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); param_pa = ihk_device_map_memory(dev, param_rpa, sizeof(*param)); param = ihk_device_map_virtual(dev, param_pa, sizeof(*param), NULL, 0); np = sysfsm_mkdir(&udp->sysfsm_data, param->path); if (IS_ERR(np)) { error = PTR_ERR(np); goto out; } error = 0; param->handle = (long)np; out: param->error = error; wmb(); param->busy = 0; ihk_device_unmap_virtual(dev, param, sizeof(*param)); ihk_device_unmap_memory(dev, param_pa, sizeof(*param)); return; } /* sysfsm_req_mkdir() */ static void sysfsm_req_symlink(void *os, long param_rpa) { int error; ihk_device_t dev = ihk_os_to_dev(os); long param_pa; struct sysfs_req_symlink_param *param; struct sysfsm_node *np; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); param_pa = ihk_device_map_memory(dev, param_rpa, sizeof(*param)); param = ihk_device_map_virtual(dev, param_pa, sizeof(*param), NULL, 0); np = sysfsm_symlink(&udp->sysfsm_data, (void *)param->target, param->path); if (IS_ERR(np)) { error = PTR_ERR(np); goto out; } error = 0; out: param->error = error; wmb(); param->busy = 0; ihk_device_unmap_virtual(dev, param, sizeof(*param)); ihk_device_unmap_memory(dev, param_pa, sizeof(*param)); return; } /* sysfsm_req_symlink() */ static void sysfsm_req_lookup(void *os, long param_rpa) { int error; ihk_device_t dev = ihk_os_to_dev(os); long param_pa; struct sysfs_req_lookup_param *param; struct sysfsm_node *np; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); param_pa = ihk_device_map_memory(dev, param_rpa, sizeof(*param)); param = ihk_device_map_virtual(dev, param_pa, sizeof(*param), NULL, 0); np = sysfsm_lookup(&udp->sysfsm_data, param->path); if (IS_ERR(np)) { error = PTR_ERR(np); goto out; } error = 0; param->handle = (long)np; out: param->error = error; wmb(); param->busy = 0; ihk_device_unmap_virtual(dev, param, sizeof(*param)); ihk_device_unmap_memory(dev, param_pa, sizeof(*param)); return; } /* sysfsm_req_lookup() */ static void sysfsm_req_unlink(void *os, long param_rpa) { int error; ihk_device_t dev = ihk_os_to_dev(os); long param_pa; struct sysfs_req_unlink_param *param; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); param_pa = ihk_device_map_memory(dev, param_rpa, sizeof(*param)); param = ihk_device_map_virtual(dev, param_pa, sizeof(*param), NULL, 0); error = sysfsm_unlink(&udp->sysfsm_data, param->path, param->flags); if (error) { goto out; } error = 0; out: param->error = error; wmb(); param->busy = 0; ihk_device_unmap_virtual(dev, param, sizeof(*param)); ihk_device_unmap_memory(dev, param_pa, sizeof(*param)); return; } /* sysfsm_req_unlink() */ static void sysfsm_resp_show(void *os, struct sysfsm_node *np, ssize_t ssize) { struct sysfsm_data *sdp = np->sdp; struct sysfsm_req *req = &sdp->sysfs_req; dprintk("mcctrl:sysfsm_resp_show(%p,%s,%ld)\n", os, np->name, ssize); req->lresult = ssize; wmb(); req->busy = 0; wake_up(&req->wq); dprintk("mcctrl:sysfsm_resp_show(%p,%s,%ld):\n", os, np->name, ssize); return; } /* sysfsm_resp_show() */ static void sysfsm_resp_store(void *os, struct sysfsm_node *np, ssize_t ssize) { struct sysfsm_data *sdp = np->sdp; struct sysfsm_req *req = &sdp->sysfs_req; dprintk("mcctrl:sysfsm_resp_store(%p,%s,%ld)\n", os, np->name, ssize); req->lresult = ssize; wmb(); req->busy = 0; wake_up(&req->wq); dprintk("mcctrl:sysfsm_resp_store(%p,%s,%ld):\n", os, np->name, ssize); return; } /* sysfsm_resp_store() */ static void sysfsm_resp_release(void *os, struct sysfsm_node *np, int error) { struct sysfsm_data *sdp = np->sdp; struct sysfsm_req *req = &sdp->sysfs_req; dprintk("mcctrl:sysfsm_resp_release(%p,%p %s,%d)\n", os, np, np->name, error); req->lresult = error; wmb(); req->busy = 0; wake_up(&req->wq); dprintk("mcctrl:sysfsm_resp_release(%p,%p,%d):\n", os, np, error); return; } /* sysfsm_resp_release() */ static void sysfsm_work_main(struct work_struct *work0) { struct sysfs_work *work = container_of(work0, struct sysfs_work, work); switch (work->msg) { case SCD_MSG_SYSFS_REQ_SETUP: sysfsm_req_setup(work->os, work->arg1); break; case SCD_MSG_SYSFS_REQ_CREATE: sysfsm_req_create(work->os, work->arg1); break; case SCD_MSG_SYSFS_REQ_MKDIR: sysfsm_req_mkdir(work->os, work->arg1); break; case SCD_MSG_SYSFS_REQ_SYMLINK: sysfsm_req_symlink(work->os, work->arg1); break; case SCD_MSG_SYSFS_REQ_LOOKUP: sysfsm_req_lookup(work->os, work->arg1); break; case SCD_MSG_SYSFS_REQ_UNLINK: sysfsm_req_unlink(work->os, work->arg1); break; case SCD_MSG_SYSFS_RESP_SHOW: sysfsm_resp_show(work->os, (void *)work->arg1, work->arg2); break; case SCD_MSG_SYSFS_RESP_STORE: sysfsm_resp_store(work->os, (void *)work->arg1, work->arg2); break; case SCD_MSG_SYSFS_RESP_RELEASE: sysfsm_resp_release(work->os, (void *)work->arg1, work->err); break; default: wprintk("mcctrl:sysfsm_work_main:unknown work (%d,%p,%#lx,%#lx)\n", work->msg, work->os, work->arg1, work->arg2); break; } kfree(work); return; } /* sysfsm_work_main() */ void sysfsm_packet_handler(void *os, int msg, int err, long arg1, long arg2) { struct sysfs_work *work = NULL; work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) { eprintk("mcctrl:sysfsm_packet_handler:kzalloc failed\n"); return; } work->os = os; work->msg = msg; work->err = err; work->arg1 = arg1; work->arg2 = arg2; INIT_WORK(&work->work, &sysfsm_work_main); schedule_work(&work->work); return; } /* sysfsm_packet_handler() */ static ssize_t sysfsm_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct sysfsm_node *np = container_of(attr, struct sysfsm_node, attr); ssize_t ssize; ssize = -ENOSPC; if (np->server_ops && np->server_ops->show) { ssize = (*np->server_ops->show)(np->server_ops, np, buf, PAGE_SIZE); } return ssize; } /* sysfsm_show() */ static ssize_t sysfsm_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t bufsize) { struct sysfsm_node *np = container_of(attr, struct sysfsm_node, attr); ssize_t ssize; ssize = -ENOSPC; if (np->server_ops && np->server_ops->store) { ssize = (*np->server_ops->store)(np->server_ops, np, buf, bufsize); } return ssize; } /* sysfsm_store() */ static void sysfsm_release(struct kobject *kobj) { struct sysfsm_node *np = container_of(kobj, struct sysfsm_node, kobj); if (np->server_ops && np->server_ops->release) { (*np->server_ops->release)(np->server_ops, np); } return; } /* sysfsm_release() */ static struct sysfs_ops the_ops = { .show = &sysfsm_show, .store = &sysfsm_store, }; static struct kobj_type the_ktype = { .sysfs_ops = &the_ops, .release = &sysfsm_release, }; static struct sysfsm_ops remote_ops = { .show = &remote_show, .store = &remote_store, .release = &remote_release, }; static ssize_t local_show(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct sysfsm_ops *client_ops; ssize_t ssize; dprintk("mcctrl:local_show(%p,%p,%p,%#lx)\n", ops, instance, buf, bufsize); client_ops = (void *)np->client_ops; ssize = -ENOSPC; if (client_ops && client_ops->show) { ssize = (*client_ops->show)(client_ops, (void *)np->client_instance, buf, PAGE_SIZE); } dprintk("mcctrl:local_show(%p,%p,%p,%#lx): %ld\n", ops, instance, buf, bufsize, ssize); return ssize; } /* local_show() */ static ssize_t local_store(struct sysfsm_ops *ops, void *instance, const void *buf, size_t bufsize) { struct sysfsm_node *np = instance; struct sysfsm_ops *client_ops; ssize_t ssize; dprintk("mcctrl:local_store(%p,%p,%p,%#lx)\n", ops, instance, buf, bufsize); client_ops = (void *)np->client_ops; ssize = -ENOSPC; if (client_ops && client_ops->store) { ssize = (*client_ops->store)(client_ops, (void *)np->client_instance, buf, bufsize); } dprintk("mcctrl:local_store(%p,%p,%p,%#lx): %ld\n", ops, instance, buf, bufsize, ssize); return ssize; } /* local_store() */ static void local_release(struct sysfsm_ops *ops, void *instance) { struct sysfsm_node *np = instance; struct sysfsm_ops *client_ops; dprintk("mcctrl:local_release(%p,%p)\n", ops, instance); client_ops = (void *)np->client_ops; if ((np->type == SNT_FILE) && client_ops && client_ops->release) { (*client_ops->release)(client_ops, (void *)np->client_instance); } dprintk("mcctrl:local_release(%p,%p):\n", ops, instance); return; } /* local_release() */ static struct sysfsm_ops local_ops = { .show = &local_show, .store = &local_store, .release = &local_release, }; /*********************************************************************** * local snooping */ static void cleanup_special_local_create(struct sysfsm_ops *ops, void *instance) { kfree(instance); return; } /* cleanup_special_local_create() */ /**** local int ****/ static ssize_t snooping_local_show_d32(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { return sprintf(buf, "%d\n", *(int *)instance); } /* snooping_local_show_d32() */ struct sysfsm_ops snooping_local_ops_d32 = { .show = &snooping_local_show_d32, }; /**** local long ****/ static ssize_t snooping_local_show_d64(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { return sprintf(buf, "%ld\n", *(long *)instance); } /* snooping_local_show_d64() */ struct sysfsm_ops snooping_local_ops_d64 = { .show = &snooping_local_show_d64, }; /**** local unsigned ****/ static ssize_t snooping_local_show_u32(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { return sprintf(buf, "%u\n", *(unsigned *)instance); } /* snooping_local_show_u32() */ struct sysfsm_ops snooping_local_ops_u32 = { .show = &snooping_local_show_u32, }; /**** local unsigned long ****/ static ssize_t snooping_local_show_u64(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { return sprintf(buf, "%lu\n", *(unsigned long *)instance); } /* snooping_local_show_u64() */ struct sysfsm_ops snooping_local_ops_u64 = { .show = &snooping_local_show_u64, }; /**** local string ****/ static ssize_t snooping_local_show_s(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { return sprintf(buf, "%s\n", (char *)instance); } /* snooping_local_show_s() */ struct sysfsm_ops snooping_local_ops_s = { .show = &snooping_local_show_s, }; /**** local list ****/ static ssize_t snooping_local_show_pbl(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { size_t ret; const struct sysfsm_bitmap_param *p = instance; ret = bitmap_scnlistprintf(buf, bufsize, p->ptr, p->nbits); if (ret < bufsize - 1) { sprintf(buf + ret, "\n"); return ret + 1; } return 0; } /* snooping_local_show_pbl() */ struct sysfsm_ops snooping_local_ops_pbl = { .show = &snooping_local_show_pbl, .release = &cleanup_special_local_create, }; /**** local map ****/ static ssize_t snooping_local_show_pb(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { size_t ret; const struct sysfsm_bitmap_param *p = instance; ret = bitmap_scnprintf(buf, bufsize, p->ptr, p->nbits); if (ret < bufsize - 1) { sprintf(buf + ret, "\n"); return ret + 1; } return 0; } /* snooping_local_show_pb() */ struct sysfsm_ops snooping_local_ops_pb = { .show = &snooping_local_show_pb, .release = &cleanup_special_local_create, }; /**** local K unsigned ****/ static ssize_t snooping_local_show_u32K(struct sysfsm_ops *ops, void *instance, void *buf, size_t bufsize) { return sprintf(buf, "%uK\n", (*(unsigned *)instance >> 10)); } /* snooping_local_show_u32K() */ struct sysfsm_ops snooping_local_ops_u32K = { .show = &snooping_local_show_u32K, }; struct sysfsm_ops * const local_snooping_ops_table[] = { [(long)SYSFS_SNOOPING_OPS_d32] = &snooping_local_ops_d32, [(long)SYSFS_SNOOPING_OPS_d64] = &snooping_local_ops_d64, [(long)SYSFS_SNOOPING_OPS_u32] = &snooping_local_ops_u32, [(long)SYSFS_SNOOPING_OPS_u64] = &snooping_local_ops_u64, [(long)SYSFS_SNOOPING_OPS_s] = &snooping_local_ops_s, [(long)SYSFS_SNOOPING_OPS_pbl] = &snooping_local_ops_pbl, [(long)SYSFS_SNOOPING_OPS_pb] = &snooping_local_ops_pb, [(long)SYSFS_SNOOPING_OPS_u32K] = &snooping_local_ops_u32K, }; static int setup_special_local_create(struct sysfs_req_create_param *param) { struct sysfsm_bitmap_param *p = NULL; switch (param->client_ops) { case (long)SYSFS_SNOOPING_OPS_d32: case (long)SYSFS_SNOOPING_OPS_d64: case (long)SYSFS_SNOOPING_OPS_u32: case (long)SYSFS_SNOOPING_OPS_u64: case (long)SYSFS_SNOOPING_OPS_s: case (long)SYSFS_SNOOPING_OPS_u32K: param->client_ops = (long)local_snooping_ops_table[param->client_ops]; return 0; case (long)SYSFS_SNOOPING_OPS_pbl: case (long)SYSFS_SNOOPING_OPS_pb: p = kmalloc(sizeof(*p), GFP_KERNEL); if (!p) { return -ENOMEM; } memcpy(p, (void *)param->client_instance, sizeof(*p)); param->client_ops = (long)local_snooping_ops_table[param->client_ops]; param->client_instance = (long)p; return 0; } eprintk("mcctrl:setup_special_local_create:unknown ops %#lx\n", param->client_ops); return -EINVAL; } /* setup_special_local_create() */ int sysfsm_createf(ihk_os_t os, struct sysfsm_ops *ops, void *instance, int mode, const char *fmt, ...) { int error; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); va_list ap; ssize_t n; struct sysfs_req_create_param *param = NULL; struct sysfsm_node *np; char special = 0; dprintk("mcctrl:sysfsm_createf(%p,%p,%p,%#o,%s,...)\n", os, ops, instance, mode, fmt); param = (void *)__get_free_page(GFP_KERNEL); if (!param) { error = -ENOMEM; eprintk("mcctrl:sysfsm_createf:__get_free_page failed. %d\n", error); goto out; } param->client_ops = (long)ops; param->client_instance = (long)instance; param->mode = mode; va_start(ap, fmt); n = vsnprintf(param->path, sizeof(param->path), fmt, ap); va_end(ap); if (n >= sizeof(param->path)) { error = -ENAMETOOLONG; eprintk("mcctrl:sysfsm_createf:vsnprintf failed. %d\n", error); goto out; } dprintk("mcctrl:sysfsm_createf:path %s\n", param->path); if (param->path[0] != '/') { error = -ENOENT; eprintk("mcctrl:sysfsm_createf:not an absolute path. %d\n", error); goto out; } if (is_special_sysfs_ops((void *)param->client_ops)) { error = setup_special_local_create(param); if (error) { eprintk("mcctrl:sysfsm_createf:setup_special_local_create failed. %d\n", error); goto out; } special = 1; } np = sysfsm_create(&udp->sysfsm_data, param->path, param->mode, &local_ops, param->client_ops, param->client_instance); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_createf:sysfsm_create failed. %d\n", error); goto out; } error = 0; out: if (error && special) { cleanup_special_local_create((void *)param->client_ops, (void *)param->client_instance); } free_page((uintptr_t)param); if (error) { eprintk("mcctrl:sysfsm_createf(%p,%p,%p,%#o,%s,...): %d\n", os, ops, instance, mode, fmt, error); } dprintk("mcctrl:sysfsm_createf(%p,%p,%p,%#o,%s,...): %d\n", os, ops, instance, mode, fmt, error); return error; } /* sysfsm_createf() */ int sysfsm_mkdirf(ihk_os_t os, sysfs_handle_t *dirhp, const char *fmt, ...) { int error; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); struct sysfs_req_mkdir_param *param = NULL; va_list ap; int n; struct sysfsm_node *np; dprintk("mcctrl:sysfsm_mkdirf(%p,%p,%s,...)\n", os, dirhp, fmt); param = (void *)__get_free_page(GFP_KERNEL); if (!param) { error = -ENOMEM; eprintk("mcctrl:sysfsm_mkdirf:__get_free_page failed. %d\n", error); goto out; } va_start(ap, fmt); n = vsnprintf(param->path, sizeof(param->path), fmt, ap); va_end(ap); if (n >= sizeof(param->path)) { error = -ENAMETOOLONG; eprintk("mcctrl:sysfsm_mkdirf:vsnprintf failed. %d\n", error); goto out; } dprintk("mcctrl:sysfsm_mkdirf:path %s\n", param->path); if (param->path[0] != '/') { error = -ENOENT; eprintk("mcctrl:sysfsm_mkdirf:not an absolute path. %d\n", error); goto out; } np = sysfsm_mkdir(&udp->sysfsm_data, param->path); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_mkdirf:sysfsm_mkdir failed. %d\n", error); goto out; } error = 0; if (dirhp) { dirhp->handle = (long)np; } out: free_page((uintptr_t)param); if (error) { eprintk("mcctrl:sysfsm_mkdirf(%p,%p,%s,...): %d\n", os, dirhp, fmt, error); } dprintk("mcctrl:sysfsm_mkdirf(%p,%p,%s,...): %d %#lx\n", os, dirhp, fmt, error, (dirhp)?dirhp->handle:0); return error; } /* sysfsm_mkdirf() */ int sysfsm_symlinkf(ihk_os_t os, sysfs_handle_t targeth, const char *fmt, ...) { int error; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); struct sysfs_req_symlink_param *param = NULL; va_list ap; int n; struct sysfsm_node *np; dprintk("mcctrl:sysfsm_symlinkf(%p,%#lx,%s,...)\n", os, targeth.handle, fmt); param = (void *)__get_free_page(GFP_KERNEL); if (!param) { error = -ENOMEM; eprintk("mcctrl:sysfsm_symlinkf:__get_free_page failed. %d\n", error); goto out; } va_start(ap, fmt); n = vsnprintf(param->path, sizeof(param->path), fmt, ap); va_end(ap); if (n >= sizeof(param->path)) { error = -ENAMETOOLONG; eprintk("mcctrl:sysfsm_symlinkf:vsnprintf failed. %d\n", error); goto out; } dprintk("mcctrl:sysfsm_symlinkf:path %s\n", param->path); if (param->path[0] != '/') { error = -ENOENT; eprintk("mcctrl:sysfsm_symlinkf:not an absolute path. %d\n", error); goto out; } np = sysfsm_symlink(&udp->sysfsm_data, (void *)targeth.handle, param->path); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_symlinkf:sysfsm_symlink failed. %d\n", error); goto out; } error = 0; out: free_page((uintptr_t)param); if (error) { eprintk("mcctrl:sysfsm_symlinkf(%p,%#lx,%s,...): %d\n", os, targeth.handle, fmt, error); } dprintk("mcctrl:sysfsm_symlinkf(%p,%#lx,%s,...): %d\n", os, targeth.handle, fmt, error); return error; } /* sysfsm_symlinkf() */ int sysfsm_lookupf(ihk_os_t os, sysfs_handle_t *objhp, const char *fmt, ...) { int error; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); struct sysfs_req_lookup_param *param = NULL; va_list ap; int n; struct sysfsm_node *np; dprintk("mcctrl:sysfsm_lookupf(%p,%p,%s,...)\n", os, objhp, fmt); param = (void *)__get_free_page(GFP_KERNEL); if (!param) { error = -ENOMEM; eprintk("mcctrl:sysfsm_lookupf:__get_free_page failed. %d\n", error); goto out; } va_start(ap, fmt); n = vsnprintf(param->path, sizeof(param->path), fmt, ap); va_end(ap); if (n >= sizeof(param->path)) { error = -ENAMETOOLONG; eprintk("mcctrl:sysfsm_lookupf:vsnprintf failed. %d\n", error); goto out; } dprintk("mcctrl:sysfsm_lookupf:path %s\n", param->path); if (param->path[0] != '/') { error = -ENOENT; eprintk("mcctrl:sysfsm_lookupf:not an absolute path. %d\n", error); goto out; } np = sysfsm_lookup(&udp->sysfsm_data, param->path); if (IS_ERR(np)) { error = PTR_ERR(np); eprintk("mcctrl:sysfsm_lookupf:sysfsm_lookup failed. %d\n", error); goto out; } error = 0; if (objhp) { objhp->handle = (long)np; } out: free_page((uintptr_t)param); if (error) { eprintk("mcctrl:sysfsm_lookupf(%p,%p,%s,...): %d\n", os, objhp, fmt, error); } dprintk("mcctrl:sysfsm_lookupf(%p,%p,%s,...): %d %#lx\n", os, objhp, fmt, error, (objhp)?objhp->handle:0); return error; } /* sysfsm_lookupf() */ int sysfsm_unlinkf(ihk_os_t os, int flags, const char *fmt, ...) { int error; struct mcctrl_usrdata *udp = ihk_host_os_get_usrdata(os); struct sysfs_req_unlink_param *param = NULL; va_list ap; int n; dprintk("mcctrl:sysfsm_unlinkf(%p,%#x,%s,...)\n", os, flags, fmt); param = (void *)__get_free_page(GFP_KERNEL); if (!param) { error = -ENOMEM; eprintk("mcctrl:sysfsm_unlinkf:__get_free_page failed. %d\n", error); goto out; } va_start(ap, fmt); n = vsnprintf(param->path, sizeof(param->path), fmt, ap); va_end(ap); if (n >= sizeof(param->path)) { error = -ENAMETOOLONG; eprintk("mcctrl:sysfsm_unlinkf:vsnprintf failed. %d\n", error); goto out; } dprintk("mcctrl:sysfsm_unlinkf:path %s\n", param->path); if (param->path[0] != '/') { error = -ENOENT; eprintk("mcctrl:sysfsm_unlinkf:not an absolute path. %d\n", error); goto out; } error = sysfsm_unlink(&udp->sysfsm_data, param->path, flags); if (error) { eprintk("mcctrl:sysfsm_unlinkf:sysfsm_unlink failed. %d\n", error); goto out; } error = 0; out: free_page((uintptr_t)param); if (error) { eprintk("mcctrl:sysfsm_unlinkf(%p,%#x,%s,...): %d\n", os, flags, fmt, error); } dprintk("mcctrl:sysfsm_unlinkf(%p,%#x,%s,...): %d\n", os, flags, fmt, error); return error; } /* sysfsm_unlinkf() */ /**** End of File ****/