Changeset 20e666 in git for kernel


Ignore:
Timestamp:
May 21, 2022, 7:37:48 PM (2 years ago)
Author:
Hans Schoenemann <hannes@…>
Branches:
(u'spielwiese', 'fe61d9c35bf7c61f2b6cbf1b56e25e2f08d536cc')
Children:
5f28fbf066626fa9c4a8f0e6408c0bb362fb386c
Parents:
656e63aeef7d71d8dcb77240341ef1b1904fe8d7
Message:
simplify vspace.cc (gcc<9 implies gcc<12)
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/oswrapper/vspace.cc

    r656e63a r20e666  
    194194  }
    195195  fflush(stdout);
     196}
     197
     198void 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
     246vaddr_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
     292void 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
     301void 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
     307void 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
     313void lock_metapage() {
     314  lock_file(vmem.fd, 0);
     315}
     316
     317void unlock_metapage() {
     318  unlock_file(vmem.fd, 0);
     319}
     320
     321void 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
     338static void lock_process(int processno) {
     339  lock_file(vmem.fd,
     340      metapageaddr(process_info)
     341          + sizeof(ProcessInfo) * vmem.current_process);
     342}
     343
     344static void unlock_process(int processno) {
     345  unlock_file(vmem.fd,
     346      metapageaddr(process_info)
     347          + sizeof(ProcessInfo) * vmem.current_process);
     348}
     349
     350static ProcessInfo &process_info(int processno) {
     351  return vmem.metapage->process_info[processno];
     352}
     353
     354bool 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
     377ipc_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
     414void accept_signals() {
     415  lock_process(vmem.current_process);
     416  process_info(vmem.current_process).sigstate = Waiting;
     417  unlock_process(vmem.current_process);
     418}
     419
     420ipc_signal_t wait_signal(bool lock) {
     421  return check_signal(true, lock);
     422}
     423
     424} // namespace internals
     425
     426pid_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
     456void 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
     474bool 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
     485void 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
     499bool 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
     514bool 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
     536void 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
     546int 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
     569namespace vspace {
     570namespace internals {
     571
     572size_t config[4]
     573    = { METABLOCK_SIZE, MAX_PROCESS, SEGMENT_SIZE, MAX_SEGMENTS };
     574
     575VMem 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
     583size_t VMem::filesize() {
     584  struct stat stat;
     585  fstat(fd, &stat);
     586  return stat.st_size;
     587}
     588
     589Status 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
     612Status 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
     623Status 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
     634void 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
     656void *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
     670void 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
     681void 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
     705void 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
     720static void lock_allocator() {
     721  vmem.metapage->allocator_lock.lock();
     722}
     723
     724static void unlock_allocator() {
     725  vmem.metapage->allocator_lock.unlock();
     726}
     727
     728static 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);
    196754}
    197755
     
    251809  lock_allocator();
    252810  #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);
    254812  #else
    255813  size_t alloc_size = size + offsetof(Block, data);
     
    333891      NULL, METABLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, vmem.fd, 0);
    334892  if (create) {
    335     memcpy(vmem.metapage->config_header, config, sizeof(config));
     893    std::memcpy(vmem.metapage->config_header, config, sizeof(config));
    336894    for (int i = 0; i <= LOG2_SEGMENT_SIZE; i++) {
    337895      vmem.metapage->freelist[i] = VADDR_NULL;
     
    340898    vmem.metapage->allocator_lock = FastLock(metapageaddr(allocator_lock));
    341899  } 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);
    343902  }
    344903}
     
    5681127
    5691128} // namespace vspace
    570 #else
    571 #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 construct
    586 // 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 table
    638   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 state
    670     // 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_THREADS
    691   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     else
    702       vmem.metapage->process_info[_tail].next = p;
    703     _tail = p;
    704   }
    705   _lock.clear();
    706   if (!empty)
    707     wait_signal(false);
    708 #else
    709   lock_file(vmem.fd, _offset);
    710 #endif
    711 }
    712 
    713 void FastLock::unlock() {
    714 #ifdef HAVE_CPP_THREADS
    715   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 #else
    724   unlock_file(vmem.fd, _offset);
    725 #endif
    726 }
    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   #else
    769   vaddr -= offsetof(Block, data);
    770   #endif
    771   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 buddy
    800     level++;
    801     if (buddy < addr)
    802       addr = buddy;
    803   }
    804   // Add coalesced block to free list
    805   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   #else
    821   size_t alloc_size = size + offsetof(Block, data);
    822   #endif
    823   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 block
    833     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   #else
    855   vaddr_t result = vaddr + offsetof(Block, data);
    856   #endif
    857   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).sigstate
    973           = 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 internals
    1000 
    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         // error
    1009         return -1;
    1010       } else if (pid == 0) {
    1011         // child process
    1012         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 process
    1020         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 vspace
    11371129#endif
    11381130#endif
Note: See TracChangeset for help on using the changeset viewer.