summaryrefslogtreecommitdiffstats
path: root/fs/lockd
diff options
context:
space:
mode:
Diffstat (limited to 'fs/lockd')
-rw-r--r--fs/lockd/clntproc.c195
-rw-r--r--fs/lockd/svc4proc.c34
-rw-r--r--fs/lockd/svclock.c66
-rw-r--r--fs/lockd/svcproc.c33
4 files changed, 129 insertions, 199 deletions
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 7a23986..3f8ad7c 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -152,9 +152,8 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req)
int
nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
{
- struct nfs_server *nfssrv = NFS_SERVER(inode);
struct nlm_host *host;
- struct nlm_rqst reqst, *call = &reqst;
+ struct nlm_rqst *call;
sigset_t oldset;
unsigned long flags;
int status, proto, vers;
@@ -168,23 +167,17 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
/* Retrieve transport protocol from NFS client */
proto = NFS_CLIENT(inode)->cl_xprt->prot;
- if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers)))
+ host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers);
+ if (host == NULL)
return -ENOLCK;
- /* Create RPC client handle if not there, and copy soft
- * and intr flags from NFS client. */
- if (host->h_rpcclnt == NULL) {
- struct rpc_clnt *clnt;
+ call = nlm_alloc_call(host);
+ if (call == NULL)
+ return -ENOMEM;
- /* Bind an rpc client to this host handle (does not
- * perform a portmapper lookup) */
- if (!(clnt = nlm_bind_host(host))) {
- status = -ENOLCK;
- goto done;
- }
- clnt->cl_softrtry = nfssrv->client->cl_softrtry;
- clnt->cl_intr = nfssrv->client->cl_intr;
- }
+ nlmclnt_locks_init_private(fl, host);
+ /* Set up the argument struct */
+ nlmclnt_setlockargs(call, fl);
/* Keep the old signal mask */
spin_lock_irqsave(&current->sighand->siglock, flags);
@@ -197,26 +190,10 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
&& (current->flags & PF_EXITING)) {
sigfillset(&current->blocked); /* Mask all signals */
recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
- call = nlmclnt_alloc_call();
- if (!call) {
- status = -ENOMEM;
- goto out_restore;
- }
call->a_flags = RPC_TASK_ASYNC;
- } else {
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
- memset(call, 0, sizeof(*call));
- locks_init_lock(&call->a_args.lock.fl);
- locks_init_lock(&call->a_res.lock.fl);
}
- call->a_host = host;
-
- nlmclnt_locks_init_private(fl, host);
-
- /* Set up the argument struct */
- nlmclnt_setlockargs(call, fl);
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
if (fl->fl_type != F_UNLCK) {
@@ -229,24 +206,26 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
else
status = -EINVAL;
- out_restore:
+ fl->fl_ops->fl_release_private(fl);
+ fl->fl_ops = NULL;
+
spin_lock_irqsave(&current->sighand->siglock, flags);
current->blocked = oldset;
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, flags);
-done:
dprintk("lockd: clnt proc returns %d\n", status);
- nlm_release_host(host);
return status;
}
EXPORT_SYMBOL(nlmclnt_proc);
/*
* Allocate an NLM RPC call struct
+ *
+ * Note: the caller must hold a reference to host. In case of failure,
+ * this reference will be released.
*/
-struct nlm_rqst *
-nlmclnt_alloc_call(void)
+struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
{
struct nlm_rqst *call;
@@ -255,16 +234,30 @@ nlmclnt_alloc_call(void)
if (call != NULL) {
locks_init_lock(&call->a_args.lock.fl);
locks_init_lock(&call->a_res.lock.fl);
+ call->a_host = host;
return call;
}
if (signalled())
break;
- printk("nlmclnt_alloc_call: failed, waiting for memory\n");
+ printk("nlm_alloc_call: failed, waiting for memory\n");
schedule_timeout_interruptible(5*HZ);
}
+ nlm_release_host(host);
return NULL;
}
+void nlm_release_call(struct nlm_rqst *call)
+{
+ nlm_release_host(call->a_host);
+ nlmclnt_release_lockargs(call);
+ kfree(call);
+}
+
+static void nlmclnt_rpc_release(void *data)
+{
+ return nlm_release_call(data);
+}
+
static int nlm_wait_on_grace(wait_queue_head_t *queue)
{
DEFINE_WAIT(wait);
@@ -361,7 +354,7 @@ in_grace_period:
/*
* Generic NLM call, async version.
*/
-int nlmsvc_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
+int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
{
struct nlm_host *host = req->a_host;
struct rpc_clnt *clnt;
@@ -369,48 +362,23 @@ int nlmsvc_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops
.rpc_argp = &req->a_args,
.rpc_resp = &req->a_res,
};
- int status;
-
- dprintk("lockd: call procedure %d on %s (async)\n",
- (int)proc, host->h_name);
-
- /* If we have no RPC client yet, create one. */
- if ((clnt = nlm_bind_host(host)) == NULL)
- return -ENOLCK;
- msg.rpc_proc = &clnt->cl_procinfo[proc];
-
- /* bootstrap and kick off the async RPC call */
- status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req);
-
- return status;
-}
-
-static int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
-{
- struct nlm_host *host = req->a_host;
- struct rpc_clnt *clnt;
- struct nlm_args *argp = &req->a_args;
- struct nlm_res *resp = &req->a_res;
- struct rpc_message msg = {
- .rpc_argp = argp,
- .rpc_resp = resp,
- };
- int status;
+ int status = -ENOLCK;
dprintk("lockd: call procedure %d on %s (async)\n",
(int)proc, host->h_name);
/* If we have no RPC client yet, create one. */
- if ((clnt = nlm_bind_host(host)) == NULL)
- return -ENOLCK;
+ clnt = nlm_bind_host(host);
+ if (clnt == NULL)
+ goto out_err;
msg.rpc_proc = &clnt->cl_procinfo[proc];
- /* Increment host refcount */
- nlm_get_host(host);
/* bootstrap and kick off the async RPC call */
status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req);
- if (status < 0)
- nlm_release_host(host);
+ if (status == 0)
+ return 0;
+out_err:
+ nlm_release_call(req);
return status;
}
@@ -423,26 +391,28 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
int status;
status = nlmclnt_call(req, NLMPROC_TEST);
- nlmclnt_release_lockargs(req);
if (status < 0)
- return status;
+ goto out;
- status = req->a_res.status;
- if (status == NLM_LCK_GRANTED) {
- fl->fl_type = F_UNLCK;
- } if (status == NLM_LCK_DENIED) {
- /*
- * Report the conflicting lock back to the application.
- */
- fl->fl_start = req->a_res.lock.fl.fl_start;
- fl->fl_end = req->a_res.lock.fl.fl_start;
- fl->fl_type = req->a_res.lock.fl.fl_type;
- fl->fl_pid = 0;
- } else {
- return nlm_stat_to_errno(req->a_res.status);
+ switch (req->a_res.status) {
+ case NLM_LCK_GRANTED:
+ fl->fl_type = F_UNLCK;
+ break;
+ case NLM_LCK_DENIED:
+ /*
+ * Report the conflicting lock back to the application.
+ */
+ fl->fl_start = req->a_res.lock.fl.fl_start;
+ fl->fl_end = req->a_res.lock.fl.fl_start;
+ fl->fl_type = req->a_res.lock.fl.fl_type;
+ fl->fl_pid = 0;
+ break;
+ default:
+ status = nlm_stat_to_errno(req->a_res.status);
}
-
- return 0;
+out:
+ nlm_release_call(req);
+ return status;
}
static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
@@ -560,7 +530,7 @@ out_unblock:
if (resp->status == NLM_LCK_BLOCKED)
nlmclnt_cancel(host, req->a_args.block, fl);
out:
- nlmclnt_release_lockargs(req);
+ nlm_release_call(req);
return status;
}
@@ -623,32 +593,24 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
*/
do_vfs_lock(fl);
- if (req->a_flags & RPC_TASK_ASYNC) {
- status = nlmclnt_async_call(req, NLMPROC_UNLOCK,
- &nlmclnt_unlock_ops);
- /* Hrmf... Do the unlock early since locks_remove_posix()
- * really expects us to free the lock synchronously */
- if (status < 0) {
- nlmclnt_release_lockargs(req);
- kfree(req);
- }
- return status;
- }
+ if (req->a_flags & RPC_TASK_ASYNC)
+ return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
status = nlmclnt_call(req, NLMPROC_UNLOCK);
- nlmclnt_release_lockargs(req);
if (status < 0)
- return status;
+ goto out;
+ status = 0;
if (resp->status == NLM_LCK_GRANTED)
- return 0;
+ goto out;
if (resp->status != NLM_LCK_DENIED_NOLOCKS)
printk("lockd: unexpected unlock status: %d\n", resp->status);
-
/* What to do now? I'm out of my depth... */
-
- return -ENOLCK;
+ status = -ENOLCK;
+out:
+ nlm_release_call(req);
+ return status;
}
static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
@@ -670,9 +632,6 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
if (status != NLM_LCK_GRANTED)
printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status);
die:
- nlm_release_host(req->a_host);
- nlmclnt_release_lockargs(req);
- kfree(req);
return;
retry_rebind:
nlm_rebind_host(req->a_host);
@@ -682,6 +641,7 @@ die:
static const struct rpc_call_ops nlmclnt_unlock_ops = {
.rpc_call_done = nlmclnt_unlock_callback,
+ .rpc_release = nlmclnt_rpc_release,
};
/*
@@ -703,20 +663,15 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, flags);
- req = nlmclnt_alloc_call();
+ req = nlm_alloc_call(nlm_get_host(host));
if (!req)
return -ENOMEM;
- req->a_host = host;
req->a_flags = RPC_TASK_ASYNC;
nlmclnt_setlockargs(req, fl);
req->a_args.block = block;
- status = nlmclnt_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops);
- if (status < 0) {
- nlmclnt_release_lockargs(req);
- kfree(req);
- }
+ status = nlm_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops);
spin_lock_irqsave(&current->sighand->siglock, flags);
current->blocked = oldset;
@@ -757,9 +712,6 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
}
die:
- nlm_release_host(req->a_host);
- nlmclnt_release_lockargs(req);
- kfree(req);
return;
retry_cancel:
@@ -773,6 +725,7 @@ retry_cancel:
static const struct rpc_call_ops nlmclnt_cancel_ops = {
.rpc_call_done = nlmclnt_cancel_callback,
+ .rpc_release = nlmclnt_rpc_release,
};
/*
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index ac4a700..cb51c70 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -480,43 +480,37 @@ nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp)
struct nlm_host *host;
struct nlm_rqst *call;
- if (!(call = nlmclnt_alloc_call()))
+ host = nlmsvc_lookup_host(rqstp);
+ if (host == NULL)
return rpc_system_err;
- host = nlmsvc_lookup_host(rqstp);
- if (!host) {
- kfree(call);
+ call = nlm_alloc_call(host);
+ if (call == NULL)
return rpc_system_err;
- }
+
call->a_flags = RPC_TASK_ASYNC;
- call->a_host = host;
memcpy(&call->a_args, resp, sizeof(*resp));
- if (nlmsvc_async_call(call, proc, &nlm4svc_callback_ops) < 0)
- goto error;
-
+ if (nlm_async_call(call, proc, &nlm4svc_callback_ops) < 0)
+ return rpc_system_err;
return rpc_success;
- error:
- kfree(call);
- nlm_release_host(host);
- return rpc_system_err;
}
static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
{
- struct nlm_rqst *call = data;
+ dprintk("lockd: %4d callback returned %d\n", task->tk_pid,
+ -task->tk_status);
+}
- if (task->tk_status < 0) {
- dprintk("lockd: %4d callback failed (errno = %d)\n",
- task->tk_pid, -task->tk_status);
- }
- nlm_release_host(call->a_host);
- kfree(call);
+static void nlm4svc_callback_release(void *data)
+{
+ nlm_release_call(data);
}
static const struct rpc_call_ops nlm4svc_callback_ops = {
.rpc_call_done = nlm4svc_callback_exit,
+ .rpc_release = nlm4svc_callback_release,
};
/*
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index a95d260..185bf7e 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -117,12 +117,12 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove)
(long long)lock->fl.fl_start,
(long long)lock->fl.fl_end, lock->fl.fl_type);
for (head = &nlm_blocked; (block = *head) != 0; head = &block->b_next) {
- fl = &block->b_call.a_args.lock.fl;
+ fl = &block->b_call->a_args.lock.fl;
dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n",
block->b_file, fl->fl_pid,
(long long)fl->fl_start,
(long long)fl->fl_end, fl->fl_type,
- nlmdbg_cookie2a(&block->b_call.a_args.cookie));
+ nlmdbg_cookie2a(&block->b_call->a_args.cookie));
if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
if (remove) {
*head = block->b_next;
@@ -156,7 +156,7 @@ nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin)
for (block = nlm_blocked; block; block = block->b_next) {
dprintk("cookie: head of blocked queue %p, block %p\n",
nlm_blocked, block);
- if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie)
+ if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie)
&& nlm_cmp_addr(sin, &block->b_host->h_addr))
break;
}
@@ -182,28 +182,30 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
{
struct nlm_block *block;
struct nlm_host *host;
- struct nlm_rqst *call;
+ struct nlm_rqst *call = NULL;
/* Create host handle for callback */
host = nlmsvc_lookup_host(rqstp);
if (host == NULL)
return NULL;
+ call = nlm_alloc_call(host);
+ if (call == NULL)
+ return NULL;
+
/* Allocate memory for block, and initialize arguments */
- if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL)))
+ block = kzalloc(sizeof(*block), GFP_KERNEL);
+ if (block == NULL)
goto failed;
- memset(block, 0, sizeof(*block));
- locks_init_lock(&block->b_call.a_args.lock.fl);
- locks_init_lock(&block->b_call.a_res.lock.fl);
kref_init(&block->b_count);
- if (!nlmsvc_setgrantargs(&block->b_call, lock))
+ if (!nlmsvc_setgrantargs(call, lock))
goto failed_free;
/* Set notifier function for VFS, and init args */
- block->b_call.a_args.lock.fl.fl_flags |= FL_SLEEP;
- block->b_call.a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations;
- block->b_call.a_args.cookie = *cookie; /* see above */
+ call->a_args.lock.fl.fl_flags |= FL_SLEEP;
+ call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations;
+ call->a_args.cookie = *cookie; /* see above */
dprintk("lockd: created block %p...\n", block);
@@ -217,16 +219,16 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
file->f_blocks = block;
/* Set up RPC arguments for callback */
- call = &block->b_call;
- call->a_host = host;
+ block->b_call = call;
call->a_flags = RPC_TASK_ASYNC;
+ call->a_block = block;
return block;
failed_free:
kfree(block);
failed:
- nlm_release_host(host);
+ nlm_release_call(call);
return NULL;
}
@@ -242,7 +244,7 @@ static int nlmsvc_unlink_block(struct nlm_block *block)
dprintk("lockd: unlinking block %p...\n", block);
/* Remove block from list */
- status = posix_unblock_lock(block->b_file->f_file, &block->b_call.a_args.lock.fl);
+ status = posix_unblock_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl);
nlmsvc_remove_block(block);
return status;
}
@@ -263,9 +265,8 @@ static void nlmsvc_free_block(struct kref *kref)
}
}
- if (block->b_host)
- nlm_release_host(block->b_host);
- nlmsvc_freegrantargs(&block->b_call);
+ nlmsvc_freegrantargs(block->b_call);
+ nlm_release_call(block->b_call);
kfree(block);
}
@@ -316,10 +317,8 @@ static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
if (lock->oh.len > NLMCLNT_OHSIZE) {
void *data = kmalloc(lock->oh.len, GFP_KERNEL);
- if (!data) {
- nlmsvc_freegrantargs(call);
+ if (!data)
return 0;
- }
call->a_args.lock.oh.data = (u8 *) data;
}
@@ -329,17 +328,8 @@ static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
static void nlmsvc_freegrantargs(struct nlm_rqst *call)
{
- struct file_lock *fl = &call->a_args.lock.fl;
- /*
- * Check whether we allocated memory for the owner.
- */
- if (call->a_args.lock.oh.data != (u8 *) call->a_owner) {
+ if (call->a_args.lock.oh.data != call->a_owner)
kfree(call->a_args.lock.oh.data);
- }
- if (fl->fl_ops && fl->fl_ops->fl_release_private)
- fl->fl_ops->fl_release_private(fl);
- if (fl->fl_lmops && fl->fl_lmops->fl_release_private)
- fl->fl_lmops->fl_release_private(fl);
}
/*
@@ -371,9 +361,9 @@ again:
block = nlmsvc_lookup_block(file, lock, 0);
if (block == NULL) {
if (newblock != NULL)
- lock = &newblock->b_call.a_args.lock;
+ lock = &newblock->b_call->a_args.lock;
} else
- lock = &block->b_call.a_args.lock;
+ lock = &block->b_call->a_args.lock;
error = posix_lock_file(file->f_file, &lock->fl);
lock->fl.fl_flags &= ~FL_SLEEP;
@@ -523,7 +513,7 @@ nlmsvc_notify_blocked(struct file_lock *fl)
dprintk("lockd: VFS unblock notification for block %p\n", fl);
for (bp = &nlm_blocked; (block = *bp) != 0; bp = &block->b_next) {
- if (nlm_compare_locks(&block->b_call.a_args.lock.fl, fl)) {
+ if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
nlmsvc_insert_block(block, 0);
svc_wake_up(block->b_daemon);
return;
@@ -558,7 +548,7 @@ static void
nlmsvc_grant_blocked(struct nlm_block *block)
{
struct nlm_file *file = block->b_file;
- struct nlm_lock *lock = &block->b_call.a_args.lock;
+ struct nlm_lock *lock = &block->b_call->a_args.lock;
int error;
dprintk("lockd: grant blocked lock %p\n", block);
@@ -606,7 +596,7 @@ callback:
/* Call the client */
kref_get(&block->b_count);
- if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
+ if (nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG,
&nlmsvc_grant_ops) < 0)
nlmsvc_release_block(block);
out_unlock:
@@ -624,7 +614,7 @@ out_unlock:
static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
{
struct nlm_rqst *call = data;
- struct nlm_block *block = container_of(call, struct nlm_block, b_call);
+ struct nlm_block *block = call->a_block;
unsigned long timeout;
dprintk("lockd: GRANT_MSG RPC callback\n");
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 4986fbe..956d1d7 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -505,43 +505,36 @@ nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp)
struct nlm_host *host;
struct nlm_rqst *call;
- if (!(call = nlmclnt_alloc_call()))
+ host = nlmsvc_lookup_host(rqstp);
+ if (host == NULL)
return rpc_system_err;
- host = nlmsvc_lookup_host(rqstp);
- if (!host) {
- kfree(call);
+ call = nlm_alloc_call(host);
+ if (call == NULL)
return rpc_system_err;
- }
call->a_flags = RPC_TASK_ASYNC;
- call->a_host = host;
memcpy(&call->a_args, resp, sizeof(*resp));
- if (nlmsvc_async_call(call, proc, &nlmsvc_callback_ops) < 0)
- goto error;
-
+ if (nlm_async_call(call, proc, &nlmsvc_callback_ops) < 0)
+ return rpc_system_err;
return rpc_success;
- error:
- nlm_release_host(host);
- kfree(call);
- return rpc_system_err;
}
static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
{
- struct nlm_rqst *call = data;
+ dprintk("lockd: %4d callback returned %d\n", task->tk_pid,
+ -task->tk_status);
+}
- if (task->tk_status < 0) {
- dprintk("lockd: %4d callback failed (errno = %d)\n",
- task->tk_pid, -task->tk_status);
- }
- nlm_release_host(call->a_host);
- kfree(call);
+static void nlmsvc_callback_release(void *data)
+{
+ nlm_release_call(data);
}
static const struct rpc_call_ops nlmsvc_callback_ops = {
.rpc_call_done = nlmsvc_callback_exit,
+ .rpc_release = nlmsvc_callback_release,
};
/*
OpenPOWER on IntegriCloud