aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fat/inode.c
diff options
context:
space:
mode:
authorSteven J. Magnani <steve@digidescorp.com>2012-10-04 20:14:44 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-05 14:05:09 -0400
commit21b6633d516c4f5d03ec02ede6374e320191003f (patch)
tree1117879875a138230c3fb2dea435026012270141 /fs/fat/inode.c
parent4b63709861e431e73f0be6b83f420fdd8fc518f5 (diff)
fat (exportfs): move NFS support code
Under memory pressure, the system may evict dentries from cache. When the FAT driver receives a NFS request involving an evicted dentry, it is unable to reconnect it to the filesystem root. This causes the request to fail, often with ENOENT. This is partially due to ineffectiveness of the current FAT NFS implementation, and partially due to an unimplemented fh_to_parent method. The latter can cause file accesses to fail on shares exported with subtree_check. This patch set provides the FAT driver with the ability to reconnect dentries. NFS file handle generation and lookups are simplified and made congruent with ext2. Testing has involved a memory-starved virtual machine running 3.5-rc5 that exports a ~2 GB vfat filesystem containing a kernel tree (~770 MB, ~40000 files, 9 levels). Both 'cp -r' and 'ls -lR' operations were performed from a client, some overlapping, some consecutive. Exports with 'subtree_check' and 'no_subtree_check' have been tested. Note that while this patch set improves FAT's NFS support, it does not eliminate ESTALE errors completely. The following should be considered for NFS clients who are sensitive to ESTALE: * Mounting with lookupcache=none Unfortunately this can degrade performance severely, particularly for deep filesystems. * Incorporating VFS patches to retry ESTALE failures on the client-side, such as https://lkml.org/lkml/2012/6/29/381 * Handling ESTALE errors in client application code This patch: Move NFS-related code into its own C file. No functional changes. Signed-off-by: Steven J. Magnani <steve@digidescorp.com> Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/fat/inode.c')
-rw-r--r--fs/fat/inode.c130
1 files changed, 0 insertions, 130 deletions
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 4e5a6ac54ebd..169f6ebddf96 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -562,20 +562,6 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
562 return 0; 562 return 0;
563} 563}
564 564
565static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
566 struct inode *inode)
567{
568 loff_t i_pos;
569#if BITS_PER_LONG == 32
570 spin_lock(&sbi->inode_hash_lock);
571#endif
572 i_pos = MSDOS_I(inode)->i_pos;
573#if BITS_PER_LONG == 32
574 spin_unlock(&sbi->inode_hash_lock);
575#endif
576 return i_pos;
577}
578
579static int __fat_write_inode(struct inode *inode, int wait) 565static int __fat_write_inode(struct inode *inode, int wait)
580{ 566{
581 struct super_block *sb = inode->i_sb; 567 struct super_block *sb = inode->i_sb;
@@ -668,122 +654,6 @@ static const struct super_operations fat_sops = {
668 .show_options = fat_show_options, 654 .show_options = fat_show_options,
669}; 655};
670 656
671/*
672 * a FAT file handle with fhtype 3 is
673 * 0/ i_ino - for fast, reliable lookup if still in the cache
674 * 1/ i_generation - to see if i_ino is still valid
675 * bit 0 == 0 iff directory
676 * 2/ i_pos(8-39) - if ino has changed, but still in cache
677 * 3/ i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
678 * 4/ i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
679 *
680 * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
681 * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
682 * of i_logstart is used to store the directory entry offset.
683 */
684
685static struct dentry *fat_fh_to_dentry(struct super_block *sb,
686 struct fid *fid, int fh_len, int fh_type)
687{
688 struct inode *inode = NULL;
689 u32 *fh = fid->raw;
690
691 if (fh_len < 5 || fh_type != 3)
692 return NULL;
693
694 inode = ilookup(sb, fh[0]);
695 if (!inode || inode->i_generation != fh[1]) {
696 if (inode)
697 iput(inode);
698 inode = NULL;
699 }
700 if (!inode) {
701 loff_t i_pos;
702 int i_logstart = fh[3] & 0x0fffffff;
703
704 i_pos = (loff_t)fh[2] << 8;
705 i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
706
707 /* try 2 - see if i_pos is in F-d-c
708 * require i_logstart to be the same
709 * Will fail if you truncate and then re-write
710 */
711
712 inode = fat_iget(sb, i_pos);
713 if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
714 iput(inode);
715 inode = NULL;
716 }
717 }
718
719 /*
720 * For now, do nothing if the inode is not found.
721 *
722 * What we could do is:
723 *
724 * - follow the file starting at fh[4], and record the ".." entry,
725 * and the name of the fh[2] entry.
726 * - then follow the ".." file finding the next step up.
727 *
728 * This way we build a path to the root of the tree. If this works, we
729 * lookup the path and so get this inode into the cache. Finally try
730 * the fat_iget lookup again. If that fails, then we are totally out
731 * of luck. But all that is for another day
732 */
733 return d_obtain_alias(inode);
734}
735
736static int
737fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent)
738{
739 int len = *lenp;
740 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
741 loff_t i_pos;
742
743 if (len < 5) {
744 *lenp = 5;
745 return 255; /* no room */
746 }
747
748 i_pos = fat_i_pos_read(sbi, inode);
749 *lenp = 5;
750 fh[0] = inode->i_ino;
751 fh[1] = inode->i_generation;
752 fh[2] = i_pos >> 8;
753 fh[3] = ((i_pos & 0xf0) << 24) | MSDOS_I(inode)->i_logstart;
754 fh[4] = (i_pos & 0x0f) << 28;
755 if (parent)
756 fh[4] |= MSDOS_I(parent)->i_logstart;
757 return 3;
758}
759
760static struct dentry *fat_get_parent(struct dentry *child)
761{
762 struct super_block *sb = child->d_sb;
763 struct buffer_head *bh;
764 struct msdos_dir_entry *de;
765 loff_t i_pos;
766 struct dentry *parent;
767 struct inode *inode;
768 int err;
769
770 lock_super(sb);
771
772 err = fat_get_dotdot_entry(child->d_inode, &bh, &de, &i_pos);
773 if (err) {
774 parent = ERR_PTR(err);
775 goto out;
776 }
777 inode = fat_build_inode(sb, de, i_pos);
778 brelse(bh);
779
780 parent = d_obtain_alias(inode);
781out:
782 unlock_super(sb);
783
784 return parent;
785}
786
787static const struct export_operations fat_export_ops = { 657static const struct export_operations fat_export_ops = {
788 .encode_fh = fat_encode_fh, 658 .encode_fh = fat_encode_fh,
789 .fh_to_dentry = fat_fh_to_dentry, 659 .fh_to_dentry = fat_fh_to_dentry,