aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/dir.c
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2006-08-22 20:06:23 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-09-22 23:24:53 -0400
commit873101b33776780d32610fc4c90c7358a5e98f51 (patch)
tree39bff3860ecc522e8a716c8cf2eebf55588f081a /fs/nfs/dir.c
parent4f390c152bc87165da4b1f5b7d870b46fb106d4e (diff)
NFS: copy symlinks into page cache before sending NFS SYMLINK request
Currently the NFS client does not cache symlinks it creates. They get cached only when the NFS client reads them back from the server. Copy the symlink into the page cache before sending it. Test plan: Connectathon, all NFS versions. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r--fs/nfs/dir.c86
1 files changed, 68 insertions, 18 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index affd3ae52e55..b483e5d206cb 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -30,6 +30,7 @@
30#include <linux/nfs_mount.h> 30#include <linux/nfs_mount.h>
31#include <linux/pagemap.h> 31#include <linux/pagemap.h>
32#include <linux/smp_lock.h> 32#include <linux/smp_lock.h>
33#include <linux/pagevec.h>
33#include <linux/namei.h> 34#include <linux/namei.h>
34#include <linux/mount.h> 35#include <linux/mount.h>
35 36
@@ -1441,39 +1442,88 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
1441 return error; 1442 return error;
1442} 1443}
1443 1444
1444static int 1445/*
1445nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) 1446 * To create a symbolic link, most file systems instantiate a new inode,
1447 * add a page to it containing the path, then write it out to the disk
1448 * using prepare_write/commit_write.
1449 *
1450 * Unfortunately the NFS client can't create the in-core inode first
1451 * because it needs a file handle to create an in-core inode (see
1452 * fs/nfs/inode.c:nfs_fhget). We only have a file handle *after* the
1453 * symlink request has completed on the server.
1454 *
1455 * So instead we allocate a raw page, copy the symname into it, then do
1456 * the SYMLINK request with the page as the buffer. If it succeeds, we
1457 * now have a new file handle and can instantiate an in-core NFS inode
1458 * and move the raw page into its mapping.
1459 */
1460static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
1446{ 1461{
1462 struct pagevec lru_pvec;
1463 struct page *page;
1464 char *kaddr;
1447 struct iattr attr; 1465 struct iattr attr;
1448 struct qstr qsymname; 1466 unsigned int pathlen = strlen(symname);
1467 struct qstr qsymname = {
1468 .name = symname,
1469 .len = pathlen,
1470 };
1449 int error; 1471 int error;
1450 1472
1451 dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id, 1473 dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
1452 dir->i_ino, dentry->d_name.name, symname); 1474 dir->i_ino, dentry->d_name.name, symname);
1453 1475
1454#ifdef NFS_PARANOIA 1476 if (pathlen > PAGE_SIZE)
1455if (dentry->d_inode) 1477 return -ENAMETOOLONG;
1456printk("nfs_proc_symlink: %s/%s not negative!\n",
1457dentry->d_parent->d_name.name, dentry->d_name.name);
1458#endif
1459 /*
1460 * Fill in the sattr for the call.
1461 * Note: SunOS 4.1.2 crashes if the mode isn't initialized!
1462 */
1463 attr.ia_valid = ATTR_MODE;
1464 attr.ia_mode = S_IFLNK | S_IRWXUGO;
1465 1478
1466 qsymname.name = symname; 1479 attr.ia_mode = S_IFLNK | S_IRWXUGO;
1467 qsymname.len = strlen(symname); 1480 attr.ia_valid = ATTR_MODE;
1468 1481
1469 lock_kernel(); 1482 lock_kernel();
1483
1484 page = alloc_page(GFP_KERNEL);
1485 if (!page) {
1486 unlock_kernel();
1487 return -ENOMEM;
1488 }
1489
1490 kaddr = kmap_atomic(page, KM_USER0);
1491 memcpy(kaddr, symname, pathlen);
1492 if (pathlen < PAGE_SIZE)
1493 memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
1494 kunmap_atomic(kaddr, KM_USER0);
1495
1496 /* XXX: eventually this will pass in {page, pathlen},
1497 * instead of qsymname; need XDR changes for that */
1470 nfs_begin_data_update(dir); 1498 nfs_begin_data_update(dir);
1471 error = NFS_PROTO(dir)->symlink(dir, dentry, &qsymname, &attr); 1499 error = NFS_PROTO(dir)->symlink(dir, dentry, &qsymname, &attr);
1472 nfs_end_data_update(dir); 1500 nfs_end_data_update(dir);
1473 if (!error) 1501 if (error != 0) {
1502 dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",
1503 dir->i_sb->s_id, dir->i_ino,
1504 dentry->d_name.name, symname, error);
1474 d_drop(dentry); 1505 d_drop(dentry);
1506 __free_page(page);
1507 unlock_kernel();
1508 return error;
1509 }
1510
1511 /*
1512 * No big deal if we can't add this page to the page cache here.
1513 * READLINK will get the missing page from the server if needed.
1514 */
1515 pagevec_init(&lru_pvec, 0);
1516 if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0,
1517 GFP_KERNEL)) {
1518 if (!pagevec_add(&lru_pvec, page))
1519 __pagevec_lru_add(&lru_pvec);
1520 SetPageUptodate(page);
1521 unlock_page(page);
1522 } else
1523 __free_page(page);
1524
1475 unlock_kernel(); 1525 unlock_kernel();
1476 return error; 1526 return 0;
1477} 1527}
1478 1528
1479static int 1529static int