Add support for O_ATOMIC
authorChris Mason <chris.mason@fusionio.com>
Fri, 15 Nov 2013 22:52:58 +0000 (15:52 -0700)
committerJens Axboe <axboe@kernel.dk>
Fri, 15 Nov 2013 22:55:41 +0000 (15:55 -0700)
O_ATOMIC makes writes atomic, meaning that they are fully stable
on media (in the event of a power cut) when acknowledged by the
device and OS.

This only truly works on Linux with the pending patches to
add O_ATOMIC.

Updated by Jens to:

- Add man page and HOWTO description of the option
- Make O_ATOMIC imply O_DIRECT, so that it actually works if you
  don't set O_DIRECT manually.
- Add the option to the conversion list so it works for
  client/server.
- Error handling so that if atomic=1 is set and the OS does not
  support it, error out instead of just pretending it works.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
12 files changed:
HOWTO
backend.c
cconv.c
filesetup.c
fio.1
init.c
memory.c
options.c
os/os-linux.h
os/os.h
server.h
thread_options.h

diff --git a/HOWTO b/HOWTO
index eb2ed25403cb8dfe2ae96e8a93d5e99aa98c8b39..250bc582f548640a92087a6d2119962d5f63d81e 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -695,6 +695,11 @@ direct=bool        If value is true, use non-buffered io. This is usually
                O_DIRECT. Note that ZFS on Solaris doesn't support direct io.
                On Windows the synchronous ioengines don't support direct io.
 
+atomic=bool    If value is true, attempt to use atomic direct IO. Atomic
+               writes are guaranteed to be stable once acknowledged by
+               the operating system. Only Linux supports O_ATOMIC right
+               now.
+
 buffered=bool  If value is true, use buffered io. This is the opposite
                of the 'direct' option. Defaults to true.
 
index 00a23db6ecd1dfa6caaf98124605653bd2c85c97..2ec478c1ac206ebf4163a2e854fb77002dba2366 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -926,7 +926,8 @@ static int init_io_u(struct thread_data *td)
         * overflow later. this adjustment may be too much if we get
         * lucky and the allocator gives us an aligned address.
         */
-       if (td->o.odirect || td->o.mem_align || (td->io_ops->flags & FIO_RAWIO))
+       if (td->o.odirect || td->o.mem_align || td->o.oatomic ||
+           (td->io_ops->flags & FIO_RAWIO))
                td->orig_buffer_size += page_mask + td->o.mem_align;
 
        if (td->o.mem_type == MEM_SHMHUGE || td->o.mem_type == MEM_MMAPHUGE) {
@@ -944,7 +945,7 @@ static int init_io_u(struct thread_data *td)
        if (data_xfer && allocate_io_mem(td))
                return 1;
 
-       if (td->o.odirect || td->o.mem_align ||
+       if (td->o.odirect || td->o.mem_align || td->o.oatomic ||
            (td->io_ops->flags & FIO_RAWIO))
                p = PAGE_ALIGN(td->orig_buffer) + td->o.mem_align;
        else
diff --git a/cconv.c b/cconv.c
index 21e3a51bb4dfa6a431c05f3562b6a9415a45a14b..82383b2dedfc32a6bb7aed75c1390642123b5196 100644 (file)
--- a/cconv.c
+++ b/cconv.c
@@ -89,6 +89,7 @@ void convert_thread_options_to_cpu(struct thread_options *o,
        o->open_files = le32_to_cpu(top->open_files);
        o->file_lock_mode = le32_to_cpu(top->file_lock_mode);
        o->odirect = le32_to_cpu(top->odirect);
+       o->oatomic = le32_to_cpu(top->oatomic);
        o->invalidate_cache = le32_to_cpu(top->invalidate_cache);
        o->create_serialize = le32_to_cpu(top->create_serialize);
        o->create_fsync = le32_to_cpu(top->create_fsync);
@@ -252,6 +253,7 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
        top->open_files = cpu_to_le32(o->open_files);
        top->file_lock_mode = cpu_to_le32(o->file_lock_mode);
        top->odirect = cpu_to_le32(o->odirect);
+       top->oatomic = cpu_to_le32(o->oatomic);
        top->invalidate_cache = cpu_to_le32(o->invalidate_cache);
        top->create_serialize = cpu_to_le32(o->create_serialize);
        top->create_fsync = cpu_to_le32(o->create_fsync);
index 4265e3830a73c57f0f3a1c1d24e87db6b3344d9a..c9b060b7e8aba6b8cad4c498d3ee6736caaa6dc0 100644 (file)
@@ -519,6 +519,13 @@ int generic_open_file(struct thread_data *td, struct fio_file *f)
                goto skip_flags;
        if (td->o.odirect)
                flags |= OS_O_DIRECT;
+       if (td->o.oatomic) {
+               if (!FIO_O_ATOMIC) {
+                       td_verror(td, EINVAL, "OS does not support atomic IO");
+                       return 1;
+               }
+               flags |= OS_O_DIRECT | FIO_O_ATOMIC;
+       }
        if (td->o.sync_io)
                flags |= O_SYNC;
        if (td->o.create_on_open)
diff --git a/fio.1 b/fio.1
index e910e0102e76ff756c3cd85217df3d6123ba3a54..15a1ac5d00a42b59bfe6596e60074a3ef4009537 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -569,6 +569,11 @@ Low watermark indicating when to start filling the queue again.  Default:
 .BI direct \fR=\fPbool
 If true, use non-buffered I/O (usually O_DIRECT).  Default: false.
 .TP
+.BI atomic \fR=\fPbool
+If value is true, attempt to use atomic direct IO. Atomic writes are guaranteed
+to be stable once acknowledged by the operating system. Only Linux supports
+O_ATOMIC right now.
+.TP
 .BI buffered \fR=\fPbool
 If true, use buffered I/O.  This is the opposite of the \fBdirect\fR parameter.
 Default: true.
diff --git a/init.c b/init.c
index b45b039ee686d0e18b66bb2cf647c5d324455e16..1841ffc0fd152a81f72989a669aae08fe7dcc26e 100644 (file)
--- a/init.c
+++ b/init.c
@@ -629,6 +629,12 @@ static int fixup_options(struct thread_data *td)
                ret = 1;
        }
 
+       /*
+        * O_ATOMIC implies O_DIRECT
+        */
+       if (td->o.oatomic)
+               td->o.odirect = 1;
+
        return ret;
 }
 
index e06cab2900a531e1515308fb166e626b6076274f..b208320c5d88cc619f7167626e3b7502ded07799 100644 (file)
--- a/memory.c
+++ b/memory.c
@@ -209,7 +209,7 @@ int allocate_io_mem(struct thread_data *td)
 
        total_mem = td->orig_buffer_size;
 
-       if (td->o.odirect || td->o.mem_align ||
+       if (td->o.odirect || td->o.mem_align || td->o.oatomic ||
            (td->io_ops->flags & FIO_MEMALIGN)) {
                total_mem += page_mask;
                if (td->o.mem_align && td->o.mem_align > page_size)
@@ -240,7 +240,7 @@ void free_io_mem(struct thread_data *td)
        unsigned int total_mem;
 
        total_mem = td->orig_buffer_size;
-       if (td->o.odirect)
+       if (td->o.odirect || td->o.oatomic)
                total_mem += page_mask;
 
        if (td->o.mem_type == MEM_MALLOC)
index f26ff77647329a645f0fa4ead9eb9a58adcceba7..4b4c251b92c3e6895d9dafe99c54dc1c9c388147 100644 (file)
--- a/options.c
+++ b/options.c
@@ -1892,6 +1892,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_IO_TYPE,
        },
+       {
+               .name   = "atomic",
+               .lname  = "Atomic I/O",
+               .type   = FIO_OPT_BOOL,
+               .off1   = td_var_offset(oatomic),
+               .help   = "Use Atomic IO with O_DIRECT (implies O_DIRECT)",
+               .def    = "0",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_TYPE,
+       },
        {
                .name   = "buffered",
                .lname  = "Buffered I/O",
index 869a25d8ee5d86c684762d91c4f7a4496f2b5d7f..5d1d62db27a0e2234ceaed73878e6bf4d37316ec 100644 (file)
@@ -196,6 +196,12 @@ static inline int fio_lookup_raw(dev_t dev, int *majdev, int *mindev)
 #define FIO_O_NOATIME  0
 #endif
 
+#ifdef O_ATOMIC
+#define OS_O_ATOMIC    O_ATOMIC
+#else
+#define OS_O_ATOMIC    040000000
+#endif
+
 #ifdef MADV_REMOVE
 #define FIO_MADV_FREE  MADV_REMOVE
 #endif
diff --git a/os/os.h b/os/os.h
index 4416ae482fb46a176cccf252bea8307dbc24b0c8..715f2260a3f6992b13e2e6f164511b7a08e54e82 100644 (file)
--- a/os/os.h
+++ b/os/os.h
@@ -90,6 +90,12 @@ typedef unsigned long os_cpu_mask_t;
 #define OS_O_DIRECT                    O_DIRECT
 #endif
 
+#ifdef OS_O_ATOMIC
+#define FIO_O_ATOMIC                   OS_O_ATOMIC
+#else
+#define FIO_O_ATOMIC                   0
+#endif
+
 #ifndef FIO_HAVE_HUGETLB
 #define SHM_HUGETLB                    0
 #define MAP_HUGETLB                    0
index 5d9b6ccadae36c8114918466f9e8b52f794c79bd..405370e32103f233c372e7da03222d86ba21df40 100644 (file)
--- a/server.h
+++ b/server.h
@@ -38,7 +38,7 @@ struct fio_net_cmd_reply {
 };
 
 enum {
-       FIO_SERVER_VER                  = 26,
+       FIO_SERVER_VER                  = 27,
 
        FIO_SERVER_MAX_FRAGMENT_PDU     = 1024,
 
index 484b16a56e7568dc42fd55847be967e469ae1070..44cbf91acb4ed81e72a5352267f8f9f46604b234 100644 (file)
@@ -72,6 +72,7 @@ struct thread_options {
        enum file_lock_mode file_lock_mode;
 
        unsigned int odirect;
+       unsigned int oatomic;
        unsigned int invalidate_cache;
        unsigned int create_serialize;
        unsigned int create_fsync;
@@ -286,6 +287,7 @@ struct thread_options_pack {
        uint32_t file_lock_mode;
 
        uint32_t odirect;
+       uint32_t oatomic;
        uint32_t invalidate_cache;
        uint32_t create_serialize;
        uint32_t create_fsync;