aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd/vfs.c
diff options
context:
space:
mode:
authorDavid Woodhouse <David.Woodhouse@intel.com>2008-07-31 15:29:12 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2008-10-23 05:13:05 -0400
commit14f7dd632011bb89c035722edd6ea0d90ca6b078 (patch)
treed49a5fca0245eac9bd947aa200985d33267dfb19 /fs/nfsd/vfs.c
parent2628b766363aa3791e816a7e5807373ef551c776 (diff)
[PATCH] Copy XFS readdir hack into nfsd code.
Some file systems with their own internal locking have problems with the way that nfsd calls the ->lookup() method from within a filldir function called from their ->readdir() method. The recursion back into the file system code can cause deadlock. XFS has a fairly hackish solution to this which involves doing the readdir() into a locally-allocated buffer, then going back through it calling the filldir function afterwards. It's not ideal, but it works. It's particularly suboptimal because XFS does this for local file systems too, where it's completely unnecessary. Copy this hack into the NFS code where it can be used only for NFS export. In response to feedback, use it unconditionally rather than only for the affected file systems. Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r--fs/nfsd/vfs.c108
1 files changed, 93 insertions, 15 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 14eda20cc291..93b22f661d9d 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1813,27 +1813,105 @@ out:
1813 return err; 1813 return err;
1814} 1814}
1815 1815
1816static int nfsd_do_readdir(struct file *file, filldir_t func, 1816/*
1817 struct readdir_cd *cdp, loff_t *offsetp) 1817 * We do this buffering because we must not call back into the file
1818 * system's ->lookup() method from the filldir callback. That may well
1819 * deadlock a number of file systems.
1820 *
1821 * This is based heavily on the implementation of same in XFS.
1822 */
1823struct buffered_dirent {
1824 u64 ino;
1825 loff_t offset;
1826 int namlen;
1827 unsigned int d_type;
1828 char name[];
1829};
1830
1831struct readdir_data {
1832 char *dirent;
1833 size_t used;
1834};
1835
1836static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen,
1837 loff_t offset, u64 ino, unsigned int d_type)
1838{
1839 struct readdir_data *buf = __buf;
1840 struct buffered_dirent *de = (void *)(buf->dirent + buf->used);
1841 unsigned int reclen;
1842
1843 reclen = ALIGN(sizeof(struct buffered_dirent) + namlen, sizeof(u64));
1844 if (buf->used + reclen > PAGE_SIZE)
1845 return -EINVAL;
1846
1847 de->namlen = namlen;
1848 de->offset = offset;
1849 de->ino = ino;
1850 de->d_type = d_type;
1851 memcpy(de->name, name, namlen);
1852 buf->used += reclen;
1853
1854 return 0;
1855}
1856
1857static int nfsd_buffered_readdir(struct file *file, filldir_t func,
1858 struct readdir_cd *cdp, loff_t *offsetp)
1818{ 1859{
1860 struct readdir_data buf;
1861 struct buffered_dirent *de;
1819 int host_err; 1862 int host_err;
1863 int size;
1864 loff_t offset;
1820 1865
1821 /* 1866 buf.dirent = (void *)__get_free_page(GFP_KERNEL);
1822 * Read the directory entries. This silly loop is necessary because 1867 if (!buf.dirent)
1823 * readdir() is not guaranteed to fill up the entire buffer, but 1868 return -ENOMEM;
1824 * may choose to do less. 1869
1825 */ 1870 offset = *offsetp;
1826 do { 1871 cdp->err = nfserr_eof; /* will be cleared on successful read */
1827 cdp->err = nfserr_eof; /* will be cleared on successful read */
1828 host_err = vfs_readdir(file, func, cdp);
1829 } while (host_err >=0 && cdp->err == nfs_ok);
1830 1872
1831 *offsetp = vfs_llseek(file, 0, 1); 1873 while (1) {
1874 unsigned int reclen;
1875
1876 buf.used = 0;
1877
1878 host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf);
1879 if (host_err)
1880 break;
1881
1882 size = buf.used;
1883
1884 if (!size)
1885 break;
1886
1887
1888 de = (struct buffered_dirent *)buf.dirent;
1889 while (size > 0) {
1890 offset = de->offset;
1891
1892 if (func(cdp, de->name, de->namlen, de->offset,
1893 de->ino, de->d_type))
1894 goto done;
1895
1896 if (cdp->err != nfs_ok)
1897 goto done;
1898
1899 reclen = ALIGN(sizeof(*de) + de->namlen,
1900 sizeof(u64));
1901 size -= reclen;
1902 de = (struct buffered_dirent *)((char *)de + reclen);
1903 }
1904 offset = vfs_llseek(file, 0, 1);
1905 }
1906
1907 done:
1908 free_page((unsigned long)(buf.dirent));
1832 1909
1833 if (host_err) 1910 if (host_err)
1834 return nfserrno(host_err); 1911 return nfserrno(host_err);
1835 else 1912
1836 return cdp->err; 1913 *offsetp = offset;
1914 return cdp->err;
1837} 1915}
1838 1916
1839/* 1917/*
@@ -1858,7 +1936,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
1858 goto out_close; 1936 goto out_close;
1859 } 1937 }
1860 1938
1861 err = nfsd_do_readdir(file, func, cdp, offsetp); 1939 err = nfsd_buffered_readdir(file, func, cdp, offsetp);
1862 1940
1863 if (err == nfserr_eof || err == nfserr_toosmall) 1941 if (err == nfserr_eof || err == nfserr_toosmall)
1864 err = nfs_ok; /* can still be found in ->err */ 1942 err = nfs_ok; /* can still be found in ->err */