* [PATCH] eCryptfs: Privileged kthread for lower file opens
@ 2008-05-20 21:46 Michael Halcrow
2008-05-20 23:16 ` Andrea Righi
2008-05-21 22:59 ` [PATCH] eCryptfs: Privileged kthread for lower file opens Andrew Morton
0 siblings, 2 replies; 10+ messages in thread
From: Michael Halcrow @ 2008-05-20 21:46 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, Rusty Russell, David J. Kleikamp, SERGE E. HALLYN
eCryptfs would really like to have read-write access to all files in
the lower filesystem. Right now, the persistent lower file may be
opened read-only if the attempt to open it read-write fails. One way
to keep from having to do that is to have a privileged kthread that
can open the lower persistent file on behalf of the user opening the
eCryptfs file; this patch implements this functionality.
This patch will properly allow a less-privileged user to open the
eCryptfs file, followed by a more-privileged user opening the eCryptfs
file, with the first user only being able to read and the second user
being able to both read and write. eCryptfs currently does this wrong;
it will wind up calling vfs_write() on a file that was opened
read-only. This is fixed in this patch.
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>
---
fs/ecryptfs/Makefile | 2 +-
fs/ecryptfs/ecryptfs_kernel.h | 31 ++++++
fs/ecryptfs/file.c | 7 ++
fs/ecryptfs/kthread.c | 211 +++++++++++++++++++++++++++++++++++++++++
fs/ecryptfs/main.c | 42 ++++----
5 files changed, 271 insertions(+), 22 deletions(-)
create mode 100644 fs/ecryptfs/kthread.c
diff --git a/fs/ecryptfs/Makefile b/fs/ecryptfs/Makefile
index 1e34a7f..b4755a8 100644
--- a/fs/ecryptfs/Makefile
+++ b/fs/ecryptfs/Makefile
@@ -4,4 +4,4 @@
obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o
-ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o read_write.o crypto.o keystore.o messaging.o netlink.o miscdev.o debug.o
+ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o read_write.o crypto.o keystore.o messaging.o netlink.o miscdev.o kthread.o debug.o
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 951ee33..8ca910a 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -559,6 +559,32 @@ extern struct kmem_cache *ecryptfs_key_record_cache;
extern struct kmem_cache *ecryptfs_key_sig_cache;
extern struct kmem_cache *ecryptfs_global_auth_tok_cache;
extern struct kmem_cache *ecryptfs_key_tfm_cache;
+extern struct kmem_cache *ecryptfs_open_req_cache;
+
+extern struct task_struct *ecryptfs_kthread;
+
+struct ecryptfs_open_req {
+#define ECRYPTFS_REQ_PROCESSED 0x00000001
+#define ECRYPTFS_REQ_DROPPED 0x00000002
+#define ECRYPTFS_REQ_ZOMBIE 0x00000004
+ u32 flags;
+ struct file **lower_file;
+ struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
+ struct task_struct *requesting_task;
+ struct mutex mux;
+ struct list_head kthread_ctl_list;
+};
+
+struct ecryptfs_kthread_ctl {
+#define ECRYPTFS_KTHREAD_ZOMBIE 0x00000001
+ u32 flags;
+ struct mutex mux;
+ struct list_head req_list;
+ wait_queue_head_t wait;
+};
+
+extern struct ecryptfs_kthread_ctl ecryptfs_kthread_ctl;
int ecryptfs_interpose(struct dentry *hidden_dentry,
struct dentry *this_dentry, struct super_block *sb,
@@ -692,5 +718,10 @@ void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx);
int
ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, uid_t euid,
struct user_namespace *user_ns, struct pid *pid);
+int ecryptfs_init_kthread(void);
+void ecryptfs_destroy_kthread(void);
+int ecryptfs_privileged_open(struct file **lower_file,
+ struct dentry *lower_dentry,
+ struct vfsmount *lower_mnt);
#endif /* #ifndef ECRYPTFS_KERNEL_H */
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 2258b8f..aced6f9 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -191,6 +191,13 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
| ECRYPTFS_ENCRYPTED);
}
mutex_unlock(&crypt_stat->cs_mutex);
+ if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_RDONLY)
+ && !(file->f_flags & O_RDONLY)) {
+ rc = -EPERM;
+ printk(KERN_WARNING "%s: Lower persistent file is RO; eCryptfs "
+ "file must thus be opened RO\n", __func__);
+ goto out;
+ }
ecryptfs_set_file_lower(
file, ecryptfs_inode_to_private(inode)->lower_file);
if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
new file mode 100644
index 0000000..dba7c67
--- /dev/null
+++ b/fs/ecryptfs/kthread.c
@@ -0,0 +1,211 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 2008 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/wait.h>
+#include <linux/mount.h>
+#include "ecryptfs_kernel.h"
+
+struct task_struct *ecryptfs_kthread;
+struct ecryptfs_kthread_ctl ecryptfs_kthread_ctl;
+
+struct kmem_cache *ecryptfs_open_req_cache;
+
+/**
+ * ecryptfs_threadfn
+ * @ignored: ignored
+ *
+ * The eCryptfs kernel thread that has the responsibility of getting
+ * the lower persistent file with RW permissions.
+ *
+ * Returns zero on success; non-zero otherwise
+ */
+static int ecryptfs_threadfn(void *ignored)
+{
+ set_freezable();
+ while (1) {
+ struct ecryptfs_open_req *req;
+
+ wait_event_freezable(
+ ecryptfs_kthread_ctl.wait,
+ !list_empty(&ecryptfs_kthread_ctl.req_list));
+ mutex_lock(&ecryptfs_kthread_ctl.mux);
+ if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
+ mutex_unlock(&ecryptfs_kthread_ctl.mux);
+ goto out;
+ }
+ while (!list_empty(&ecryptfs_kthread_ctl.req_list)) {
+ req = list_first_entry(&ecryptfs_kthread_ctl.req_list,
+ struct ecryptfs_open_req,
+ kthread_ctl_list);
+ BUG_ON(!req);
+ mutex_lock(&req->mux);
+ list_del(&req->kthread_ctl_list);
+ if (req->flags & ECRYPTFS_REQ_ZOMBIE)
+ mutex_unlock(&req->mux);
+ else {
+ dget(req->lower_dentry);
+ mntget(req->lower_mnt);
+ (*req->lower_file) = dentry_open(
+ req->lower_dentry, req->lower_mnt,
+ (O_RDWR | O_LARGEFILE));
+ req->flags |= ECRYPTFS_REQ_PROCESSED;
+ wake_up_process(req->requesting_task);
+ mutex_unlock(&req->mux);
+ }
+ }
+ mutex_unlock(&ecryptfs_kthread_ctl.mux);
+ }
+out:
+ do_exit(0);
+}
+
+int ecryptfs_init_kthread(void)
+{
+ int rc = 0;
+
+ mutex_init(&ecryptfs_kthread_ctl.mux);
+ init_waitqueue_head(&ecryptfs_kthread_ctl.wait);
+ INIT_LIST_HEAD(&ecryptfs_kthread_ctl.req_list);
+ ecryptfs_kthread = kthread_create(&ecryptfs_threadfn, NULL,
+ "ecryptfs-kthread");
+ if (IS_ERR(ecryptfs_kthread)) {
+ rc = PTR_ERR(ecryptfs_kthread);
+ printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]"
+ "\n", __func__, rc);
+ }
+ wake_up_process(ecryptfs_kthread);
+ return rc;
+}
+
+void ecryptfs_destroy_kthread(void)
+{
+ struct ecryptfs_open_req tmp_req;
+ struct ecryptfs_open_req *req;
+
+ mutex_lock(&ecryptfs_kthread_ctl.mux);
+ ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
+ list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list,
+ kthread_ctl_list) {
+ mutex_lock(&req->mux);
+ req->flags |= ECRYPTFS_REQ_ZOMBIE;
+ wake_up_process(req->requesting_task);
+ mutex_unlock(&req->mux);
+ }
+ memset(&tmp_req, 0, sizeof(tmp_req));
+ tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
+ list_add_tail(&tmp_req.kthread_ctl_list,
+ &ecryptfs_kthread_ctl.req_list);
+ mutex_unlock(&ecryptfs_kthread_ctl.mux);
+ wake_up(&ecryptfs_kthread_ctl.wait);
+}
+
+/**
+ * ecryptfs_privileged_open
+ * @lower_file: Result of dentry_open by root on lower dentry
+ * @lower_dentry: Lower dentry for file to open
+ * @lower_mnt: Lower vfsmount for file to open
+ *
+ * This function gets a r/w file opened againt the lower dentry.
+ *
+ * Returns zero on success; non-zero otherwise
+ */
+int ecryptfs_privileged_open(struct file **lower_file,
+ struct dentry *lower_dentry,
+ struct vfsmount *lower_mnt)
+{
+ struct ecryptfs_open_req *req;
+ int rc = 0;
+
+ /* Corresponding dput() and mntput() are done when the
+ * persistent file is fput() when the eCryptfs inode is
+ * destroyed. */
+ dget(lower_dentry);
+ mntget(lower_mnt);
+ (*lower_file) = dentry_open(lower_dentry, lower_mnt,
+ (O_RDWR | O_LARGEFILE));
+ if (!IS_ERR(*lower_file))
+ goto out;
+ req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
+ if (!req) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ mutex_init(&req->mux);
+ req->lower_file = lower_file;
+ req->lower_dentry = lower_dentry;
+ req->lower_mnt = lower_mnt;
+ req->requesting_task = current;
+ req->flags = 0;
+ mutex_lock(&ecryptfs_kthread_ctl.mux);
+ mutex_lock(&req->mux);
+ if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
+ rc = -EIO;
+ mutex_unlock(&ecryptfs_kthread_ctl.mux);
+ printk(KERN_ERR "%s: We are in the middle of shutting down; "
+ "aborting privileged request to open lower file\n",
+ __func__);
+ goto out_free;
+ }
+ list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
+ mutex_unlock(&req->mux);
+ mutex_unlock(&ecryptfs_kthread_ctl.mux);
+ wake_up(&ecryptfs_kthread_ctl.wait);
+schedule:
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ mutex_lock(&req->mux);
+ if (req->flags == 0) {
+ mutex_unlock(&req->mux);
+ goto schedule;
+ }
+ set_current_state(TASK_RUNNING);
+ if (req->flags & ECRYPTFS_REQ_DROPPED
+ || req->flags & ECRYPTFS_REQ_ZOMBIE) {
+ rc = -EIO;
+ printk(KERN_WARNING "%s: Privileged open request dropped\n",
+ __func__);
+ goto out_unlock;
+ }
+ if (IS_ERR(*req->lower_file)) {
+ rc = PTR_ERR(*req->lower_file);
+ dget(lower_dentry);
+ mntget(lower_mnt);
+ (*lower_file) = dentry_open(lower_dentry, lower_mnt,
+ (O_RDONLY | O_LARGEFILE));
+ if (IS_ERR(*lower_file)) {
+ rc = PTR_ERR(*req->lower_file);
+ (*lower_file) = NULL;
+ printk(KERN_WARNING "%s: Error attempting privileged "
+ "open of lower file with either RW or RO "
+ "perms; rc = [%d]. Giving up.\n",
+ __func__, rc);
+ }
+ }
+out_unlock:
+ mutex_unlock(&req->mux);
+out_free:
+ kmem_cache_free(ecryptfs_open_req_cache, req);
+out:
+ return rc;
+}
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index d603631..23f272f 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -130,26 +130,12 @@ static int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry)
ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry);
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
- /* Corresponding dput() and mntput() are done when the
- * persistent file is fput() when the eCryptfs inode
- * is destroyed. */
- dget(lower_dentry);
- mntget(lower_mnt);
- inode_info->lower_file = dentry_open(lower_dentry,
- lower_mnt,
- (O_RDWR | O_LARGEFILE));
- if (IS_ERR(inode_info->lower_file)) {
- dget(lower_dentry);
- mntget(lower_mnt);
- inode_info->lower_file = dentry_open(lower_dentry,
- lower_mnt,
- (O_RDONLY
- | O_LARGEFILE));
- }
- if (IS_ERR(inode_info->lower_file)) {
+ rc = ecryptfs_privileged_open(&inode_info->lower_file,
+ lower_dentry, lower_mnt);
+ if (rc || IS_ERR(inode_info->lower_file)) {
printk(KERN_ERR "Error opening lower persistent file "
- "for lower_dentry [0x%p] and lower_mnt [0x%p]\n",
- lower_dentry, lower_mnt);
+ "for lower_dentry [0x%p] and lower_mnt [0x%p]; "
+ "rc = [%d]\n", lower_dentry, lower_mnt, rc);
rc = PTR_ERR(inode_info->lower_file);
inode_info->lower_file = NULL;
}
@@ -679,6 +665,11 @@ static struct ecryptfs_cache_info {
.name = "ecryptfs_key_tfm_cache",
.size = sizeof(struct ecryptfs_key_tfm),
},
+ {
+ .cache = &ecryptfs_open_req_cache,
+ .name = "ecryptfs_open_req_cache",
+ .size = sizeof(struct ecryptfs_open_req),
+ },
};
static void ecryptfs_free_kmem_caches(void)
@@ -795,11 +786,17 @@ static int __init ecryptfs_init(void)
printk(KERN_ERR "sysfs registration failed\n");
goto out_unregister_filesystem;
}
+ rc = ecryptfs_init_kthread();
+ if (rc) {
+ printk(KERN_ERR "%s: kthread initialization failed; "
+ "rc = [%d]\n", __func__);
+ goto out_do_sysfs_unregistration;
+ }
rc = ecryptfs_init_messaging(ecryptfs_transport);
if (rc) {
- ecryptfs_printk(KERN_ERR, "Failure occured while attempting to "
+ printk(KERN_ERR "Failure occured while attempting to "
"initialize the eCryptfs netlink socket\n");
- goto out_do_sysfs_unregistration;
+ goto out_destroy_kthread;
}
rc = ecryptfs_init_crypto();
if (rc) {
@@ -814,6 +811,8 @@ static int __init ecryptfs_init(void)
goto out;
out_release_messaging:
ecryptfs_release_messaging(ecryptfs_transport);
+out_destroy_kthread:
+ ecryptfs_destroy_kthread();
out_do_sysfs_unregistration:
do_sysfs_unregistration();
out_unregister_filesystem:
@@ -833,6 +832,7 @@ static void __exit ecryptfs_exit(void)
printk(KERN_ERR "Failure whilst attempting to destroy crypto; "
"rc = [%d]\n", rc);
ecryptfs_release_messaging(ecryptfs_transport);
+ ecryptfs_destroy_kthread();
do_sysfs_unregistration();
unregister_filesystem(&ecryptfs_fs_type);
ecryptfs_free_kmem_caches();
--
1.5.3.6
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH] eCryptfs: Privileged kthread for lower file opens
2008-05-20 21:46 [PATCH] eCryptfs: Privileged kthread for lower file opens Michael Halcrow
@ 2008-05-20 23:16 ` Andrea Righi
2008-05-21 15:14 ` Michael Halcrow
2008-05-21 22:59 ` [PATCH] eCryptfs: Privileged kthread for lower file opens Andrew Morton
1 sibling, 1 reply; 10+ messages in thread
From: Andrea Righi @ 2008-05-20 23:16 UTC (permalink / raw)
To: Michael Halcrow
Cc: Andrew Morton, LKML, Rusty Russell, David J. Kleikamp,
SERGE E. HALLYN
Michael Halcrow wrote:
[snip]
Hi,
2 comments on the patch.
> diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
> new file mode 100644
> index 0000000..dba7c67
> --- /dev/null
> +++ b/fs/ecryptfs/kthread.c
> @@ -0,0 +1,211 @@
> +/**
> + * eCryptfs: Linux filesystem encryption layer
> + *
> + * Copyright (C) 2008 International Business Machines Corp.
> + * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
> + * 02111-1307, USA.
> + */
> +
> +#include <linux/kthread.h>
> +#include <linux/freezer.h>
> +#include <linux/wait.h>
> +#include <linux/mount.h>
> +#include "ecryptfs_kernel.h"
> +
> +struct task_struct *ecryptfs_kthread;
> +struct ecryptfs_kthread_ctl ecryptfs_kthread_ctl;
> +
> +struct kmem_cache *ecryptfs_open_req_cache;
> +
> +/**
> + * ecryptfs_threadfn
> + * @ignored: ignored
> + *
> + * The eCryptfs kernel thread that has the responsibility of getting
> + * the lower persistent file with RW permissions.
> + *
> + * Returns zero on success; non-zero otherwise
> + */
> +static int ecryptfs_threadfn(void *ignored)
> +{
> + set_freezable();
> + while (1) {
> + struct ecryptfs_open_req *req;
> +
> + wait_event_freezable(
> + ecryptfs_kthread_ctl.wait,
> + !list_empty(&ecryptfs_kthread_ctl.req_list));
> + mutex_lock(&ecryptfs_kthread_ctl.mux);
> + if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + goto out;
> + }
> + while (!list_empty(&ecryptfs_kthread_ctl.req_list)) {
> + req = list_first_entry(&ecryptfs_kthread_ctl.req_list,
> + struct ecryptfs_open_req,
> + kthread_ctl_list);
> + BUG_ON(!req);
> + mutex_lock(&req->mux);
> + list_del(&req->kthread_ctl_list);
> + if (req->flags & ECRYPTFS_REQ_ZOMBIE)
> + mutex_unlock(&req->mux);
> + else {
> + dget(req->lower_dentry);
> + mntget(req->lower_mnt);
> + (*req->lower_file) = dentry_open(
> + req->lower_dentry, req->lower_mnt,
> + (O_RDWR | O_LARGEFILE));
> + req->flags |= ECRYPTFS_REQ_PROCESSED;
> + wake_up_process(req->requesting_task);
> + mutex_unlock(&req->mux);
> + }
Why not:
if (!(req->flags & ECRYPTFS_REQ_ZOMBIE))
dget(req->lower_dentry);
mntget(req->lower_mnt);
(*req->lower_file) = dentry_open(
req->lower_dentry, req->lower_mnt,
(O_RDWR | O_LARGEFILE));
req->flags |= ECRYPTFS_REQ_PROCESSED;
wake_up_process(req->requesting_task);
}
mutex_unlock(&req->mux);
> + }
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + }
> +out:
> + do_exit(0);
> +}
> +
> +int ecryptfs_init_kthread(void)
> +{
> + int rc = 0;
> +
> + mutex_init(&ecryptfs_kthread_ctl.mux);
> + init_waitqueue_head(&ecryptfs_kthread_ctl.wait);
> + INIT_LIST_HEAD(&ecryptfs_kthread_ctl.req_list);
> + ecryptfs_kthread = kthread_create(&ecryptfs_threadfn, NULL,
> + "ecryptfs-kthread");
> + if (IS_ERR(ecryptfs_kthread)) {
> + rc = PTR_ERR(ecryptfs_kthread);
> + printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]"
> + "\n", __func__, rc);
> + }
> + wake_up_process(ecryptfs_kthread);
> + return rc;
> +}
> +
> +void ecryptfs_destroy_kthread(void)
> +{
> + struct ecryptfs_open_req tmp_req;
> + struct ecryptfs_open_req *req;
> +
> + mutex_lock(&ecryptfs_kthread_ctl.mux);
> + ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
> + list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list,
> + kthread_ctl_list) {
> + mutex_lock(&req->mux);
> + req->flags |= ECRYPTFS_REQ_ZOMBIE;
> + wake_up_process(req->requesting_task);
> + mutex_unlock(&req->mux);
> + }
> + memset(&tmp_req, 0, sizeof(tmp_req));
> + tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> + list_add_tail(&tmp_req.kthread_ctl_list,
> + &ecryptfs_kthread_ctl.req_list);
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + wake_up(&ecryptfs_kthread_ctl.wait);
> +}
> +
> +/**
> + * ecryptfs_privileged_open
> + * @lower_file: Result of dentry_open by root on lower dentry
> + * @lower_dentry: Lower dentry for file to open
> + * @lower_mnt: Lower vfsmount for file to open
> + *
> + * This function gets a r/w file opened againt the lower dentry.
> + *
> + * Returns zero on success; non-zero otherwise
> + */
> +int ecryptfs_privileged_open(struct file **lower_file,
> + struct dentry *lower_dentry,
> + struct vfsmount *lower_mnt)
> +{
> + struct ecryptfs_open_req *req;
> + int rc = 0;
> +
> + /* Corresponding dput() and mntput() are done when the
> + * persistent file is fput() when the eCryptfs inode is
> + * destroyed. */
> + dget(lower_dentry);
> + mntget(lower_mnt);
> + (*lower_file) = dentry_open(lower_dentry, lower_mnt,
> + (O_RDWR | O_LARGEFILE));
> + if (!IS_ERR(*lower_file))
> + goto out;
> + req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
> + if (!req) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + mutex_init(&req->mux);
> + req->lower_file = lower_file;
> + req->lower_dentry = lower_dentry;
> + req->lower_mnt = lower_mnt;
> + req->requesting_task = current;
> + req->flags = 0;
> + mutex_lock(&ecryptfs_kthread_ctl.mux);
> + mutex_lock(&req->mux);
> + if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
> + rc = -EIO;
Isn't a mutex_unlock(&req->mux) missing here?
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + printk(KERN_ERR "%s: We are in the middle of shutting down; "
> + "aborting privileged request to open lower file\n",
> + __func__);
> + goto out_free;
> + }
> + list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
> + mutex_unlock(&req->mux);
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + wake_up(&ecryptfs_kthread_ctl.wait);
> +schedule:
> + set_current_state(TASK_INTERRUPTIBLE);
> + schedule();
> + mutex_lock(&req->mux);
> + if (req->flags == 0) {
> + mutex_unlock(&req->mux);
> + goto schedule;
> + }
> + set_current_state(TASK_RUNNING);
> + if (req->flags & ECRYPTFS_REQ_DROPPED
> + || req->flags & ECRYPTFS_REQ_ZOMBIE) {
> + rc = -EIO;
> + printk(KERN_WARNING "%s: Privileged open request dropped\n",
> + __func__);
> + goto out_unlock;
> + }
> + if (IS_ERR(*req->lower_file)) {
> + rc = PTR_ERR(*req->lower_file);
> + dget(lower_dentry);
> + mntget(lower_mnt);
> + (*lower_file) = dentry_open(lower_dentry, lower_mnt,
> + (O_RDONLY | O_LARGEFILE));
> + if (IS_ERR(*lower_file)) {
> + rc = PTR_ERR(*req->lower_file);
> + (*lower_file) = NULL;
> + printk(KERN_WARNING "%s: Error attempting privileged "
> + "open of lower file with either RW or RO "
> + "perms; rc = [%d]. Giving up.\n",
> + __func__, rc);
> + }
> + }
> +out_unlock:
> + mutex_unlock(&req->mux);
> +out_free:
> + kmem_cache_free(ecryptfs_open_req_cache, req);
> +out:
> + return rc;
> +}
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] eCryptfs: Privileged kthread for lower file opens
2008-05-20 23:16 ` Andrea Righi
@ 2008-05-21 15:14 ` Michael Halcrow
2008-05-21 15:39 ` [PATCH] eCryptfs: Remove useless lock Michael Halcrow
0 siblings, 1 reply; 10+ messages in thread
From: Michael Halcrow @ 2008-05-21 15:14 UTC (permalink / raw)
To: Andrea Righi
Cc: Andrew Morton, LKML, Rusty Russell, David J. Kleikamp,
SERGE E. HALLYN
On Wed, May 21, 2008 at 01:16:15AM +0200, Andrea Righi wrote:
> Michael Halcrow wrote:
>> + req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
>> + if (!req) {
>> + rc = -ENOMEM;
>> + goto out;
>> + }
>> + mutex_init(&req->mux);
>> + req->lower_file = lower_file;
>> + req->lower_dentry = lower_dentry;
>> + req->lower_mnt = lower_mnt;
>> + req->requesting_task = current;
>> + req->flags = 0;
>> + mutex_lock(&ecryptfs_kthread_ctl.mux);
>> + mutex_lock(&req->mux);
>> + if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
>> + rc = -EIO;
>
> Isn't a mutex_unlock(&req->mux) missing here?
Nobody else actually has a reference to req at this point, so the
unlock is not really necessary. It might not be a bad idea to jump to
out_unlock anyway just to avoid future bugs.
>> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
>> + printk(KERN_ERR "%s: We are in the middle of shutting down; "
>> + "aborting privileged request to open lower file\n",
>> + __func__);
>> + goto out_free;
>> + }
>> + list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
>> + mutex_unlock(&req->mux);
>> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
>> + wake_up(&ecryptfs_kthread_ctl.wait);
>> +schedule:
>> + set_current_state(TASK_INTERRUPTIBLE);
>> + schedule();
>> + mutex_lock(&req->mux);
>> + if (req->flags == 0) {
>> + mutex_unlock(&req->mux);
>> + goto schedule;
>> + }
>> + set_current_state(TASK_RUNNING);
>> + if (req->flags & ECRYPTFS_REQ_DROPPED
>> + || req->flags & ECRYPTFS_REQ_ZOMBIE) {
>> + rc = -EIO;
>> + printk(KERN_WARNING "%s: Privileged open request dropped\n",
>> + __func__);
>> + goto out_unlock;
>> + }
>> + if (IS_ERR(*req->lower_file)) {
>> + rc = PTR_ERR(*req->lower_file);
>> + dget(lower_dentry);
>> + mntget(lower_mnt);
>> + (*lower_file) = dentry_open(lower_dentry, lower_mnt,
>> + (O_RDONLY | O_LARGEFILE));
>> + if (IS_ERR(*lower_file)) {
>> + rc = PTR_ERR(*req->lower_file);
>> + (*lower_file) = NULL;
>> + printk(KERN_WARNING "%s: Error attempting privileged "
>> + "open of lower file with either RW or RO "
>> + "perms; rc = [%d]. Giving up.\n",
>> + __func__, rc);
>> + }
>> + }
>> +out_unlock:
>> + mutex_unlock(&req->mux);
>> +out_free:
>> + kmem_cache_free(ecryptfs_open_req_cache, req);
>> +out:
>> + return rc;
>> +}
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH] eCryptfs: Remove useless lock
2008-05-21 15:14 ` Michael Halcrow
@ 2008-05-21 15:39 ` Michael Halcrow
0 siblings, 0 replies; 10+ messages in thread
From: Michael Halcrow @ 2008-05-21 15:39 UTC (permalink / raw)
To: Andrea Righi
Cc: Andrew Morton, LKML, Rusty Russell, David J. Kleikamp,
SERGE E. HALLYN
On Wed, May 21, 2008 at 10:14:30AM -0500, Michael Halcrow wrote:
> On Wed, May 21, 2008 at 01:16:15AM +0200, Andrea Righi wrote:
> > Michael Halcrow wrote:
> >> + req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
> >> + if (!req) {
> >> + rc = -ENOMEM;
> >> + goto out;
> >> + }
> >> + mutex_init(&req->mux);
> >> + req->lower_file = lower_file;
> >> + req->lower_dentry = lower_dentry;
> >> + req->lower_mnt = lower_mnt;
> >> + req->requesting_task = current;
> >> + req->flags = 0;
> >> + mutex_lock(&ecryptfs_kthread_ctl.mux);
> >> + mutex_lock(&req->mux);
> >> + if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
> >> + rc = -EIO;
> >
> > Isn't a mutex_unlock(&req->mux) missing here?
>
> Nobody else actually has a reference to req at this point, so the
> unlock is not really necessary. It might not be a bad idea to jump to
> out_unlock anyway just to avoid future bugs.
>
> >> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> >> + printk(KERN_ERR "%s: We are in the middle of shutting down; "
> >> + "aborting privileged request to open lower file\n",
> >> + __func__);
> >> + goto out_free;
> >> + }
> >> + list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
> >> + mutex_unlock(&req->mux);
> >> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
Nobody can get a reference to req so long as &ecryptfs_kthread_ctl.mux
is held. Remove lock of req->mux for newly created req.
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>
---
fs/ecryptfs/kthread.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
index dba7c67..3d34327 100644
--- a/fs/ecryptfs/kthread.c
+++ b/fs/ecryptfs/kthread.c
@@ -158,7 +158,6 @@ int ecryptfs_privileged_open(struct file **lower_file,
req->requesting_task = current;
req->flags = 0;
mutex_lock(&ecryptfs_kthread_ctl.mux);
- mutex_lock(&req->mux);
if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
rc = -EIO;
mutex_unlock(&ecryptfs_kthread_ctl.mux);
@@ -168,7 +167,6 @@ int ecryptfs_privileged_open(struct file **lower_file,
goto out_free;
}
list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
- mutex_unlock(&req->mux);
mutex_unlock(&ecryptfs_kthread_ctl.mux);
wake_up(&ecryptfs_kthread_ctl.wait);
schedule:
--
1.5.3.6
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] eCryptfs: Privileged kthread for lower file opens
2008-05-20 21:46 [PATCH] eCryptfs: Privileged kthread for lower file opens Michael Halcrow
2008-05-20 23:16 ` Andrea Righi
@ 2008-05-21 22:59 ` Andrew Morton
2008-05-22 19:31 ` [PATCH] eCryptfs: Clean up kthread synchronization Michael Halcrow
1 sibling, 1 reply; 10+ messages in thread
From: Andrew Morton @ 2008-05-21 22:59 UTC (permalink / raw)
To: Michael Halcrow; +Cc: linux-kernel, prussell, shaggy, sergeh
On Tue, 20 May 2008 16:46:10 -0500
Michael Halcrow <mhalcrow@us.ibm.com> wrote:
> eCryptfs would really like to have read-write access to all files in
> the lower filesystem. Right now, the persistent lower file may be
> opened read-only if the attempt to open it read-write fails. One way
> to keep from having to do that is to have a privileged kthread that
> can open the lower persistent file on behalf of the user opening the
> eCryptfs file; this patch implements this functionality.
>
> This patch will properly allow a less-privileged user to open the
> eCryptfs file, followed by a more-privileged user opening the eCryptfs
> file, with the first user only being able to read and the second user
> being able to both read and write. eCryptfs currently does this wrong;
> it will wind up calling vfs_write() on a file that was opened
> read-only. This is fixed in this patch.
>
hm, interesting. A bit scary though.
> fs/ecryptfs/main.c | 42 ++++----
> 5 files changed, 271 insertions(+), 22 deletions(-)
> create mode 100644 fs/ecryptfs/kthread.c
>
> diff --git a/fs/ecryptfs/Makefile b/fs/ecryptfs/Makefile
> index 1e34a7f..b4755a8 100644
> --- a/fs/ecryptfs/Makefile
> +++ b/fs/ecryptfs/Makefile
> @@ -4,4 +4,4 @@
>
> obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o
>
> -ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o read_write.o crypto.o keystore.o messaging.o netlink.o miscdev.o debug.o
> +ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o read_write.o crypto.o keystore.o messaging.o netlink.o miscdev.o kthread.o debug.o
> diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
> index 951ee33..8ca910a 100644
> --- a/fs/ecryptfs/ecryptfs_kernel.h
> +++ b/fs/ecryptfs/ecryptfs_kernel.h
> @@ -559,6 +559,32 @@ extern struct kmem_cache *ecryptfs_key_record_cache;
> extern struct kmem_cache *ecryptfs_key_sig_cache;
> extern struct kmem_cache *ecryptfs_global_auth_tok_cache;
> extern struct kmem_cache *ecryptfs_key_tfm_cache;
> +extern struct kmem_cache *ecryptfs_open_req_cache;
> +
> +extern struct task_struct *ecryptfs_kthread;
This can be made static to fs/ecryptfs/kthread.c.
> +struct ecryptfs_open_req {
> +#define ECRYPTFS_REQ_PROCESSED 0x00000001
> +#define ECRYPTFS_REQ_DROPPED 0x00000002
> +#define ECRYPTFS_REQ_ZOMBIE 0x00000004
> + u32 flags;
> + struct file **lower_file;
> + struct dentry *lower_dentry;
> + struct vfsmount *lower_mnt;
> + struct task_struct *requesting_task;
> + struct mutex mux;
> + struct list_head kthread_ctl_list;
> +};
>
> +struct ecryptfs_kthread_ctl {
> +#define ECRYPTFS_KTHREAD_ZOMBIE 0x00000001
> + u32 flags;
> + struct mutex mux;
> + struct list_head req_list;
> + wait_queue_head_t wait;
> +};
>
> +extern struct ecryptfs_kthread_ctl ecryptfs_kthread_ctl;
I think this guy can be made static too. As a result of which, entire
data structure definitions could possibly be pushed down into
kthread.c?
> int ecryptfs_interpose(struct dentry *hidden_dentry,
> struct dentry *this_dentry, struct super_block *sb,
> @@ -692,5 +718,10 @@ void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx);
> int
> ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, uid_t euid,
> struct user_namespace *user_ns, struct pid *pid);
> +int ecryptfs_init_kthread(void);
> +void ecryptfs_destroy_kthread(void);
> +int ecryptfs_privileged_open(struct file **lower_file,
> + struct dentry *lower_dentry,
> + struct vfsmount *lower_mnt);
>
> #endif /* #ifndef ECRYPTFS_KERNEL_H */
> diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
> index 2258b8f..aced6f9 100644
> --- a/fs/ecryptfs/file.c
> +++ b/fs/ecryptfs/file.c
> @@ -191,6 +191,13 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
> | ECRYPTFS_ENCRYPTED);
> }
> mutex_unlock(&crypt_stat->cs_mutex);
> + if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_RDONLY)
> + && !(file->f_flags & O_RDONLY)) {
> + rc = -EPERM;
> + printk(KERN_WARNING "%s: Lower persistent file is RO; eCryptfs "
> + "file must thus be opened RO\n", __func__);
"hence" ;)
> + goto out;
> + }
> ecryptfs_set_file_lower(
> file, ecryptfs_inode_to_private(inode)->lower_file);
> if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
>
> ...
>
> +/**
> + * ecryptfs_threadfn
> + * @ignored: ignored
> + *
> + * The eCryptfs kernel thread that has the responsibility of getting
> + * the lower persistent file with RW permissions.
> + *
> + * Returns zero on success; non-zero otherwise
> + */
> +static int ecryptfs_threadfn(void *ignored)
> +{
> + set_freezable();
> + while (1) {
> + struct ecryptfs_open_req *req;
> +
> + wait_event_freezable(
> + ecryptfs_kthread_ctl.wait,
> + !list_empty(&ecryptfs_kthread_ctl.req_list));
> + mutex_lock(&ecryptfs_kthread_ctl.mux);
> + if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + goto out;
> + }
> + while (!list_empty(&ecryptfs_kthread_ctl.req_list)) {
> + req = list_first_entry(&ecryptfs_kthread_ctl.req_list,
> + struct ecryptfs_open_req,
> + kthread_ctl_list);
> + BUG_ON(!req);
> + mutex_lock(&req->mux);
> + list_del(&req->kthread_ctl_list);
> + if (req->flags & ECRYPTFS_REQ_ZOMBIE)
> + mutex_unlock(&req->mux);
> + else {
> + dget(req->lower_dentry);
> + mntget(req->lower_mnt);
> + (*req->lower_file) = dentry_open(
> + req->lower_dentry, req->lower_mnt,
> + (O_RDWR | O_LARGEFILE));
> + req->flags |= ECRYPTFS_REQ_PROCESSED;
> + wake_up_process(req->requesting_task);
> + mutex_unlock(&req->mux);
> + }
> + }
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + }
> +out:
> + do_exit(0);
A plain old `return 0;' will suffice here, and is more typical. I'm
moderately surprised that do_exit() is exported to modules, actually.
> +}
> +
> +int ecryptfs_init_kthread(void)
> +{
> + int rc = 0;
> +
> + mutex_init(&ecryptfs_kthread_ctl.mux);
> + init_waitqueue_head(&ecryptfs_kthread_ctl.wait);
> + INIT_LIST_HEAD(&ecryptfs_kthread_ctl.req_list);
> + ecryptfs_kthread = kthread_create(&ecryptfs_threadfn, NULL,
> + "ecryptfs-kthread");
> + if (IS_ERR(ecryptfs_kthread)) {
> + rc = PTR_ERR(ecryptfs_kthread);
> + printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]"
> + "\n", __func__, rc);
> + }
> + wake_up_process(ecryptfs_kthread);
> + return rc;
> +}
kthread_run() does the kthread_create() and the wake_up_process() for you.
> +void ecryptfs_destroy_kthread(void)
> +{
> + struct ecryptfs_open_req tmp_req;
> + struct ecryptfs_open_req *req;
> +
> + mutex_lock(&ecryptfs_kthread_ctl.mux);
> + ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
> + list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list,
> + kthread_ctl_list) {
> + mutex_lock(&req->mux);
> + req->flags |= ECRYPTFS_REQ_ZOMBIE;
> + wake_up_process(req->requesting_task);
> + mutex_unlock(&req->mux);
> + }
> + memset(&tmp_req, 0, sizeof(tmp_req));
> + tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> + list_add_tail(&tmp_req.kthread_ctl_list,
> + &ecryptfs_kthread_ctl.req_list);
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + wake_up(&ecryptfs_kthread_ctl.wait);
> +}
eh? We attach a local variable to a global list and then return? That
won't last very long.
> +/**
> + * ecryptfs_privileged_open
> + * @lower_file: Result of dentry_open by root on lower dentry
> + * @lower_dentry: Lower dentry for file to open
> + * @lower_mnt: Lower vfsmount for file to open
> + *
> + * This function gets a r/w file opened againt the lower dentry.
> + *
> + * Returns zero on success; non-zero otherwise
> + */
> +int ecryptfs_privileged_open(struct file **lower_file,
> + struct dentry *lower_dentry,
> + struct vfsmount *lower_mnt)
> +{
> + struct ecryptfs_open_req *req;
> + int rc = 0;
> +
> + /* Corresponding dput() and mntput() are done when the
> + * persistent file is fput() when the eCryptfs inode is
> + * destroyed. */
> + dget(lower_dentry);
> + mntget(lower_mnt);
> + (*lower_file) = dentry_open(lower_dentry, lower_mnt,
> + (O_RDWR | O_LARGEFILE));
> + if (!IS_ERR(*lower_file))
> + goto out;
> + req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
> + if (!req) {
> + rc = -ENOMEM;
> + goto out;
Did we just leak the dentry_open() result?
> + }
> + mutex_init(&req->mux);
> + req->lower_file = lower_file;
> + req->lower_dentry = lower_dentry;
> + req->lower_mnt = lower_mnt;
> + req->requesting_task = current;
> + req->flags = 0;
> + mutex_lock(&ecryptfs_kthread_ctl.mux);
> + mutex_lock(&req->mux);
> + if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
> + rc = -EIO;
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + printk(KERN_ERR "%s: We are in the middle of shutting down; "
> + "aborting privileged request to open lower file\n",
> + __func__);
> + goto out_free;
ditto.
> + }
> + list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
> + mutex_unlock(&req->mux);
> + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> + wake_up(&ecryptfs_kthread_ctl.wait);
> +schedule:
> + set_current_state(TASK_INTERRUPTIBLE);
> + schedule();
This looks racy to me. I assume we're waiting for ecryptfs_kthread to
wake us up. But that thread could have sent us its wakeup _before_ we
did the set_current_state+schedule. In which case we lost the wakeup
and we'll get stuck.
> + mutex_lock(&req->mux);
> + if (req->flags == 0) {
> + mutex_unlock(&req->mux);
> + goto schedule;
> + }
> + set_current_state(TASK_RUNNING);
This is unneeded. schedule() always returns in state TASK_RUNNING.
> + if (req->flags & ECRYPTFS_REQ_DROPPED
> + || req->flags & ECRYPTFS_REQ_ZOMBIE) {
> + rc = -EIO;
> + printk(KERN_WARNING "%s: Privileged open request dropped\n",
> + __func__);
> + goto out_unlock;
> + }
> + if (IS_ERR(*req->lower_file)) {
> + rc = PTR_ERR(*req->lower_file);
> + dget(lower_dentry);
> + mntget(lower_mnt);
> + (*lower_file) = dentry_open(lower_dentry, lower_mnt,
> + (O_RDONLY | O_LARGEFILE));
> + if (IS_ERR(*lower_file)) {
> + rc = PTR_ERR(*req->lower_file);
> + (*lower_file) = NULL;
> + printk(KERN_WARNING "%s: Error attempting privileged "
> + "open of lower file with either RW or RO "
> + "perms; rc = [%d]. Giving up.\n",
> + __func__, rc);
> + }
> + }
> +out_unlock:
> + mutex_unlock(&req->mux);
> +out_free:
> + kmem_cache_free(ecryptfs_open_req_cache, req);
> +out:
> + return rc;
> +}
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH] eCryptfs: Clean up kthread synchronization
2008-05-21 22:59 ` [PATCH] eCryptfs: Privileged kthread for lower file opens Andrew Morton
@ 2008-05-22 19:31 ` Michael Halcrow
2008-05-22 19:41 ` Andrew Morton
0 siblings, 1 reply; 10+ messages in thread
From: Michael Halcrow @ 2008-05-22 19:31 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel, prussell, shaggy, sergeh
On Wed, May 21, 2008 at 03:59:59PM -0700, Andrew Morton wrote:
> On Tue, 20 May 2008 16:46:10 -0500
> Michael Halcrow <mhalcrow@us.ibm.com> wrote:
> > +extern struct task_struct *ecryptfs_kthread;
>
> This can be made static to fs/ecryptfs/kthread.c.
Done.
> > +extern struct ecryptfs_kthread_ctl ecryptfs_kthread_ctl;
>
> I think this guy can be made static too. As a result of which,
> entire data structure definitions could possibly be pushed down into
> kthread.c?
struct ecryptfs_kthread_ctl can be moved there.
> > +static int ecryptfs_threadfn(void *ignored)
...
> > +out:
> > + do_exit(0);
>
> A plain old `return 0;' will suffice here, and is more typical. I'm
> moderately surprised that do_exit() is exported to modules,
> actually.
Done.
> > + ecryptfs_kthread = kthread_create(&ecryptfs_threadfn, NULL,
> > + "ecryptfs-kthread");
> > + if (IS_ERR(ecryptfs_kthread)) {
> > + rc = PTR_ERR(ecryptfs_kthread);
> > + printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]"
> > + "\n", __func__, rc);
> > + }
> > + wake_up_process(ecryptfs_kthread);
> > + return rc;
> > +}
>
> kthread_run() does the kthread_create() and the wake_up_process()
> for you.
Okay.
> > +void ecryptfs_destroy_kthread(void)
> > +{
> > + struct ecryptfs_open_req tmp_req;
> > + struct ecryptfs_open_req *req;
> > +
> > + mutex_lock(&ecryptfs_kthread_ctl.mux);
> > + ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
> > + list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list,
> > + kthread_ctl_list) {
> > + mutex_lock(&req->mux);
> > + req->flags |= ECRYPTFS_REQ_ZOMBIE;
> > + wake_up_process(req->requesting_task);
> > + mutex_unlock(&req->mux);
> > + }
> > + memset(&tmp_req, 0, sizeof(tmp_req));
> > + tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> > + list_add_tail(&tmp_req.kthread_ctl_list,
> > + &ecryptfs_kthread_ctl.req_list);
> > + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> > + wake_up(&ecryptfs_kthread_ctl.wait);
> > +}
>
> eh? We attach a local variable to a global list and then return?
> That won't last very long.
Adding this dummy entry to the list is just my own way of getting the
kthread to wake up and shut down. This actually works, albeit a little
ugly. The list and its contents get dropped on the floor at this point
because (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE). The
only consumer of this list (the kthread) checks for this flag
immediately after getting the mux, and if it is there, it just
exits. The only producer on this list (ecryptfs_privileged_open())
checks for this flag immediately after getting the mux and bows out if
it is set. In other words, once this flag is set, the list and its
contents become untouchable by anything other than
ecryptfs_destroy_kthread().
> > +/**
> > + * ecryptfs_privileged_open
> > + * @lower_file: Result of dentry_open by root on lower dentry
> > + * @lower_dentry: Lower dentry for file to open
> > + * @lower_mnt: Lower vfsmount for file to open
> > + *
> > + * This function gets a r/w file opened againt the lower dentry.
> > + *
> > + * Returns zero on success; non-zero otherwise
> > + */
> > +int ecryptfs_privileged_open(struct file **lower_file,
> > + struct dentry *lower_dentry,
> > + struct vfsmount *lower_mnt)
> > +{
> > + struct ecryptfs_open_req *req;
> > + int rc = 0;
> > +
> > + /* Corresponding dput() and mntput() are done when the
> > + * persistent file is fput() when the eCryptfs inode is
> > + * destroyed. */
> > + dget(lower_dentry);
> > + mntget(lower_mnt);
> > + (*lower_file) = dentry_open(lower_dentry, lower_mnt,
> > + (O_RDWR | O_LARGEFILE));
> > + if (!IS_ERR(*lower_file))
> > + goto out;
> > + req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
> > + if (!req) {
> > + rc = -ENOMEM;
> > + goto out;
>
> Did we just leak the dentry_open() result?
Nope; IS_ERR(*lower_file) is true.
> > + }
> > + mutex_init(&req->mux);
> > + req->lower_file = lower_file;
> > + req->lower_dentry = lower_dentry;
> > + req->lower_mnt = lower_mnt;
> > + req->requesting_task = current;
> > + req->flags = 0;
> > + mutex_lock(&ecryptfs_kthread_ctl.mux);
> > + mutex_lock(&req->mux);
> > + if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
> > + rc = -EIO;
> > + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> > + printk(KERN_ERR "%s: We are in the middle of shutting down; "
> > + "aborting privileged request to open lower file\n",
> > + __func__);
> > + goto out_free;
>
> ditto.
IS_ERR(*lower_file) is true here too.
> > + }
> > + list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
> > + mutex_unlock(&req->mux);
> > + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> > + wake_up(&ecryptfs_kthread_ctl.wait);
> > +schedule:
> > + set_current_state(TASK_INTERRUPTIBLE);
> > + schedule();
>
> This looks racy to me. I assume we're waiting for ecryptfs_kthread
> to wake us up. But that thread could have sent us its wakeup
> _before_ we did the set_current_state+schedule. In which case we
> lost the wakeup and we'll get stuck.
>
> > + mutex_lock(&req->mux);
> > + if (req->flags == 0) {
> > + mutex_unlock(&req->mux);
> > + goto schedule;
> > + }
> > + set_current_state(TASK_RUNNING);
>
> This is unneeded. schedule() always returns in state TASK_RUNNING.
How about just another wait queue then?
---
Replace the schedule/wake_up_process pair with a wait queue. Just
return from the kthread rather than call do_exit(). Use kthread_run()
rather than kthread_create(). Move struct ecryptfs_kthread_ctl
definition into kthread.c. Remove ecryptfs_kthread reference from
ecryptfs_kernel.h.
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>
---
fs/ecryptfs/ecryptfs_kernel.h | 14 +------------
fs/ecryptfs/file.c | 2 +-
fs/ecryptfs/kthread.c | 44 +++++++++++++++++++---------------------
fs/ecryptfs/main.c | 2 +-
4 files changed, 24 insertions(+), 38 deletions(-)
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 8ca910a..c1f4de1 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -561,8 +561,6 @@ extern struct kmem_cache *ecryptfs_global_auth_tok_cache;
extern struct kmem_cache *ecryptfs_key_tfm_cache;
extern struct kmem_cache *ecryptfs_open_req_cache;
-extern struct task_struct *ecryptfs_kthread;
-
struct ecryptfs_open_req {
#define ECRYPTFS_REQ_PROCESSED 0x00000001
#define ECRYPTFS_REQ_DROPPED 0x00000002
@@ -571,21 +569,11 @@ struct ecryptfs_open_req {
struct file **lower_file;
struct dentry *lower_dentry;
struct vfsmount *lower_mnt;
- struct task_struct *requesting_task;
+ wait_queue_head_t wait;
struct mutex mux;
struct list_head kthread_ctl_list;
};
-struct ecryptfs_kthread_ctl {
-#define ECRYPTFS_KTHREAD_ZOMBIE 0x00000001
- u32 flags;
- struct mutex mux;
- struct list_head req_list;
- wait_queue_head_t wait;
-};
-
-extern struct ecryptfs_kthread_ctl ecryptfs_kthread_ctl;
-
int ecryptfs_interpose(struct dentry *hidden_dentry,
struct dentry *this_dentry, struct super_block *sb,
int flag);
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index aced6f9..f2b3e56 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -195,7 +195,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
&& !(file->f_flags & O_RDONLY)) {
rc = -EPERM;
printk(KERN_WARNING "%s: Lower persistent file is RO; eCryptfs "
- "file must thus be opened RO\n", __func__);
+ "file must hence be opened RO\n", __func__);
goto out;
}
ecryptfs_set_file_lower(
diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
index 3d34327..4295296 100644
--- a/fs/ecryptfs/kthread.c
+++ b/fs/ecryptfs/kthread.c
@@ -26,11 +26,18 @@
#include <linux/mount.h>
#include "ecryptfs_kernel.h"
-struct task_struct *ecryptfs_kthread;
-struct ecryptfs_kthread_ctl ecryptfs_kthread_ctl;
-
struct kmem_cache *ecryptfs_open_req_cache;
+static struct ecryptfs_kthread_ctl {
+#define ECRYPTFS_KTHREAD_ZOMBIE 0x00000001
+ u32 flags;
+ struct mutex mux;
+ struct list_head req_list;
+ wait_queue_head_t wait;
+} ecryptfs_kthread_ctl;
+
+static struct task_struct *ecryptfs_kthread;
+
/**
* ecryptfs_threadfn
* @ignored: ignored
@@ -58,26 +65,23 @@ static int ecryptfs_threadfn(void *ignored)
req = list_first_entry(&ecryptfs_kthread_ctl.req_list,
struct ecryptfs_open_req,
kthread_ctl_list);
- BUG_ON(!req);
mutex_lock(&req->mux);
list_del(&req->kthread_ctl_list);
- if (req->flags & ECRYPTFS_REQ_ZOMBIE)
- mutex_unlock(&req->mux);
- else {
+ if (!(req->flags & ECRYPTFS_REQ_ZOMBIE)) {
dget(req->lower_dentry);
mntget(req->lower_mnt);
(*req->lower_file) = dentry_open(
req->lower_dentry, req->lower_mnt,
(O_RDWR | O_LARGEFILE));
req->flags |= ECRYPTFS_REQ_PROCESSED;
- wake_up_process(req->requesting_task);
- mutex_unlock(&req->mux);
}
+ wake_up(&req->wait);
+ mutex_unlock(&req->mux);
}
mutex_unlock(&ecryptfs_kthread_ctl.mux);
}
out:
- do_exit(0);
+ return 0;
}
int ecryptfs_init_kthread(void)
@@ -87,14 +91,13 @@ int ecryptfs_init_kthread(void)
mutex_init(&ecryptfs_kthread_ctl.mux);
init_waitqueue_head(&ecryptfs_kthread_ctl.wait);
INIT_LIST_HEAD(&ecryptfs_kthread_ctl.req_list);
- ecryptfs_kthread = kthread_create(&ecryptfs_threadfn, NULL,
- "ecryptfs-kthread");
+ ecryptfs_kthread = kthread_run(&ecryptfs_threadfn, NULL,
+ "ecryptfs-kthread");
if (IS_ERR(ecryptfs_kthread)) {
rc = PTR_ERR(ecryptfs_kthread);
printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]"
"\n", __func__, rc);
}
- wake_up_process(ecryptfs_kthread);
return rc;
}
@@ -109,11 +112,12 @@ void ecryptfs_destroy_kthread(void)
kthread_ctl_list) {
mutex_lock(&req->mux);
req->flags |= ECRYPTFS_REQ_ZOMBIE;
- wake_up_process(req->requesting_task);
+ wake_up(&req->wait);
mutex_unlock(&req->mux);
}
memset(&tmp_req, 0, sizeof(tmp_req));
tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
+ /* Both the list and dummy entry are entirely defunct at this point */
list_add_tail(&tmp_req.kthread_ctl_list,
&ecryptfs_kthread_ctl.req_list);
mutex_unlock(&ecryptfs_kthread_ctl.mux);
@@ -155,7 +159,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
req->lower_file = lower_file;
req->lower_dentry = lower_dentry;
req->lower_mnt = lower_mnt;
- req->requesting_task = current;
+ init_waitqueue_head(&req->wait);
req->flags = 0;
mutex_lock(&ecryptfs_kthread_ctl.mux);
if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
@@ -169,15 +173,9 @@ int ecryptfs_privileged_open(struct file **lower_file,
list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
mutex_unlock(&ecryptfs_kthread_ctl.mux);
wake_up(&ecryptfs_kthread_ctl.wait);
-schedule:
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
+ wait_event(req->wait, (req->flags != 0));
mutex_lock(&req->mux);
- if (req->flags == 0) {
- mutex_unlock(&req->mux);
- goto schedule;
- }
- set_current_state(TASK_RUNNING);
+ BUG_ON(req->flags == 0);
if (req->flags & ECRYPTFS_REQ_DROPPED
|| req->flags & ECRYPTFS_REQ_ZOMBIE) {
rc = -EIO;
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 23f272f..f36ab2f 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -789,7 +789,7 @@ static int __init ecryptfs_init(void)
rc = ecryptfs_init_kthread();
if (rc) {
printk(KERN_ERR "%s: kthread initialization failed; "
- "rc = [%d]\n", __func__);
+ "rc = [%d]\n", __func__, rc);
goto out_do_sysfs_unregistration;
}
rc = ecryptfs_init_messaging(ecryptfs_transport);
--
1.5.3.6
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH] eCryptfs: Clean up kthread synchronization
2008-05-22 19:31 ` [PATCH] eCryptfs: Clean up kthread synchronization Michael Halcrow
@ 2008-05-22 19:41 ` Andrew Morton
2008-05-22 21:16 ` Michael Halcrow
0 siblings, 1 reply; 10+ messages in thread
From: Andrew Morton @ 2008-05-22 19:41 UTC (permalink / raw)
To: Michael Halcrow; +Cc: linux-kernel, prussell, shaggy, sergeh
On Thu, 22 May 2008 14:31:55 -0500
Michael Halcrow <mhalcrow@us.ibm.com> wrote:
> > > +void ecryptfs_destroy_kthread(void)
> > > +{
> > > + struct ecryptfs_open_req tmp_req;
> > > + struct ecryptfs_open_req *req;
> > > +
> > > + mutex_lock(&ecryptfs_kthread_ctl.mux);
> > > + ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
> > > + list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list,
> > > + kthread_ctl_list) {
> > > + mutex_lock(&req->mux);
> > > + req->flags |= ECRYPTFS_REQ_ZOMBIE;
> > > + wake_up_process(req->requesting_task);
> > > + mutex_unlock(&req->mux);
> > > + }
> > > + memset(&tmp_req, 0, sizeof(tmp_req));
> > > + tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> > > + list_add_tail(&tmp_req.kthread_ctl_list,
> > > + &ecryptfs_kthread_ctl.req_list);
> > > + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> > > + wake_up(&ecryptfs_kthread_ctl.wait);
> > > +}
> >
> > eh? We attach a local variable to a global list and then return?
> > That won't last very long.
>
> Adding this dummy entry to the list is just my own way of getting the
> kthread to wake up and shut down. This actually works, albeit a little
> ugly. The list and its contents get dropped on the floor at this point
> because (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE). The
> only consumer of this list (the kthread) checks for this flag
> immediately after getting the mux, and if it is there, it just
> exits. The only producer on this list (ecryptfs_privileged_open())
> checks for this flag immediately after getting the mux and bows out if
> it is set. In other words, once this flag is set, the list and its
> contents become untouchable by anything other than
> ecryptfs_destroy_kthread().
Unconvinced.
As soon as ecryptfs_destroy_kthread() returns, tmp_req is destroyed.
But it remains on ecryptfs_kthread_ctl.req_list.
> memset(&tmp_req, 0, sizeof(tmp_req));
> tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> list_add_tail(&tmp_req.kthread_ctl_list,
> &ecryptfs_kthread_ctl.req_list);
> mutex_unlock(&ecryptfs_kthread_ctl.mux);
> wake_up(&ecryptfs_kthread_ctl.wait);
-> it's dead.
> }
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] eCryptfs: Clean up kthread synchronization
2008-05-22 19:41 ` Andrew Morton
@ 2008-05-22 21:16 ` Michael Halcrow
2008-05-22 21:44 ` [PATCH] eCryptfs: Use kthread_should_stop() instead of the non-empty list hack Michael Halcrow
2008-05-22 22:03 ` [PATCH] eCryptfs: Clean up kthread synchronization Andrew Morton
0 siblings, 2 replies; 10+ messages in thread
From: Michael Halcrow @ 2008-05-22 21:16 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel, prussell, shaggy, sergeh
On Thu, May 22, 2008 at 12:41:42PM -0700, Andrew Morton wrote:
> On Thu, 22 May 2008 14:31:55 -0500
> Michael Halcrow <mhalcrow@us.ibm.com> wrote:
>
> > > > +void ecryptfs_destroy_kthread(void)
> > > > +{
> > > > + struct ecryptfs_open_req tmp_req;
> > > > + struct ecryptfs_open_req *req;
> > > > +
> > > > + mutex_lock(&ecryptfs_kthread_ctl.mux);
> > > > + ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
> > > > + list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list,
> > > > + kthread_ctl_list) {
> > > > + mutex_lock(&req->mux);
> > > > + req->flags |= ECRYPTFS_REQ_ZOMBIE;
> > > > + wake_up_process(req->requesting_task);
> > > > + mutex_unlock(&req->mux);
> > > > + }
> > > > + memset(&tmp_req, 0, sizeof(tmp_req));
> > > > + tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> > > > + list_add_tail(&tmp_req.kthread_ctl_list,
> > > > + &ecryptfs_kthread_ctl.req_list);
> > > > + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> > > > + wake_up(&ecryptfs_kthread_ctl.wait);
> > > > +}
> > >
> > > eh? We attach a local variable to a global list and then return?
> > > That won't last very long.
> >
> > Adding this dummy entry to the list is just my own way of getting the
> > kthread to wake up and shut down. This actually works, albeit a little
> > ugly. The list and its contents get dropped on the floor at this point
> > because (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE). The
> > only consumer of this list (the kthread) checks for this flag
> > immediately after getting the mux, and if it is there, it just
> > exits. The only producer on this list (ecryptfs_privileged_open())
> > checks for this flag immediately after getting the mux and bows out if
> > it is set. In other words, once this flag is set, the list and its
> > contents become untouchable by anything other than
> > ecryptfs_destroy_kthread().
>
> Unconvinced.
>
> As soon as ecryptfs_destroy_kthread() returns, tmp_req is destroyed.
> But it remains on ecryptfs_kthread_ctl.req_list.
I intend for ecryptfs_kthread_ctl.req_list to be irrelevant once
ecryptfs_destroy_kthread() grabs the mux and sets
(ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE); nobody will
ever do anything with that list any more. The state of the list --
including the dangling list pointer -- simply does not matter any
more.
> > memset(&tmp_req, 0, sizeof(tmp_req));
> > tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> > list_add_tail(&tmp_req.kthread_ctl_list,
> > &ecryptfs_kthread_ctl.req_list);
> > mutex_unlock(&ecryptfs_kthread_ctl.mux);
> > wake_up(&ecryptfs_kthread_ctl.wait);
>
> -> it's dead.
This is what gets woken up:
---
wait_event_freezable(
ecryptfs_kthread_ctl.wait,
!list_empty(&ecryptfs_kthread_ctl.req_list));
mutex_lock(&ecryptfs_kthread_ctl.mux);
if (ecryptfs_kthread_ctl.flags &
ECRYPTFS_KTHREAD_ZOMBIE) {
mutex_unlock(&ecryptfs_kthread_ctl.mux);
goto out;
}
---
So the flag causes the kthread to just quit, ignoring the list
altogether.
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH] eCryptfs: Use kthread_should_stop() instead of the non-empty list hack
2008-05-22 21:16 ` Michael Halcrow
@ 2008-05-22 21:44 ` Michael Halcrow
2008-05-22 22:03 ` [PATCH] eCryptfs: Clean up kthread synchronization Andrew Morton
1 sibling, 0 replies; 10+ messages in thread
From: Michael Halcrow @ 2008-05-22 21:44 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel, prussell, shaggy, sergeh
On Thu, May 22, 2008 at 04:16:25PM -0500, Michael Halcrow wrote:
> This is what gets woken up:
>
> ---
> wait_event_freezable(
> ecryptfs_kthread_ctl.wait,
> !list_empty(&ecryptfs_kthread_ctl.req_list));
> mutex_lock(&ecryptfs_kthread_ctl.mux);
> if (ecryptfs_kthread_ctl.flags &
> ECRYPTFS_KTHREAD_ZOMBIE) {
> mutex_unlock(&ecryptfs_kthread_ctl.mux);
> goto out;
> }
> ---
>
> So the flag causes the kthread to just quit, ignoring the list
> altogether.
The code for shutting down the kthread is ugly. This patch uses the
kthread_stop() call to shut down the kthread instead.
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>
---
fs/ecryptfs/kthread.c | 10 +++-------
1 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
index 4295296..c440c6b 100644
--- a/fs/ecryptfs/kthread.c
+++ b/fs/ecryptfs/kthread.c
@@ -55,7 +55,8 @@ static int ecryptfs_threadfn(void *ignored)
wait_event_freezable(
ecryptfs_kthread_ctl.wait,
- !list_empty(&ecryptfs_kthread_ctl.req_list));
+ (!list_empty(&ecryptfs_kthread_ctl.req_list)
+ || kthread_should_stop()));
mutex_lock(&ecryptfs_kthread_ctl.mux);
if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
mutex_unlock(&ecryptfs_kthread_ctl.mux);
@@ -103,7 +104,6 @@ int ecryptfs_init_kthread(void)
void ecryptfs_destroy_kthread(void)
{
- struct ecryptfs_open_req tmp_req;
struct ecryptfs_open_req *req;
mutex_lock(&ecryptfs_kthread_ctl.mux);
@@ -115,12 +115,8 @@ void ecryptfs_destroy_kthread(void)
wake_up(&req->wait);
mutex_unlock(&req->mux);
}
- memset(&tmp_req, 0, sizeof(tmp_req));
- tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
- /* Both the list and dummy entry are entirely defunct at this point */
- list_add_tail(&tmp_req.kthread_ctl_list,
- &ecryptfs_kthread_ctl.req_list);
mutex_unlock(&ecryptfs_kthread_ctl.mux);
+ kthread_stop(ecryptfs_kthread);
wake_up(&ecryptfs_kthread_ctl.wait);
}
--
1.5.3.6
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH] eCryptfs: Clean up kthread synchronization
2008-05-22 21:16 ` Michael Halcrow
2008-05-22 21:44 ` [PATCH] eCryptfs: Use kthread_should_stop() instead of the non-empty list hack Michael Halcrow
@ 2008-05-22 22:03 ` Andrew Morton
1 sibling, 0 replies; 10+ messages in thread
From: Andrew Morton @ 2008-05-22 22:03 UTC (permalink / raw)
To: Michael Halcrow; +Cc: linux-kernel, prussell, shaggy, sergeh
On Thu, 22 May 2008 16:16:25 -0500
Michael Halcrow <mhalcrow@us.ibm.com> wrote:
> On Thu, May 22, 2008 at 12:41:42PM -0700, Andrew Morton wrote:
> > On Thu, 22 May 2008 14:31:55 -0500
> > Michael Halcrow <mhalcrow@us.ibm.com> wrote:
> >
> > > > > +void ecryptfs_destroy_kthread(void)
> > > > > +{
> > > > > + struct ecryptfs_open_req tmp_req;
> > > > > + struct ecryptfs_open_req *req;
> > > > > +
> > > > > + mutex_lock(&ecryptfs_kthread_ctl.mux);
> > > > > + ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
> > > > > + list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list,
> > > > > + kthread_ctl_list) {
> > > > > + mutex_lock(&req->mux);
> > > > > + req->flags |= ECRYPTFS_REQ_ZOMBIE;
> > > > > + wake_up_process(req->requesting_task);
> > > > > + mutex_unlock(&req->mux);
> > > > > + }
> > > > > + memset(&tmp_req, 0, sizeof(tmp_req));
> > > > > + tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> > > > > + list_add_tail(&tmp_req.kthread_ctl_list,
> > > > > + &ecryptfs_kthread_ctl.req_list);
> > > > > + mutex_unlock(&ecryptfs_kthread_ctl.mux);
> > > > > + wake_up(&ecryptfs_kthread_ctl.wait);
> > > > > +}
> > > >
> > > > eh? We attach a local variable to a global list and then return?
> > > > That won't last very long.
> > >
> > > Adding this dummy entry to the list is just my own way of getting the
> > > kthread to wake up and shut down. This actually works, albeit a little
> > > ugly. The list and its contents get dropped on the floor at this point
> > > because (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE). The
> > > only consumer of this list (the kthread) checks for this flag
> > > immediately after getting the mux, and if it is there, it just
> > > exits. The only producer on this list (ecryptfs_privileged_open())
> > > checks for this flag immediately after getting the mux and bows out if
> > > it is set. In other words, once this flag is set, the list and its
> > > contents become untouchable by anything other than
> > > ecryptfs_destroy_kthread().
> >
> > Unconvinced.
> >
> > As soon as ecryptfs_destroy_kthread() returns, tmp_req is destroyed.
> > But it remains on ecryptfs_kthread_ctl.req_list.
>
> I intend for ecryptfs_kthread_ctl.req_list to be irrelevant once
> ecryptfs_destroy_kthread() grabs the mux and sets
> (ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE); nobody will
> ever do anything with that list any more. The state of the list --
> including the dangling list pointer -- simply does not matter any
> more.
OK.
> > > memset(&tmp_req, 0, sizeof(tmp_req));
> > > tmp_req.flags = ECRYPTFS_REQ_ZOMBIE;
> > > list_add_tail(&tmp_req.kthread_ctl_list,
> > > &ecryptfs_kthread_ctl.req_list);
> > > mutex_unlock(&ecryptfs_kthread_ctl.mux);
> > > wake_up(&ecryptfs_kthread_ctl.wait);
> >
> > -> it's dead.
So the above horridly-wrong code is not needed at all, and tmp_req can
be removed.
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2008-05-22 22:04 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-20 21:46 [PATCH] eCryptfs: Privileged kthread for lower file opens Michael Halcrow
2008-05-20 23:16 ` Andrea Righi
2008-05-21 15:14 ` Michael Halcrow
2008-05-21 15:39 ` [PATCH] eCryptfs: Remove useless lock Michael Halcrow
2008-05-21 22:59 ` [PATCH] eCryptfs: Privileged kthread for lower file opens Andrew Morton
2008-05-22 19:31 ` [PATCH] eCryptfs: Clean up kthread synchronization Michael Halcrow
2008-05-22 19:41 ` Andrew Morton
2008-05-22 21:16 ` Michael Halcrow
2008-05-22 21:44 ` [PATCH] eCryptfs: Use kthread_should_stop() instead of the non-empty list hack Michael Halcrow
2008-05-22 22:03 ` [PATCH] eCryptfs: Clean up kthread synchronization Andrew Morton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox