Tetsuo Handa wrote: > I got a freeze (without lockdep warning) with a small out-of-tree patch > applied on 2.6.38. It seems to me that the deadlock occurred when running > pivot_root(). I guess it is spinning at spin_lock() below. It seems to me that this is a deadlock between rename_lock and vfsmount_lock. CPU 0: __d_path() CPU 1: pivot_root() write_seqlock(&rename_lock); br_write_lock(vfsmount_lock); br_read_lock(vfsmount_lock); seq = read_seqbegin(&rename_lock); __d_path() calls prepend_path() with rename_lock lock held for write. char *__d_path(const struct path *path, struct path *root, char *buf, int buflen) { char *res = buf + buflen; int error; prepend(&res, &buflen, "\0", 1); write_seqlock(&rename_lock); error = prepend_path(path, root, &res, &buflen); write_sequnlock(&rename_lock); if (error) return ERR_PTR(error); return res; } prepend_path() tries to hold vfsmount_lock for read. static int prepend_path(const struct path *path, struct path *root, char **buffer, int *buflen) { struct dentry *dentry = path->dentry; struct vfsmount *vfsmnt = path->mnt; bool slash = false; int error = 0; br_read_lock(vfsmount_lock); while (dentry != root->dentry || vfsmnt != root->mnt) { struct dentry * parent; if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { /* Global root? */ if (vfsmnt->mnt_parent == vfsmnt) { goto global_root; } dentry = vfsmnt->mnt_mountpoint; vfsmnt = vfsmnt->mnt_parent; continue; } parent = dentry->d_parent; prefetch(parent); spin_lock(&dentry->d_lock); error = prepend_name(buffer, buflen, &dentry->d_name); spin_unlock(&dentry->d_lock); if (!error) error = prepend(buffer, buflen, "/", 1); if (error) break; slash = true; dentry = parent; } out: if (!error && !slash) error = prepend(buffer, buflen, "/", 1); br_read_unlock(vfsmount_lock); return error; global_root: /* * Filesystems needing to implement special "root names" * should do so with ->d_dname() */ if (IS_ROOT(dentry) && (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) { WARN(1, "Root dentry has weird name <%.*s>\n", (int) dentry->d_name.len, dentry->d_name.name); } root->mnt = vfsmnt; root->dentry = dentry; goto out; } pivot_root() calls is_subdir() with vfsmount_lock lock held for write. SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, const char __user *, put_old) { (...snipped...) br_write_lock(vfsmount_lock); if (tmp != new.mnt) { for (;;) { if (tmp->mnt_parent == tmp) goto out3; /* already mounted on put_old */ if (tmp->mnt_parent == new.mnt) break; tmp = tmp->mnt_parent; } if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) goto out3; } else if (!is_subdir(old.dentry, new.dentry)) goto out3; detach_mnt(new.mnt, &parent_path); detach_mnt(root.mnt, &root_parent); /* mount old root on put_old */ attach_mnt(root.mnt, &old); /* mount new_root on / */ attach_mnt(new.mnt, &root_parent); touch_mnt_namespace(current->nsproxy->mnt_ns); br_write_unlock(vfsmount_lock); (...snipped...) } is_subdir() tries to hold rename_lock for read. int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry) { int result; unsigned seq; if (new_dentry == old_dentry) return 1; do { /* for restarting inner loop in case of seq retry */ seq = read_seqbegin(&rename_lock); /* * Need rcu_readlock to protect against the d_parent trashing * due to d_move */ rcu_read_lock(); if (d_ancestor(old_dentry, new_dentry)) result = 1; else result = 0; rcu_read_unlock(); } while (read_seqretry(&rename_lock, seq)); return result; } is_subdir() tries to hold rename_lock lock, but it is held by __d_path(). prepend_path() tries to hold vfsmount_lock lock but is held by pivot_root(). Regards. -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html