Home | History | Annotate | Download | only in engines
      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 
     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  */
     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  */
    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 
    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 
    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 
    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 
    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 
    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 
    264 static void fio_init fio_mmapio_register(void)
    265 {
    266 	register_ioengine(&ioengine);
    267 }
    268 
    269 static void fio_exit fio_mmapio_unregister(void)
    270 {
    271 	unregister_ioengine(&ioengine);
    272 }
    273