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
40extern char *progname;
41
42#define IOSHARK_MAX_MMAP_IOLEN	(16*1024)
43
44#define MMAP_ENTS		16
45
46struct mmap_io_ent_tab_s {
47	off_t offset;
48	size_t len;
49};
50
51struct 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
57static void
58setup_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
78static size_t
79mmap_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
105static void
106mmap_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
164void
165ioshark_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