6671fc0e66d8e78e9f7c0fbeac8dd98a8b4d0946
[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 2GB of mapped files in total
19  */
20 #define MMAP_TOTAL_SZ   (2 * 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 & ~mmap_map_mask;
94         if (io_u->offset + io_u->buflen >= f->mmap_off + f->mmap_sz)
95                 f->mmap_off -= io_u->buflen;
96
97         ret = fio_mmap_file(td, f, f->mmap_sz, f->mmap_off);
98 done:
99         if (!ret)
100                 io_u->mmap_data = f->mmap_ptr + io_u->offset - f->mmap_off -
101                                         f->file_offset;
102 err:
103         return ret;
104 }
105
106 static int fio_mmapio_queue(struct thread_data *td, struct io_u *io_u)
107 {
108         struct fio_file *f = io_u->file;
109
110         fio_ro_check(td, io_u);
111
112         if (io_u->ddir == DDIR_READ)
113                 memcpy(io_u->xfer_buf, io_u->mmap_data, io_u->xfer_buflen);
114         else if (io_u->ddir == DDIR_WRITE)
115                 memcpy(io_u->mmap_data, io_u->xfer_buf, io_u->xfer_buflen);
116         else if (ddir_sync(io_u->ddir)) {
117                 if (msync(f->mmap_ptr, f->mmap_sz, MS_SYNC)) {
118                         io_u->error = errno;
119                         td_verror(td, io_u->error, "msync");
120                 }
121         }
122
123         /*
124          * not really direct, but should drop the pages from the cache
125          */
126         if (td->o.odirect && !ddir_sync(io_u->ddir)) {
127                 if (msync(io_u->mmap_data, io_u->xfer_buflen, MS_SYNC) < 0) {
128                         io_u->error = errno;
129                         td_verror(td, io_u->error, "msync");
130                 }
131                 if (madvise(io_u->mmap_data, io_u->xfer_buflen,  MADV_DONTNEED) < 0) {
132                         io_u->error = errno;
133                         td_verror(td, io_u->error, "madvise");
134                 }
135         }
136
137         return FIO_Q_COMPLETED;
138 }
139
140 static int fio_mmapio_init(struct thread_data *td)
141 {
142         unsigned long shift, mask;
143
144         mmap_map_size = MMAP_TOTAL_SZ / td->o.nr_files;
145         mask = mmap_map_size;
146         shift = 0;
147         do {
148                 mask >>= 1;
149                 if (!mask)
150                         break;
151                 shift++;
152         } while (1);
153                 
154         mmap_map_mask = 1UL << shift;
155         return 0;
156 }
157
158 static struct ioengine_ops ioengine = {
159         .name           = "mmap",
160         .version        = FIO_IOOPS_VERSION,
161         .init           = fio_mmapio_init,
162         .prep           = fio_mmapio_prep,
163         .queue          = fio_mmapio_queue,
164         .open_file      = generic_open_file,
165         .close_file     = generic_close_file,
166         .get_file_size  = generic_get_file_size,
167         .flags          = FIO_SYNCIO | FIO_NOEXTEND,
168 };
169
170 static void fio_init fio_mmapio_register(void)
171 {
172         register_ioengine(&ioengine);
173 }
174
175 static void fio_exit fio_mmapio_unregister(void)
176 {
177         unregister_ioengine(&ioengine);
178 }