source: git/kernel/oswrapper/vspace.cc @ f80133

fieker-DuValspielwiese
Last change on this file since f80133 was f80133, checked in by Hans Schoenemann <hannes@…>, 3 years ago
update vspace
  • Property mode set to 100644
File size: 14.0 KB
Line 
1// https://github.com/rbehrends/vspace
2#include "vspace.h"
3#include "kernel/mod2.h"
4#ifdef HAVE_VSPACE
5#include <cstdlib>
6#include <unistd.h>
7#include <sys/mman.h>
8#include <sys/stat.h>
9
10#ifdef HAVE_CPP_THREADS
11#include <thread>
12#endif
13
14namespace vspace {
15namespace internals {
16
17size_t config[4]
18    = { METABLOCK_SIZE, MAX_PROCESS, SEGMENT_SIZE, MAX_SEGMENTS };
19
20VMem VMem::vmem_global;
21
22// offsetof() only works for POD types, so we need to construct
23// a portable version of it for metapage fields.
24
25#define metapageaddr(field) \
26  ((char *) &vmem.metapage->field - (char *) vmem.metapage)
27
28size_t VMem::filesize() {
29  struct stat stat;
30  fstat(fd, &stat);
31  return stat.st_size;
32}
33
34Status VMem::init(int fd) {
35  this->fd = fd;
36  for (int i = 0; i < MAX_SEGMENTS; i++)
37    segments[i] = VSeg(NULL);
38  for (int i = 0; i < MAX_PROCESS; i++) {
39    int channel[2];
40    if (pipe(channel) < 0) {
41      for (int j = 0; j < i; j++) {
42        close(channels[j].fd_read);
43        close(channels[j].fd_write);
44      }
45      return Status(ErrOS);
46    }
47    channels[i].fd_read = channel[0];
48    channels[i].fd_write = channel[1];
49  }
50  lock_metapage();
51  init_metapage(filesize() == 0);
52  unlock_metapage();
53  freelist = metapage->freelist;
54  return Status(ErrNone);
55}
56
57Status VMem::init() {
58  FILE *fp = tmpfile();
59  Status result = init(fileno(fp));
60  if (!result.ok())
61    return result;
62  current_process = 0;
63  file_handle = fp;
64  metapage->process_info[0].pid = getpid();
65  return Status(ErrNone);
66}
67
68Status VMem::init(const char *path) {
69  int fd = open(path, O_RDWR | O_CREAT, 0600);
70  if (fd < 0)
71    return Status(ErrFile);
72  init(fd);
73  lock_metapage();
74  // TODO: enter process in meta table
75  unlock_metapage();
76  return Status(ErrNone);
77}
78
79void VMem::deinit() {
80  if (file_handle) {
81    fclose(file_handle);
82    file_handle = NULL;
83  } else {
84    close(fd);
85  }
86  munmap(metapage, METABLOCK_SIZE);
87  metapage = NULL;
88  current_process = -1;
89  freelist = NULL;
90  for (int i = 0; i < MAX_SEGMENTS; i++) {
91    if (!segments[i].is_free())
92      munmap(segments[i].base, SEGMENT_SIZE);
93    segments[i] = VSeg(NULL);
94  }
95  for (int i = 0; i < MAX_PROCESS; i++) {
96    close(channels[i].fd_read);
97    close(channels[i].fd_write);
98  }
99}
100
101void *VMem::mmap_segment(int seg) {
102  lock_metapage();
103  void *map = mmap(NULL, SEGMENT_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
104      METABLOCK_SIZE + seg * SEGMENT_SIZE);
105  if (map == MAP_FAILED) {
106    // This is an "impossible to proceed from here, because system state
107    // is impossible to proceed from" situation, so we abort the program.
108    perror("mmap");
109    abort();
110  }
111  unlock_metapage();
112  return map;
113}
114
115void VMem::add_segment() {
116  int seg = metapage->segment_count++;
117  ftruncate(fd, METABLOCK_SIZE + metapage->segment_count * SEGMENT_SIZE);
118  void *map_addr = mmap_segment(seg);
119  segments[seg] = VSeg(map_addr);
120  Block *top = block_ptr(seg * SEGMENT_SIZE);
121  top->next = freelist[LOG2_SEGMENT_SIZE];
122  top->prev = VADDR_NULL;
123  freelist[LOG2_SEGMENT_SIZE] = seg * SEGMENT_SIZE;
124}
125
126void FastLock::lock() {
127#ifdef HAVE_CPP_THREADS
128  while (_lock.test_and_set()) {
129  }
130  bool empty = _owner < 0;
131  if (empty) {
132    _owner = vmem.current_process;
133  } else {
134    int p = vmem.current_process;
135    vmem.metapage->process_info[p].next = -1;
136    if (_head < 0)
137      _head = p;
138    else
139      vmem.metapage->process_info[_tail].next = p;
140    _tail = p;
141  }
142  _lock.clear();
143  if (!empty)
144    wait_signal(false);
145#else
146  lock_file(vmem.fd, _offset);
147#endif
148}
149
150void FastLock::unlock() {
151#ifdef HAVE_CPP_THREADS
152  while (_lock.test_and_set()) {
153  }
154  _owner = _head;
155  if (_owner >= 0)
156    _head = vmem.metapage->process_info[_head].next;
157  _lock.clear();
158  if (_owner >= 0)
159    send_signal(_owner, 0, false);
160#else
161  unlock_file(vmem.fd, _offset);
162#endif
163}
164
165static void lock_allocator() {
166  vmem.metapage->allocator_lock.lock();
167}
168
169static void unlock_allocator() {
170  vmem.metapage->allocator_lock.unlock();
171}
172
173static void print_freelists() {
174  for (int i = 0; i <= LOG2_SEGMENT_SIZE; i++) {
175    vaddr_t vaddr = vmem.freelist[i];
176    if (vaddr != VADDR_NULL) {
177      std::printf("%2d: %ld", i, vaddr);
178      vaddr_t prev = block_ptr(vaddr)->prev;
179      if (prev != VADDR_NULL) {
180        std::printf("(%ld)", prev);
181      }
182      assert(block_ptr(vaddr)->prev == VADDR_NULL);
183      for (;;) {
184        vaddr_t last_vaddr = vaddr;
185        Block *block = block_ptr(vaddr);
186        vaddr = block->next;
187        if (vaddr == VADDR_NULL)
188          break;
189        std::printf(" -> %ld", vaddr);
190        vaddr_t prev = block_ptr(vaddr)->prev;
191        if (prev != last_vaddr) {
192          std::printf("(%ld)", prev);
193        }
194      }
195      std::printf("\n");
196    }
197  }
198  std::fflush(stdout);
199}
200
201void vmem_free(vaddr_t vaddr) {
202  lock_allocator();
203  vaddr -= offsetof(Block, data);
204  vmem.ensure_is_mapped(vaddr);
205  size_t segno = vmem.segment_no(vaddr);
206  VSeg seg = vmem.segment(vaddr);
207  segaddr_t addr = vmem.segaddr(vaddr);
208  int level = seg.block_ptr(addr)->level();
209  assert(!seg.is_free(addr));
210  while (level < LOG2_SEGMENT_SIZE) {
211    segaddr_t buddy = find_buddy(addr, level);
212    Block *block = seg.block_ptr(buddy);
213    // is buddy free and at the same level?
214    if (!block->is_free() || block->level() != level)
215      break;
216    // remove buddy from freelist.
217    Block *prev = vmem.block_ptr(block->prev);
218    Block *next = vmem.block_ptr(block->next);
219    block->data[0] = level;
220    if (prev) {
221      assert(prev->next == vmem.vaddr(segno, buddy));
222      prev->next = block->next;
223    } else {
224      // head of freelist.
225      assert(vmem.freelist[level] == vmem.vaddr(segno, buddy));
226      vmem.freelist[level] = block->next;
227    }
228    if (next) {
229      assert(next->prev == vmem.vaddr(segno, buddy));
230      next->prev = block->prev;
231    }
232    // coalesce block with buddy
233    level++;
234    if (buddy < addr)
235      addr = buddy;
236  }
237  // Add coalesced block to free list
238  Block *block = seg.block_ptr(addr);
239  block->prev = VADDR_NULL;
240  block->next = vmem.freelist[level];
241  block->mark_as_free(level);
242  vaddr_t blockaddr = vmem.vaddr(segno, addr);
243  if (block->next != VADDR_NULL)
244    vmem.block_ptr(block->next)->prev = blockaddr;
245  vmem.freelist[level] = blockaddr;
246  unlock_allocator();
247}
248
249vaddr_t vmem_alloc(size_t size) {
250  lock_allocator();
251  size_t alloc_size = size + offsetof(Block, data);
252  int level = find_level(alloc_size);
253  int flevel = level;
254  while (flevel < LOG2_SEGMENT_SIZE && vmem.freelist[flevel] == VADDR_NULL)
255    flevel++;
256  if (vmem.freelist[flevel] == VADDR_NULL) {
257    vmem.add_segment();
258  }
259  vmem.ensure_is_mapped(vmem.freelist[flevel]);
260  while (flevel > level) {
261    // get and split a block
262    vaddr_t blockaddr = vmem.freelist[flevel];
263    assert((blockaddr & ((1 << flevel) - 1)) == 0);
264    Block *block = vmem.block_ptr(blockaddr);
265    vmem.freelist[flevel] = block->next;
266    if (vmem.freelist[flevel] != VADDR_NULL)
267      vmem.block_ptr(vmem.freelist[flevel])->prev = VADDR_NULL;
268    vaddr_t blockaddr2 = blockaddr + (1 << (flevel - 1));
269    Block *block2 = vmem.block_ptr(blockaddr2);
270    flevel--;
271    block2->next = vmem.freelist[flevel];
272    block2->prev = blockaddr;
273    block->next = blockaddr2;
274    block->prev = VADDR_NULL;
275    // block->prev == VADDR_NULL already.
276    vmem.freelist[flevel] = blockaddr;
277  }
278  assert(vmem.freelist[level] != VADDR_NULL);
279  Block *block = vmem.block_ptr(vmem.freelist[level]);
280  vaddr_t vaddr = vmem.freelist[level];
281  vaddr_t result = vaddr + offsetof(Block, data);
282  vmem.freelist[level] = block->next;
283  if (block->next != VADDR_NULL)
284    vmem.block_ptr(block->next)->prev = VADDR_NULL;
285  block->mark_as_allocated(vaddr, level);
286  unlock_allocator();
287  memset(block->data, 0, size);
288  return result;
289}
290
291void init_flock_struct(
292    struct flock &lock_info, size_t offset, size_t len, bool lock) {
293  lock_info.l_start = offset;
294  lock_info.l_len = len;
295  lock_info.l_pid = 0;
296  lock_info.l_type = lock ? F_WRLCK : F_UNLCK;
297  lock_info.l_whence = SEEK_SET;
298}
299
300void lock_file(int fd, size_t offset, size_t len) {
301  struct flock lock_info;
302  init_flock_struct(lock_info, offset, len, true);
303  fcntl(fd, F_SETLKW, &lock_info);
304}
305
306void unlock_file(int fd, size_t offset, size_t len) {
307  struct flock lock_info;
308  init_flock_struct(lock_info, offset, len, false);
309  fcntl(fd, F_SETLKW, &lock_info);
310}
311
312void lock_metapage() {
313  lock_file(vmem.fd, 0);
314}
315
316void unlock_metapage() {
317  unlock_file(vmem.fd, 0);
318}
319
320void init_metapage(bool create) {
321  if (create)
322    ftruncate(vmem.fd, METABLOCK_SIZE);
323  vmem.metapage = (MetaPage *) mmap(
324      NULL, METABLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, vmem.fd, 0);
325  if (create) {
326    std::memcpy(vmem.metapage->config_header, config, sizeof(config));
327    for (int i = 0; i <= LOG2_SEGMENT_SIZE; i++) {
328      vmem.metapage->freelist[i] = VADDR_NULL;
329    }
330    vmem.metapage->segment_count = 0;
331    vmem.metapage->allocator_lock = FastLock(metapageaddr(allocator_lock));
332  } else {
333    assert(std::memcmp(vmem.metapage->config_header, config,
334        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#endif
Note: See TracBrowser for help on using the repository browser.