bd9a9428661abe38048233c0c56c5be256ee7311
[fio.git] / engines / mmap.c
1 /*
2  * mmap engine
3  *
4  * IO engine that reads/writes from files by doing memcpy to/from
5  * a memory mapped region of the file.
6  *
7  */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <sys/mman.h>
13
14 #include "../fio.h"
15 #include "../verify.h"
16
17 /*
18  * Limits us to 1GB of mapped files in total
19  */
20 #define MMAP_TOTAL_SZ   (1 * 1024 * 1024 * 1024UL)
21
22 static unsigned long mmap_map_size;
23 static unsigned long mmap_map_mask;
24
25 static int fio_mmap_file(struct thread_data *td, struct fio_file *f,
26                          size_t length, off_t off)
27 {
28         int flags = 0;
29         int ret = 0;
30
31         if (td_rw(td))
32                 flags = PROT_READ | PROT_WRITE;
33         else if (td_write(td)) {
34                 flags = PROT_WRITE;
35
36                 if (td->o.verify != VERIFY_NONE)
37                         flags |= PROT_READ;
38         } else
39                 flags = PROT_READ;
40
41         f->mmap_ptr = mmap(NULL, length, flags, MAP_SHARED, f->fd, off);
42         if (f->mmap_ptr == MAP_FAILED) {
43                 int err = errno;
44
45                 f->mmap_ptr = NULL;
46                 td_verror(td, err, "mmap");
47                 goto err;
48         }
49
50         if (!td_random(td)) {
51                 if (madvise(f->mmap_ptr, length, MADV_SEQUENTIAL) < 0) {
52                         td_verror(td, errno, "madvise");
53                         goto err;
54                 }
55         } else {
56                 if (madvise(f->mmap_ptr, length, MADV_RANDOM) < 0) {
57                         td_verror(td, errno, "madvise");
58                         goto err;
59                 }
60         }
61
62 err:
63         return ret;
64 }
65
66 static int fio_mmapio_prep(struct thread_data *td, struct io_u *io_u)
67 {
68         struct fio_file *f = io_u->file;
69         int ret = 0;
70
71         if (io_u->buflen > mmap_map_size) {
72                 log_err("fio: bs too big for mmap engine\n");
73                 ret = EIO;
74                 goto err;
75         }
76
77         if (io_u->offset >= f->mmap_off &&
78             io_u->offset + io_u->buflen < f->mmap_off + f->mmap_sz)
79                 goto done;
80
81         if (f->mmap_ptr) {
82                 if (munmap(f->mmap_ptr, f->mmap_sz) < 0) {
83                         ret = errno;
84                         goto err;
85                 }
86                 f->mmap_ptr = NULL;
87         }
88
89         f->mmap_sz = mmap_map_size;
90         if (f->mmap_sz  > f->io_size)
91                 f->mmap_sz = f->io_size;
92
93         f->mmap_off = io_u->offset;
94
95         ret = fio_mmap_file(td, f, f->mmap_sz, f->mmap_off);
96 done:
97         if (!ret)
98                 io_u->mmap_data = f->mmap_ptr + io_u->offset - f->mmap_off -
99                                         f->file_offset;
100 err:
101         return ret;
102 }
103
104 static int fio_mmapio_queue(struct thread_data *td, struct io_u *io_u)
105 {
106         struct fio_file *f = io_u->file;
107
108         fio_ro_check(td, io_u);
109
110         if (io_u->ddir == DDIR_READ)
111                 memcpy(io_u->xfer_buf, io_u->mmap_data, io_u->xfer_buflen);
112         else if (io_u->ddir == DDIR_WRITE)
113                 memcpy(io_u->mmap_data, io_u->xfer_buf, io_u->xfer_buflen);
114         else if (ddir_sync(io_u->ddir)) {
115                 if (msync(f->mmap_ptr, f->mmap_sz, MS_SYNC)) {
116                         io_u->error = errno;
117                         td_verror(td, io_u->error, "msync");
118                 }
119         }
120
121         /*
122          * not really direct, but should drop the pages from the cache
123          */
124         if (td->o.odirect && !ddir_sync(io_u->ddir)) {
125                 if (msync(io_u->mmap_data, io_u->xfer_buflen, MS_SYNC) < 0) {
126                         io_u->error = errno;
127                         td_verror(td, io_u->error, "msync");
128                 }
129                 if (madvise(io_u->mmap_data, io_u->xfer_buflen,  MADV_DONTNEED) < 0) {
130                         io_u->error = errno;
131                         td_verror(td, io_u->error, "madvise");
132                 }
133         }
134
135         return FIO_Q_COMPLETED;
136 }
137
138 static int fio_mmapio_init(struct thread_data *td)
139 {
140         unsigned long shift, mask;
141
142         mmap_map_size = MMAP_TOTAL_SZ / td->o.nr_files;
143         mask = mmap_map_size;
144         shift = 0;
145         do {
146                 mask >>= 1;
147                 if (!mask)
148                         break;
149                 shift++;
150         } while (1);
151                 
152         mmap_map_mask = 1UL << shift;
153         return 0;
154 }
155
156 static struct ioengine_ops ioengine = {
157         .name           = "mmap",
158         .version        = FIO_IOOPS_VERSION,
159         .init           = fio_mmapio_init,
160         .prep           = fio_mmapio_prep,
161         .queue          = fio_mmapio_queue,
162         .open_file      = generic_open_file,
163         .close_file     = generic_close_file,
164         .get_file_size  = generic_get_file_size,
165         .flags          = FIO_SYNCIO | FIO_NOEXTEND,
166 };
167
168 static void fio_init fio_mmapio_register(void)
169 {
170         register_ioengine(&ioengine);
171 }
172
173 static void fio_exit fio_mmapio_unregister(void)
174 {
175         unregister_ioengine(&ioengine);
176 }