Files
mckernel/executer/kernel/mcctrl/sysfs.c
Dominique Martinet d213efac79 mcctrl/sysfs: add parenthesis around SYSFS_UNLINK_KEEP_ANCESTOR check
! has more priority than &, so !flags & SYSFS_UNLINK_KEEP_ANCESTOR is
not very likely. Change to !(flags & SYSFS_UNLINK_KEEP_ANCESTOR)
2017-10-13 10:02:11 +09:00

2503 lines
58 KiB
C

/**
* \file sysfs.c
* License details are found in the file LICENSE.
* \brief
* sysfs framework, IHK-Master side
* \author Gou Nakamura <go.nakamura.yw@hitachi-solutions.com> \par
* Copyright (C) 2015 - 2016 RIKEN AICS
*/
/*
* HISTORY:
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#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 ****/