Commit da977b2c authored by Eric Van Hensbergen's avatar Eric Van Hensbergen Committed by Linus Torvalds

[PATCH] 9p: fix segfault caused by race condition in meta-data operations

Running dbench multithreaded exposed a race condition where fid structures
were removed while in use.  This patch adds semaphores to meta-data operations
to protect the fid structure.  Some cleanup of error-case handling in the
inode operations is also included.
Signed-off-by: default avatarEric Van Hensbergen <ericvh@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ff76e1df
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <asm/semaphore.h>
#include "debug.h" #include "debug.h"
#include "v9fs.h" #include "v9fs.h"
...@@ -84,6 +85,7 @@ struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid) ...@@ -84,6 +85,7 @@ struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid)
new->iounit = 0; new->iounit = 0;
new->rdir_pos = 0; new->rdir_pos = 0;
new->rdir_fcall = NULL; new->rdir_fcall = NULL;
init_MUTEX(&new->lock);
INIT_LIST_HEAD(&new->list); INIT_LIST_HEAD(&new->list);
return new; return new;
...@@ -102,11 +104,11 @@ void v9fs_fid_destroy(struct v9fs_fid *fid) ...@@ -102,11 +104,11 @@ void v9fs_fid_destroy(struct v9fs_fid *fid)
} }
/** /**
* v9fs_fid_lookup - retrieve the right fid from a particular dentry * v9fs_fid_lookup - return a locked fid from a dentry
* @dentry: dentry to look for fid in * @dentry: dentry to look for fid in
* @type: intent of lookup (operation or traversal)
* *
* find a fid in the dentry * find a fid in the dentry, obtain its semaphore and return a reference to it.
* code calling lookup is responsible for releasing lock
* *
* TODO: only match fids that have the same uid as current user * TODO: only match fids that have the same uid as current user
* *
...@@ -124,7 +126,68 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) ...@@ -124,7 +126,68 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry)
if (!return_fid) { if (!return_fid) {
dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n");
return_fid = ERR_PTR(-EBADF);
} }
if(down_interruptible(&return_fid->lock))
return ERR_PTR(-EINTR);
return return_fid; return return_fid;
} }
/**
* v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it
* @dentry: dentry to look for fid in
*
* find a fid in the dentry and then clone to a new private fid
*
* TODO: only match fids that have the same uid as current user
*
*/
struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry)
{
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF);
struct v9fs_fcall *fcall = NULL;
int fid, err;
base_fid = v9fs_fid_lookup(dentry);
if(IS_ERR(base_fid))
return base_fid;
if(base_fid) { /* clone fid */
fid = v9fs_get_idpool(&v9ses->fidpool);
if (fid < 0) {
eprintk(KERN_WARNING, "newfid fails!\n");
new_fid = ERR_PTR(-ENOSPC);
goto Release_Fid;
}
err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall);
if (err < 0) {
dprintk(DEBUG_ERROR, "clone walk didn't work\n");
v9fs_put_idpool(fid, &v9ses->fidpool);
new_fid = ERR_PTR(err);
goto Free_Fcall;
}
new_fid = v9fs_fid_create(v9ses, fid);
if (new_fid == NULL) {
dprintk(DEBUG_ERROR, "out of memory\n");
new_fid = ERR_PTR(-ENOMEM);
}
Free_Fcall:
kfree(fcall);
}
Release_Fid:
up(&base_fid->lock);
return new_fid;
}
void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid)
{
v9fs_t_clunk(v9ses, fid->fid);
v9fs_fid_destroy(fid);
}
...@@ -30,6 +30,8 @@ struct v9fs_fid { ...@@ -30,6 +30,8 @@ struct v9fs_fid {
struct list_head list; /* list of fids associated with a dentry */ struct list_head list; /* list of fids associated with a dentry */
struct list_head active; /* XXX - debug */ struct list_head active; /* XXX - debug */
struct semaphore lock;
u32 fid; u32 fid;
unsigned char fidopen; /* set when fid is opened */ unsigned char fidopen; /* set when fid is opened */
unsigned char fidclunked; /* set when fid has already been clunked */ unsigned char fidclunked; /* set when fid has already been clunked */
...@@ -55,3 +57,6 @@ struct v9fs_fid *v9fs_fid_get_created(struct dentry *); ...@@ -55,3 +57,6 @@ struct v9fs_fid *v9fs_fid_get_created(struct dentry *);
void v9fs_fid_destroy(struct v9fs_fid *fid); void v9fs_fid_destroy(struct v9fs_fid *fid);
struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid); struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid);
int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry); int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry);
struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry);
void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid);
...@@ -55,53 +55,22 @@ int v9fs_file_open(struct inode *inode, struct file *file) ...@@ -55,53 +55,22 @@ int v9fs_file_open(struct inode *inode, struct file *file)
struct v9fs_fid *vfid; struct v9fs_fid *vfid;
struct v9fs_fcall *fcall = NULL; struct v9fs_fcall *fcall = NULL;
int omode; int omode;
int fid = V9FS_NOFID;
int err; int err;
dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file); dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file);
vfid = v9fs_fid_lookup(file->f_path.dentry); vfid = v9fs_fid_clone(file->f_path.dentry);
if (!vfid) { if (IS_ERR(vfid))
dprintk(DEBUG_ERROR, "Couldn't resolve fid from dentry\n"); return PTR_ERR(vfid);
return -EBADF;
}
fid = v9fs_get_idpool(&v9ses->fidpool);
if (fid < 0) {
eprintk(KERN_WARNING, "newfid fails!\n");
return -ENOSPC;
}
err = v9fs_t_walk(v9ses, vfid->fid, fid, NULL, &fcall);
if (err < 0) {
dprintk(DEBUG_ERROR, "rewalk didn't work\n");
if (fcall && fcall->id == RWALK)
goto clunk_fid;
else {
v9fs_put_idpool(fid, &v9ses->fidpool);
goto free_fcall;
}
}
kfree(fcall);
/* TODO: do special things for O_EXCL, O_NOFOLLOW, O_SYNC */
/* translate open mode appropriately */
omode = v9fs_uflags2omode(file->f_flags); omode = v9fs_uflags2omode(file->f_flags);
err = v9fs_t_open(v9ses, fid, omode, &fcall); err = v9fs_t_open(v9ses, vfid->fid, omode, &fcall);
if (err < 0) { if (err < 0) {
PRINT_FCALL_ERROR("open failed", fcall); PRINT_FCALL_ERROR("open failed", fcall);
goto clunk_fid; goto Clunk_Fid;
}
vfid = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL);
if (vfid == NULL) {
dprintk(DEBUG_ERROR, "out of memory\n");
err = -ENOMEM;
goto clunk_fid;
} }
file->private_data = vfid; file->private_data = vfid;
vfid->fid = fid;
vfid->fidopen = 1; vfid->fidopen = 1;
vfid->fidclunked = 0; vfid->fidclunked = 0;
vfid->iounit = fcall->params.ropen.iounit; vfid->iounit = fcall->params.ropen.iounit;
...@@ -112,10 +81,8 @@ int v9fs_file_open(struct inode *inode, struct file *file) ...@@ -112,10 +81,8 @@ int v9fs_file_open(struct inode *inode, struct file *file)
return 0; return 0;
clunk_fid: Clunk_Fid:
v9fs_t_clunk(v9ses, fid); v9fs_fid_clunk(v9ses, vfid);
free_fcall:
kfree(fcall); kfree(fcall);
return err; return err;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment