diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2008-08-06 14:42:33 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:06 -0400 |
commit | cbdf5a2442330102c08f5a2ad3058e29e90a43a9 (patch) | |
tree | b41e0da448591f22e00afdda4f6e68b85baad030 /fs/btrfs/inode.c | |
parent | be6e8dc0ba84029997075a1ec77b4ddb863cbe15 (diff) |
Btrfs: Implement our own copy of the nfsd readdir hack, for older kernels
Date: Wed, 6 Aug 2008 19:42:33 +0100
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r-- | fs/btrfs/inode.c | 104 |
1 files changed, 102 insertions, 2 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 266ca5514b33..0da1ae4ee407 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -1915,7 +1915,8 @@ static unsigned char btrfs_filetype_table[] = { | |||
1915 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK | 1915 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK |
1916 | }; | 1916 | }; |
1917 | 1917 | ||
1918 | static int btrfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | 1918 | static int btrfs_real_readdir(struct file *filp, void *dirent, |
1919 | filldir_t filldir) | ||
1919 | { | 1920 | { |
1920 | struct inode *inode = filp->f_dentry->d_inode; | 1921 | struct inode *inode = filp->f_dentry->d_inode; |
1921 | struct btrfs_root *root = BTRFS_I(inode)->root; | 1922 | struct btrfs_root *root = BTRFS_I(inode)->root; |
@@ -2064,6 +2065,101 @@ err: | |||
2064 | return ret; | 2065 | return ret; |
2065 | } | 2066 | } |
2066 | 2067 | ||
2068 | /* Kernels earlier than 2.6.28 still have the NFS deadlock where nfsd | ||
2069 | will call the file system's ->lookup() method from within its | ||
2070 | filldir callback, which in turn was called from the file system's | ||
2071 | ->readdir() method. And will deadlock for many file systems. */ | ||
2072 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) | ||
2073 | |||
2074 | struct nfshack_dirent { | ||
2075 | u64 ino; | ||
2076 | loff_t offset; | ||
2077 | int namlen; | ||
2078 | unsigned int d_type; | ||
2079 | char name[]; | ||
2080 | }; | ||
2081 | |||
2082 | struct nfshack_readdir { | ||
2083 | char *dirent; | ||
2084 | size_t used; | ||
2085 | }; | ||
2086 | |||
2087 | |||
2088 | |||
2089 | static int btrfs_nfshack_filldir(void *__buf, const char *name, int namlen, | ||
2090 | loff_t offset, u64 ino, unsigned int d_type) | ||
2091 | { | ||
2092 | struct nfshack_readdir *buf = __buf; | ||
2093 | struct nfshack_dirent *de = (void *)(buf->dirent + buf->used); | ||
2094 | unsigned int reclen; | ||
2095 | |||
2096 | reclen = ALIGN(sizeof(struct nfshack_dirent) + namlen, sizeof(u64)); | ||
2097 | if (buf->used + reclen > PAGE_SIZE) | ||
2098 | return -EINVAL; | ||
2099 | |||
2100 | de->namlen = namlen; | ||
2101 | de->offset = offset; | ||
2102 | de->ino = ino; | ||
2103 | de->d_type = d_type; | ||
2104 | memcpy(de->name, name, namlen); | ||
2105 | buf->used += reclen; | ||
2106 | |||
2107 | return 0; | ||
2108 | } | ||
2109 | |||
2110 | static int btrfs_nfshack_readdir(struct file *file, void *dirent, | ||
2111 | filldir_t filldir) | ||
2112 | { | ||
2113 | struct nfshack_readdir buf; | ||
2114 | struct nfshack_dirent *de; | ||
2115 | int err; | ||
2116 | int size; | ||
2117 | loff_t offset; | ||
2118 | |||
2119 | buf.dirent = (void *)__get_free_page(GFP_KERNEL); | ||
2120 | if (!buf.dirent) | ||
2121 | return -ENOMEM; | ||
2122 | |||
2123 | offset = file->f_pos; | ||
2124 | |||
2125 | while (1) { | ||
2126 | unsigned int reclen; | ||
2127 | |||
2128 | buf.used = 0; | ||
2129 | |||
2130 | err = btrfs_real_readdir(file, &buf, btrfs_nfshack_filldir); | ||
2131 | if (err) | ||
2132 | break; | ||
2133 | |||
2134 | size = buf.used; | ||
2135 | |||
2136 | if (!size) | ||
2137 | break; | ||
2138 | |||
2139 | de = (struct nfshack_dirent *)buf.dirent; | ||
2140 | while (size > 0) { | ||
2141 | offset = de->offset; | ||
2142 | |||
2143 | if (filldir(dirent, de->name, de->namlen, de->offset, | ||
2144 | de->ino, de->d_type)) | ||
2145 | goto done; | ||
2146 | offset = file->f_pos; | ||
2147 | |||
2148 | reclen = ALIGN(sizeof(*de) + de->namlen, | ||
2149 | sizeof(u64)); | ||
2150 | size -= reclen; | ||
2151 | de = (struct nfshack_dirent *)((char *)de + reclen); | ||
2152 | } | ||
2153 | } | ||
2154 | |||
2155 | done: | ||
2156 | free_page((unsigned long)buf.dirent); | ||
2157 | file->f_pos = offset; | ||
2158 | |||
2159 | return err; | ||
2160 | } | ||
2161 | #endif | ||
2162 | |||
2067 | int btrfs_write_inode(struct inode *inode, int wait) | 2163 | int btrfs_write_inode(struct inode *inode, int wait) |
2068 | { | 2164 | { |
2069 | struct btrfs_root *root = BTRFS_I(inode)->root; | 2165 | struct btrfs_root *root = BTRFS_I(inode)->root; |
@@ -3623,7 +3719,11 @@ static struct inode_operations btrfs_dir_ro_inode_operations = { | |||
3623 | static struct file_operations btrfs_dir_file_operations = { | 3719 | static struct file_operations btrfs_dir_file_operations = { |
3624 | .llseek = generic_file_llseek, | 3720 | .llseek = generic_file_llseek, |
3625 | .read = generic_read_dir, | 3721 | .read = generic_read_dir, |
3626 | .readdir = btrfs_readdir, | 3722 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) |
3723 | .readdir = btrfs_nfshack_readdir, | ||
3724 | #else /* NFSd readdir/lookup deadlock is fixed */ | ||
3725 | .readdir = btrfs_real_readdir, | ||
3726 | #endif | ||
3627 | .unlocked_ioctl = btrfs_ioctl, | 3727 | .unlocked_ioctl = btrfs_ioctl, |
3628 | #ifdef CONFIG_COMPAT | 3728 | #ifdef CONFIG_COMPAT |
3629 | .compat_ioctl = btrfs_ioctl, | 3729 | .compat_ioctl = btrfs_ioctl, |