smsh.c (12653B)
1 /* Copyright (C) 2020-2023, 2025 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200809L /* mmap support */ 17 #define _DEFAULT_SOURCE 1 /* MAP_POPULATE support */ 18 #define _BSD_SOURCE 1 /* MAP_POPULATE for glibc < 2.19 */ 19 20 #include "smsh.h" 21 #include "smsh_c.h" 22 #include "smsh_log.h" 23 24 #include <rsys/cstr.h> 25 #include <rsys/mem_allocator.h> 26 27 #include <errno.h> 28 #include <unistd.h> 29 #include <sys/mman.h> /* mmap */ 30 #include <sys/stat.h> /* fstat */ 31 32 /******************************************************************************* 33 * Helper functions 34 ******************************************************************************/ 35 static INLINE int 36 is_stdin(FILE* stream) 37 { 38 struct stat stream_buf; 39 struct stat stdin_buf; 40 ASSERT(stream); 41 42 CHK(fstat(fileno(stream), &stream_buf) == 0); 43 CHK(fstat(STDIN_FILENO, &stdin_buf) == 0); 44 return stream_buf.st_dev == stdin_buf.st_dev; 45 } 46 47 static INLINE res_T 48 check_smsh_create_args(const struct smsh_create_args* args) 49 { 50 /* Nothing to check. Only return RES_BAD_ARG if args is NULL */ 51 return args ? RES_OK : RES_BAD_ARG; 52 } 53 54 static INLINE res_T 55 check_smsh_load_args(const struct smsh_load_args* args) 56 { 57 if(!args || !args->path) return RES_BAD_ARG; 58 return RES_OK; 59 } 60 61 static INLINE res_T 62 check_smsh_load_stream_args 63 (struct smsh* smsh, 64 const struct smsh_load_stream_args* args) 65 { 66 if(!args || !args->stream || !args->name) return RES_BAD_ARG; 67 if(args->memory_mapping && is_stdin(args->stream)) { 68 log_err(smsh, 69 "%s: unable to use memory mapping on data loaded from stdin\n", 70 args->name); 71 return RES_BAD_ARG; 72 } 73 return RES_OK; 74 } 75 76 static void 77 reset_smsh(struct smsh* smsh) 78 { 79 ASSERT(smsh); 80 smsh->nnodes = 0; 81 smsh->ncells = 0; 82 smsh->pagesize = 0; 83 if(smsh->nodes) { 84 if(!smsh->map_len_nodes) { 85 MEM_RM(smsh->allocator, smsh->nodes); 86 } else if(smsh->nodes != MAP_FAILED) { 87 munmap(smsh->nodes, smsh->map_len_nodes); 88 } 89 } 90 if(smsh->cells) { 91 if(!smsh->map_len_cells) { 92 MEM_RM(smsh->allocator, smsh->cells); 93 } else if(smsh->nodes != MAP_FAILED) { 94 munmap(smsh->cells, smsh->map_len_cells); 95 } 96 } 97 smsh->nodes = NULL; 98 smsh->cells = NULL; 99 smsh->map_len_nodes = 0; 100 smsh->map_len_cells = 0; 101 } 102 103 static res_T 104 map_data 105 (struct smsh* smsh, 106 const char* name, 107 const int fd, /* File descriptor */ 108 const size_t filesz, /* Overall filesize */ 109 const char* data_name, 110 const off_t offset, /* Offset of the data into file */ 111 const size_t map_len, 112 void** out_map) /* Lenght of the data to map */ 113 { 114 void* map = NULL; 115 res_T res = RES_OK; 116 ASSERT(smsh && name && filesz && data_name && map_len && out_map); 117 ASSERT(IS_ALIGNED((size_t)offset, (size_t)smsh->pagesize)); 118 119 if((size_t)offset + map_len > filesz) { 120 log_err(smsh, "%s: the %s to load exceed the file size.\n", 121 name, data_name); 122 res = RES_IO_ERR; 123 goto error; 124 } 125 126 map = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE|MAP_POPULATE, fd, offset); 127 if(map == MAP_FAILED) { 128 log_err(smsh, "%s: could not map the %s -- %s.\n", 129 name, data_name, strerror(errno)); 130 res = RES_IO_ERR; 131 goto error; 132 } 133 134 exit: 135 *out_map = map; 136 return res; 137 error: 138 if(map == MAP_FAILED) map = NULL; 139 goto exit; 140 } 141 142 static res_T 143 map_file(struct smsh* smsh, FILE* stream, const char* name) 144 { 145 off_t pos_offset; 146 off_t ids_offset; 147 size_t filesz; 148 res_T res = RES_OK; 149 ASSERT(smsh && stream && name); 150 151 /* Compute the length in bytes of the data to map */ 152 smsh->map_len_nodes = smsh->nnodes * sizeof(double) * smsh->dnode; 153 smsh->map_len_cells = smsh->ncells * sizeof(uint64_t) * smsh->dcell; 154 smsh->map_len_nodes = ALIGN_SIZE(smsh->map_len_nodes, (size_t)smsh->pagesize); 155 smsh->map_len_cells = ALIGN_SIZE(smsh->map_len_cells, (size_t)smsh->pagesize); 156 157 /* Find the offsets of the positions/indices data into the stream */ 158 pos_offset = (off_t)ALIGN_SIZE((uint64_t)ftell(stream), smsh->pagesize); 159 ids_offset = (off_t)((size_t)pos_offset + smsh->map_len_nodes); 160 161 /* Retrieve the overall filesize */ 162 fseek(stream, 0, SEEK_END); 163 filesz = (size_t)ftell(stream); 164 165 /* Map the nodes */ 166 res = map_data(smsh, name, fileno(stream), filesz, "nodes", 167 pos_offset, smsh->map_len_nodes, (void**)&smsh->nodes); 168 if(res != RES_OK) goto error; 169 170 /* Map the cells */ 171 res = map_data(smsh, name, fileno(stream), filesz, "cells", 172 ids_offset, smsh->map_len_cells, (void**)&smsh->cells); 173 if(res != RES_OK) goto error; 174 175 exit: 176 return res; 177 error: 178 goto exit; 179 } 180 181 static res_T 182 read_padding(FILE* stream, const size_t padding) 183 { 184 char chunk[1024]; 185 size_t remaining_nbytes = padding; 186 187 while(remaining_nbytes) { 188 const size_t nbytes = MMIN(sizeof(chunk), remaining_nbytes); 189 if(fread(chunk, 1, nbytes, stream) != nbytes) return RES_IO_ERR; 190 remaining_nbytes -= nbytes; 191 } 192 return RES_OK; 193 } 194 195 static res_T 196 load_file(struct smsh* smsh, FILE* stream, const char* name) 197 { 198 size_t ncoords; 199 size_t nindices; 200 size_t padding_bytes; 201 size_t sizeof_nodes; 202 size_t sizeof_cells; 203 size_t sizeof_header; 204 res_T res = RES_OK; 205 ASSERT(smsh && stream && name); 206 207 ncoords = smsh->nnodes * smsh->dnode; 208 nindices = smsh->ncells * smsh->dcell; 209 sizeof_nodes = ncoords * sizeof(*smsh->nodes); 210 sizeof_cells = nindices * sizeof(*smsh->cells); 211 sizeof_header = 212 sizeof(smsh->pagesize) 213 + sizeof(smsh->nnodes) 214 + sizeof(smsh->ncells) 215 + sizeof(smsh->dnode) 216 + sizeof(smsh->dcell); 217 218 /* Allocate the memory space where the loaded data will be stored */ 219 smsh->nodes = MEM_CALLOC(smsh->allocator, ncoords, sizeof(*smsh->nodes)); 220 if(!smsh->nodes) { res = RES_MEM_ERR; goto error; } 221 smsh->cells = MEM_CALLOC(smsh->allocator, nindices, sizeof(*smsh->cells)); 222 if(!smsh->cells) { res = RES_MEM_ERR; goto error; } 223 224 padding_bytes = ALIGN_SIZE(sizeof_header, smsh->pagesize) - sizeof_header; 225 if((res = read_padding(stream, padding_bytes)) != RES_OK) goto error; 226 227 /* Load the nodes */ 228 if(fread(smsh->nodes, sizeof(*smsh->nodes), ncoords, stream) != ncoords) { 229 res = RES_IO_ERR; 230 goto error; 231 } 232 233 padding_bytes = ALIGN_SIZE(sizeof_nodes, smsh->pagesize) - sizeof_nodes; 234 if((res = read_padding(stream, padding_bytes)) != RES_OK) goto error; 235 236 /* Load the cells */ 237 if(fread(smsh->cells, sizeof(*smsh->cells), nindices, stream) != nindices) { 238 res = RES_IO_ERR; 239 goto error; 240 } 241 242 padding_bytes = ALIGN_SIZE(sizeof_cells, smsh->pagesize) - sizeof_cells; 243 if((res = read_padding(stream, padding_bytes)) != RES_OK) goto error; 244 245 exit: 246 return res; 247 error: 248 log_err(smsh, "%s: error while loading data -- %s.\n", 249 name, res_to_cstr(res)); 250 goto exit; 251 } 252 253 static res_T 254 load_stream(struct smsh* smsh, const struct smsh_load_stream_args* args) 255 { 256 res_T res = RES_OK; 257 ASSERT(smsh && check_smsh_load_stream_args(smsh, args) == RES_OK); 258 259 reset_smsh(smsh); 260 261 /* Read file header */ 262 if(fread(&smsh->pagesize, sizeof(&smsh->pagesize), 1, args->stream) != 1) { 263 if(ferror(args->stream)) { 264 log_err(smsh, "%s: could not read the pagesize.\n", args->name); 265 } 266 res = RES_IO_ERR; 267 goto error; 268 } 269 270 #define READ(Var, N, Name) { \ 271 if(fread((Var), sizeof(*(Var)), (N), args->stream) != (N)) { \ 272 log_err(smsh, "%s: could not read the %s.\n", args->name, (Name)); \ 273 res = RES_IO_ERR; \ 274 goto error; \ 275 } \ 276 } (void)0 277 READ(&smsh->nnodes, 1, "number of nodes"); 278 READ(&smsh->ncells, 1, "number of cells"); 279 READ(&smsh->dnode, 1, "node dimension"); 280 READ(&smsh->dcell, 1, "cell dimension"); 281 #undef READ 282 283 if(!IS_ALIGNED(smsh->pagesize, smsh->pagesize_os)) { 284 log_err(smsh, 285 "%s: invalid page size %li. The page size attribute must be aligned on " 286 "the page size of the operating system (%lu).\n", 287 args->name, smsh->pagesize, (unsigned long)smsh->pagesize_os); 288 res = RES_BAD_ARG; 289 goto error; 290 } 291 292 if(args->memory_mapping) { 293 res = map_file(smsh, args->stream, args->name); 294 if(res != RES_OK) goto error; 295 } else { 296 res = load_file(smsh, args->stream, args->name); 297 if(res != RES_OK) goto error; 298 } 299 300 exit: 301 return res; 302 error: 303 reset_smsh(smsh); 304 goto exit; 305 } 306 307 static void 308 release_smsh(ref_T* ref) 309 { 310 struct smsh* smsh; 311 ASSERT(ref); 312 smsh = CONTAINER_OF(ref, struct smsh, ref); 313 reset_smsh(smsh); 314 if(smsh->logger == &smsh->logger__) logger_release(&smsh->logger__); 315 MEM_RM(smsh->allocator, smsh); 316 } 317 318 /******************************************************************************* 319 * Exported functions 320 ******************************************************************************/ 321 res_T 322 smsh_create 323 (const struct smsh_create_args* args, 324 struct smsh** out_smsh) 325 { 326 struct smsh* smsh = NULL; 327 struct mem_allocator* allocator = NULL; 328 res_T res = RES_OK; 329 330 if(!out_smsh) { res = RES_BAD_ARG; goto error; } 331 res = check_smsh_create_args(args); 332 if(res != RES_OK) goto error; 333 334 allocator = args->allocator ? args->allocator : &mem_default_allocator; 335 smsh = MEM_CALLOC(allocator, 1, sizeof(*smsh)); 336 if(!smsh) { 337 if(args->verbose) { 338 #define ERR_STR "Could not allocate the Star-Mesh device.\n" 339 if(args->logger) { 340 logger_print(args->logger, LOG_ERROR, ERR_STR); 341 } else { 342 fprintf(stderr, MSG_ERROR_PREFIX ERR_STR); 343 } 344 #undef ERR_STR 345 } 346 res = RES_MEM_ERR; 347 goto error; 348 } 349 ref_init(&smsh->ref); 350 smsh->allocator = allocator; 351 smsh->verbose = args->verbose; 352 smsh->pagesize_os = (size_t)sysconf(_SC_PAGESIZE); 353 if(args->logger) { 354 smsh->logger = args->logger; 355 } else { 356 setup_log_default(smsh); 357 } 358 359 exit: 360 if(out_smsh) *out_smsh = smsh; 361 return res; 362 error: 363 if(smsh) { 364 SMSH(ref_put(smsh)); 365 smsh = NULL; 366 } 367 goto exit; 368 } 369 370 res_T 371 smsh_ref_get(struct smsh* smsh) 372 { 373 if(!smsh) return RES_BAD_ARG; 374 ref_get(&smsh->ref); 375 return RES_OK; 376 } 377 378 res_T 379 smsh_ref_put(struct smsh* smsh) 380 { 381 if(!smsh) return RES_BAD_ARG; 382 ref_put(&smsh->ref, release_smsh); 383 return RES_OK; 384 } 385 386 387 res_T 388 smsh_load(struct smsh* smsh, const struct smsh_load_args* args) 389 { 390 struct smsh_load_stream_args stream_args = SMSH_LOAD_STREAM_ARGS_NULL; 391 FILE* file = NULL; 392 res_T res = RES_OK; 393 394 if(!smsh) { res = RES_BAD_ARG; goto error; } 395 res = check_smsh_load_args(args); 396 if(res != RES_OK) goto error; 397 398 file = fopen(args->path, "r"); 399 if(!file) { 400 log_err(smsh, "%s: error opening file `%s'.\n", FUNC_NAME, args->path); 401 res = RES_IO_ERR; 402 goto error; 403 } 404 405 stream_args.stream = file; 406 stream_args.name = args->path; 407 stream_args.memory_mapping = args->memory_mapping; 408 res = load_stream(smsh, &stream_args); 409 if(res != RES_OK) goto error; 410 411 exit: 412 if(file) fclose(file); 413 return res; 414 error: 415 goto exit; 416 } 417 418 res_T 419 smsh_load_stream(struct smsh* smsh, const struct smsh_load_stream_args* args) 420 { 421 res_T res = RES_OK; 422 if(!smsh) return RES_BAD_ARG; 423 res = check_smsh_load_stream_args(smsh, args); 424 if(res != RES_OK) return res; 425 return load_stream(smsh, args); 426 } 427 428 res_T 429 smsh_get_desc(const struct smsh* smsh, struct smsh_desc* desc) 430 { 431 if(!smsh || !desc) return RES_BAD_ARG; 432 desc->nodes = smsh->nodes; 433 desc->cells = smsh->cells; 434 desc->nnodes = smsh->nnodes; 435 desc->ncells = smsh->ncells; 436 desc->dnode = smsh->dnode; 437 desc->dcell = smsh->dcell; 438 return RES_OK; 439 } 440 441 res_T 442 smsh_desc_compute_hash(const struct smsh_desc* desc, hash256_T hash) 443 { 444 struct sha256_ctx ctx; 445 446 if(!desc || !hash) return RES_BAD_ARG; 447 448 #define HASH(Var, Nb) \ 449 sha256_ctx_update(&ctx, (const char*)(Var), sizeof(*Var)*(Nb)); 450 451 sha256_ctx_init(&ctx); 452 HASH(desc->nodes, desc->nnodes*desc->dnode); 453 HASH(desc->cells, desc->ncells*desc->dcell); 454 HASH(&desc->nnodes, 1); 455 HASH(&desc->ncells, 1); 456 HASH(&desc->dnode, 1); 457 HASH(&desc->dcell, 1); 458 sha256_ctx_finalize(&ctx, hash); 459 460 #undef HASH 461 462 return RES_OK; 463 }