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 <thread> 30 31 #include <ipc/cap_alloc.h> 32 #include <ipc/mem_ipc.h> 33 #include <ipc/thread.h> 34 35 #include "dataspace_client.h" 36 #include "opener_client.h" 37 #include "opener_context_client.h" 38 #include "mapped_file_client.h" 39 #include "memory_utils.h" 40 41 42 43 /* Test parameters affected by capability limits. */ 44 45 const unsigned long NUMBER_OF_FILES = 10; 46 const unsigned int START_LIMIT = 10; 47 const unsigned int ACTIVITY_ITERATIONS = 20; 48 49 /* Test parameters unaffected by any capability limits. */ 50 51 const unsigned int MAP_PAGES = 20; 52 const unsigned int REGION_ITERATIONS = 10; 53 54 55 56 /* An activity opening and reading from a file. */ 57 58 static long activity(l4_cap_idx_t context_ref, unsigned long fileid, unsigned int start_page) 59 { 60 client_OpenerContext context(context_ref); 61 unsigned long step = page(1); 62 unsigned long sample = page(1); 63 64 /* Allocate a buffer for sampling from the file. */ 65 66 char buf[sample + 1]; 67 68 /* Invoke the open method to receive the file reference. */ 69 70 unsigned long size; 71 l4_cap_idx_t file_ref; 72 73 long err = context.open(L4_FPAGE_RW, &size, &file_ref); 74 75 if (err) 76 { 77 printf("Could not obtain file for %ld @ page %d: %s\n", fileid, start_page, l4sys_errtostr(err)); 78 return err; 79 } 80 81 client_MappedFile file(file_ref); 82 83 /* Some memory to be mapped. */ 84 85 size_t start_pos, end_pos, data_end; 86 87 err = file.mmap(page(start_page), page(MAP_PAGES), &start_pos, &end_pos, &data_end); 88 89 if (err) 90 { 91 printf("Could not map file region for %ld @ page %d: %s\n", fileid, start_page, l4sys_errtostr(err)); 92 ipc_cap_free_um(file_ref); 93 return err; 94 } 95 96 size = end_pos - start_pos; 97 98 char *memory; 99 100 err = ipc_attach_dataspace(file_ref, size, (void **) &memory); 101 102 if (err) 103 { 104 printf("Could not map memory for %ld @ page %d: %s\n", fileid, start_page, l4sys_errtostr(err)); 105 ipc_cap_free_um(file_ref); 106 return err; 107 } 108 109 if (data_end < size) 110 size = data_end; 111 112 /* Read the region a number of times. */ 113 114 for (unsigned int read_counter = 0; read_counter < REGION_ITERATIONS; read_counter++) 115 { 116 for (unsigned long offset = 0; offset < size; offset += step) 117 { 118 unsigned long remaining = size - offset; 119 unsigned long sample_remaining = remaining < sample ? remaining : sample; 120 121 strncpy(buf, (memory + offset), sample_remaining); 122 buf[sample_remaining] = '\0'; 123 124 /* Test the data obtained. */ 125 126 unsigned long filepos = start_pos + offset; 127 unsigned long _fileid = 0, _filepos = 0; 128 char *sep = strchr(buf, ':'); 129 130 if (sep != NULL) 131 { 132 *sep = '\0'; sep++; 133 _fileid = atol(buf); _filepos = atol(sep); 134 } 135 136 if ((fileid != _fileid) || (filepos != _filepos)) 137 printf("! %ld:%ld is not %ld:%ld\n", _fileid, _filepos, fileid, filepos); 138 } 139 } 140 141 ipc_cap_free_um(file_ref); 142 ipc_detach_dataspace(memory); 143 144 return L4_EOK; 145 } 146 147 148 149 static long activity_iterate(l4_cap_idx_t context_ref, unsigned long fileid, unsigned int start_page) 150 { 151 long err; 152 153 /* Open the file, read pages, close the file, over and over. */ 154 155 for (unsigned int iteration = 0; iteration < ACTIVITY_ITERATIONS; iteration++) 156 { 157 err = activity(context_ref, fileid, start_page); 158 if (err) 159 break; 160 } 161 162 return err; 163 } 164 165 166 167 static long context_for_file(unsigned long fileid, l4_cap_idx_t *context_ref, char **filename) 168 { 169 /* Obtain access to the filesystem. */ 170 171 l4_cap_idx_t server = l4re_env_get_cap("server"); 172 client_Opener opener(server); 173 174 long err = opener.context(context_ref); 175 176 if (err) 177 { 178 printf("Could not obtain context: %s\n", l4sys_errtostr(err)); 179 return err; 180 } 181 182 client_Dataspace context_ds(*context_ref); 183 unsigned long size, flags; 184 185 err = context_ds.info(&size, &flags); 186 187 if (err) 188 { 189 printf("Could not obtain context info: %s\n", l4sys_errtostr(err)); 190 ipc_cap_free_um(*context_ref); 191 return err; 192 } 193 194 /* Map context memory to write the filename. */ 195 196 err = ipc_attach_dataspace(*context_ref, size, (void **) filename); 197 198 if (err) 199 { 200 printf("Could not map memory: %s\n", l4sys_errtostr(err)); 201 ipc_cap_free_um(*context_ref); 202 return err; 203 } 204 205 /* Write the filename. */ 206 207 sprintf(*filename, "%ld", fileid); 208 209 return L4_EOK; 210 } 211 212 213 214 int main(void) 215 { 216 /* Introduce concurrency control. */ 217 218 ipc_cap_alloc_init(); 219 ipc_mem_init(); 220 221 std::thread *activities[NUMBER_OF_FILES * START_LIMIT]; 222 l4_cap_idx_t context_refs[NUMBER_OF_FILES]; 223 char *filenames[NUMBER_OF_FILES]; 224 225 /* Obtain opener contexts for the files. */ 226 227 unsigned long fileid; 228 229 for (fileid = 0; fileid < NUMBER_OF_FILES; fileid++) 230 { 231 long err = context_for_file(fileid, &context_refs[fileid], &filenames[fileid]); 232 233 if (err) 234 { 235 printf("Context allocation failed.\n"); 236 return 1; 237 } 238 } 239 240 /* Start threads accessing all the files. */ 241 242 int current = 0; 243 244 for (fileid = 0; fileid < NUMBER_OF_FILES; fileid++) 245 for (unsigned int start_page = 0; start_page < START_LIMIT; start_page++) 246 activities[current++] = new std::thread(activity_iterate, context_refs[fileid], fileid, start_page); 247 248 /* Wait for the threads. */ 249 250 int limit = current; 251 252 printf("Waiting for %d threads...\n", limit); 253 254 for (current = 0; current < limit; current++) 255 activities[current]->join(); 256 257 /* Discard the contexts. */ 258 259 for (fileid = 0; fileid < NUMBER_OF_FILES; fileid++) 260 { 261 ipc_cap_free_um(context_refs[fileid]); 262 ipc_detach_dataspace(filenames[fileid]); 263 } 264 265 printf("Activities completed.\n"); 266 267 return 0; 268 }