diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-09-12 21:04:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-09-12 21:04:13 -0400 |
commit | 22b4e63ebe062e2e3d4a3a2b468e47ca9575d598 (patch) | |
tree | 3813950eaf97dd563b52c7365f43501aff595355 | |
parent | 0bd1189e239c76eb3a50e458548fbe7e4a5dfff1 (diff) | |
parent | 7b281ee026552f10862b617a2a51acf49c829554 (diff) |
Merge tag 'nfs-for-3.6-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
Pull NFS client bugfixes from Trond Myklebust:
- Final (hopefully) fix for the range checking code in NFSv4 getacl.
This should fix the Oopses being seen when the acl size is close to
PAGE_SIZE.
- Fix a regression with the legacy binary mount code
- Fix a regression in the readdir cookieverf initialisation
- Fix an RPC over UDP regression
- Ensure that we report all errors in the NFSv4 open code
- Ensure that fsync() reports all relevant synchronisation errors.
* tag 'nfs-for-3.6-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
NFS: fsync() must exit with an error if page writeback failed
SUNRPC: Fix a UDP transport regression
NFS: return error from decode_getfh in decode open
NFSv4: Fix buffer overflow checking in __nfs4_get_acl_uncached
NFSv4: Fix range checking in __nfs4_get_acl_uncached and __nfs4_proc_set_acl
NFS: Fix a problem with the legacy binary mount code
NFS: Fix the initialisation of the readdir 'cookieverf' array
-rw-r--r-- | fs/nfs/file.c | 4 | ||||
-rw-r--r-- | fs/nfs/inode.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4file.c | 4 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 55 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 17 | ||||
-rw-r--r-- | fs/nfs/super.c | 2 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 5 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 2 | ||||
-rw-r--r-- | include/linux/sunrpc/xprt.h | 3 | ||||
-rw-r--r-- | net/sunrpc/xprt.c | 34 | ||||
-rw-r--r-- | net/sunrpc/xprtrdma/transport.c | 1 | ||||
-rw-r--r-- | net/sunrpc/xprtsock.c | 3 |
13 files changed, 70 insertions, 64 deletions
diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 75d6d0a3d32e..6a7fcab7ecb3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c | |||
@@ -287,10 +287,12 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) | |||
287 | struct inode *inode = file->f_path.dentry->d_inode; | 287 | struct inode *inode = file->f_path.dentry->d_inode; |
288 | 288 | ||
289 | ret = filemap_write_and_wait_range(inode->i_mapping, start, end); | 289 | ret = filemap_write_and_wait_range(inode->i_mapping, start, end); |
290 | if (ret != 0) | ||
291 | goto out; | ||
290 | mutex_lock(&inode->i_mutex); | 292 | mutex_lock(&inode->i_mutex); |
291 | ret = nfs_file_fsync_commit(file, start, end, datasync); | 293 | ret = nfs_file_fsync_commit(file, start, end, datasync); |
292 | mutex_unlock(&inode->i_mutex); | 294 | mutex_unlock(&inode->i_mutex); |
293 | 295 | out: | |
294 | return ret; | 296 | return ret; |
295 | } | 297 | } |
296 | 298 | ||
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c6e895f0fbf3..9b47610338f5 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -154,7 +154,7 @@ static void nfs_zap_caches_locked(struct inode *inode) | |||
154 | nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); | 154 | nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); |
155 | nfsi->attrtimeo_timestamp = jiffies; | 155 | nfsi->attrtimeo_timestamp = jiffies; |
156 | 156 | ||
157 | memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); | 157 | memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf)); |
158 | if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) | 158 | if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) |
159 | nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; | 159 | nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; |
160 | else | 160 | else |
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index d6b3b5f2d779..69322096c325 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
@@ -643,7 +643,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | |||
643 | u64 cookie, struct page **pages, unsigned int count, int plus) | 643 | u64 cookie, struct page **pages, unsigned int count, int plus) |
644 | { | 644 | { |
645 | struct inode *dir = dentry->d_inode; | 645 | struct inode *dir = dentry->d_inode; |
646 | __be32 *verf = NFS_COOKIEVERF(dir); | 646 | __be32 *verf = NFS_I(dir)->cookieverf; |
647 | struct nfs3_readdirargs arg = { | 647 | struct nfs3_readdirargs arg = { |
648 | .fh = NFS_FH(dir), | 648 | .fh = NFS_FH(dir), |
649 | .cookie = cookie, | 649 | .cookie = cookie, |
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index acb65e7887f8..eb5eb8eef4d3 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c | |||
@@ -96,13 +96,15 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) | |||
96 | struct inode *inode = file->f_path.dentry->d_inode; | 96 | struct inode *inode = file->f_path.dentry->d_inode; |
97 | 97 | ||
98 | ret = filemap_write_and_wait_range(inode->i_mapping, start, end); | 98 | ret = filemap_write_and_wait_range(inode->i_mapping, start, end); |
99 | if (ret != 0) | ||
100 | goto out; | ||
99 | mutex_lock(&inode->i_mutex); | 101 | mutex_lock(&inode->i_mutex); |
100 | ret = nfs_file_fsync_commit(file, start, end, datasync); | 102 | ret = nfs_file_fsync_commit(file, start, end, datasync); |
101 | if (!ret && !datasync) | 103 | if (!ret && !datasync) |
102 | /* application has asked for meta-data sync */ | 104 | /* application has asked for meta-data sync */ |
103 | ret = pnfs_layoutcommit_inode(inode, true); | 105 | ret = pnfs_layoutcommit_inode(inode, true); |
104 | mutex_unlock(&inode->i_mutex); | 106 | mutex_unlock(&inode->i_mutex); |
105 | 107 | out: | |
106 | return ret; | 108 | return ret; |
107 | } | 109 | } |
108 | 110 | ||
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 635274140b18..1e50326d00dd 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -3215,11 +3215,11 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | |||
3215 | dentry->d_parent->d_name.name, | 3215 | dentry->d_parent->d_name.name, |
3216 | dentry->d_name.name, | 3216 | dentry->d_name.name, |
3217 | (unsigned long long)cookie); | 3217 | (unsigned long long)cookie); |
3218 | nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args); | 3218 | nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args); |
3219 | res.pgbase = args.pgbase; | 3219 | res.pgbase = args.pgbase; |
3220 | status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0); | 3220 | status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0); |
3221 | if (status >= 0) { | 3221 | if (status >= 0) { |
3222 | memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE); | 3222 | memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE); |
3223 | status += args.pgbase; | 3223 | status += args.pgbase; |
3224 | } | 3224 | } |
3225 | 3225 | ||
@@ -3653,11 +3653,11 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server) | |||
3653 | && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); | 3653 | && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); |
3654 | } | 3654 | } |
3655 | 3655 | ||
3656 | /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that | 3656 | /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that |
3657 | * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on | 3657 | * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on |
3658 | * the stack. | 3658 | * the stack. |
3659 | */ | 3659 | */ |
3660 | #define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT) | 3660 | #define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE) |
3661 | 3661 | ||
3662 | static int buf_to_pages_noslab(const void *buf, size_t buflen, | 3662 | static int buf_to_pages_noslab(const void *buf, size_t buflen, |
3663 | struct page **pages, unsigned int *pgbase) | 3663 | struct page **pages, unsigned int *pgbase) |
@@ -3668,7 +3668,7 @@ static int buf_to_pages_noslab(const void *buf, size_t buflen, | |||
3668 | spages = pages; | 3668 | spages = pages; |
3669 | 3669 | ||
3670 | do { | 3670 | do { |
3671 | len = min_t(size_t, PAGE_CACHE_SIZE, buflen); | 3671 | len = min_t(size_t, PAGE_SIZE, buflen); |
3672 | newpage = alloc_page(GFP_KERNEL); | 3672 | newpage = alloc_page(GFP_KERNEL); |
3673 | 3673 | ||
3674 | if (newpage == NULL) | 3674 | if (newpage == NULL) |
@@ -3739,7 +3739,7 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size | |||
3739 | struct nfs4_cached_acl *acl; | 3739 | struct nfs4_cached_acl *acl; |
3740 | size_t buflen = sizeof(*acl) + acl_len; | 3740 | size_t buflen = sizeof(*acl) + acl_len; |
3741 | 3741 | ||
3742 | if (pages && buflen <= PAGE_SIZE) { | 3742 | if (buflen <= PAGE_SIZE) { |
3743 | acl = kmalloc(buflen, GFP_KERNEL); | 3743 | acl = kmalloc(buflen, GFP_KERNEL); |
3744 | if (acl == NULL) | 3744 | if (acl == NULL) |
3745 | goto out; | 3745 | goto out; |
@@ -3782,17 +3782,15 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu | |||
3782 | .rpc_argp = &args, | 3782 | .rpc_argp = &args, |
3783 | .rpc_resp = &res, | 3783 | .rpc_resp = &res, |
3784 | }; | 3784 | }; |
3785 | int ret = -ENOMEM, npages, i; | 3785 | unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE); |
3786 | size_t acl_len = 0; | 3786 | int ret = -ENOMEM, i; |
3787 | 3787 | ||
3788 | npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
3789 | /* As long as we're doing a round trip to the server anyway, | 3788 | /* As long as we're doing a round trip to the server anyway, |
3790 | * let's be prepared for a page of acl data. */ | 3789 | * let's be prepared for a page of acl data. */ |
3791 | if (npages == 0) | 3790 | if (npages == 0) |
3792 | npages = 1; | 3791 | npages = 1; |
3793 | 3792 | if (npages > ARRAY_SIZE(pages)) | |
3794 | /* Add an extra page to handle the bitmap returned */ | 3793 | return -ERANGE; |
3795 | npages++; | ||
3796 | 3794 | ||
3797 | for (i = 0; i < npages; i++) { | 3795 | for (i = 0; i < npages; i++) { |
3798 | pages[i] = alloc_page(GFP_KERNEL); | 3796 | pages[i] = alloc_page(GFP_KERNEL); |
@@ -3808,11 +3806,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu | |||
3808 | args.acl_len = npages * PAGE_SIZE; | 3806 | args.acl_len = npages * PAGE_SIZE; |
3809 | args.acl_pgbase = 0; | 3807 | args.acl_pgbase = 0; |
3810 | 3808 | ||
3811 | /* Let decode_getfacl know not to fail if the ACL data is larger than | ||
3812 | * the page we send as a guess */ | ||
3813 | if (buf == NULL) | ||
3814 | res.acl_flags |= NFS4_ACL_LEN_REQUEST; | ||
3815 | |||
3816 | dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", | 3809 | dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", |
3817 | __func__, buf, buflen, npages, args.acl_len); | 3810 | __func__, buf, buflen, npages, args.acl_len); |
3818 | ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), | 3811 | ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), |
@@ -3820,20 +3813,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu | |||
3820 | if (ret) | 3813 | if (ret) |
3821 | goto out_free; | 3814 | goto out_free; |
3822 | 3815 | ||
3823 | acl_len = res.acl_len; | 3816 | /* Handle the case where the passed-in buffer is too short */ |
3824 | if (acl_len > args.acl_len) | 3817 | if (res.acl_flags & NFS4_ACL_TRUNC) { |
3825 | nfs4_write_cached_acl(inode, NULL, 0, acl_len); | 3818 | /* Did the user only issue a request for the acl length? */ |
3826 | else | 3819 | if (buf == NULL) |
3827 | nfs4_write_cached_acl(inode, pages, res.acl_data_offset, | 3820 | goto out_ok; |
3828 | acl_len); | ||
3829 | if (buf) { | ||
3830 | ret = -ERANGE; | 3821 | ret = -ERANGE; |
3831 | if (acl_len > buflen) | 3822 | goto out_free; |
3832 | goto out_free; | ||
3833 | _copy_from_pages(buf, pages, res.acl_data_offset, | ||
3834 | acl_len); | ||
3835 | } | 3823 | } |
3836 | ret = acl_len; | 3824 | nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len); |
3825 | if (buf) | ||
3826 | _copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len); | ||
3827 | out_ok: | ||
3828 | ret = res.acl_len; | ||
3837 | out_free: | 3829 | out_free: |
3838 | for (i = 0; i < npages; i++) | 3830 | for (i = 0; i < npages; i++) |
3839 | if (pages[i]) | 3831 | if (pages[i]) |
@@ -3891,10 +3883,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl | |||
3891 | .rpc_argp = &arg, | 3883 | .rpc_argp = &arg, |
3892 | .rpc_resp = &res, | 3884 | .rpc_resp = &res, |
3893 | }; | 3885 | }; |
3886 | unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE); | ||
3894 | int ret, i; | 3887 | int ret, i; |
3895 | 3888 | ||
3896 | if (!nfs4_server_supports_acls(server)) | 3889 | if (!nfs4_server_supports_acls(server)) |
3897 | return -EOPNOTSUPP; | 3890 | return -EOPNOTSUPP; |
3891 | if (npages > ARRAY_SIZE(pages)) | ||
3892 | return -ERANGE; | ||
3898 | i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); | 3893 | i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); |
3899 | if (i < 0) | 3894 | if (i < 0) |
3900 | return i; | 3895 | return i; |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 1bfbd67c556d..8dba6bd48557 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -5072,18 +5072,14 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5072 | * are stored with the acl data to handle the problem of | 5072 | * are stored with the acl data to handle the problem of |
5073 | * variable length bitmaps.*/ | 5073 | * variable length bitmaps.*/ |
5074 | res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset; | 5074 | res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset; |
5075 | |||
5076 | /* We ignore &savep and don't do consistency checks on | ||
5077 | * the attr length. Let userspace figure it out.... */ | ||
5078 | res->acl_len = attrlen; | 5075 | res->acl_len = attrlen; |
5079 | if (attrlen > (xdr->nwords << 2)) { | 5076 | |
5080 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { | 5077 | /* Check for receive buffer overflow */ |
5081 | /* getxattr interface called with a NULL buf */ | 5078 | if (res->acl_len > (xdr->nwords << 2) || |
5082 | goto out; | 5079 | res->acl_len + res->acl_data_offset > xdr->buf->page_len) { |
5083 | } | 5080 | res->acl_flags |= NFS4_ACL_TRUNC; |
5084 | dprintk("NFS: acl reply: attrlen %u > page_len %u\n", | 5081 | dprintk("NFS: acl reply: attrlen %u > page_len %u\n", |
5085 | attrlen, xdr->nwords << 2); | 5082 | attrlen, xdr->nwords << 2); |
5086 | return -EINVAL; | ||
5087 | } | 5083 | } |
5088 | } else | 5084 | } else |
5089 | status = -EOPNOTSUPP; | 5085 | status = -EOPNOTSUPP; |
@@ -6229,7 +6225,8 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr, | |||
6229 | status = decode_open(xdr, res); | 6225 | status = decode_open(xdr, res); |
6230 | if (status) | 6226 | if (status) |
6231 | goto out; | 6227 | goto out; |
6232 | if (decode_getfh(xdr, &res->fh) != 0) | 6228 | status = decode_getfh(xdr, &res->fh); |
6229 | if (status) | ||
6233 | goto out; | 6230 | goto out; |
6234 | decode_getfattr(xdr, res->f_attr, res->server); | 6231 | decode_getfattr(xdr, res->f_attr, res->server); |
6235 | out: | 6232 | out: |
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 239aff7338eb..b8eda700584b 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -1867,6 +1867,7 @@ static int nfs23_validate_mount_data(void *options, | |||
1867 | 1867 | ||
1868 | memcpy(sap, &data->addr, sizeof(data->addr)); | 1868 | memcpy(sap, &data->addr, sizeof(data->addr)); |
1869 | args->nfs_server.addrlen = sizeof(data->addr); | 1869 | args->nfs_server.addrlen = sizeof(data->addr); |
1870 | args->nfs_server.port = ntohs(data->addr.sin_port); | ||
1870 | if (!nfs_verify_server_address(sap)) | 1871 | if (!nfs_verify_server_address(sap)) |
1871 | goto out_no_address; | 1872 | goto out_no_address; |
1872 | 1873 | ||
@@ -2564,6 +2565,7 @@ static int nfs4_validate_mount_data(void *options, | |||
2564 | return -EFAULT; | 2565 | return -EFAULT; |
2565 | if (!nfs_verify_server_address(sap)) | 2566 | if (!nfs_verify_server_address(sap)) |
2566 | goto out_no_address; | 2567 | goto out_no_address; |
2568 | args->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port); | ||
2567 | 2569 | ||
2568 | if (data->auth_flavourlen) { | 2570 | if (data->auth_flavourlen) { |
2569 | if (data->auth_flavourlen > 1) | 2571 | if (data->auth_flavourlen > 1) |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 1f8fc7f9bcd8..4b03f56e280e 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -265,11 +265,6 @@ static inline const struct nfs_rpc_ops *NFS_PROTO(const struct inode *inode) | |||
265 | return NFS_SERVER(inode)->nfs_client->rpc_ops; | 265 | return NFS_SERVER(inode)->nfs_client->rpc_ops; |
266 | } | 266 | } |
267 | 267 | ||
268 | static inline __be32 *NFS_COOKIEVERF(const struct inode *inode) | ||
269 | { | ||
270 | return NFS_I(inode)->cookieverf; | ||
271 | } | ||
272 | |||
273 | static inline unsigned NFS_MINATTRTIMEO(const struct inode *inode) | 268 | static inline unsigned NFS_MINATTRTIMEO(const struct inode *inode) |
274 | { | 269 | { |
275 | struct nfs_server *nfss = NFS_SERVER(inode); | 270 | struct nfs_server *nfss = NFS_SERVER(inode); |
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index ac7c8ae254f2..be9cf3c7e79e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -652,7 +652,7 @@ struct nfs_getaclargs { | |||
652 | }; | 652 | }; |
653 | 653 | ||
654 | /* getxattr ACL interface flags */ | 654 | /* getxattr ACL interface flags */ |
655 | #define NFS4_ACL_LEN_REQUEST 0x0001 /* zero length getxattr buffer */ | 655 | #define NFS4_ACL_TRUNC 0x0001 /* ACL was truncated */ |
656 | struct nfs_getaclres { | 656 | struct nfs_getaclres { |
657 | size_t acl_len; | 657 | size_t acl_len; |
658 | size_t acl_data_offset; | 658 | size_t acl_data_offset; |
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index cff40aa7db62..bf8c49ff7530 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h | |||
@@ -114,6 +114,7 @@ struct rpc_xprt_ops { | |||
114 | void (*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize); | 114 | void (*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize); |
115 | int (*reserve_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); | 115 | int (*reserve_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); |
116 | void (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); | 116 | void (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); |
117 | void (*alloc_slot)(struct rpc_xprt *xprt, struct rpc_task *task); | ||
117 | void (*rpcbind)(struct rpc_task *task); | 118 | void (*rpcbind)(struct rpc_task *task); |
118 | void (*set_port)(struct rpc_xprt *xprt, unsigned short port); | 119 | void (*set_port)(struct rpc_xprt *xprt, unsigned short port); |
119 | void (*connect)(struct rpc_task *task); | 120 | void (*connect)(struct rpc_task *task); |
@@ -281,6 +282,8 @@ void xprt_connect(struct rpc_task *task); | |||
281 | void xprt_reserve(struct rpc_task *task); | 282 | void xprt_reserve(struct rpc_task *task); |
282 | int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task); | 283 | int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task); |
283 | int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task); | 284 | int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task); |
285 | void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task); | ||
286 | void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task); | ||
284 | int xprt_prepare_transmit(struct rpc_task *task); | 287 | int xprt_prepare_transmit(struct rpc_task *task); |
285 | void xprt_transmit(struct rpc_task *task); | 288 | void xprt_transmit(struct rpc_task *task); |
286 | void xprt_end_transmit(struct rpc_task *task); | 289 | void xprt_end_transmit(struct rpc_task *task); |
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index a5a402a7d21f..5d7f61d7559c 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
@@ -969,11 +969,11 @@ static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) | |||
969 | return false; | 969 | return false; |
970 | } | 970 | } |
971 | 971 | ||
972 | static void xprt_alloc_slot(struct rpc_task *task) | 972 | void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) |
973 | { | 973 | { |
974 | struct rpc_xprt *xprt = task->tk_xprt; | ||
975 | struct rpc_rqst *req; | 974 | struct rpc_rqst *req; |
976 | 975 | ||
976 | spin_lock(&xprt->reserve_lock); | ||
977 | if (!list_empty(&xprt->free)) { | 977 | if (!list_empty(&xprt->free)) { |
978 | req = list_entry(xprt->free.next, struct rpc_rqst, rq_list); | 978 | req = list_entry(xprt->free.next, struct rpc_rqst, rq_list); |
979 | list_del(&req->rq_list); | 979 | list_del(&req->rq_list); |
@@ -994,12 +994,29 @@ static void xprt_alloc_slot(struct rpc_task *task) | |||
994 | default: | 994 | default: |
995 | task->tk_status = -EAGAIN; | 995 | task->tk_status = -EAGAIN; |
996 | } | 996 | } |
997 | spin_unlock(&xprt->reserve_lock); | ||
997 | return; | 998 | return; |
998 | out_init_req: | 999 | out_init_req: |
999 | task->tk_status = 0; | 1000 | task->tk_status = 0; |
1000 | task->tk_rqstp = req; | 1001 | task->tk_rqstp = req; |
1001 | xprt_request_init(task, xprt); | 1002 | xprt_request_init(task, xprt); |
1003 | spin_unlock(&xprt->reserve_lock); | ||
1004 | } | ||
1005 | EXPORT_SYMBOL_GPL(xprt_alloc_slot); | ||
1006 | |||
1007 | void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) | ||
1008 | { | ||
1009 | /* Note: grabbing the xprt_lock_write() ensures that we throttle | ||
1010 | * new slot allocation if the transport is congested (i.e. when | ||
1011 | * reconnecting a stream transport or when out of socket write | ||
1012 | * buffer space). | ||
1013 | */ | ||
1014 | if (xprt_lock_write(xprt, task)) { | ||
1015 | xprt_alloc_slot(xprt, task); | ||
1016 | xprt_release_write(xprt, task); | ||
1017 | } | ||
1002 | } | 1018 | } |
1019 | EXPORT_SYMBOL_GPL(xprt_lock_and_alloc_slot); | ||
1003 | 1020 | ||
1004 | static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) | 1021 | static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) |
1005 | { | 1022 | { |
@@ -1083,20 +1100,9 @@ void xprt_reserve(struct rpc_task *task) | |||
1083 | if (task->tk_rqstp != NULL) | 1100 | if (task->tk_rqstp != NULL) |
1084 | return; | 1101 | return; |
1085 | 1102 | ||
1086 | /* Note: grabbing the xprt_lock_write() here is not strictly needed, | ||
1087 | * but ensures that we throttle new slot allocation if the transport | ||
1088 | * is congested (e.g. if reconnecting or if we're out of socket | ||
1089 | * write buffer space). | ||
1090 | */ | ||
1091 | task->tk_timeout = 0; | 1103 | task->tk_timeout = 0; |
1092 | task->tk_status = -EAGAIN; | 1104 | task->tk_status = -EAGAIN; |
1093 | if (!xprt_lock_write(xprt, task)) | 1105 | xprt->ops->alloc_slot(xprt, task); |
1094 | return; | ||
1095 | |||
1096 | spin_lock(&xprt->reserve_lock); | ||
1097 | xprt_alloc_slot(task); | ||
1098 | spin_unlock(&xprt->reserve_lock); | ||
1099 | xprt_release_write(xprt, task); | ||
1100 | } | 1106 | } |
1101 | 1107 | ||
1102 | static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt) | 1108 | static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt) |
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 06cdbff79e4a..5d9202dc7cb1 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c | |||
@@ -713,6 +713,7 @@ static void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) | |||
713 | static struct rpc_xprt_ops xprt_rdma_procs = { | 713 | static struct rpc_xprt_ops xprt_rdma_procs = { |
714 | .reserve_xprt = xprt_rdma_reserve_xprt, | 714 | .reserve_xprt = xprt_rdma_reserve_xprt, |
715 | .release_xprt = xprt_release_xprt_cong, /* sunrpc/xprt.c */ | 715 | .release_xprt = xprt_release_xprt_cong, /* sunrpc/xprt.c */ |
716 | .alloc_slot = xprt_alloc_slot, | ||
716 | .release_request = xprt_release_rqst_cong, /* ditto */ | 717 | .release_request = xprt_release_rqst_cong, /* ditto */ |
717 | .set_retrans_timeout = xprt_set_retrans_timeout_def, /* ditto */ | 718 | .set_retrans_timeout = xprt_set_retrans_timeout_def, /* ditto */ |
718 | .rpcbind = rpcb_getport_async, /* sunrpc/rpcb_clnt.c */ | 719 | .rpcbind = rpcb_getport_async, /* sunrpc/rpcb_clnt.c */ |
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 400567243f84..a35b8e52e551 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
@@ -2473,6 +2473,7 @@ static void bc_destroy(struct rpc_xprt *xprt) | |||
2473 | static struct rpc_xprt_ops xs_local_ops = { | 2473 | static struct rpc_xprt_ops xs_local_ops = { |
2474 | .reserve_xprt = xprt_reserve_xprt, | 2474 | .reserve_xprt = xprt_reserve_xprt, |
2475 | .release_xprt = xs_tcp_release_xprt, | 2475 | .release_xprt = xs_tcp_release_xprt, |
2476 | .alloc_slot = xprt_alloc_slot, | ||
2476 | .rpcbind = xs_local_rpcbind, | 2477 | .rpcbind = xs_local_rpcbind, |
2477 | .set_port = xs_local_set_port, | 2478 | .set_port = xs_local_set_port, |
2478 | .connect = xs_connect, | 2479 | .connect = xs_connect, |
@@ -2489,6 +2490,7 @@ static struct rpc_xprt_ops xs_udp_ops = { | |||
2489 | .set_buffer_size = xs_udp_set_buffer_size, | 2490 | .set_buffer_size = xs_udp_set_buffer_size, |
2490 | .reserve_xprt = xprt_reserve_xprt_cong, | 2491 | .reserve_xprt = xprt_reserve_xprt_cong, |
2491 | .release_xprt = xprt_release_xprt_cong, | 2492 | .release_xprt = xprt_release_xprt_cong, |
2493 | .alloc_slot = xprt_alloc_slot, | ||
2492 | .rpcbind = rpcb_getport_async, | 2494 | .rpcbind = rpcb_getport_async, |
2493 | .set_port = xs_set_port, | 2495 | .set_port = xs_set_port, |
2494 | .connect = xs_connect, | 2496 | .connect = xs_connect, |
@@ -2506,6 +2508,7 @@ static struct rpc_xprt_ops xs_udp_ops = { | |||
2506 | static struct rpc_xprt_ops xs_tcp_ops = { | 2508 | static struct rpc_xprt_ops xs_tcp_ops = { |
2507 | .reserve_xprt = xprt_reserve_xprt, | 2509 | .reserve_xprt = xprt_reserve_xprt, |
2508 | .release_xprt = xs_tcp_release_xprt, | 2510 | .release_xprt = xs_tcp_release_xprt, |
2511 | .alloc_slot = xprt_lock_and_alloc_slot, | ||
2509 | .rpcbind = rpcb_getport_async, | 2512 | .rpcbind = rpcb_getport_async, |
2510 | .set_port = xs_set_port, | 2513 | .set_port = xs_set_port, |
2511 | .connect = xs_connect, | 2514 | .connect = xs_connect, |