diff options
author | Christoph Hellwig <hch@lst.de> | 2016-10-30 12:42:03 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-10-30 13:09:42 -0400 |
commit | 89319d31d2d097da8e27fb0e0ae9d532f4f16827 (patch) | |
tree | 157f0809709c4dd88818e5f76d84ef4aad196146 /fs/aio.c | |
parent | 723c038475b78edc9327eb952f95f9881cc9d79d (diff) |
fs: remove aio_run_iocb
Pass the ABI iocb structure to aio_setup_rw and let it handle the
non-vectored I/O case as well. With that and a new helper for the AIO
return value handling we can now define new aio_read and aio_write
helpers that implement reads and writes in a self-contained way without
duplicating too much code.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/aio.c')
-rw-r--r-- | fs/aio.c | 182 |
1 files changed, 94 insertions, 88 deletions
@@ -1392,110 +1392,100 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) | |||
1392 | return -EINVAL; | 1392 | return -EINVAL; |
1393 | } | 1393 | } |
1394 | 1394 | ||
1395 | typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *); | 1395 | static int aio_setup_rw(int rw, struct iocb *iocb, struct iovec **iovec, |
1396 | 1396 | bool vectored, bool compat, struct iov_iter *iter) | |
1397 | static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len, | ||
1398 | struct iovec **iovec, | ||
1399 | bool compat, | ||
1400 | struct iov_iter *iter) | ||
1401 | { | 1397 | { |
1398 | void __user *buf = (void __user *)(uintptr_t)iocb->aio_buf; | ||
1399 | size_t len = iocb->aio_nbytes; | ||
1400 | |||
1401 | if (!vectored) { | ||
1402 | ssize_t ret = import_single_range(rw, buf, len, *iovec, iter); | ||
1403 | *iovec = NULL; | ||
1404 | return ret; | ||
1405 | } | ||
1402 | #ifdef CONFIG_COMPAT | 1406 | #ifdef CONFIG_COMPAT |
1403 | if (compat) | 1407 | if (compat) |
1404 | return compat_import_iovec(rw, | 1408 | return compat_import_iovec(rw, buf, len, UIO_FASTIOV, iovec, |
1405 | (struct compat_iovec __user *)buf, | 1409 | iter); |
1406 | len, UIO_FASTIOV, iovec, iter); | ||
1407 | #endif | 1410 | #endif |
1408 | return import_iovec(rw, (struct iovec __user *)buf, | 1411 | return import_iovec(rw, buf, len, UIO_FASTIOV, iovec, iter); |
1409 | len, UIO_FASTIOV, iovec, iter); | ||
1410 | } | 1412 | } |
1411 | 1413 | ||
1412 | /* | 1414 | static inline ssize_t aio_ret(struct kiocb *req, ssize_t ret) |
1413 | * aio_run_iocb: | 1415 | { |
1414 | * Performs the initial checks and io submission. | 1416 | switch (ret) { |
1415 | */ | 1417 | case -EIOCBQUEUED: |
1416 | static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, | 1418 | return ret; |
1417 | char __user *buf, size_t len, bool compat) | 1419 | case -ERESTARTSYS: |
1420 | case -ERESTARTNOINTR: | ||
1421 | case -ERESTARTNOHAND: | ||
1422 | case -ERESTART_RESTARTBLOCK: | ||
1423 | /* | ||
1424 | * There's no easy way to restart the syscall since other AIO's | ||
1425 | * may be already running. Just fail this IO with EINTR. | ||
1426 | */ | ||
1427 | ret = -EINTR; | ||
1428 | /*FALLTHRU*/ | ||
1429 | default: | ||
1430 | aio_complete(req, ret, 0); | ||
1431 | return 0; | ||
1432 | } | ||
1433 | } | ||
1434 | |||
1435 | static ssize_t aio_read(struct kiocb *req, struct iocb *iocb, bool vectored, | ||
1436 | bool compat) | ||
1418 | { | 1437 | { |
1419 | struct file *file = req->ki_filp; | 1438 | struct file *file = req->ki_filp; |
1420 | ssize_t ret; | ||
1421 | int rw; | ||
1422 | fmode_t mode; | ||
1423 | rw_iter_op *iter_op; | ||
1424 | struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; | 1439 | struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; |
1425 | struct iov_iter iter; | 1440 | struct iov_iter iter; |
1441 | ssize_t ret; | ||
1426 | 1442 | ||
1427 | switch (opcode) { | 1443 | if (unlikely(!(file->f_mode & FMODE_READ))) |
1428 | case IOCB_CMD_PREAD: | 1444 | return -EBADF; |
1429 | case IOCB_CMD_PREADV: | 1445 | if (unlikely(!file->f_op->read_iter)) |
1430 | mode = FMODE_READ; | 1446 | return -EINVAL; |
1431 | rw = READ; | ||
1432 | iter_op = file->f_op->read_iter; | ||
1433 | goto rw_common; | ||
1434 | |||
1435 | case IOCB_CMD_PWRITE: | ||
1436 | case IOCB_CMD_PWRITEV: | ||
1437 | mode = FMODE_WRITE; | ||
1438 | rw = WRITE; | ||
1439 | iter_op = file->f_op->write_iter; | ||
1440 | goto rw_common; | ||
1441 | rw_common: | ||
1442 | if (unlikely(!(file->f_mode & mode))) | ||
1443 | return -EBADF; | ||
1444 | |||
1445 | if (!iter_op) | ||
1446 | return -EINVAL; | ||
1447 | |||
1448 | if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) | ||
1449 | ret = aio_setup_vectored_rw(rw, buf, len, | ||
1450 | &iovec, compat, &iter); | ||
1451 | else { | ||
1452 | ret = import_single_range(rw, buf, len, iovec, &iter); | ||
1453 | iovec = NULL; | ||
1454 | } | ||
1455 | if (!ret) | ||
1456 | ret = rw_verify_area(rw, file, &req->ki_pos, | ||
1457 | iov_iter_count(&iter)); | ||
1458 | if (ret < 0) { | ||
1459 | kfree(iovec); | ||
1460 | return ret; | ||
1461 | } | ||
1462 | |||
1463 | get_file(file); | ||
1464 | if (rw == WRITE) | ||
1465 | file_start_write(file); | ||
1466 | 1447 | ||
1467 | ret = iter_op(req, &iter); | 1448 | ret = aio_setup_rw(READ, iocb, &iovec, vectored, compat, &iter); |
1449 | if (ret) | ||
1450 | return ret; | ||
1451 | ret = rw_verify_area(READ, file, &req->ki_pos, iov_iter_count(&iter)); | ||
1452 | if (!ret) | ||
1453 | ret = aio_ret(req, file->f_op->read_iter(req, &iter)); | ||
1454 | kfree(iovec); | ||
1455 | return ret; | ||
1456 | } | ||
1468 | 1457 | ||
1469 | if (rw == WRITE) | 1458 | static ssize_t aio_write(struct kiocb *req, struct iocb *iocb, bool vectored, |
1470 | file_end_write(file); | 1459 | bool compat) |
1471 | fput(file); | 1460 | { |
1472 | kfree(iovec); | 1461 | struct file *file = req->ki_filp; |
1473 | break; | 1462 | struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; |
1463 | struct iov_iter iter; | ||
1464 | ssize_t ret; | ||
1474 | 1465 | ||
1475 | default: | 1466 | if (unlikely(!(file->f_mode & FMODE_WRITE))) |
1476 | pr_debug("EINVAL: no operation provided\n"); | 1467 | return -EBADF; |
1468 | if (unlikely(!file->f_op->write_iter)) | ||
1477 | return -EINVAL; | 1469 | return -EINVAL; |
1478 | } | ||
1479 | 1470 | ||
1480 | if (ret != -EIOCBQUEUED) { | 1471 | ret = aio_setup_rw(WRITE, iocb, &iovec, vectored, compat, &iter); |
1481 | /* | 1472 | if (ret) |
1482 | * There's no easy way to restart the syscall since other AIO's | 1473 | return ret; |
1483 | * may be already running. Just fail this IO with EINTR. | 1474 | ret = rw_verify_area(WRITE, file, &req->ki_pos, iov_iter_count(&iter)); |
1484 | */ | 1475 | if (!ret) { |
1485 | if (unlikely(ret == -ERESTARTSYS || ret == -ERESTARTNOINTR || | 1476 | file_start_write(file); |
1486 | ret == -ERESTARTNOHAND || | 1477 | ret = aio_ret(req, file->f_op->write_iter(req, &iter)); |
1487 | ret == -ERESTART_RESTARTBLOCK)) | 1478 | file_end_write(file); |
1488 | ret = -EINTR; | ||
1489 | aio_complete(req, ret, 0); | ||
1490 | } | 1479 | } |
1491 | 1480 | kfree(iovec); | |
1492 | return 0; | 1481 | return ret; |
1493 | } | 1482 | } |
1494 | 1483 | ||
1495 | static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, | 1484 | static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, |
1496 | struct iocb *iocb, bool compat) | 1485 | struct iocb *iocb, bool compat) |
1497 | { | 1486 | { |
1498 | struct aio_kiocb *req; | 1487 | struct aio_kiocb *req; |
1488 | struct file *file; | ||
1499 | ssize_t ret; | 1489 | ssize_t ret; |
1500 | 1490 | ||
1501 | /* enforce forwards compatibility on users */ | 1491 | /* enforce forwards compatibility on users */ |
@@ -1518,7 +1508,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, | |||
1518 | if (unlikely(!req)) | 1508 | if (unlikely(!req)) |
1519 | return -EAGAIN; | 1509 | return -EAGAIN; |
1520 | 1510 | ||
1521 | req->common.ki_filp = fget(iocb->aio_fildes); | 1511 | req->common.ki_filp = file = fget(iocb->aio_fildes); |
1522 | if (unlikely(!req->common.ki_filp)) { | 1512 | if (unlikely(!req->common.ki_filp)) { |
1523 | ret = -EBADF; | 1513 | ret = -EBADF; |
1524 | goto out_put_req; | 1514 | goto out_put_req; |
@@ -1553,13 +1543,29 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, | |||
1553 | req->ki_user_iocb = user_iocb; | 1543 | req->ki_user_iocb = user_iocb; |
1554 | req->ki_user_data = iocb->aio_data; | 1544 | req->ki_user_data = iocb->aio_data; |
1555 | 1545 | ||
1556 | ret = aio_run_iocb(&req->common, iocb->aio_lio_opcode, | 1546 | get_file(file); |
1557 | (char __user *)(unsigned long)iocb->aio_buf, | 1547 | switch (iocb->aio_lio_opcode) { |
1558 | iocb->aio_nbytes, | 1548 | case IOCB_CMD_PREAD: |
1559 | compat); | 1549 | ret = aio_read(&req->common, iocb, false, compat); |
1560 | if (ret) | 1550 | break; |
1561 | goto out_put_req; | 1551 | case IOCB_CMD_PWRITE: |
1552 | ret = aio_write(&req->common, iocb, false, compat); | ||
1553 | break; | ||
1554 | case IOCB_CMD_PREADV: | ||
1555 | ret = aio_read(&req->common, iocb, true, compat); | ||
1556 | break; | ||
1557 | case IOCB_CMD_PWRITEV: | ||
1558 | ret = aio_write(&req->common, iocb, true, compat); | ||
1559 | break; | ||
1560 | default: | ||
1561 | pr_debug("invalid aio operation %d\n", iocb->aio_lio_opcode); | ||
1562 | ret = -EINVAL; | ||
1563 | break; | ||
1564 | } | ||
1565 | fput(file); | ||
1562 | 1566 | ||
1567 | if (ret && ret != -EIOCBQUEUED) | ||
1568 | goto out_put_req; | ||
1563 | return 0; | 1569 | return 0; |
1564 | out_put_req: | 1570 | out_put_req: |
1565 | put_reqs_available(ctx, 1); | 1571 | put_reqs_available(ctx, 1); |