aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2006-08-22 20:06:07 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-09-22 23:24:30 -0400
commit770bfad846ab6628444428467b11fa6773ae9ea1 (patch)
tree5fd803319fe718367dca081b23abd36bfc85f78a /fs
parent979df72e6f963b42ee484f2eca049c3344da0ba7 (diff)
NFS: Add dentry materialisation op
The attached patch adds a new directory cache management function that prepares a disconnected anonymous function to be connected into the dentry tree. The anonymous dentry is transferred the name and parentage from another dentry. The following changes were made in [try #2]: (*) d_materialise_dentry() now switches the parentage of the two nodes around correctly when one or other of them is self-referential. The following changes were made in [try #7]: (*) d_instantiate_unique() has had the interior part split out as function __d_instantiate_unique(). Callers of this latter function must be holding the appropriate locks. (*) _d_rehash() has been added as a wrapper around __d_rehash() to call it with the most obvious hash list (the one from the name). d_rehash() now calls _d_rehash(). (*) d_materialise_dentry() is now __d_materialise_dentry() and is static. (*) d_materialise_unique() added to perform the combination of d_find_alias(), d_materialise_dentry() and d_add_unique() that the NFS client was doing twice, all within a single dcache_lock critical section. This reduces the number of times two different spinlocks were being accessed. The following further changes were made: (*) Add the dentries onto their parents d_subdirs lists. Signed-Off-By: David Howells <dhowells@redhat.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c164
1 files changed, 150 insertions, 14 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 1b4a3a34ec57..17b392a2049e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -828,17 +828,19 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
828 * (or otherwise set) by the caller to indicate that it is now 828 * (or otherwise set) by the caller to indicate that it is now
829 * in use by the dcache. 829 * in use by the dcache.
830 */ 830 */
831struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode) 831static struct dentry *__d_instantiate_unique(struct dentry *entry,
832 struct inode *inode)
832{ 833{
833 struct dentry *alias; 834 struct dentry *alias;
834 int len = entry->d_name.len; 835 int len = entry->d_name.len;
835 const char *name = entry->d_name.name; 836 const char *name = entry->d_name.name;
836 unsigned int hash = entry->d_name.hash; 837 unsigned int hash = entry->d_name.hash;
837 838
838 BUG_ON(!list_empty(&entry->d_alias)); 839 if (!inode) {
839 spin_lock(&dcache_lock); 840 entry->d_inode = NULL;
840 if (!inode) 841 return NULL;
841 goto do_negative; 842 }
843
842 list_for_each_entry(alias, &inode->i_dentry, d_alias) { 844 list_for_each_entry(alias, &inode->i_dentry, d_alias) {
843 struct qstr *qstr = &alias->d_name; 845 struct qstr *qstr = &alias->d_name;
844 846
@@ -851,19 +853,35 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
851 if (memcmp(qstr->name, name, len)) 853 if (memcmp(qstr->name, name, len))
852 continue; 854 continue;
853 dget_locked(alias); 855 dget_locked(alias);
854 spin_unlock(&dcache_lock);
855 BUG_ON(!d_unhashed(alias));
856 iput(inode);
857 return alias; 856 return alias;
858 } 857 }
858
859 list_add(&entry->d_alias, &inode->i_dentry); 859 list_add(&entry->d_alias, &inode->i_dentry);
860do_negative:
861 entry->d_inode = inode; 860 entry->d_inode = inode;
862 fsnotify_d_instantiate(entry, inode); 861 fsnotify_d_instantiate(entry, inode);
863 spin_unlock(&dcache_lock);
864 security_d_instantiate(entry, inode);
865 return NULL; 862 return NULL;
866} 863}
864
865struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
866{
867 struct dentry *result;
868
869 BUG_ON(!list_empty(&entry->d_alias));
870
871 spin_lock(&dcache_lock);
872 result = __d_instantiate_unique(entry, inode);
873 spin_unlock(&dcache_lock);
874
875 if (!result) {
876 security_d_instantiate(entry, inode);
877 return NULL;
878 }
879
880 BUG_ON(!d_unhashed(result));
881 iput(inode);
882 return result;
883}
884
867EXPORT_SYMBOL(d_instantiate_unique); 885EXPORT_SYMBOL(d_instantiate_unique);
868 886
869/** 887/**
@@ -1235,6 +1253,11 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
1235 hlist_add_head_rcu(&entry->d_hash, list); 1253 hlist_add_head_rcu(&entry->d_hash, list);
1236} 1254}
1237 1255
1256static void _d_rehash(struct dentry * entry)
1257{
1258 __d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));
1259}
1260
1238/** 1261/**
1239 * d_rehash - add an entry back to the hash 1262 * d_rehash - add an entry back to the hash
1240 * @entry: dentry to add to the hash 1263 * @entry: dentry to add to the hash
@@ -1244,11 +1267,9 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
1244 1267
1245void d_rehash(struct dentry * entry) 1268void d_rehash(struct dentry * entry)
1246{ 1269{
1247 struct hlist_head *list = d_hash(entry->d_parent, entry->d_name.hash);
1248
1249 spin_lock(&dcache_lock); 1270 spin_lock(&dcache_lock);
1250 spin_lock(&entry->d_lock); 1271 spin_lock(&entry->d_lock);
1251 __d_rehash(entry, list); 1272 _d_rehash(entry);
1252 spin_unlock(&entry->d_lock); 1273 spin_unlock(&entry->d_lock);
1253 spin_unlock(&dcache_lock); 1274 spin_unlock(&dcache_lock);
1254} 1275}
@@ -1386,6 +1407,120 @@ already_unhashed:
1386 spin_unlock(&dcache_lock); 1407 spin_unlock(&dcache_lock);
1387} 1408}
1388 1409
1410/*
1411 * Prepare an anonymous dentry for life in the superblock's dentry tree as a
1412 * named dentry in place of the dentry to be replaced.
1413 */
1414static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
1415{
1416 struct dentry *dparent, *aparent;
1417
1418 switch_names(dentry, anon);
1419 do_switch(dentry->d_name.len, anon->d_name.len);
1420 do_switch(dentry->d_name.hash, anon->d_name.hash);
1421
1422 dparent = dentry->d_parent;
1423 aparent = anon->d_parent;
1424
1425 dentry->d_parent = (aparent == anon) ? dentry : aparent;
1426 list_del(&dentry->d_u.d_child);
1427 if (!IS_ROOT(dentry))
1428 list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
1429 else
1430 INIT_LIST_HEAD(&dentry->d_u.d_child);
1431
1432 anon->d_parent = (dparent == dentry) ? anon : dparent;
1433 list_del(&anon->d_u.d_child);
1434 if (!IS_ROOT(anon))
1435 list_add(&anon->d_u.d_child, &anon->d_parent->d_subdirs);
1436 else
1437 INIT_LIST_HEAD(&anon->d_u.d_child);
1438
1439 anon->d_flags &= ~DCACHE_DISCONNECTED;
1440}
1441
1442/**
1443 * d_materialise_unique - introduce an inode into the tree
1444 * @dentry: candidate dentry
1445 * @inode: inode to bind to the dentry, to which aliases may be attached
1446 *
1447 * Introduces an dentry into the tree, substituting an extant disconnected
1448 * root directory alias in its place if there is one
1449 */
1450struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
1451{
1452 struct dentry *alias, *actual;
1453
1454 BUG_ON(!d_unhashed(dentry));
1455
1456 spin_lock(&dcache_lock);
1457
1458 if (!inode) {
1459 actual = dentry;
1460 dentry->d_inode = NULL;
1461 goto found_lock;
1462 }
1463
1464 /* See if a disconnected directory already exists as an anonymous root
1465 * that we should splice into the tree instead */
1466 if (S_ISDIR(inode->i_mode) && (alias = __d_find_alias(inode, 1))) {
1467 spin_lock(&alias->d_lock);
1468
1469 /* Is this a mountpoint that we could splice into our tree? */
1470 if (IS_ROOT(alias))
1471 goto connect_mountpoint;
1472
1473 if (alias->d_name.len == dentry->d_name.len &&
1474 alias->d_parent == dentry->d_parent &&
1475 memcmp(alias->d_name.name,
1476 dentry->d_name.name,
1477 dentry->d_name.len) == 0)
1478 goto replace_with_alias;
1479
1480 spin_unlock(&alias->d_lock);
1481
1482 /* Doh! Seem to be aliasing directories for some reason... */
1483 dput(alias);
1484 }
1485
1486 /* Add a unique reference */
1487 actual = __d_instantiate_unique(dentry, inode);
1488 if (!actual)
1489 actual = dentry;
1490 else if (unlikely(!d_unhashed(actual)))
1491 goto shouldnt_be_hashed;
1492
1493found_lock:
1494 spin_lock(&actual->d_lock);
1495found:
1496 _d_rehash(actual);
1497 spin_unlock(&actual->d_lock);
1498 spin_unlock(&dcache_lock);
1499
1500 if (actual == dentry) {
1501 security_d_instantiate(dentry, inode);
1502 return NULL;
1503 }
1504
1505 iput(inode);
1506 return actual;
1507
1508 /* Convert the anonymous/root alias into an ordinary dentry */
1509connect_mountpoint:
1510 __d_materialise_dentry(dentry, alias);
1511
1512 /* Replace the candidate dentry with the alias in the tree */
1513replace_with_alias:
1514 __d_drop(alias);
1515 actual = alias;
1516 goto found;
1517
1518shouldnt_be_hashed:
1519 spin_unlock(&dcache_lock);
1520 BUG();
1521 goto shouldnt_be_hashed;
1522}
1523
1389/** 1524/**
1390 * d_path - return the path of a dentry 1525 * d_path - return the path of a dentry
1391 * @dentry: dentry to report 1526 * @dentry: dentry to report
@@ -1784,6 +1919,7 @@ EXPORT_SYMBOL(d_instantiate);
1784EXPORT_SYMBOL(d_invalidate); 1919EXPORT_SYMBOL(d_invalidate);
1785EXPORT_SYMBOL(d_lookup); 1920EXPORT_SYMBOL(d_lookup);
1786EXPORT_SYMBOL(d_move); 1921EXPORT_SYMBOL(d_move);
1922EXPORT_SYMBOL_GPL(d_materialise_unique);
1787EXPORT_SYMBOL(d_path); 1923EXPORT_SYMBOL(d_path);
1788EXPORT_SYMBOL(d_prune_aliases); 1924EXPORT_SYMBOL(d_prune_aliases);
1789EXPORT_SYMBOL(d_rehash); 1925EXPORT_SYMBOL(d_rehash);