Commit a6dd0b0b authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] (5/5) more BKL shifting

old_inode is locked by vfs_link().
parent c89d22eb
...@@ -55,7 +55,7 @@ locking rules: ...@@ -55,7 +55,7 @@ locking rules:
BKL i_sem(inode) BKL i_sem(inode)
lookup: no yes lookup: no yes
create: no yes create: no yes
link: no yes link: no yes (both)
mknod: no yes mknod: no yes
symlink: no yes symlink: no yes
mkdir: no yes mkdir: no yes
......
...@@ -14,7 +14,13 @@ locks victim and calls the method. ...@@ -14,7 +14,13 @@ locks victim and calls the method.
the parent, finds source and target, if target already exists - locks it the parent, finds source and target, if target already exists - locks it
and then calls the method. and then calls the method.
5) cross-directory rename. The trickiest in the whole bunch. Locking 5) link creation. Locking rules:
* lock parent
* check that source is not a directory
* lock source
* call the method.
6) cross-directory rename. The trickiest in the whole bunch. Locking
rules: rules:
* lock the filesystem * lock the filesystem
* lock parents in "ancestors first" order. * lock parents in "ancestors first" order.
...@@ -39,7 +45,7 @@ objects - A < B iff A is an ancestor of B. ...@@ -39,7 +45,7 @@ objects - A < B iff A is an ancestor of B.
That ordering can change. However, the following is true: That ordering can change. However, the following is true:
(1) if operation different from cross-directory rename holds lock on A and (1) if object removal or non-cross-directory rename holds lock on A and
attempts to acquire lock on B, A will remain the parent of B until we attempts to acquire lock on B, A will remain the parent of B until we
acquire the lock on B. (Proof: only cross-directory rename can change acquire the lock on B. (Proof: only cross-directory rename can change
the parent of object and it would have to lock the parent). the parent of object and it would have to lock the parent).
...@@ -49,12 +55,20 @@ objects - A < B iff A is an ancestor of B. ...@@ -49,12 +55,20 @@ objects - A < B iff A is an ancestor of B.
renames will be blocked on filesystem lock and we don't start changing renames will be blocked on filesystem lock and we don't start changing
the order until we had acquired all locks). the order until we had acquired all locks).
(3) any operation holds at most one lock on non-directory object and
that lock is acquired after all other locks. (Proof: see descriptions
of operations).
Now consider the minimal deadlock. Each process is blocked on Now consider the minimal deadlock. Each process is blocked on
attempt to acquire some lock and already holds at least one lock. Let's attempt to acquire some lock and already holds at least one lock. Let's
consider the set of contended locks. First of all, filesystem lock is consider the set of contended locks. First of all, filesystem lock is
not contended, since any process blocked on it is not holding any locks. not contended, since any process blocked on it is not holding any locks.
Thus all processes are blocked on ->i_sem. Thus all processes are blocked on ->i_sem.
Non-directory objects are not contended due to (3). Thus link
creation can't be a part of deadlock - it can't be blocked on source
and it means that it doesn't hold any locks.
Any contended object is either held by cross-directory rename or Any contended object is either held by cross-directory rename or
has a child that is also contended. Indeed, suppose that it is held by has a child that is also contended. Indeed, suppose that it is held by
operation other than cross-directory rename. Then the lock this operation operation other than cross-directory rename. Then the lock this operation
......
...@@ -92,3 +92,9 @@ exactly what needs to be protected. ...@@ -92,3 +92,9 @@ exactly what needs to be protected.
check for ->link() target not being a directory is done by callers. Feel check for ->link() target not being a directory is done by callers. Feel
free to drop it... free to drop it...
---
[informational]
->link() callers hold ->i_sem on the object we are linking to. Some of your
problems might be over...
...@@ -159,9 +159,7 @@ static int pcihpfs_link (struct dentry *old_dentry, struct inode *dir, ...@@ -159,9 +159,7 @@ static int pcihpfs_link (struct dentry *old_dentry, struct inode *dir,
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
lock_kernel();
inode->i_nlink++; inode->i_nlink++;
unlock_kernel();
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
dget(dentry); dget(dentry);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
...@@ -198,9 +196,7 @@ static int pcihpfs_unlink (struct inode *dir, struct dentry *dentry) ...@@ -198,9 +196,7 @@ static int pcihpfs_unlink (struct inode *dir, struct dentry *dentry)
if (pcihpfs_empty(dentry)) { if (pcihpfs_empty(dentry)) {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
lock_kernel();
inode->i_nlink--; inode->i_nlink--;
unlock_kernel();
dput(dentry); dput(dentry);
error = 0; error = 0;
} }
......
...@@ -216,9 +216,7 @@ static int usbfs_link (struct dentry *old_dentry, struct inode *dir, ...@@ -216,9 +216,7 @@ static int usbfs_link (struct dentry *old_dentry, struct inode *dir,
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
lock_kernel();
inode->i_nlink++; inode->i_nlink++;
unlock_kernel();
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
dget(dentry); dget(dentry);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
...@@ -255,9 +253,7 @@ static int usbfs_unlink (struct inode *dir, struct dentry *dentry) ...@@ -255,9 +253,7 @@ static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
if (usbfs_empty(dentry)) { if (usbfs_empty(dentry)) {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
lock_kernel();
inode->i_nlink--; inode->i_nlink--;
unlock_kernel();
dput(dentry); dput(dentry);
error = 0; error = 0;
} }
......
...@@ -39,17 +39,13 @@ ...@@ -39,17 +39,13 @@
static inline void ext2_inc_count(struct inode *inode) static inline void ext2_inc_count(struct inode *inode)
{ {
lock_kernel();
inode->i_nlink++; inode->i_nlink++;
unlock_kernel();
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
static inline void ext2_dec_count(struct inode *inode) static inline void ext2_dec_count(struct inode *inode)
{ {
lock_kernel();
inode->i_nlink--; inode->i_nlink--;
unlock_kernel();
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
...@@ -168,15 +164,11 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir, ...@@ -168,15 +164,11 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir,
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
lock_kernel(); if (inode->i_nlink >= EXT2_LINK_MAX)
if (inode->i_nlink >= EXT2_LINK_MAX) {
unlock_kernel();
return -EMLINK; return -EMLINK;
}
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
ext2_inc_count(inode); ext2_inc_count(inode);
unlock_kernel();
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
return ext2_add_nondir(dentry, inode); return ext2_add_nondir(dentry, inode);
...@@ -299,11 +291,8 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, ...@@ -299,11 +291,8 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
ext2_inc_count(old_inode); ext2_inc_count(old_inode);
ext2_set_link(new_dir, new_de, new_page, old_inode); ext2_set_link(new_dir, new_de, new_page, old_inode);
new_inode->i_ctime = CURRENT_TIME; new_inode->i_ctime = CURRENT_TIME;
if (dir_de) { if (dir_de)
lock_kernel();
new_inode->i_nlink--; new_inode->i_nlink--;
unlock_kernel();
}
ext2_dec_count(new_inode); ext2_dec_count(new_inode);
} else { } else {
if (dir_de) { if (dir_de) {
......
...@@ -11,17 +11,13 @@ ...@@ -11,17 +11,13 @@
static inline void inc_count(struct inode *inode) static inline void inc_count(struct inode *inode)
{ {
lock_kernel();
inode->i_nlink++; inode->i_nlink++;
unlock_kernel();
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
static inline void dec_count(struct inode *inode) static inline void dec_count(struct inode *inode)
{ {
lock_kernel();
inode->i_nlink--; inode->i_nlink--;
unlock_kernel();
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
...@@ -136,15 +132,11 @@ static int minix_link(struct dentry * old_dentry, struct inode * dir, ...@@ -136,15 +132,11 @@ static int minix_link(struct dentry * old_dentry, struct inode * dir,
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
lock_kernel(); if (inode->i_nlink >= inode->i_sb->u.minix_sb.s_link_max)
if (inode->i_nlink >= inode->i_sb->u.minix_sb.s_link_max) {
unlock_kernel();
return -EMLINK; return -EMLINK;
}
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
inc_count(inode); inc_count(inode);
unlock_kernel();
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
return add_nondir(dentry, inode); return add_nondir(dentry, inode);
} }
...@@ -265,11 +257,8 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry, ...@@ -265,11 +257,8 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
inc_count(old_inode); inc_count(old_inode);
minix_set_link(new_de, new_page, old_inode); minix_set_link(new_de, new_page, old_inode);
new_inode->i_ctime = CURRENT_TIME; new_inode->i_ctime = CURRENT_TIME;
if (dir_de) { if (dir_de)
lock_kernel();
new_inode->i_nlink--; new_inode->i_nlink--;
unlock_kernel();
}
dec_count(new_inode); dec_count(new_inode);
} else { } else {
if (dir_de) { if (dir_de) {
......
...@@ -1614,8 +1614,10 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de ...@@ -1614,8 +1614,10 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if (S_ISDIR(old_dentry->d_inode->i_mode)) if (S_ISDIR(old_dentry->d_inode->i_mode))
return -EPERM; return -EPERM;
down(&old_dentry->d_inode->i_sem);
DQUOT_INIT(dir); DQUOT_INIT(dir);
error = dir->i_op->link(old_dentry, dir, new_dentry); error = dir->i_op->link(old_dentry, dir, new_dentry);
up(&old_dentry->d_inode->i_sem);
if (!error) if (!error)
inode_dir_notify(dir, DN_CREATE); inode_dir_notify(dir, DN_CREATE);
return error; return error;
......
...@@ -166,9 +166,7 @@ static int ramfs_link(struct dentry *old_dentry, struct inode * dir, struct dent ...@@ -166,9 +166,7 @@ static int ramfs_link(struct dentry *old_dentry, struct inode * dir, struct dent
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
lock_kernel();
inode->i_nlink++; inode->i_nlink++;
unlock_kernel();
atomic_inc(&inode->i_count); /* New dentry reference */ atomic_inc(&inode->i_count); /* New dentry reference */
dget(dentry); /* Extra pinning count for the created dentry */ dget(dentry); /* Extra pinning count for the created dentry */
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
...@@ -219,9 +217,7 @@ static int ramfs_unlink(struct inode * dir, struct dentry *dentry) ...@@ -219,9 +217,7 @@ static int ramfs_unlink(struct inode * dir, struct dentry *dentry)
if (ramfs_empty(dentry)) { if (ramfs_empty(dentry)) {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
lock_kernel();
inode->i_nlink--; inode->i_nlink--;
unlock_kernel();
dput(dentry); /* Undo the count from "create" - this does all the work */ dput(dentry); /* Undo the count from "create" - this does all the work */
retval = 0; retval = 0;
} }
......
...@@ -19,17 +19,13 @@ ...@@ -19,17 +19,13 @@
static inline void inc_count(struct inode *inode) static inline void inc_count(struct inode *inode)
{ {
lock_kernel();
inode->i_nlink++; inode->i_nlink++;
unlock_kernel();
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
static inline void dec_count(struct inode *inode) static inline void dec_count(struct inode *inode)
{ {
lock_kernel();
inode->i_nlink--; inode->i_nlink--;
unlock_kernel();
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
...@@ -142,15 +138,11 @@ static int sysv_link(struct dentry * old_dentry, struct inode * dir, ...@@ -142,15 +138,11 @@ static int sysv_link(struct dentry * old_dentry, struct inode * dir,
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
lock_kernel(); if (inode->i_nlink >= inode->i_sb->sv_link_max)
if (inode->i_nlink >= inode->i_sb->sv_link_max) {
unlock_kernel();
return -EMLINK; return -EMLINK;
}
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
inc_count(inode); inc_count(inode);
unlock_kernel();
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
return add_nondir(dentry, inode); return add_nondir(dentry, inode);
...@@ -273,11 +265,8 @@ static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, ...@@ -273,11 +265,8 @@ static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
inc_count(old_inode); inc_count(old_inode);
sysv_set_link(new_de, new_page, old_inode); sysv_set_link(new_de, new_page, old_inode);
new_inode->i_ctime = CURRENT_TIME; new_inode->i_ctime = CURRENT_TIME;
if (dir_de) { if (dir_de)
lock_kernel();
new_inode->i_nlink--; new_inode->i_nlink--;
unlock_kernel();
}
dec_count(new_inode); dec_count(new_inode);
} else { } else {
if (dir_de) { if (dir_de) {
......
...@@ -1022,9 +1022,7 @@ static int shmem_link(struct dentry *old_dentry, struct inode * dir, struct dent ...@@ -1022,9 +1022,7 @@ static int shmem_link(struct dentry *old_dentry, struct inode * dir, struct dent
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
lock_kernel();
inode->i_nlink++; inode->i_nlink++;
unlock_kernel();
atomic_inc(&inode->i_count); /* New dentry reference */ atomic_inc(&inode->i_count); /* New dentry reference */
dget(dentry); /* Extra pinning count for the created dentry */ dget(dentry); /* Extra pinning count for the created dentry */
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
...@@ -1068,9 +1066,7 @@ static int shmem_unlink(struct inode * dir, struct dentry *dentry) ...@@ -1068,9 +1066,7 @@ static int shmem_unlink(struct inode * dir, struct dentry *dentry)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
lock_kernel();
inode->i_nlink--; inode->i_nlink--;
unlock_kernel();
dput(dentry); /* Undo the count from "create" - this does all the work */ dput(dentry); /* Undo the count from "create" - this does all the work */
return 0; return 0;
} }
......
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