|
37 | 37 |
|
38 | 38 | #include "internal.h"
|
39 | 39 |
|
| 40 | +/* |
| 41 | + * Splice doesn't support FMODE_NOWAIT. Since pipes may set this flag to |
| 42 | + * indicate they support non-blocking reads or writes, we must clear it |
| 43 | + * here if set to avoid blocking other users of this pipe if splice is |
| 44 | + * being done on it. |
| 45 | + */ |
| 46 | +static noinline void noinline pipe_clear_nowait(struct file *file) |
| 47 | +{ |
| 48 | + fmode_t fmode = READ_ONCE(file->f_mode); |
| 49 | + |
| 50 | + do { |
| 51 | + if (!(fmode & FMODE_NOWAIT)) |
| 52 | + break; |
| 53 | + } while (!try_cmpxchg(&file->f_mode, &fmode, fmode & ~FMODE_NOWAIT)); |
| 54 | +} |
| 55 | + |
40 | 56 | /*
|
41 | 57 | * Attempt to steal a page from a pipe buffer. This should perhaps go into
|
42 | 58 | * a vm helper function, it's already simplified quite a bit by the
|
@@ -1211,10 +1227,16 @@ static long __do_splice(struct file *in, loff_t __user *off_in,
|
1211 | 1227 | ipipe = get_pipe_info(in, true);
|
1212 | 1228 | opipe = get_pipe_info(out, true);
|
1213 | 1229 |
|
1214 |
| - if (ipipe && off_in) |
1215 |
| - return -ESPIPE; |
1216 |
| - if (opipe && off_out) |
1217 |
| - return -ESPIPE; |
| 1230 | + if (ipipe) { |
| 1231 | + if (off_in) |
| 1232 | + return -ESPIPE; |
| 1233 | + pipe_clear_nowait(in); |
| 1234 | + } |
| 1235 | + if (opipe) { |
| 1236 | + if (off_out) |
| 1237 | + return -ESPIPE; |
| 1238 | + pipe_clear_nowait(out); |
| 1239 | + } |
1218 | 1240 |
|
1219 | 1241 | if (off_out) {
|
1220 | 1242 | if (copy_from_user(&offset, off_out, sizeof(loff_t)))
|
@@ -1311,6 +1333,8 @@ static long vmsplice_to_user(struct file *file, struct iov_iter *iter,
|
1311 | 1333 | if (!pipe)
|
1312 | 1334 | return -EBADF;
|
1313 | 1335 |
|
| 1336 | + pipe_clear_nowait(file); |
| 1337 | + |
1314 | 1338 | if (sd.total_len) {
|
1315 | 1339 | pipe_lock(pipe);
|
1316 | 1340 | ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
|
@@ -1339,6 +1363,8 @@ static long vmsplice_to_pipe(struct file *file, struct iov_iter *iter,
|
1339 | 1363 | if (!pipe)
|
1340 | 1364 | return -EBADF;
|
1341 | 1365 |
|
| 1366 | + pipe_clear_nowait(file); |
| 1367 | + |
1342 | 1368 | pipe_lock(pipe);
|
1343 | 1369 | ret = wait_for_space(pipe, flags);
|
1344 | 1370 | if (!ret)
|
|
0 commit comments