• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 1GiB of mapped files in total
19  */
20 #define MMAP_TOTAL_SZ	(1 * 1024 * 1024 * 1024UL)
21 
22 static unsigned long mmap_map_size;
23 
24 struct fio_mmap_data {
25 	void *mmap_ptr;
26 	size_t mmap_sz;
27 	off_t mmap_off;
28 };
29 
fio_mmap_file(struct thread_data * td,struct fio_file * f,size_t length,off_t off)30 static int fio_mmap_file(struct thread_data *td, struct fio_file *f,
31 			 size_t length, off_t off)
32 {
33 	struct fio_mmap_data *fmd = FILE_ENG_DATA(f);
34 	int flags = 0;
35 
36 	if (td_rw(td))
37 		flags = PROT_READ | PROT_WRITE;
38 	else if (td_write(td)) {
39 		flags = PROT_WRITE;
40 
41 		if (td->o.verify != VERIFY_NONE)
42 			flags |= PROT_READ;
43 	} else
44 		flags = PROT_READ;
45 
46 	fmd->mmap_ptr = mmap(NULL, length, flags, MAP_SHARED, f->fd, off);
47 	if (fmd->mmap_ptr == MAP_FAILED) {
48 		fmd->mmap_ptr = NULL;
49 		td_verror(td, errno, "mmap");
50 		goto err;
51 	}
52 
53 	if (!td_random(td)) {
54 		if (posix_madvise(fmd->mmap_ptr, length, POSIX_MADV_SEQUENTIAL) < 0) {
55 			td_verror(td, errno, "madvise");
56 			goto err;
57 		}
58 	} else {
59 		if (posix_madvise(fmd->mmap_ptr, length, POSIX_MADV_RANDOM) < 0) {
60 			td_verror(td, errno, "madvise");
61 			goto err;
62 		}
63 	}
64 	if (posix_madvise(fmd->mmap_ptr, length, POSIX_MADV_DONTNEED) < 0) {
65 		td_verror(td, errno, "madvise");
66 		goto err;
67 	}
68 
69 #ifdef FIO_MADV_FREE
70 	if (f->filetype == FIO_TYPE_BLOCK)
71 		(void) posix_madvise(fmd->mmap_ptr, fmd->mmap_sz, FIO_MADV_FREE);
72 #endif
73 
74 err:
75 	if (td->error && fmd->mmap_ptr)
76 		munmap(fmd->mmap_ptr, length);
77 
78 	return td->error;
79 }
80 
81 /*
82  * Just mmap an appropriate portion, we cannot mmap the full extent
83  */
fio_mmapio_prep_limited(struct thread_data * td,struct io_u * io_u)84 static int fio_mmapio_prep_limited(struct thread_data *td, struct io_u *io_u)
85 {
86 	struct fio_file *f = io_u->file;
87 	struct fio_mmap_data *fmd = FILE_ENG_DATA(f);
88 
89 	if (io_u->buflen > mmap_map_size) {
90 		log_err("fio: bs too big for mmap engine\n");
91 		return EIO;
92 	}
93 
94 	fmd->mmap_sz = mmap_map_size;
95 	if (fmd->mmap_sz  > f->io_size)
96 		fmd->mmap_sz = f->io_size;
97 
98 	fmd->mmap_off = io_u->offset;
99 
100 	return fio_mmap_file(td, f, fmd->mmap_sz, fmd->mmap_off);
101 }
102 
103 /*
104  * Attempt to mmap the entire file
105  */
fio_mmapio_prep_full(struct thread_data * td,struct io_u * io_u)106 static int fio_mmapio_prep_full(struct thread_data *td, struct io_u *io_u)
107 {
108 	struct fio_file *f = io_u->file;
109 	struct fio_mmap_data *fmd = FILE_ENG_DATA(f);
110 	int ret;
111 
112 	if (fio_file_partial_mmap(f))
113 		return EINVAL;
114 	if (io_u->offset != (size_t) io_u->offset ||
115 	    f->io_size != (size_t) f->io_size) {
116 		fio_file_set_partial_mmap(f);
117 		return EINVAL;
118 	}
119 
120 	fmd->mmap_sz = f->io_size;
121 	fmd->mmap_off = 0;
122 
123 	ret = fio_mmap_file(td, f, fmd->mmap_sz, fmd->mmap_off);
124 	if (ret)
125 		fio_file_set_partial_mmap(f);
126 
127 	return ret;
128 }
129 
fio_mmapio_prep(struct thread_data * td,struct io_u * io_u)130 static int fio_mmapio_prep(struct thread_data *td, struct io_u *io_u)
131 {
132 	struct fio_file *f = io_u->file;
133 	struct fio_mmap_data *fmd = FILE_ENG_DATA(f);
134 	int ret;
135 
136 	/*
137 	 * It fits within existing mapping, use it
138 	 */
139 	if (io_u->offset >= fmd->mmap_off &&
140 	    io_u->offset + io_u->buflen < fmd->mmap_off + fmd->mmap_sz)
141 		goto done;
142 
143 	/*
144 	 * unmap any existing mapping
145 	 */
146 	if (fmd->mmap_ptr) {
147 		if (munmap(fmd->mmap_ptr, fmd->mmap_sz) < 0)
148 			return errno;
149 		fmd->mmap_ptr = NULL;
150 	}
151 
152 	if (fio_mmapio_prep_full(td, io_u)) {
153 		td_clear_error(td);
154 		ret = fio_mmapio_prep_limited(td, io_u);
155 		if (ret)
156 			return ret;
157 	}
158 
159 done:
160 	io_u->mmap_data = fmd->mmap_ptr + io_u->offset - fmd->mmap_off -
161 				f->file_offset;
162 	return 0;
163 }
164 
fio_mmapio_queue(struct thread_data * td,struct io_u * io_u)165 static int fio_mmapio_queue(struct thread_data *td, struct io_u *io_u)
166 {
167 	struct fio_file *f = io_u->file;
168 	struct fio_mmap_data *fmd = FILE_ENG_DATA(f);
169 
170 	fio_ro_check(td, io_u);
171 
172 	if (io_u->ddir == DDIR_READ)
173 		memcpy(io_u->xfer_buf, io_u->mmap_data, io_u->xfer_buflen);
174 	else if (io_u->ddir == DDIR_WRITE)
175 		memcpy(io_u->mmap_data, io_u->xfer_buf, io_u->xfer_buflen);
176 	else if (ddir_sync(io_u->ddir)) {
177 		if (msync(fmd->mmap_ptr, fmd->mmap_sz, MS_SYNC)) {
178 			io_u->error = errno;
179 			td_verror(td, io_u->error, "msync");
180 		}
181 	} else if (io_u->ddir == DDIR_TRIM) {
182 		int ret = do_io_u_trim(td, io_u);
183 
184 		if (!ret)
185 			td_verror(td, io_u->error, "trim");
186 	}
187 
188 
189 	/*
190 	 * not really direct, but should drop the pages from the cache
191 	 */
192 	if (td->o.odirect && ddir_rw(io_u->ddir)) {
193 		if (msync(io_u->mmap_data, io_u->xfer_buflen, MS_SYNC) < 0) {
194 			io_u->error = errno;
195 			td_verror(td, io_u->error, "msync");
196 		}
197 		if (posix_madvise(io_u->mmap_data, io_u->xfer_buflen, POSIX_MADV_DONTNEED) < 0) {
198 			io_u->error = errno;
199 			td_verror(td, io_u->error, "madvise");
200 		}
201 	}
202 
203 	return FIO_Q_COMPLETED;
204 }
205 
fio_mmapio_init(struct thread_data * td)206 static int fio_mmapio_init(struct thread_data *td)
207 {
208 	struct thread_options *o = &td->o;
209 
210 	if ((o->rw_min_bs & page_mask) &&
211 	    (o->odirect || o->fsync_blocks || o->fdatasync_blocks)) {
212 		log_err("fio: mmap options dictate a minimum block size of "
213 			"%llu bytes\n", (unsigned long long) page_size);
214 		return 1;
215 	}
216 
217 	mmap_map_size = MMAP_TOTAL_SZ / o->nr_files;
218 	return 0;
219 }
220 
fio_mmapio_open_file(struct thread_data * td,struct fio_file * f)221 static int fio_mmapio_open_file(struct thread_data *td, struct fio_file *f)
222 {
223 	struct fio_mmap_data *fmd;
224 	int ret;
225 
226 	ret = generic_open_file(td, f);
227 	if (ret)
228 		return ret;
229 
230 	fmd = calloc(1, sizeof(*fmd));
231 	if (!fmd) {
232 		int fio_unused __ret;
233 		__ret = generic_close_file(td, f);
234 		return 1;
235 	}
236 
237 	FILE_SET_ENG_DATA(f, fmd);
238 	return 0;
239 }
240 
fio_mmapio_close_file(struct thread_data * td,struct fio_file * f)241 static int fio_mmapio_close_file(struct thread_data *td, struct fio_file *f)
242 {
243 	struct fio_mmap_data *fmd = FILE_ENG_DATA(f);
244 
245 	FILE_SET_ENG_DATA(f, NULL);
246 	free(fmd);
247 	fio_file_clear_partial_mmap(f);
248 
249 	return generic_close_file(td, f);
250 }
251 
252 static struct ioengine_ops ioengine = {
253 	.name		= "mmap",
254 	.version	= FIO_IOOPS_VERSION,
255 	.init		= fio_mmapio_init,
256 	.prep		= fio_mmapio_prep,
257 	.queue		= fio_mmapio_queue,
258 	.open_file	= fio_mmapio_open_file,
259 	.close_file	= fio_mmapio_close_file,
260 	.get_file_size	= generic_get_file_size,
261 	.flags		= FIO_SYNCIO | FIO_NOEXTEND,
262 };
263 
fio_mmapio_register(void)264 static void fio_init fio_mmapio_register(void)
265 {
266 	register_ioengine(&ioengine);
267 }
268 
fio_mmapio_unregister(void)269 static void fio_exit fio_mmapio_unregister(void)
270 {
271 	unregister_ioengine(&ioengine);
272 }
273