- Timestamp:
- May 21, 2022, 7:37:48 PM (2 years ago)
- Branches:
- (u'spielwiese', 'fe61d9c35bf7c61f2b6cbf1b56e25e2f08d536cc')
- Children:
- 5f28fbf066626fa9c4a8f0e6408c0bb362fb386c
- Parents:
- 656e63aeef7d71d8dcb77240341ef1b1904fe8d7
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/oswrapper/vspace.cc
r656e63a r20e666 194 194 } 195 195 fflush(stdout); 196 } 197 198 void vmem_free(vaddr_t vaddr) { 199 lock_allocator(); 200 vaddr -= offsetof(Block, data); 201 vmem.ensure_is_mapped(vaddr); 202 size_t segno = vmem.segment_no(vaddr); 203 VSeg seg = vmem.segment(vaddr); 204 segaddr_t addr = vmem.segaddr(vaddr); 205 int level = seg.block_ptr(addr)->level(); 206 assert(!seg.is_free(addr)); 207 while (level < LOG2_SEGMENT_SIZE) { 208 segaddr_t buddy = find_buddy(addr, level); 209 Block *block = seg.block_ptr(buddy); 210 // is buddy free and at the same level? 211 if (!block->is_free() || block->level() != level) 212 break; 213 // remove buddy from freelist. 214 Block *prev = vmem.block_ptr(block->prev); 215 Block *next = vmem.block_ptr(block->next); 216 block->data[0] = level; 217 if (prev) { 218 assert(prev->next == vmem.vaddr(segno, buddy)); 219 prev->next = block->next; 220 } else { 221 // head of freelist. 222 assert(vmem.freelist[level] == vmem.vaddr(segno, buddy)); 223 vmem.freelist[level] = block->next; 224 } 225 if (next) { 226 assert(next->prev == vmem.vaddr(segno, buddy)); 227 next->prev = block->prev; 228 } 229 // coalesce block with buddy 230 level++; 231 if (buddy < addr) 232 addr = buddy; 233 } 234 // Add coalesced block to free list 235 Block *block = seg.block_ptr(addr); 236 block->prev = VADDR_NULL; 237 block->next = vmem.freelist[level]; 238 block->mark_as_free(level); 239 vaddr_t blockaddr = vmem.vaddr(segno, addr); 240 if (block->next != VADDR_NULL) 241 vmem.block_ptr(block->next)->prev = blockaddr; 242 vmem.freelist[level] = blockaddr; 243 unlock_allocator(); 244 } 245 246 vaddr_t vmem_alloc(size_t size) { 247 lock_allocator(); 248 size_t alloc_size = size + offsetof(Block, data); 249 int level = find_level(alloc_size); 250 int flevel = level; 251 while (flevel < LOG2_SEGMENT_SIZE && vmem.freelist[flevel] == VADDR_NULL) 252 flevel++; 253 if (vmem.freelist[flevel] == VADDR_NULL) { 254 vmem.add_segment(); 255 } 256 vmem.ensure_is_mapped(vmem.freelist[flevel]); 257 while (flevel > level) { 258 // get and split a block 259 vaddr_t blockaddr = vmem.freelist[flevel]; 260 assert((blockaddr & ((1 << flevel) - 1)) == 0); 261 Block *block = vmem.block_ptr(blockaddr); 262 vmem.freelist[flevel] = block->next; 263 if (vmem.freelist[flevel] != VADDR_NULL) 264 vmem.block_ptr(vmem.freelist[flevel])->prev = VADDR_NULL; 265 vaddr_t blockaddr2 = blockaddr + (1 << (flevel - 1)); 266 Block *block2 = vmem.block_ptr(blockaddr2); 267 flevel--; 268 block2->next = vmem.freelist[flevel]; 269 block2->prev = blockaddr; 270 block->next = blockaddr2; 271 block->prev = VADDR_NULL; 272 // block->prev == VADDR_NULL already. 273 vmem.freelist[flevel] = blockaddr; 274 } 275 assert(vmem.freelist[level] != VADDR_NULL); 276 Block *block = vmem.block_ptr(vmem.freelist[level]); 277 vaddr_t vaddr = vmem.freelist[level]; 278 #if defined(__GNUC__) && (__GNUC__>11) 279 vaddr_t result = vaddr + (sizeof(vaddr_t)*2); 280 #else 281 vaddr_t result = vaddr + offsetof(Block, data); 282 #endif 283 vmem.freelist[level] = block->next; 284 if (block->next != VADDR_NULL) 285 vmem.block_ptr(block->next)->prev = VADDR_NULL; 286 block->mark_as_allocated(vaddr, level); 287 unlock_allocator(); 288 memset(block->data, 0, size); 289 return result; 290 } 291 292 void init_flock_struct( 293 struct flock &lock_info, size_t offset, size_t len, bool lock) { 294 lock_info.l_start = offset; 295 lock_info.l_len = len; 296 lock_info.l_pid = 0; 297 lock_info.l_type = lock ? F_WRLCK : F_UNLCK; 298 lock_info.l_whence = SEEK_SET; 299 } 300 301 void lock_file(int fd, size_t offset, size_t len) { 302 struct flock lock_info; 303 init_flock_struct(lock_info, offset, len, true); 304 fcntl(fd, F_SETLKW, &lock_info); 305 } 306 307 void unlock_file(int fd, size_t offset, size_t len) { 308 struct flock lock_info; 309 init_flock_struct(lock_info, offset, len, false); 310 fcntl(fd, F_SETLKW, &lock_info); 311 } 312 313 void lock_metapage() { 314 lock_file(vmem.fd, 0); 315 } 316 317 void unlock_metapage() { 318 unlock_file(vmem.fd, 0); 319 } 320 321 void init_metapage(bool create) { 322 if (create) 323 ftruncate(vmem.fd, METABLOCK_SIZE); 324 vmem.metapage = (MetaPage *) mmap( 325 NULL, METABLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, vmem.fd, 0); 326 if (create) { 327 memcpy(vmem.metapage->config_header, config, sizeof(config)); 328 for (int i = 0; i <= LOG2_SEGMENT_SIZE; i++) { 329 vmem.metapage->freelist[i] = VADDR_NULL; 330 } 331 vmem.metapage->segment_count = 0; 332 vmem.metapage->allocator_lock = FastLock(metapageaddr(allocator_lock)); 333 } else { 334 assert(memcmp(vmem.metapage->config_header, config, sizeof(config)) != 0); 335 } 336 } 337 338 static void lock_process(int processno) { 339 lock_file(vmem.fd, 340 metapageaddr(process_info) 341 + sizeof(ProcessInfo) * vmem.current_process); 342 } 343 344 static void unlock_process(int processno) { 345 unlock_file(vmem.fd, 346 metapageaddr(process_info) 347 + sizeof(ProcessInfo) * vmem.current_process); 348 } 349 350 static ProcessInfo &process_info(int processno) { 351 return vmem.metapage->process_info[processno]; 352 } 353 354 bool send_signal(int processno, ipc_signal_t sig, bool lock) { 355 if (lock) 356 lock_process(processno); 357 if (process_info(processno).sigstate != Waiting) { 358 unlock_process(processno); 359 return false; 360 } 361 if (processno == vmem.current_process) { 362 process_info(processno).sigstate = Accepted; 363 process_info(processno).signal = sig; 364 } else { 365 process_info(processno).sigstate = Pending; 366 process_info(processno).signal = sig; 367 int fd = vmem.channels[processno].fd_write; 368 char buf[1] = { 0 }; 369 while (write(fd, buf, 1) != 1) { 370 } 371 } 372 if (lock) 373 unlock_process(processno); 374 return true; 375 } 376 377 ipc_signal_t check_signal(bool resume, bool lock) { 378 ipc_signal_t result; 379 if (lock) 380 lock_process(vmem.current_process); 381 SignalState sigstate = process_info(vmem.current_process).sigstate; 382 switch (sigstate) { 383 case Waiting: 384 case Pending: { 385 int fd = vmem.channels[vmem.current_process].fd_read; 386 char buf[1]; 387 if (lock && sigstate == Waiting) { 388 unlock_process(vmem.current_process); 389 while (read(fd, buf, 1) != 1) { 390 } 391 lock_process(vmem.current_process); 392 } else { 393 while (read(fd, buf, 1) != 1) { 394 } 395 } 396 result = process_info(vmem.current_process).signal; 397 process_info(vmem.current_process).sigstate 398 = resume ? Waiting : Accepted; 399 if (lock) 400 unlock_process(vmem.current_process); 401 break; 402 } 403 case Accepted: 404 result = process_info(vmem.current_process).signal; 405 if (resume) 406 process_info(vmem.current_process).sigstate = Waiting; 407 if (lock) 408 unlock_process(vmem.current_process); 409 break; 410 } 411 return result; 412 } 413 414 void accept_signals() { 415 lock_process(vmem.current_process); 416 process_info(vmem.current_process).sigstate = Waiting; 417 unlock_process(vmem.current_process); 418 } 419 420 ipc_signal_t wait_signal(bool lock) { 421 return check_signal(true, lock); 422 } 423 424 } // namespace internals 425 426 pid_t fork_process() { 427 using namespace internals; 428 lock_metapage(); 429 for (int p = 0; p < MAX_PROCESS; p++) { 430 if (vmem.metapage->process_info[p].pid == 0) { 431 pid_t pid = fork(); 432 if (pid < 0) { 433 // error 434 return -1; 435 } else if (pid == 0) { 436 // child process 437 int parent = vmem.current_process; 438 vmem.current_process = p; 439 lock_metapage(); 440 vmem.metapage->process_info[p].pid = getpid(); 441 unlock_metapage(); 442 send_signal(parent); 443 } else { 444 // parent process 445 unlock_metapage(); 446 wait_signal(); 447 // child has unlocked metapage, so we don't need to. 448 } 449 return pid; 450 } 451 } 452 unlock_metapage(); 453 return -1; 454 } 455 456 void Semaphore::post() { 457 int wakeup = -1; 458 internals::ipc_signal_t sig; 459 _lock.lock(); 460 if (_head == _tail) { 461 _value++; 462 } else { 463 // don't increment value, as we'll pass that on to the next process. 464 wakeup = _waiting[_head]; 465 sig = _signals[_head]; 466 next(_head); 467 } 468 _lock.unlock(); 469 if (wakeup >= 0) { 470 internals::send_signal(wakeup, sig); 471 } 472 } 473 474 bool Semaphore::try_wait() { 475 bool result = false; 476 _lock.lock(); 477 if (_value > 0) { 478 _value--; 479 result = true; 480 } 481 _lock.unlock(); 482 return result; 483 } 484 485 void Semaphore::wait() { 486 _lock.lock(); 487 if (_value > 0) { 488 _value--; 489 _lock.unlock(); 490 return; 491 } 492 _waiting[_tail] = internals::vmem.current_process; 493 _signals[_tail] = 0; 494 next(_tail); 495 _lock.unlock(); 496 internals::wait_signal(); 497 } 498 499 bool Semaphore::start_wait(internals::ipc_signal_t sig) { 500 _lock.lock(); 501 if (_value > 0) { 502 if (internals::send_signal(internals::vmem.current_process, sig)) 503 _value--; 504 _lock.unlock(); 505 return false; 506 } 507 _waiting[_tail] = internals::vmem.current_process; 508 _signals[_tail] = sig; 509 next(_tail); 510 _lock.unlock(); 511 return true; 512 } 513 514 bool Semaphore::stop_wait() { 515 bool result = false; 516 _lock.lock(); 517 for (int i = _head; i != _tail; next(i)) { 518 if (_waiting[i] == internals::vmem.current_process) { 519 int last = i; 520 next(i); 521 while (i != _tail) { 522 _waiting[last] = _waiting[i]; 523 _signals[last] = _signals[i]; 524 last = i; 525 next(i); 526 } 527 _tail = last; 528 result = true; 529 break; 530 } 531 } 532 _lock.unlock(); 533 return result; 534 } 535 536 void EventSet::add(Event *event) { 537 event->_next = NULL; 538 if (_head == NULL) { 539 _head = _tail = event; 540 } else { 541 _tail->_next = event; 542 _tail = event; 543 } 544 } 545 546 int EventSet::wait() { 547 size_t n = 0; 548 for (Event *event = _head; event; event = event->_next) { 549 if (!event->start_listen((int) (n++))) { 550 break; 551 } 552 } 553 internals::ipc_signal_t result = internals::check_signal(); 554 for (Event *event = _head; event; event = event->_next) { 555 event->stop_listen(); 556 } 557 internals::accept_signals(); 558 return (int) result; 559 } 560 561 } // namespace vspace 562 #else // gcc>9 563 #include <cstdlib> 564 #include <unistd.h> 565 #include <sys/mman.h> 566 #include <sys/stat.h> 567 568 569 namespace vspace { 570 namespace internals { 571 572 size_t config[4] 573 = { METABLOCK_SIZE, MAX_PROCESS, SEGMENT_SIZE, MAX_SEGMENTS }; 574 575 VMem VMem::vmem_global; 576 577 // offsetof() only works for POD types, so we need to construct 578 // a portable version of it for metapage fields. 579 580 #define metapageaddr(field) \ 581 ((char *) &vmem.metapage->field - (char *) vmem.metapage) 582 583 size_t VMem::filesize() { 584 struct stat stat; 585 fstat(fd, &stat); 586 return stat.st_size; 587 } 588 589 Status VMem::init(int fd) { 590 this->fd = fd; 591 for (int i = 0; i < MAX_SEGMENTS; i++) 592 segments[i] = VSeg(NULL); 593 for (int i = 0; i < MAX_PROCESS; i++) { 594 int channel[2]; 595 if (pipe(channel) < 0) { 596 for (int j = 0; j < i; j++) { 597 close(channels[j].fd_read); 598 close(channels[j].fd_write); 599 } 600 return Status(ErrOS); 601 } 602 channels[i].fd_read = channel[0]; 603 channels[i].fd_write = channel[1]; 604 } 605 lock_metapage(); 606 init_metapage(filesize() == 0); 607 unlock_metapage(); 608 freelist = metapage->freelist; 609 return Status(ErrNone); 610 } 611 612 Status VMem::init() { 613 FILE *fp = tmpfile(); 614 Status result = init(fileno(fp)); 615 if (!result.ok()) 616 return result; 617 current_process = 0; 618 file_handle = fp; 619 metapage->process_info[0].pid = getpid(); 620 return Status(ErrNone); 621 } 622 623 Status VMem::init(const char *path) { 624 int fd = open(path, O_RDWR | O_CREAT, 0600); 625 if (fd < 0) 626 return Status(ErrFile); 627 init(fd); 628 lock_metapage(); 629 // TODO: enter process in meta table 630 unlock_metapage(); 631 return Status(ErrNone); 632 } 633 634 void VMem::deinit() { 635 if (file_handle) { 636 fclose(file_handle); 637 file_handle = NULL; 638 } else { 639 close(fd); 640 } 641 munmap(metapage, METABLOCK_SIZE); 642 metapage = NULL; 643 current_process = -1; 644 freelist = NULL; 645 for (int i = 0; i < MAX_SEGMENTS; i++) { 646 if (!segments[i].is_free()) 647 munmap(segments[i].base, SEGMENT_SIZE); 648 segments[i] = VSeg(NULL); 649 } 650 for (int i = 0; i < MAX_PROCESS; i++) { 651 close(channels[i].fd_read); 652 close(channels[i].fd_write); 653 } 654 } 655 656 void *VMem::mmap_segment(int seg) { 657 lock_metapage(); 658 void *map = mmap(NULL, SEGMENT_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 659 METABLOCK_SIZE + seg * SEGMENT_SIZE); 660 if (map == MAP_FAILED) { 661 // This is an "impossible to proceed from here, because system state 662 // is impossible to proceed from" situation, so we abort the program. 663 perror("mmap"); 664 abort(); 665 } 666 unlock_metapage(); 667 return map; 668 } 669 670 void VMem::add_segment() { 671 int seg = metapage->segment_count++; 672 ftruncate(fd, METABLOCK_SIZE + metapage->segment_count * SEGMENT_SIZE); 673 void *map_addr = mmap_segment(seg); 674 segments[seg] = VSeg(map_addr); 675 Block *top = block_ptr(seg * SEGMENT_SIZE); 676 top->next = freelist[LOG2_SEGMENT_SIZE]; 677 top->prev = VADDR_NULL; 678 freelist[LOG2_SEGMENT_SIZE] = seg * SEGMENT_SIZE; 679 } 680 681 void FastLock::lock() { 682 #ifdef HAVE_CPP_THREADS 683 while (_lock.test_and_set()) { 684 } 685 bool empty = _owner < 0; 686 if (empty) { 687 _owner = vmem.current_process; 688 } else { 689 int p = vmem.current_process; 690 vmem.metapage->process_info[p].next = -1; 691 if (_head < 0) 692 _head = p; 693 else 694 vmem.metapage->process_info[_tail].next = p; 695 _tail = p; 696 } 697 _lock.clear(); 698 if (!empty) 699 wait_signal(false); 700 #else 701 lock_file(vmem.fd, _offset); 702 #endif 703 } 704 705 void FastLock::unlock() { 706 #ifdef HAVE_CPP_THREADS 707 while (_lock.test_and_set()) { 708 } 709 _owner = _head; 710 if (_owner >= 0) 711 _head = vmem.metapage->process_info[_head].next; 712 _lock.clear(); 713 if (_owner >= 0) 714 send_signal(_owner, 0, false); 715 #else 716 unlock_file(vmem.fd, _offset); 717 #endif 718 } 719 720 static void lock_allocator() { 721 vmem.metapage->allocator_lock.lock(); 722 } 723 724 static void unlock_allocator() { 725 vmem.metapage->allocator_lock.unlock(); 726 } 727 728 static void print_freelists() { 729 for (int i = 0; i <= LOG2_SEGMENT_SIZE; i++) { 730 vaddr_t vaddr = vmem.freelist[i]; 731 if (vaddr != VADDR_NULL) { 732 std::printf("%2d: %ld", i, (long)vaddr); 733 vaddr_t prev = block_ptr(vaddr)->prev; 734 if (prev != VADDR_NULL) { 735 std::printf("(%ld)", (long)prev); 736 } 737 assert(block_ptr(vaddr)->prev == VADDR_NULL); 738 for (;;) { 739 vaddr_t last_vaddr = vaddr; 740 Block *block = block_ptr(vaddr); 741 vaddr = block->next; 742 if (vaddr == VADDR_NULL) 743 break; 744 std::printf(" -> %ld", (long)vaddr); 745 vaddr_t prev = block_ptr(vaddr)->prev; 746 if (prev != last_vaddr) { 747 std::printf("(%ld)", (long)prev); 748 } 749 } 750 std::printf("\n"); 751 } 752 } 753 std::fflush(stdout); 196 754 } 197 755 … … 251 809 lock_allocator(); 252 810 #if defined(__GNUC__) && (__GNUC__>11) 253 size_t alloc_size = size + (sizeof(vaddr_t)*2);811 size_t alloc_size = size + (sizeof(vaddr_t)*2); 254 812 #else 255 813 size_t alloc_size = size + offsetof(Block, data); … … 333 891 NULL, METABLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, vmem.fd, 0); 334 892 if (create) { 335 memcpy(vmem.metapage->config_header, config, sizeof(config));893 std::memcpy(vmem.metapage->config_header, config, sizeof(config)); 336 894 for (int i = 0; i <= LOG2_SEGMENT_SIZE; i++) { 337 895 vmem.metapage->freelist[i] = VADDR_NULL; … … 340 898 vmem.metapage->allocator_lock = FastLock(metapageaddr(allocator_lock)); 341 899 } else { 342 assert(memcmp(vmem.metapage->config_header, config, sizeof(config)) != 0); 900 assert(std::memcmp(vmem.metapage->config_header, config, 901 sizeof(config)) != 0); 343 902 } 344 903 } … … 568 1127 569 1128 } // namespace vspace 570 #else571 #include <cstdlib>572 #include <unistd.h>573 #include <sys/mman.h>574 #include <sys/stat.h>575 576 577 namespace vspace {578 namespace internals {579 580 size_t config[4]581 = { METABLOCK_SIZE, MAX_PROCESS, SEGMENT_SIZE, MAX_SEGMENTS };582 583 VMem VMem::vmem_global;584 585 // offsetof() only works for POD types, so we need to construct586 // a portable version of it for metapage fields.587 588 #define metapageaddr(field) \589 ((char *) &vmem.metapage->field - (char *) vmem.metapage)590 591 size_t VMem::filesize() {592 struct stat stat;593 fstat(fd, &stat);594 return stat.st_size;595 }596 597 Status VMem::init(int fd) {598 this->fd = fd;599 for (int i = 0; i < MAX_SEGMENTS; i++)600 segments[i] = VSeg(NULL);601 for (int i = 0; i < MAX_PROCESS; i++) {602 int channel[2];603 if (pipe(channel) < 0) {604 for (int j = 0; j < i; j++) {605 close(channels[j].fd_read);606 close(channels[j].fd_write);607 }608 return Status(ErrOS);609 }610 channels[i].fd_read = channel[0];611 channels[i].fd_write = channel[1];612 }613 lock_metapage();614 init_metapage(filesize() == 0);615 unlock_metapage();616 freelist = metapage->freelist;617 return Status(ErrNone);618 }619 620 Status VMem::init() {621 FILE *fp = tmpfile();622 Status result = init(fileno(fp));623 if (!result.ok())624 return result;625 current_process = 0;626 file_handle = fp;627 metapage->process_info[0].pid = getpid();628 return Status(ErrNone);629 }630 631 Status VMem::init(const char *path) {632 int fd = open(path, O_RDWR | O_CREAT, 0600);633 if (fd < 0)634 return Status(ErrFile);635 init(fd);636 lock_metapage();637 // TODO: enter process in meta table638 unlock_metapage();639 return Status(ErrNone);640 }641 642 void VMem::deinit() {643 if (file_handle) {644 fclose(file_handle);645 file_handle = NULL;646 } else {647 close(fd);648 }649 munmap(metapage, METABLOCK_SIZE);650 metapage = NULL;651 current_process = -1;652 freelist = NULL;653 for (int i = 0; i < MAX_SEGMENTS; i++) {654 if (!segments[i].is_free())655 munmap(segments[i].base, SEGMENT_SIZE);656 segments[i] = VSeg(NULL);657 }658 for (int i = 0; i < MAX_PROCESS; i++) {659 close(channels[i].fd_read);660 close(channels[i].fd_write);661 }662 }663 664 void *VMem::mmap_segment(int seg) {665 lock_metapage();666 void *map = mmap(NULL, SEGMENT_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd,667 METABLOCK_SIZE + seg * SEGMENT_SIZE);668 if (map == MAP_FAILED) {669 // This is an "impossible to proceed from here, because system state670 // is impossible to proceed from" situation, so we abort the program.671 perror("mmap");672 abort();673 }674 unlock_metapage();675 return map;676 }677 678 void VMem::add_segment() {679 int seg = metapage->segment_count++;680 ftruncate(fd, METABLOCK_SIZE + metapage->segment_count * SEGMENT_SIZE);681 void *map_addr = mmap_segment(seg);682 segments[seg] = VSeg(map_addr);683 Block *top = block_ptr(seg * SEGMENT_SIZE);684 top->next = freelist[LOG2_SEGMENT_SIZE];685 top->prev = VADDR_NULL;686 freelist[LOG2_SEGMENT_SIZE] = seg * SEGMENT_SIZE;687 }688 689 void FastLock::lock() {690 #ifdef HAVE_CPP_THREADS691 while (_lock.test_and_set()) {692 }693 bool empty = _owner < 0;694 if (empty) {695 _owner = vmem.current_process;696 } else {697 int p = vmem.current_process;698 vmem.metapage->process_info[p].next = -1;699 if (_head < 0)700 _head = p;701 else702 vmem.metapage->process_info[_tail].next = p;703 _tail = p;704 }705 _lock.clear();706 if (!empty)707 wait_signal(false);708 #else709 lock_file(vmem.fd, _offset);710 #endif711 }712 713 void FastLock::unlock() {714 #ifdef HAVE_CPP_THREADS715 while (_lock.test_and_set()) {716 }717 _owner = _head;718 if (_owner >= 0)719 _head = vmem.metapage->process_info[_head].next;720 _lock.clear();721 if (_owner >= 0)722 send_signal(_owner, 0, false);723 #else724 unlock_file(vmem.fd, _offset);725 #endif726 }727 728 static void lock_allocator() {729 vmem.metapage->allocator_lock.lock();730 }731 732 static void unlock_allocator() {733 vmem.metapage->allocator_lock.unlock();734 }735 736 static void print_freelists() {737 for (int i = 0; i <= LOG2_SEGMENT_SIZE; i++) {738 vaddr_t vaddr = vmem.freelist[i];739 if (vaddr != VADDR_NULL) {740 std::printf("%2d: %ld", i, (long)vaddr);741 vaddr_t prev = block_ptr(vaddr)->prev;742 if (prev != VADDR_NULL) {743 std::printf("(%ld)", (long)prev);744 }745 assert(block_ptr(vaddr)->prev == VADDR_NULL);746 for (;;) {747 vaddr_t last_vaddr = vaddr;748 Block *block = block_ptr(vaddr);749 vaddr = block->next;750 if (vaddr == VADDR_NULL)751 break;752 std::printf(" -> %ld", (long)vaddr);753 vaddr_t prev = block_ptr(vaddr)->prev;754 if (prev != last_vaddr) {755 std::printf("(%ld)", (long)prev);756 }757 }758 std::printf("\n");759 }760 }761 std::fflush(stdout);762 }763 764 void vmem_free(vaddr_t vaddr) {765 lock_allocator();766 #if defined(__GNUC__) && (__GNUC__>11)767 vaddr -= (sizeof(vaddr_t)*2);768 #else769 vaddr -= offsetof(Block, data);770 #endif771 vmem.ensure_is_mapped(vaddr);772 size_t segno = vmem.segment_no(vaddr);773 VSeg seg = vmem.segment(vaddr);774 segaddr_t addr = vmem.segaddr(vaddr);775 int level = seg.block_ptr(addr)->level();776 assert(!seg.is_free(addr));777 while (level < LOG2_SEGMENT_SIZE) {778 segaddr_t buddy = find_buddy(addr, level);779 Block *block = seg.block_ptr(buddy);780 // is buddy free and at the same level?781 if (!block->is_free() || block->level() != level)782 break;783 // remove buddy from freelist.784 Block *prev = vmem.block_ptr(block->prev);785 Block *next = vmem.block_ptr(block->next);786 block->data[0] = level;787 if (prev) {788 assert(prev->next == vmem.vaddr(segno, buddy));789 prev->next = block->next;790 } else {791 // head of freelist.792 assert(vmem.freelist[level] == vmem.vaddr(segno, buddy));793 vmem.freelist[level] = block->next;794 }795 if (next) {796 assert(next->prev == vmem.vaddr(segno, buddy));797 next->prev = block->prev;798 }799 // coalesce block with buddy800 level++;801 if (buddy < addr)802 addr = buddy;803 }804 // Add coalesced block to free list805 Block *block = seg.block_ptr(addr);806 block->prev = VADDR_NULL;807 block->next = vmem.freelist[level];808 block->mark_as_free(level);809 vaddr_t blockaddr = vmem.vaddr(segno, addr);810 if (block->next != VADDR_NULL)811 vmem.block_ptr(block->next)->prev = blockaddr;812 vmem.freelist[level] = blockaddr;813 unlock_allocator();814 }815 816 vaddr_t vmem_alloc(size_t size) {817 lock_allocator();818 #if defined(__GNUC__) && (__GNUC__>11)819 size_t alloc_size = size + (sizeof(vaddr_t)*2);820 #else821 size_t alloc_size = size + offsetof(Block, data);822 #endif823 int level = find_level(alloc_size);824 int flevel = level;825 while (flevel < LOG2_SEGMENT_SIZE && vmem.freelist[flevel] == VADDR_NULL)826 flevel++;827 if (vmem.freelist[flevel] == VADDR_NULL) {828 vmem.add_segment();829 }830 vmem.ensure_is_mapped(vmem.freelist[flevel]);831 while (flevel > level) {832 // get and split a block833 vaddr_t blockaddr = vmem.freelist[flevel];834 assert((blockaddr & ((1 << flevel) - 1)) == 0);835 Block *block = vmem.block_ptr(blockaddr);836 vmem.freelist[flevel] = block->next;837 if (vmem.freelist[flevel] != VADDR_NULL)838 vmem.block_ptr(vmem.freelist[flevel])->prev = VADDR_NULL;839 vaddr_t blockaddr2 = blockaddr + (1 << (flevel - 1));840 Block *block2 = vmem.block_ptr(blockaddr2);841 flevel--;842 block2->next = vmem.freelist[flevel];843 block2->prev = blockaddr;844 block->next = blockaddr2;845 block->prev = VADDR_NULL;846 // block->prev == VADDR_NULL already.847 vmem.freelist[flevel] = blockaddr;848 }849 assert(vmem.freelist[level] != VADDR_NULL);850 Block *block = vmem.block_ptr(vmem.freelist[level]);851 vaddr_t vaddr = vmem.freelist[level];852 #if defined(__GNUC__) && (__GNUC__>11)853 vaddr_t result = vaddr + (sizeof(vaddr_t)*2);854 #else855 vaddr_t result = vaddr + offsetof(Block, data);856 #endif857 vmem.freelist[level] = block->next;858 if (block->next != VADDR_NULL)859 vmem.block_ptr(block->next)->prev = VADDR_NULL;860 block->mark_as_allocated(vaddr, level);861 unlock_allocator();862 memset(block->data, 0, size);863 return result;864 }865 866 void init_flock_struct(867 struct flock &lock_info, size_t offset, size_t len, bool lock) {868 lock_info.l_start = offset;869 lock_info.l_len = len;870 lock_info.l_pid = 0;871 lock_info.l_type = lock ? F_WRLCK : F_UNLCK;872 lock_info.l_whence = SEEK_SET;873 }874 875 void lock_file(int fd, size_t offset, size_t len) {876 struct flock lock_info;877 init_flock_struct(lock_info, offset, len, true);878 fcntl(fd, F_SETLKW, &lock_info);879 }880 881 void unlock_file(int fd, size_t offset, size_t len) {882 struct flock lock_info;883 init_flock_struct(lock_info, offset, len, false);884 fcntl(fd, F_SETLKW, &lock_info);885 }886 887 void lock_metapage() {888 lock_file(vmem.fd, 0);889 }890 891 void unlock_metapage() {892 unlock_file(vmem.fd, 0);893 }894 895 void init_metapage(bool create) {896 if (create)897 ftruncate(vmem.fd, METABLOCK_SIZE);898 vmem.metapage = (MetaPage *) mmap(899 NULL, METABLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, vmem.fd, 0);900 if (create) {901 std::memcpy(vmem.metapage->config_header, config, sizeof(config));902 for (int i = 0; i <= LOG2_SEGMENT_SIZE; i++) {903 vmem.metapage->freelist[i] = VADDR_NULL;904 }905 vmem.metapage->segment_count = 0;906 vmem.metapage->allocator_lock = FastLock(metapageaddr(allocator_lock));907 } else {908 assert(std::memcmp(vmem.metapage->config_header, config,909 sizeof(config)) != 0);910 }911 }912 913 static void lock_process(int processno) {914 lock_file(vmem.fd,915 metapageaddr(process_info)916 + sizeof(ProcessInfo) * vmem.current_process);917 }918 919 static void unlock_process(int processno) {920 unlock_file(vmem.fd,921 metapageaddr(process_info)922 + sizeof(ProcessInfo) * vmem.current_process);923 }924 925 static ProcessInfo &process_info(int processno) {926 return vmem.metapage->process_info[processno];927 }928 929 bool send_signal(int processno, ipc_signal_t sig, bool lock) {930 if (lock)931 lock_process(processno);932 if (process_info(processno).sigstate != Waiting) {933 unlock_process(processno);934 return false;935 }936 if (processno == vmem.current_process) {937 process_info(processno).sigstate = Accepted;938 process_info(processno).signal = sig;939 } else {940 process_info(processno).sigstate = Pending;941 process_info(processno).signal = sig;942 int fd = vmem.channels[processno].fd_write;943 char buf[1] = { 0 };944 while (write(fd, buf, 1) != 1) {945 }946 }947 if (lock)948 unlock_process(processno);949 return true;950 }951 952 ipc_signal_t check_signal(bool resume, bool lock) {953 ipc_signal_t result;954 if (lock)955 lock_process(vmem.current_process);956 SignalState sigstate = process_info(vmem.current_process).sigstate;957 switch (sigstate) {958 case Waiting:959 case Pending: {960 int fd = vmem.channels[vmem.current_process].fd_read;961 char buf[1];962 if (lock && sigstate == Waiting) {963 unlock_process(vmem.current_process);964 while (read(fd, buf, 1) != 1) {965 }966 lock_process(vmem.current_process);967 } else {968 while (read(fd, buf, 1) != 1) {969 }970 }971 result = process_info(vmem.current_process).signal;972 process_info(vmem.current_process).sigstate973 = resume ? Waiting : Accepted;974 if (lock)975 unlock_process(vmem.current_process);976 break;977 }978 case Accepted:979 result = process_info(vmem.current_process).signal;980 if (resume)981 process_info(vmem.current_process).sigstate = Waiting;982 if (lock)983 unlock_process(vmem.current_process);984 break;985 }986 return result;987 }988 989 void accept_signals() {990 lock_process(vmem.current_process);991 process_info(vmem.current_process).sigstate = Waiting;992 unlock_process(vmem.current_process);993 }994 995 ipc_signal_t wait_signal(bool lock) {996 return check_signal(true, lock);997 }998 999 } // namespace internals1000 1001 pid_t fork_process() {1002 using namespace internals;1003 lock_metapage();1004 for (int p = 0; p < MAX_PROCESS; p++) {1005 if (vmem.metapage->process_info[p].pid == 0) {1006 pid_t pid = fork();1007 if (pid < 0) {1008 // error1009 return -1;1010 } else if (pid == 0) {1011 // child process1012 int parent = vmem.current_process;1013 vmem.current_process = p;1014 lock_metapage();1015 vmem.metapage->process_info[p].pid = getpid();1016 unlock_metapage();1017 send_signal(parent);1018 } else {1019 // parent process1020 unlock_metapage();1021 wait_signal();1022 // child has unlocked metapage, so we don't need to.1023 }1024 return pid;1025 }1026 }1027 unlock_metapage();1028 return -1;1029 }1030 1031 void Semaphore::post() {1032 int wakeup = -1;1033 internals::ipc_signal_t sig;1034 _lock.lock();1035 if (_head == _tail) {1036 _value++;1037 } else {1038 // don't increment value, as we'll pass that on to the next process.1039 wakeup = _waiting[_head];1040 sig = _signals[_head];1041 next(_head);1042 }1043 _lock.unlock();1044 if (wakeup >= 0) {1045 internals::send_signal(wakeup, sig);1046 }1047 }1048 1049 bool Semaphore::try_wait() {1050 bool result = false;1051 _lock.lock();1052 if (_value > 0) {1053 _value--;1054 result = true;1055 }1056 _lock.unlock();1057 return result;1058 }1059 1060 void Semaphore::wait() {1061 _lock.lock();1062 if (_value > 0) {1063 _value--;1064 _lock.unlock();1065 return;1066 }1067 _waiting[_tail] = internals::vmem.current_process;1068 _signals[_tail] = 0;1069 next(_tail);1070 _lock.unlock();1071 internals::wait_signal();1072 }1073 1074 bool Semaphore::start_wait(internals::ipc_signal_t sig) {1075 _lock.lock();1076 if (_value > 0) {1077 if (internals::send_signal(internals::vmem.current_process, sig))1078 _value--;1079 _lock.unlock();1080 return false;1081 }1082 _waiting[_tail] = internals::vmem.current_process;1083 _signals[_tail] = sig;1084 next(_tail);1085 _lock.unlock();1086 return true;1087 }1088 1089 bool Semaphore::stop_wait() {1090 bool result = false;1091 _lock.lock();1092 for (int i = _head; i != _tail; next(i)) {1093 if (_waiting[i] == internals::vmem.current_process) {1094 int last = i;1095 next(i);1096 while (i != _tail) {1097 _waiting[last] = _waiting[i];1098 _signals[last] = _signals[i];1099 last = i;1100 next(i);1101 }1102 _tail = last;1103 result = true;1104 break;1105 }1106 }1107 _lock.unlock();1108 return result;1109 }1110 1111 void EventSet::add(Event *event) {1112 event->_next = NULL;1113 if (_head == NULL) {1114 _head = _tail = event;1115 } else {1116 _tail->_next = event;1117 _tail = event;1118 }1119 }1120 1121 int EventSet::wait() {1122 size_t n = 0;1123 for (Event *event = _head; event; event = event->_next) {1124 if (!event->start_listen((int) (n++))) {1125 break;1126 }1127 }1128 internals::ipc_signal_t result = internals::check_signal();1129 for (Event *event = _head; event; event = event->_next) {1130 event->stop_listen();1131 }1132 internals::accept_signals();1133 return (int) result;1134 }1135 1136 } // namespace vspace1137 1129 #endif 1138 1130 #endif
Note: See TracChangeset
for help on using the changeset viewer.