aboutsummaryrefslogtreecommitdiffstats
path: root/fs/libfs.c
diff options
context:
space:
mode:
authorNick Piggin <npiggin@kernel.dk>2011-01-07 01:49:34 -0500
committerNick Piggin <npiggin@kernel.dk>2011-01-07 01:50:21 -0500
commit2fd6b7f50797f2e993eea59e0a0b8c6399c811dc (patch)
treece33b94b34844c09103836cf4cfa4364b742f217 /fs/libfs.c
parentda5029563a0a026c64821b09e8e7b4fd81d3fe1b (diff)
fs: dcache scale subdirs
Protect d_subdirs and d_child with d_lock, except in filesystems that aren't using dcache_lock for these anyway (eg. using i_mutex). Note: if we change the locking rule in future so that ->d_child protection is provided only with ->d_parent->d_lock, it may allow us to reduce some locking. But it would be an exception to an otherwise regular locking scheme, so we'd have to see some good results. Probably not worthwhile. Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs/libfs.c')
-rw-r--r--fs/libfs.c24
1 files changed, 18 insertions, 6 deletions
diff --git a/fs/libfs.c b/fs/libfs.c
index 433e7139c23a..cc4794914b52 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -81,7 +81,8 @@ int dcache_dir_close(struct inode *inode, struct file *file)
81 81
82loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) 82loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
83{ 83{
84 mutex_lock(&file->f_path.dentry->d_inode->i_mutex); 84 struct dentry *dentry = file->f_path.dentry;
85 mutex_lock(&dentry->d_inode->i_mutex);
85 switch (origin) { 86 switch (origin) {
86 case 1: 87 case 1:
87 offset += file->f_pos; 88 offset += file->f_pos;
@@ -89,7 +90,7 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
89 if (offset >= 0) 90 if (offset >= 0)
90 break; 91 break;
91 default: 92 default:
92 mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); 93 mutex_unlock(&dentry->d_inode->i_mutex);
93 return -EINVAL; 94 return -EINVAL;
94 } 95 }
95 if (offset != file->f_pos) { 96 if (offset != file->f_pos) {
@@ -100,22 +101,25 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
100 loff_t n = file->f_pos - 2; 101 loff_t n = file->f_pos - 2;
101 102
102 spin_lock(&dcache_lock); 103 spin_lock(&dcache_lock);
104 spin_lock(&dentry->d_lock);
105 /* d_lock not required for cursor */
103 list_del(&cursor->d_u.d_child); 106 list_del(&cursor->d_u.d_child);
104 p = file->f_path.dentry->d_subdirs.next; 107 p = dentry->d_subdirs.next;
105 while (n && p != &file->f_path.dentry->d_subdirs) { 108 while (n && p != &dentry->d_subdirs) {
106 struct dentry *next; 109 struct dentry *next;
107 next = list_entry(p, struct dentry, d_u.d_child); 110 next = list_entry(p, struct dentry, d_u.d_child);
108 spin_lock(&next->d_lock); 111 spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
109 if (simple_positive(next)) 112 if (simple_positive(next))
110 n--; 113 n--;
111 spin_unlock(&next->d_lock); 114 spin_unlock(&next->d_lock);
112 p = p->next; 115 p = p->next;
113 } 116 }
114 list_add_tail(&cursor->d_u.d_child, p); 117 list_add_tail(&cursor->d_u.d_child, p);
118 spin_unlock(&dentry->d_lock);
115 spin_unlock(&dcache_lock); 119 spin_unlock(&dcache_lock);
116 } 120 }
117 } 121 }
118 mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); 122 mutex_unlock(&dentry->d_inode->i_mutex);
119 return offset; 123 return offset;
120} 124}
121 125
@@ -156,6 +160,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
156 /* fallthrough */ 160 /* fallthrough */
157 default: 161 default:
158 spin_lock(&dcache_lock); 162 spin_lock(&dcache_lock);
163 spin_lock(&dentry->d_lock);
159 if (filp->f_pos == 2) 164 if (filp->f_pos == 2)
160 list_move(q, &dentry->d_subdirs); 165 list_move(q, &dentry->d_subdirs);
161 166
@@ -169,6 +174,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
169 } 174 }
170 175
171 spin_unlock(&next->d_lock); 176 spin_unlock(&next->d_lock);
177 spin_unlock(&dentry->d_lock);
172 spin_unlock(&dcache_lock); 178 spin_unlock(&dcache_lock);
173 if (filldir(dirent, next->d_name.name, 179 if (filldir(dirent, next->d_name.name,
174 next->d_name.len, filp->f_pos, 180 next->d_name.len, filp->f_pos,
@@ -176,11 +182,15 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
176 dt_type(next->d_inode)) < 0) 182 dt_type(next->d_inode)) < 0)
177 return 0; 183 return 0;
178 spin_lock(&dcache_lock); 184 spin_lock(&dcache_lock);
185 spin_lock(&dentry->d_lock);
186 spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
179 /* next is still alive */ 187 /* next is still alive */
180 list_move(q, p); 188 list_move(q, p);
189 spin_unlock(&next->d_lock);
181 p = q; 190 p = q;
182 filp->f_pos++; 191 filp->f_pos++;
183 } 192 }
193 spin_unlock(&dentry->d_lock);
184 spin_unlock(&dcache_lock); 194 spin_unlock(&dcache_lock);
185 } 195 }
186 return 0; 196 return 0;
@@ -276,6 +286,7 @@ int simple_empty(struct dentry *dentry)
276 int ret = 0; 286 int ret = 0;
277 287
278 spin_lock(&dcache_lock); 288 spin_lock(&dcache_lock);
289 spin_lock(&dentry->d_lock);
279 list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) { 290 list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
280 spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); 291 spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
281 if (simple_positive(child)) { 292 if (simple_positive(child)) {
@@ -286,6 +297,7 @@ int simple_empty(struct dentry *dentry)
286 } 297 }
287 ret = 1; 298 ret = 1;
288out: 299out:
300 spin_unlock(&dentry->d_lock);
289 spin_unlock(&dcache_lock); 301 spin_unlock(&dcache_lock);
290 return ret; 302 return ret;
291} 303}