1 /* 2 * Test dataspace operations. 3 * 4 * Copyright (C) 2020, 2021 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/re/env.h> 23 #include <l4/sys/err.h> 24 25 #include <stdio.h> 26 #include <string.h> 27 #include <stdlib.h> 28 29 #include <mutex> 30 #include <set> 31 #include <thread> 32 33 #include <ipc/cap_alloc.h> 34 #include <ipc/mem_ipc.h> 35 36 #include "dataspace_client.h" 37 #include "opener_client.h" 38 #include "opener_context_client.h" 39 #include "mapped_file_client.h" 40 #include "memory_utils.h" 41 42 43 44 const unsigned int ACTIVITY_ITERATIONS = 100; 45 const unsigned long NUMBER_OF_FILES = 10; 46 const unsigned int START_LIMIT = 10; 47 const unsigned int MAP_PAGES = 20; 48 49 50 51 static std::mutex _lock; 52 static std::set<void *> _memory; 53 54 static long attach_dataspace(l4_cap_idx_t cap, unsigned long size, void **memory) 55 { 56 std::lock_guard<std::mutex> guard(_lock); 57 58 long err = ipc_attach_dataspace(cap, size, memory); 59 60 if (err) 61 return err; 62 63 if (_memory.find(*memory) != _memory.end()) 64 { 65 printf("Memory assigned again: %p\n", *memory); 66 return -L4_EINVAL; 67 } 68 69 _memory.insert(*memory); 70 return L4_EOK; 71 } 72 73 static long detach_dataspace(void *memory) 74 { 75 std::lock_guard<std::mutex> guard(_lock); 76 77 _memory.erase(memory); 78 return ipc_detach_dataspace(memory); 79 } 80 81 82 83 static long activity(l4_cap_idx_t context_ref, unsigned long fileid, unsigned int start_page) 84 { 85 client_OpenerContext context(context_ref); 86 unsigned long step = page(1); 87 unsigned long sample = page(1); 88 89 /* Allocate a buffer for sampling from the file. */ 90 91 char buf[sample + 1]; 92 93 /* Invoke the open method to receive the file reference. */ 94 95 unsigned long size; 96 l4_cap_idx_t file_ref; 97 98 long err = context.open(L4_FPAGE_RW, &size, &file_ref); 99 100 if (err) 101 { 102 printf("Could not obtain file for %ld @ page %d: %s\n", fileid, start_page, l4sys_errtostr(err)); 103 return err; 104 } 105 106 client_MappedFile file(file_ref); 107 108 /* Some memory to be mapped. */ 109 110 size_t start_pos, end_pos, data_end; 111 112 err = file.mmap(page(start_page), page(MAP_PAGES), &start_pos, &end_pos, &data_end); 113 114 if (err) 115 { 116 printf("Could not map file region for %ld @ page %d: %s\n", fileid, start_page, l4sys_errtostr(err)); 117 ipc_cap_free_um(file_ref); 118 return err; 119 } 120 121 //printf("Mapped region from %ld to %ld with content %ld.\n", 122 // start_pos, end_pos, data_end); 123 124 size = end_pos - start_pos; 125 126 //printf("Attach region of size %ld...\n", size); 127 128 char *memory; 129 130 err = attach_dataspace(file_ref, size, (void **) &memory); 131 132 if (err) 133 { 134 printf("Could not map memory for %ld @ page %d: %s\n", fileid, start_page, l4sys_errtostr(err)); 135 ipc_cap_free_um(file_ref); 136 return err; 137 } 138 139 //printf("Mapped memory from %lx / %ld at %p\n", file_ref, page(start_page), memory); 140 141 if (data_end < size) 142 size = data_end; 143 144 for (unsigned long offset = 0; offset < size; offset += step) 145 { 146 unsigned long remaining = size - offset; 147 unsigned long sample_remaining = remaining < sample ? remaining : sample; 148 149 strncpy(buf, (memory + offset), sample_remaining); 150 buf[sample_remaining] = '\0'; 151 //printf("%p: %s\n", (memory + offset), buf); 152 153 /* Test the data obtained. */ 154 155 unsigned long filepos = start_pos + offset; 156 unsigned long _fileid = 0, _filepos = 0; 157 char *sep = strchr(buf, ':'); 158 159 if (sep != NULL) 160 { 161 *sep = '\0'; sep++; 162 _fileid = atol(buf); _filepos = atol(sep); 163 } 164 165 if ((fileid != _fileid) || (filepos != _filepos)) 166 printf("! %ld:%ld is not %ld:%ld\n", _fileid, _filepos, fileid, filepos); 167 } 168 169 detach_dataspace(memory); 170 171 //printf("Unmapped memory from %ld at %p\n", page(start_page), memory); 172 173 ipc_cap_free_um(file_ref); 174 175 return L4_EOK; 176 } 177 178 179 180 static long activity_iterate(l4_cap_idx_t context_ref, unsigned long fileid, unsigned int start_page) 181 { 182 long err; 183 184 /* Open the file, read pages, close the file, over and over. */ 185 186 for (unsigned int iteration = 0; iteration < ACTIVITY_ITERATIONS; iteration++) 187 { 188 err = activity(context_ref, fileid, start_page); 189 if (err) 190 break; 191 } 192 193 //printf("Ending: %ld @ %d\n", fileid, start_page); 194 return err; 195 } 196 197 198 199 static long context_for_file(unsigned long fileid, l4_cap_idx_t *context_ref, char **filename) 200 { 201 /* Obtain access to the filesystem. */ 202 203 l4_cap_idx_t server = l4re_env_get_cap("server"); 204 client_Opener opener(server); 205 206 long err = opener.context(context_ref); 207 208 if (err) 209 { 210 printf("Could not obtain context: %s\n", l4sys_errtostr(err)); 211 return err; 212 } 213 214 client_Dataspace context_ds(*context_ref); 215 unsigned long size, flags; 216 217 err = context_ds.info(&size, &flags); 218 219 if (err) 220 { 221 printf("Could not obtain context info: %s\n", l4sys_errtostr(err)); 222 ipc_cap_free_um(*context_ref); 223 return err; 224 } 225 226 /* Map context memory to write the filename. */ 227 228 err = attach_dataspace(*context_ref, size, (void **) filename); 229 230 if (err) 231 { 232 printf("Could not map memory: %s\n", l4sys_errtostr(err)); 233 ipc_cap_free_um(*context_ref); 234 return err; 235 } 236 237 /* Write the filename. */ 238 239 sprintf(*filename, "%ld", fileid); 240 241 return L4_EOK; 242 } 243 244 245 246 static long activity_for_file(unsigned long fileid) 247 { 248 std::thread *activities[START_LIMIT]; 249 250 l4_cap_idx_t context_ref; 251 char *filename; 252 int current = 0; 253 254 long err = context_for_file(fileid, &context_ref, &filename); 255 256 if (err) 257 return err; 258 259 for (unsigned int start_page = 0; start_page < START_LIMIT; start_page++) 260 activities[current++] = new std::thread(activity_iterate, context_ref, fileid, start_page); 261 262 /* Wait for the threads. */ 263 264 int limit = current; 265 266 printf("Waiting for %d threads for %ld...\n", limit, fileid); 267 268 for (current = 0; current < limit; current++) 269 { 270 activities[current]->join(); 271 printf("End: %ld @ %d\n", fileid, current); 272 } 273 274 /* Discard the context. */ 275 276 detach_dataspace(filename); 277 ipc_cap_free_um(context_ref); 278 279 printf("End: %ld\n", fileid); 280 return L4_EOK; 281 } 282 283 284 285 int main(void) 286 { 287 /* Introduce concurrency control. */ 288 289 ipc_cap_alloc_init(); 290 291 std::thread *activities[NUMBER_OF_FILES]; 292 293 unsigned long fileid; 294 int current = 0; 295 296 for (fileid = 0; fileid < NUMBER_OF_FILES; fileid++) 297 activities[current++] = new std::thread(activity_for_file, fileid); 298 299 /* Wait for the threads. */ 300 301 int limit = current; 302 303 printf("Waiting for %d threads...\n", limit); 304 305 for (current = 0; current < limit; current++) 306 { 307 activities[current]->join(); 308 printf("Ended: %d\n", current); 309 } 310 311 printf("Activities completed.\n"); 312 313 return 0; 314 }