diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-01-07 11:36:45 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-01-07 11:36:45 -0500 |
commit | 4c9014f2ca7e5ecf8806e838be0f07aa1810c985 (patch) | |
tree | dd33fdebedf1ce6497737113e183f2a5d8bbca63 | |
parent | 5ce2955e04a80da7287dc12f32da7f870039bf8f (diff) | |
parent | ecf0eb9edbb607d74f74b73c14af8b43f3729528 (diff) |
Merge tag 'nfs-for-3.8-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
Pull NFS client bugfixes from Trond Myklebust:
- Fix a permissions problem when opening NFSv4 files that only have the
exec bit set.
- Fix a couple of typos in pNFS (inverted logic), and the mount parsing
(missing pointer dereference).
- Work around a series of deadlock issues due to workqueues using
struct work_struct pointer address comparisons in the re-entrancy
tests. Ensure that we don't free struct work_struct prematurely if
our work function involves waiting for completion of other work items
(e.g. by calling rpc_shutdown_client).
- Revert the part of commit 168e4b3 that is causing unnecessary
warnings to be issued in the nfsd callback code.
* tag 'nfs-for-3.8-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
nfs: avoid dereferencing null pointer in initiate_bulk_draining
SUNRPC: Partial revert of commit 168e4b39d1afb79a7e3ea6c3bb246b4c82c6bdb9
NFS: Ensure that we free the rpc_task after read and write cleanups are done
SUNRPC: Ensure that we free the rpc_task after cleanups are done
nfs: fix null checking in nfs_get_option_str()
pnfs: Increase the refcount when LAYOUTGET fails the first time
NFS: Fix access to suid/sgid executables
-rw-r--r-- | fs/nfs/callback_proc.c | 2 | ||||
-rw-r--r-- | fs/nfs/dir.c | 16 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 18 | ||||
-rw-r--r-- | fs/nfs/pnfs.c | 2 | ||||
-rw-r--r-- | fs/nfs/read.c | 10 | ||||
-rw-r--r-- | fs/nfs/super.c | 2 | ||||
-rw-r--r-- | fs/nfs/write.c | 10 | ||||
-rw-r--r-- | net/sunrpc/clnt.c | 5 | ||||
-rw-r--r-- | net/sunrpc/sched.c | 27 |
9 files changed, 61 insertions, 31 deletions
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index c89b26bc9759..264d1aa935f2 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c | |||
@@ -206,7 +206,7 @@ static u32 initiate_bulk_draining(struct nfs_client *clp, | |||
206 | 206 | ||
207 | list_for_each_entry(lo, &server->layouts, plh_layouts) { | 207 | list_for_each_entry(lo, &server->layouts, plh_layouts) { |
208 | ino = igrab(lo->plh_inode); | 208 | ino = igrab(lo->plh_inode); |
209 | if (ino) | 209 | if (!ino) |
210 | continue; | 210 | continue; |
211 | spin_lock(&ino->i_lock); | 211 | spin_lock(&ino->i_lock); |
212 | /* Is this layout in the process of being freed? */ | 212 | /* Is this layout in the process of being freed? */ |
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 32e6c53520e2..1b2d7eb93796 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -2153,12 +2153,16 @@ static int nfs_open_permission_mask(int openflags) | |||
2153 | { | 2153 | { |
2154 | int mask = 0; | 2154 | int mask = 0; |
2155 | 2155 | ||
2156 | if ((openflags & O_ACCMODE) != O_WRONLY) | 2156 | if (openflags & __FMODE_EXEC) { |
2157 | mask |= MAY_READ; | 2157 | /* ONLY check exec rights */ |
2158 | if ((openflags & O_ACCMODE) != O_RDONLY) | 2158 | mask = MAY_EXEC; |
2159 | mask |= MAY_WRITE; | 2159 | } else { |
2160 | if (openflags & __FMODE_EXEC) | 2160 | if ((openflags & O_ACCMODE) != O_WRONLY) |
2161 | mask |= MAY_EXEC; | 2161 | mask |= MAY_READ; |
2162 | if ((openflags & O_ACCMODE) != O_RDONLY) | ||
2163 | mask |= MAY_WRITE; | ||
2164 | } | ||
2165 | |||
2162 | return mask; | 2166 | return mask; |
2163 | } | 2167 | } |
2164 | 2168 | ||
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5d864fb36578..cf747ef86650 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -1626,7 +1626,8 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data) | |||
1626 | 1626 | ||
1627 | static int nfs4_opendata_access(struct rpc_cred *cred, | 1627 | static int nfs4_opendata_access(struct rpc_cred *cred, |
1628 | struct nfs4_opendata *opendata, | 1628 | struct nfs4_opendata *opendata, |
1629 | struct nfs4_state *state, fmode_t fmode) | 1629 | struct nfs4_state *state, fmode_t fmode, |
1630 | int openflags) | ||
1630 | { | 1631 | { |
1631 | struct nfs_access_entry cache; | 1632 | struct nfs_access_entry cache; |
1632 | u32 mask; | 1633 | u32 mask; |
@@ -1638,11 +1639,14 @@ static int nfs4_opendata_access(struct rpc_cred *cred, | |||
1638 | 1639 | ||
1639 | mask = 0; | 1640 | mask = 0; |
1640 | /* don't check MAY_WRITE - a newly created file may not have | 1641 | /* don't check MAY_WRITE - a newly created file may not have |
1641 | * write mode bits, but POSIX allows the creating process to write */ | 1642 | * write mode bits, but POSIX allows the creating process to write. |
1642 | if (fmode & FMODE_READ) | 1643 | * use openflags to check for exec, because fmode won't |
1643 | mask |= MAY_READ; | 1644 | * always have FMODE_EXEC set when file open for exec. */ |
1644 | if (fmode & FMODE_EXEC) | 1645 | if (openflags & __FMODE_EXEC) { |
1645 | mask |= MAY_EXEC; | 1646 | /* ONLY check for exec rights */ |
1647 | mask = MAY_EXEC; | ||
1648 | } else if (fmode & FMODE_READ) | ||
1649 | mask = MAY_READ; | ||
1646 | 1650 | ||
1647 | cache.cred = cred; | 1651 | cache.cred = cred; |
1648 | cache.jiffies = jiffies; | 1652 | cache.jiffies = jiffies; |
@@ -1896,7 +1900,7 @@ static int _nfs4_do_open(struct inode *dir, | |||
1896 | if (server->caps & NFS_CAP_POSIX_LOCK) | 1900 | if (server->caps & NFS_CAP_POSIX_LOCK) |
1897 | set_bit(NFS_STATE_POSIX_LOCKS, &state->flags); | 1901 | set_bit(NFS_STATE_POSIX_LOCKS, &state->flags); |
1898 | 1902 | ||
1899 | status = nfs4_opendata_access(cred, opendata, state, fmode); | 1903 | status = nfs4_opendata_access(cred, opendata, state, fmode, flags); |
1900 | if (status != 0) | 1904 | if (status != 0) |
1901 | goto err_opendata_put; | 1905 | goto err_opendata_put; |
1902 | 1906 | ||
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index e7165d915362..d00260b08103 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c | |||
@@ -254,7 +254,7 @@ static void | |||
254 | pnfs_layout_set_fail_bit(struct pnfs_layout_hdr *lo, int fail_bit) | 254 | pnfs_layout_set_fail_bit(struct pnfs_layout_hdr *lo, int fail_bit) |
255 | { | 255 | { |
256 | lo->plh_retry_timestamp = jiffies; | 256 | lo->plh_retry_timestamp = jiffies; |
257 | if (test_and_set_bit(fail_bit, &lo->plh_flags)) | 257 | if (!test_and_set_bit(fail_bit, &lo->plh_flags)) |
258 | atomic_inc(&lo->plh_refcount); | 258 | atomic_inc(&lo->plh_refcount); |
259 | } | 259 | } |
260 | 260 | ||
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index b6bdb18e892c..a5e5d9899d56 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c | |||
@@ -91,12 +91,16 @@ void nfs_readdata_release(struct nfs_read_data *rdata) | |||
91 | put_nfs_open_context(rdata->args.context); | 91 | put_nfs_open_context(rdata->args.context); |
92 | if (rdata->pages.pagevec != rdata->pages.page_array) | 92 | if (rdata->pages.pagevec != rdata->pages.page_array) |
93 | kfree(rdata->pages.pagevec); | 93 | kfree(rdata->pages.pagevec); |
94 | if (rdata != &read_header->rpc_data) | 94 | if (rdata == &read_header->rpc_data) { |
95 | kfree(rdata); | ||
96 | else | ||
97 | rdata->header = NULL; | 95 | rdata->header = NULL; |
96 | rdata = NULL; | ||
97 | } | ||
98 | if (atomic_dec_and_test(&hdr->refcnt)) | 98 | if (atomic_dec_and_test(&hdr->refcnt)) |
99 | hdr->completion_ops->completion(hdr); | 99 | hdr->completion_ops->completion(hdr); |
100 | /* Note: we only free the rpc_task after callbacks are done. | ||
101 | * See the comment in rpc_free_task() for why | ||
102 | */ | ||
103 | kfree(rdata); | ||
100 | } | 104 | } |
101 | EXPORT_SYMBOL_GPL(nfs_readdata_release); | 105 | EXPORT_SYMBOL_GPL(nfs_readdata_release); |
102 | 106 | ||
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index c25cadf8f8c4..2e7e8c878e5d 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -1152,7 +1152,7 @@ static int nfs_get_option_str(substring_t args[], char **option) | |||
1152 | { | 1152 | { |
1153 | kfree(*option); | 1153 | kfree(*option); |
1154 | *option = match_strdup(args); | 1154 | *option = match_strdup(args); |
1155 | return !option; | 1155 | return !*option; |
1156 | } | 1156 | } |
1157 | 1157 | ||
1158 | static int nfs_get_option_ul(substring_t args[], unsigned long *option) | 1158 | static int nfs_get_option_ul(substring_t args[], unsigned long *option) |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index b673be31590e..c483cc50b82e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -126,12 +126,16 @@ void nfs_writedata_release(struct nfs_write_data *wdata) | |||
126 | put_nfs_open_context(wdata->args.context); | 126 | put_nfs_open_context(wdata->args.context); |
127 | if (wdata->pages.pagevec != wdata->pages.page_array) | 127 | if (wdata->pages.pagevec != wdata->pages.page_array) |
128 | kfree(wdata->pages.pagevec); | 128 | kfree(wdata->pages.pagevec); |
129 | if (wdata != &write_header->rpc_data) | 129 | if (wdata == &write_header->rpc_data) { |
130 | kfree(wdata); | ||
131 | else | ||
132 | wdata->header = NULL; | 130 | wdata->header = NULL; |
131 | wdata = NULL; | ||
132 | } | ||
133 | if (atomic_dec_and_test(&hdr->refcnt)) | 133 | if (atomic_dec_and_test(&hdr->refcnt)) |
134 | hdr->completion_ops->completion(hdr); | 134 | hdr->completion_ops->completion(hdr); |
135 | /* Note: we only free the rpc_task after callbacks are done. | ||
136 | * See the comment in rpc_free_task() for why | ||
137 | */ | ||
138 | kfree(wdata); | ||
135 | } | 139 | } |
136 | EXPORT_SYMBOL_GPL(nfs_writedata_release); | 140 | EXPORT_SYMBOL_GPL(nfs_writedata_release); |
137 | 141 | ||
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 822f020fa7f4..1915ffe598e3 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -610,11 +610,6 @@ EXPORT_SYMBOL_GPL(rpc_killall_tasks); | |||
610 | */ | 610 | */ |
611 | void rpc_shutdown_client(struct rpc_clnt *clnt) | 611 | void rpc_shutdown_client(struct rpc_clnt *clnt) |
612 | { | 612 | { |
613 | /* | ||
614 | * To avoid deadlock, never call rpc_shutdown_client from a | ||
615 | * workqueue context! | ||
616 | */ | ||
617 | WARN_ON_ONCE(current->flags & PF_WQ_WORKER); | ||
618 | might_sleep(); | 613 | might_sleep(); |
619 | 614 | ||
620 | dprintk_rcu("RPC: shutting down %s client for %s\n", | 615 | dprintk_rcu("RPC: shutting down %s client for %s\n", |
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index d17a704aaf5f..b4133bd13915 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c | |||
@@ -934,16 +934,35 @@ struct rpc_task *rpc_new_task(const struct rpc_task_setup *setup_data) | |||
934 | return task; | 934 | return task; |
935 | } | 935 | } |
936 | 936 | ||
937 | /* | ||
938 | * rpc_free_task - release rpc task and perform cleanups | ||
939 | * | ||
940 | * Note that we free up the rpc_task _after_ rpc_release_calldata() | ||
941 | * in order to work around a workqueue dependency issue. | ||
942 | * | ||
943 | * Tejun Heo states: | ||
944 | * "Workqueue currently considers two work items to be the same if they're | ||
945 | * on the same address and won't execute them concurrently - ie. it | ||
946 | * makes a work item which is queued again while being executed wait | ||
947 | * for the previous execution to complete. | ||
948 | * | ||
949 | * If a work function frees the work item, and then waits for an event | ||
950 | * which should be performed by another work item and *that* work item | ||
951 | * recycles the freed work item, it can create a false dependency loop. | ||
952 | * There really is no reliable way to detect this short of verifying | ||
953 | * every memory free." | ||
954 | * | ||
955 | */ | ||
937 | static void rpc_free_task(struct rpc_task *task) | 956 | static void rpc_free_task(struct rpc_task *task) |
938 | { | 957 | { |
939 | const struct rpc_call_ops *tk_ops = task->tk_ops; | 958 | unsigned short tk_flags = task->tk_flags; |
940 | void *calldata = task->tk_calldata; | 959 | |
960 | rpc_release_calldata(task->tk_ops, task->tk_calldata); | ||
941 | 961 | ||
942 | if (task->tk_flags & RPC_TASK_DYNAMIC) { | 962 | if (tk_flags & RPC_TASK_DYNAMIC) { |
943 | dprintk("RPC: %5u freeing task\n", task->tk_pid); | 963 | dprintk("RPC: %5u freeing task\n", task->tk_pid); |
944 | mempool_free(task, rpc_task_mempool); | 964 | mempool_free(task, rpc_task_mempool); |
945 | } | 965 | } |
946 | rpc_release_calldata(tk_ops, calldata); | ||
947 | } | 966 | } |
948 | 967 | ||
949 | static void rpc_async_release(struct work_struct *work) | 968 | static void rpc_async_release(struct work_struct *work) |