int err = 0;
dentry = file->f_path.dentry;
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
sb = dentry->d_sb;
/*
int bindex = 0, bstart = 0, bend = 0;
int size;
- unionfs_read_lock(inode->i_sb);
+ unionfs_read_lock(inode->i_sb, UNIONFS_SMUTEX_PARENT);
file->private_data =
kzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL);
err = -ESTALE;
goto out;
}
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
bstart = fbstart(file) = dbstart(dentry);
bend = fbend(file) = dbend(dentry);
int bindex, bstart, bend;
int fgen, err = 0;
- unionfs_read_lock(sb);
+ unionfs_read_lock(sb, UNIONFS_SMUTEX_PARENT);
/*
* Yes, we have to revalidate this file even if it's being released.
* This is important for open-but-unlinked files, as well as mmap
bstart = fbstart(file);
bend = fbend(file);
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
for (bindex = bstart; bindex <= bend; bindex++) {
lower_file = unionfs_lower_file_idx(file, bindex);
struct vfsmount *mnt;
dentry = file->f_path.dentry;
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
orig_bstart = dbstart(dentry);
orig_bend = dbend(dentry);
err = unionfs_partial_lookup(dentry);
{
long err;
- unionfs_read_lock(file->f_path.dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
struct dentry *dentry = file->f_path.dentry;
int bindex, bstart, bend;
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
/* find the parent directory dentry in unionfs */
parent_dentry = child_dentry->d_parent;
- unionfs_lock_dentry(parent_dentry);
+ dget(parent_dentry);
/* find out the lower_parent_dentry in the given branch */
lower_parent_dentry =
begin:
/* get lower parent dir in the current branch */
lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);
- unionfs_unlock_dentry(parent_dentry);
+ dput(parent_dentry);
/* init the values to lookup */
childname = child_dentry->d_name.name;
/* cleanup any leftover locks from the do/while loop above */
if (IS_ERR(lower_dentry))
while (count)
- unionfs_unlock_dentry(path[count--]);
+ dput(path[count--]);
kfree(path);
return lower_dentry;
}
* to child order.
*/
for (i = 0; i < chain_len; i++) {
- unionfs_lock_dentry(chain[i]);
+ unionfs_lock_dentry(chain[i], UNIONFS_DMUTEX_REVAL+i);
saved_bstart = dbstart(chain[i]);
saved_bend = dbend(chain[i]);
sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
{
int err;
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
err = __unionfs_d_revalidate_chain(dentry, nd, false);
if (likely(err > 0)) { /* true==1: dentry is valid */
unionfs_check_dentry(dentry);
{
int bindex, bstart, bend;
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
unionfs_check_dentry(dentry);
/* this could be a negative dentry, so check first */
int namelen;
unsigned int d_type;
- unionfs_read_lock(file->f_path.dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, false);
if (unlikely(err))
* odf cache dir calls partial lookup which expects a locked
* dentry
*/
- unionfs_lock_dentry(file->f_path.dentry);
+ unionfs_lock_dentry(file->f_path.dentry, UNIONFS_DMUTEX_CHILD);
err = odf_cache_dir(file->f_path.dentry, odf_cache,
&file->f_path.dentry->d_inode->i_mtime);
unionfs_unlock_dentry(file->f_path.dentry);
{
loff_t err;
- unionfs_read_lock(file->f_path.dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, false);
if (unlikely(err))
}
if (!UNIONFS_D(result))
- err = new_dentry_private_data(result);
+ err = new_dentry_private_data(result, UNIONFS_DMUTEX_CHILD);
if (err) {
printk(KERN_INFO "unionfs_export_iget: Not enough memory to "
"allocate dentry's private data\n");
}
/* Macros for locking a dentry. */
-static inline void unionfs_lock_dentry(struct dentry *d)
+enum unionfs_dentry_lock_class {
+ UNIONFS_DMUTEX_NORMAL,
+ UNIONFS_DMUTEX_ROOT,
+ UNIONFS_DMUTEX_PARENT,
+ UNIONFS_DMUTEX_CHILD,
+ UNIONFS_DMUTEX_WHITEOUT,
+ UNIONFS_DMUTEX_REVAL, /* for file/dentry revalidate */
+};
+
+static inline void unionfs_lock_dentry(struct dentry *d,
+ unsigned int subclass)
{
BUG_ON(!d);
- mutex_lock(&UNIONFS_D(d)->lock);
+ mutex_lock_nested(&UNIONFS_D(d)->lock, subclass);
}
static inline void unionfs_unlock_dentry(struct dentry *d)
bool willwrite;
struct file *lower_file;
- unionfs_read_lock(file->f_path.dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
/* This might be deferred to mmap's writepage */
willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
struct inode *lower_inode, *inode;
int err = -EINVAL;
- unionfs_read_lock(file->f_path.dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
goto out;
struct inode *lower_inode, *inode;
int err = 0;
- unionfs_read_lock(file->f_path.dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
goto out;
int valid = 0;
struct nameidata lower_nd;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
valid = __unionfs_d_revalidate_chain(dentry, nd, false);
/*
struct path path_save;
struct dentry *ret;
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ if (dentry != dentry->d_parent)
+ unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
/* save the dentry & vfsmnt from namei */
if (nd) {
unionfs_check_nd(nd);
if (!IS_ERR(ret))
unionfs_unlock_dentry(dentry);
+
+ if (dentry != dentry->d_parent)
+ unionfs_unlock_dentry(dentry->d_parent);
unionfs_read_unlock(dentry->d_sb);
return ret;
struct dentry *lower_new_dentry = NULL;
struct dentry *lower_dir_dentry = NULL;
- unionfs_read_lock(old_dentry->d_sb);
+ unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
unionfs_double_lock_dentry(new_dentry, old_dentry);
if (unlikely(!__unionfs_d_revalidate_chain(old_dentry, NULL, false))) {
umode_t mode;
int bstart;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(dentry->d_inode &&
!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
char *name = NULL;
int i, bend, bindex;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(dentry->d_inode &&
!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
struct dentry *lower_parent_dentry = NULL;
int bstart;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(dentry->d_inode &&
!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
int err;
struct dentry *lower_dentry;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd,
void *cookie)
{
- unionfs_read_lock(dentry->d_sb);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
- unionfs_lock_dentry(dentry);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, nd, false)))
printk(KERN_ERR
"unionfs: put_link failed to revalidate dentry\n");
int bstart, bend, bindex;
loff_t size;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
struct dentry *first_dentry = NULL;
struct dentry *first_lower_dentry = NULL;
struct vfsmount *first_lower_mnt = NULL;
- int locked_parent = 0;
const char *name;
int namelen;
struct nameidata new_nd;
case INTERPOSE_PARTIAL:
break;
case INTERPOSE_LOOKUP:
- err = new_dentry_private_data(dentry);
+ err = new_dentry_private_data(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(err))
goto out;
break;
parent_dentry = dget_parent(dentry);
/* We never partial lookup the root directory. */
- if (parent_dentry != dentry) {
- unionfs_lock_dentry(parent_dentry);
- locked_parent = 1;
- } else {
+ if (parent_dentry == dentry) {
dput(parent_dentry);
parent_dentry = NULL;
goto out;
UNIONFS_I(dentry->d_inode)->stale = 1;
}
}
- if (locked_parent)
- unionfs_unlock_dentry(parent_dentry);
dput(parent_dentry);
if (err && (lookupmode == INTERPOSE_LOOKUP))
unionfs_unlock_dentry(dentry);
/*
* This is a utility function that fills in a unionfs dentry.
+ * Caller must lock this dentry with unionfs_lock_dentry.
*
* Returns: 0 (ok), or -ERRNO if an error occurred.
*/
}
/* allocate new dentry private data */
-int new_dentry_private_data(struct dentry *dentry)
+int new_dentry_private_data(struct dentry *dentry, int subclass)
{
struct unionfs_dentry_info *info = UNIONFS_D(dentry);
return -ENOMEM;
mutex_init(&info->lock);
- mutex_lock(&info->lock);
+ mutex_lock_nested(&info->lock, subclass);
info->lower_paths = NULL;
/* link the upper and lower dentries */
sb->s_root->d_fsdata = NULL;
- err = new_dentry_private_data(sb->s_root);
+ err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT);
if (unlikely(err))
goto out_freedpd;
char *page_data = NULL;
mode_t orig_mode;
- unionfs_read_lock(file->f_path.dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, false);
if (unlikely(err))
goto out;
BUG_ON(file == NULL);
- unionfs_read_lock(file->f_path.dentry->d_sb);
+ unionfs_read_lock(file->f_path.dentry->d_sb, UNIONFS_SMUTEX_PARENT);
err = unionfs_file_revalidate(file, true);
if (unlikely(err))
goto out;
lower_dentry = create_parents(dentry->d_parent->d_inode,
dentry, dentry->d_name.name,
branch_dst);
- unionfs_lock_dentry(dentry->d_parent);
+ unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
if (!lower_dentry || IS_ERR(lower_dentry)) {
if (IS_ERR(lower_dentry))
int err = 0;
struct dentry *lower;
- unionfs_read_lock(old_dentry->d_sb);
+ unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
unionfs_double_lock_dentry(old_dentry, new_dentry);
if (unlikely(!__unionfs_d_revalidate_chain(old_dentry, NULL, false))) {
verify_locked(dentry);
- unionfs_lock_dentry(dentry->d_parent);
+ unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_CHILD);
lower_parent = unionfs_lower_dentry_idx(dentry->d_parent, bindex);
unionfs_unlock_dentry(dentry->d_parent);
sb = dentry->d_sb;
- unionfs_read_lock(sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
sb = mnt->mnt_sb;
- unionfs_read_lock(sb);
+ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);
bstart = sbstart(sb);
bend = sbend(sb);
int bindex, bstart, bend;
int perms;
- unionfs_read_lock(sb);
+ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);
- unionfs_lock_dentry(sb->s_root);
+ unionfs_lock_dentry(sb->s_root, UNIONFS_DMUTEX_CHILD);
tmp_page = (char *) __get_free_page(GFP_KERNEL);
if (unlikely(!tmp_page)) {
#define MAXRDCOOKIE (0xfff)
#define DIREOF (0xffffff)
-static inline void unionfs_read_lock(struct super_block *sb)
+/* Macros for locking a super_block. */
+enum unionfs_super_lock_class {
+ UNIONFS_SMUTEX_NORMAL,
+ UNIONFS_SMUTEX_PARENT, /* when locking on behalf of file */
+ UNIONFS_SMUTEX_CHILD, /* when locking on behalf of dentry */
+};
+static inline void unionfs_read_lock(struct super_block *sb, int subclass)
{
if (UNIONFS_SB(sb)->write_lock_owner &&
UNIONFS_SB(sb)->write_lock_owner == current->pid)
return;
- down_read(&UNIONFS_SB(sb)->rwsem);
+ down_read_nested(&UNIONFS_SB(sb)->rwsem, subclass);
}
static inline void unionfs_read_unlock(struct super_block *sb)
{
static inline void unionfs_double_lock_dentry(struct dentry *d1,
struct dentry *d2)
{
- if (d2 < d1) {
- struct dentry *tmp = d1;
- d1 = d2;
- d2 = tmp;
+ BUG_ON(d1 == d2);
+ if (d1 < d2) {
+ unionfs_lock_dentry(d1, UNIONFS_DMUTEX_PARENT);
+ unionfs_lock_dentry(d2, UNIONFS_DMUTEX_CHILD);
+ } else {
+ unionfs_lock_dentry(d2, UNIONFS_DMUTEX_PARENT);
+ unionfs_lock_dentry(d1, UNIONFS_DMUTEX_CHILD);
}
- unionfs_lock_dentry(d1);
- unionfs_lock_dentry(d2);
}
-extern int new_dentry_private_data(struct dentry *dentry);
+extern int new_dentry_private_data(struct dentry *dentry, int subclass);
extern void free_dentry_private_data(struct dentry *dentry);
extern void update_bstart(struct dentry *dentry);
extern int init_lower_nd(struct nameidata *nd, unsigned int flags);
extern int check_branch(struct nameidata *nd);
extern int parse_branch_mode(const char *name, int *perms);
-/*
- * These two functions are here because it is kind of daft to copy and paste
- * the contents of the two functions to 32+ places in unionfs
- */
+/* locking helpers */
static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+ struct dentry *dir = dget(dentry->d_parent);
+ mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
+ return dir;
+}
+static inline struct dentry *lock_parent_wh(struct dentry *dentry)
{
struct dentry *dir = dget(dentry->d_parent);
- mutex_lock(&dir->d_inode->i_mutex);
+ mutex_lock_nested(&dir->d_inode->i_mutex, UNIONFS_DMUTEX_WHITEOUT);
return dir;
}
{
int err = 0;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
struct unionfs_dir_state *namelist = NULL;
int dstart, dend;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
dstart = dbstart(dentry);
dend = dbend(dentry);
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
struct dentry *lower_dentry = NULL;
int err = -EOPNOTSUPP;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;
int err = -EOPNOTSUPP;
char *encoded_list = NULL;
- unionfs_read_lock(dentry->d_sb);
- unionfs_lock_dentry(dentry);
+ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
err = -ESTALE;