1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdio.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <sys/syscall.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <pthread.h>
25 #include <sys/stat.h>
26 #include <sys/errno.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <assert.h>
30 #include "ioshark.h"
31 #include "ioshark_bench.h"
32 
33 /*
34  * The purpose of this code is to convert mmap() calls into
35  * a mix of (semi)-random reads and writes.
36  * PROT_READ => 4KB/8KB/16KB random reads.
37  * PROT_WRITE => adds 4KB random writes.
38  */
39 
40 extern char *progname;
41 
42 #define IOSHARK_MAX_MMAP_IOLEN	(16*1024)
43 
44 #define MMAP_ENTS		16
45 
46 struct mmap_io_ent_tab_s {
47 	off_t offset;
48 	size_t len;
49 };
50 
51 struct mmap_io_ent_s {
52 	int				num_entries;
53 	struct mmap_io_ent_tab_s	table[MMAP_ENTS + 1];
54 	size_t				resid;
55 };
56 
57 static void
setup_mmap_io_state(struct mmap_io_ent_s * mio,size_t total_len,off_t offset)58 setup_mmap_io_state(struct mmap_io_ent_s *mio,
59 		    size_t total_len, off_t offset)
60 {
61 	int slice;
62 
63 	memset(mio, 0, sizeof(struct mmap_io_ent_s));
64 	mio->resid = total_len;
65 	slice = MAX(IOSHARK_MAX_MMAP_IOLEN,
66 		    total_len / MMAP_ENTS);
67 	while (total_len > 0) {
68 		assert(mio->num_entries < MMAP_ENTS + 1);
69 		mio->table[mio->num_entries].offset = offset;
70 		mio->table[mio->num_entries].len =
71 			MIN((u_int64_t)total_len, (u_int64_t)slice);
72 		total_len -= mio->table[mio->num_entries].len;
73 		offset += mio->table[mio->num_entries].len;
74 		mio->num_entries++;
75 	}
76 }
77 
78 static size_t
mmap_getnext_off_len(struct mmap_io_ent_s * mio,off_t * offset)79 mmap_getnext_off_len(struct mmap_io_ent_s *mio,
80 		     off_t *offset)
81 {
82 	int i;
83 	int find_rand_index[MMAP_ENTS + 1];
84 	int rand_index_len = 0;
85 	size_t iolength;
86 
87 	if (mio->resid == 0)
88 		return 0;
89 	/* Pick a slot with residual length > 0 at random first */
90 	for (i = 0 ; i < MMAP_ENTS + 1 ; i++) {
91 		if (mio->table[i].len > 0)
92 			find_rand_index[rand_index_len++] = i;
93 	}
94 	i = find_rand_index[rand() % rand_index_len];
95 	/* Randomize iolength 4K-16K */
96 	iolength = ((rand() % 4) + 1) * 4096;
97 	iolength = MIN(mio->table[i].len, iolength);
98 	*offset = mio->table[i].offset;
99 	mio->table[i].offset += iolength;
100 	mio->table[i].len -= iolength;
101 	mio->resid -= iolength;
102 	return iolength;
103 }
104 
105 static void
mmap_do_io(void * db_node,int prot,off_t offset,size_t len,char ** bufp,int * buflen,u_int64_t * op_counts,struct rw_bytes_s * rw_bytes)106 mmap_do_io(void *db_node, int prot, off_t offset, size_t len,
107 	   char **bufp, int *buflen, u_int64_t *op_counts,
108 	   struct rw_bytes_s *rw_bytes)
109 {
110 	char *p;
111 	int ret;
112 
113 	if (!(prot & IOSHARK_PROT_WRITE)) {
114 		/* Only preads */
115 		p = get_buf(bufp, buflen, len, 0);
116 		ret = pread(files_db_get_fd(db_node),
117 			    p, len, offset);
118 		rw_bytes->bytes_read += len;
119 		if (ret < 0) {
120 			fprintf(stderr,
121 				"%s: mapped pread(%s %zu %lu) error fd=%d %s\n",
122 				progname, files_db_get_filename(db_node),
123 				len, offset, files_db_get_fd(db_node),
124 				strerror(errno));
125 			exit(EXIT_FAILURE);
126 		}
127 		op_counts[IOSHARK_MAPPED_PREAD]++;
128 	} else {
129 		/* 50-50 R/W */
130 		if ((rand() % 2) == 1) {
131 			p = get_buf(bufp, buflen, len, 1);
132 			ret = pwrite(files_db_get_fd(db_node),
133 				     p, len, offset);
134 			rw_bytes->bytes_written += len;
135 			if (ret < 0) {
136 #if 0
137 				fprintf(stderr,
138 					"%s: mapped pwrite failed, file unwriteable ? open_flags=%x\n",
139 					progname,
140 					fcntl(files_db_get_fd(db_node), F_GETFL));
141 				exit(EXIT_FAILURE);
142 #endif
143 			} else
144 				op_counts[IOSHARK_MAPPED_PWRITE]++;
145 		} else {
146 			p = get_buf(bufp, buflen, len, 0);
147 			ret = pread(files_db_get_fd(db_node),
148 				    p, len, offset);
149 			rw_bytes->bytes_read += len;
150 			if (ret < 0) {
151 				fprintf(stderr,
152 				"%s: mapped pread(%s %zu %lu) error fd=%d %s\n",
153 					progname, files_db_get_filename(db_node),
154 					len,
155 					offset, files_db_get_fd(db_node),
156 					strerror(errno));
157 				exit(EXIT_FAILURE);
158 			}
159 			op_counts[IOSHARK_MAPPED_PREAD]++;
160 		}
161 	}
162 }
163 
164 void
ioshark_handle_mmap(void * db_node,struct ioshark_file_operation * file_op,char ** bufp,int * buflen,u_int64_t * op_counts,struct rw_bytes_s * rw_bytes)165 ioshark_handle_mmap(void *db_node,
166 		    struct ioshark_file_operation *file_op,
167 		    char **bufp, int *buflen, u_int64_t *op_counts,
168 		    struct rw_bytes_s *rw_bytes)
169 {
170 	off_t offset = file_op->mmap_offset;
171 	size_t len = file_op->mmap_len;
172 	int prot = file_op->mmap_prot;
173 	struct mmap_io_ent_s mio;
174 	struct stat statbuf;
175 
176 	if (fstat(files_db_get_fd(db_node), &statbuf) < 0) {
177 		fprintf(stderr,
178 			"%s: fstat failure %s\n",
179 			__func__, strerror(errno));
180 		exit(EXIT_FAILURE);
181 	}
182 	/*
183 	 * The size of the file better accomodate offset + len
184 	 * Else there is an issue with pre-creation
185 	 */
186 	assert(offset + len <= statbuf.st_size);
187 	if (len <= IOSHARK_MAX_MMAP_IOLEN) {
188 		mmap_do_io(db_node, prot, offset, len,
189 			   bufp, buflen, op_counts,
190 			   rw_bytes);
191 		return;
192 	}
193 	setup_mmap_io_state(&mio, len, offset);
194 	assert(mio.num_entries > 0);
195 	while ((len = mmap_getnext_off_len(&mio, &offset))) {
196 		assert((offset + len) <=
197 		       (file_op->mmap_offset + file_op->mmap_len));
198 		mmap_do_io(db_node, prot, offset, len, bufp, buflen,
199 			   op_counts, rw_bytes);
200 	}
201 }
202