Add support for Fusion-io atomic write engine
[fio.git] / engines / fusion-aw.c
1 /*
2  * Custom fio(1) engine that submits synchronous atomic writes to file.
3  *
4  * Copyright (C) 2012 Fusion-io, Inc.
5  * Author: Santhosh Kumar Koundinya.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; under version 2 of the License.  You may obtain a copy
10  * of the source code for DirectFS by sending a request to Fusion-io, Inc.,
11  * 2855 E. Cottonwood Parkway, Suite 100, Salt Lake City, UT 84121; Attention:
12  * Legal Department.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License version
17  * 2 for more details.
18  *
19  * You should have received a copy of the GNU General Public License Version 2
20  * along with this program; if not see <http://www.gnu.org/licenses/>
21  */
22
23 #include <stdlib.h>
24 #include <stdint.h>
25
26 #include "../fio.h"
27
28 #ifdef FIO_HAVE_FUSION_AW
29
30 #include <vsl_dp_experimental/vectored_write.h>
31
32 /* Fix sector size to 512 bytes independent of actual sector size, just like
33  * the linux kernel. */
34 #define SECTOR_SHIFT    9
35 #define SECTOR_SIZE    (1U<<SECTOR_SHIFT)
36
37 struct acs_file_data {
38         struct vsl_iovec iov[IO_VECTOR_LIMIT];
39 };
40
41 static int queue(struct thread_data *td, struct io_u *io_u)
42 {
43         int rc;
44         int iov_index;
45         off_t offset;
46         char *xfer_buf;
47         size_t xfer_buflen;
48         struct acs_file_data *d = io_u->file->file_data;
49
50         if (io_u->ddir != DDIR_WRITE) {
51                 td_vmsg(td, -EIO, "only writes supported", "io_u->ddir");
52                 rc = -EIO;
53                 goto out;
54         }
55         if (io_u->xfer_buflen > IO_SIZE_MAX) {
56                 td_vmsg(td, -EIO, "data too big", "io_u->xfer_buflen");
57                 rc = -EIO;
58                 goto out;
59         }
60         if (io_u->xfer_buflen & (SECTOR_SIZE - 1)) {
61                 td_vmsg(td, -EIO, "unaligned data size", "io_u->xfer_buflen");
62                 rc = -EIO;
63                 goto out;
64         }
65
66         /* Chop up the write into minimal number of iovec's necessary */
67         iov_index = 0;
68         offset = io_u->offset;
69         xfer_buf = io_u->xfer_buf;
70         xfer_buflen = io_u->xfer_buflen;
71         while (xfer_buflen) {
72                 struct vsl_iovec *iov = &d->iov[iov_index++];
73
74                 iov->iov_len = xfer_buflen > IO_VECTOR_MAX_SIZE ?
75                     IO_VECTOR_MAX_SIZE : xfer_buflen;
76                 iov->iov_base = (uint64_t) xfer_buf;
77                 iov->sector = offset >> SECTOR_SHIFT;
78                 iov->iov_flag = VSL_IOV_WRITE;
79
80                 offset += iov->iov_len;
81                 xfer_buf += iov->iov_len;
82                 xfer_buflen -= iov->iov_len;
83         }
84         assert(xfer_buflen == 0);
85         assert(iov_index <= IO_VECTOR_LIMIT);
86
87         rc = vsl_vectored_write(io_u->file->fd, d->iov, iov_index, O_ATOMIC);
88         if (rc == -1) {
89                 td_verror(td, -errno, "vsl_vectored_write");
90                 rc = -EIO;
91                 goto out;
92         } else {
93                 io_u->error = 0;
94                 io_u->file->file_pos = io_u->offset + rc;
95                 rc = FIO_Q_COMPLETED;
96         }
97
98 out:
99         if (rc < 0)
100                 io_u->error = rc;
101
102         return rc;
103 }
104
105 static int open_file(struct thread_data *td, struct fio_file *f)
106 {
107         int rc;
108         struct acs_file_data *d = NULL;
109
110         d = malloc(sizeof(*d));
111         if (!d) {
112                 td_verror(td, -ENOMEM, "malloc");
113                 rc = -ENOMEM;
114                 goto error;
115         }
116         f->file_data = d;
117
118         rc = generic_open_file(td, f);
119
120 out:
121         return rc;
122
123 error:
124         f->fd = -1;
125         f->file_data = NULL;
126         if (d)
127                 free(d);
128
129         goto out;
130 }
131
132 static int close_file(struct thread_data *td, struct fio_file *f)
133 {
134         if (f->file_data) {
135                 free(f->file_data);
136                 f->file_data = NULL;
137         }
138
139         return generic_close_file(td, f);
140 }
141
142 static struct ioengine_ops ioengine = {
143         .name = "fusion-aw-sync",
144         .version = FIO_IOOPS_VERSION,
145         .queue = queue,
146         .open_file = open_file,
147         .close_file = close_file,
148         .get_file_size = generic_get_file_size,
149         .flags = FIO_SYNCIO | FIO_RAWIO | FIO_MEMALIGN
150 };
151
152 #else /* !FUSION_HAVE_FUSION_AW */
153
154 static int fio_fusion_aw_eng_init(struct thread_data fio_unused *td)
155 {
156         log_err("fio: fusion atomic write engine not available\n");
157         return 1;
158 }
159
160 static struct ioengine_ops ioengine = {
161         .name           = "fusion-aw-sync",
162         .version        = FIO_IOOPS_VERSION,
163         .init           = fio_fusion_aw_eng_init,
164 };
165
166 #endif /* FUSION_HAVE_FUSION_AW */
167
168 static void fio_init fio_fusion_aw_init(void)
169 {
170         register_ioengine(&ioengine);
171 }
172
173 static void fio_exit fio_fusion_aw_exit(void)
174 {
175         unregister_ioengine(&ioengine);
176 }