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 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 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 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 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