@@ -20,6 +20,7 @@
#include <linux/mman.h>
#include <linux/sched/mm.h>
#include <linux/crc32.h>
+#include <linux/task_io_accounting_ops.h>
#include "zonefs.h"
@@ -582,6 +583,89 @@ static const struct iomap_dio_ops zonefs_write_dio_ops = {
.end_io = zonefs_file_write_dio_end_io,
};
+static void zonefs_zone_append_bio_endio(struct bio *bio)
+{
+ struct task_struct *waiter = bio->bi_private;
+
+ WRITE_ONCE(bio->bi_private, NULL);
+ blk_wake_io_task(waiter);
+
+ bio_release_pages(bio, false);
+ bio_put(bio);
+}
+
+static ssize_t zonefs_file_dio_append(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+ struct zonefs_inode_info *zi = ZONEFS_I(inode);
+ struct block_device *bdev = inode->i_sb->s_bdev;
+ ssize_t ret = 0;
+ ssize_t size;
+ struct bio *bio;
+ unsigned max;
+ int nr_pages;
+ blk_qc_t qc;
+
+ nr_pages = iov_iter_npages(from, BIO_MAX_PAGES);
+ if (!nr_pages)
+ return 0;
+
+ max = queue_max_zone_append_sectors(bdev_get_queue(bdev)) << 9;
+ max = ALIGN_DOWN(max, inode->i_sb->s_blocksize);
+ iov_iter_truncate(from, max);
+
+ bio = bio_alloc_bioset(GFP_NOFS, nr_pages, &fs_bio_set);
+ if (!bio)
+ return -ENOMEM;
+
+ bio_set_dev(bio, bdev);
+ bio->bi_iter.bi_sector = zi->i_zsector;
+ bio->bi_write_hint = iocb->ki_hint;
+ bio->bi_private = current;
+ bio->bi_end_io = zonefs_zone_append_bio_endio;
+ bio->bi_ioprio = iocb->ki_ioprio;
+ bio->bi_opf = REQ_OP_ZONE_APPEND | REQ_SYNC | REQ_IDLE;
+ if (iocb->ki_flags & IOCB_DSYNC)
+ bio->bi_opf |= REQ_FUA;
+
+ ret = bio_iov_iter_get_pages(bio, from);
+ if (unlikely(ret)) {
+ bio->bi_status = BLK_STS_IOERR;
+ bio_endio(bio);
+ return ret;
+ }
+ size = bio->bi_iter.bi_size;
+ task_io_account_write(ret);
+
+ if (iocb->ki_flags & IOCB_HIPRI)
+ bio_set_polled(bio, iocb);
+
+ bio_get(bio);
+ qc = submit_bio(bio);
+ for (;;) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (!READ_ONCE(bio->bi_private))
+ break;
+ if (!(iocb->ki_flags & IOCB_HIPRI) ||
+ !blk_poll(bdev_get_queue(bdev), qc, true))
+ io_schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+
+ if (unlikely(bio->bi_status))
+ ret = blk_status_to_errno(bio->bi_status);
+
+ bio_put(bio);
+
+ zonefs_file_write_dio_end_io(iocb, size, ret, 0);
+ if (ret >= 0) {
+ iocb->ki_pos += size;
+ return size;
+ }
+
+ return ret;
+}
+
/*
* Handle direct writes. For sequential zone files, this is the only possible
* write path. For these files, check that the user is issuing writes
@@ -599,6 +683,7 @@ static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from)
struct super_block *sb = inode->i_sb;
size_t count;
ssize_t ret;
+ bool sync = is_sync_kiocb(iocb);
/*
* For async direct IOs to sequential zone files, refuse IOCB_NOWAIT
@@ -637,8 +722,11 @@ static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from)
}
mutex_unlock(&zi->i_truncate_mutex);
- ret = iomap_dio_rw(iocb, from, &zonefs_iomap_ops,
- &zonefs_write_dio_ops, is_sync_kiocb(iocb));
+ if (sync && zi->i_ztype == ZONEFS_ZTYPE_SEQ)
+ ret = zonefs_file_dio_append(iocb, from);
+ else
+ ret = iomap_dio_rw(iocb, from, &zonefs_iomap_ops,
+ &zonefs_write_dio_ops, sync);
if (zi->i_ztype == ZONEFS_ZTYPE_SEQ &&
(ret > 0 || ret == -EIOCBQUEUED)) {
if (ret > 0)
Synchronous direct I/O to a sequential write only zone can be issued using the new REQ_OP_ZONE_APPEND request operation. As dispatching multiple BIOs can potentially result in reordering, we cannot support asynchronous IO via this interface. We also can only dispatch up to queue_max_zone_append_sectors() via the new zone-append method and have to return a short write back to user-space in case an IO larger than queue_max_zone_append_sectors() has been issued. Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> --- fs/zonefs/super.c | 92 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 2 deletions(-)