nanovms / nanos

A kernel designed to run one and only one application in a virtualized environment
https://nanos.org
Apache License 2.0
2.59k stars 136 forks source link

implement munmap(2) #586

Closed wjhun closed 5 years ago

wjhun commented 5 years ago

ruby is doing a lot of mmaping of large spaces, only to immediately unmap them entirely or at least partially. Since we don't implement munmap(2) and are doing physical allocations with each mmap (no demand paging), we eventually run out of physical pages to dole out (at least in the sizes requested).

                 3 mmap
                 3 mmap: target 00000000000000000, size 1811c000, prot 3, flags 22, fd -1, offset 0
                 3    anon target: 7020000000, len: 1811c000 (given size: 1811c000)
                 3 direct return: 481573208064
                 3 munmap
                 3 munmap: addr 00000007020000000, size 0x1811c000
                 3 direct return: 0

There are a number of issues here to deal with over the longer term, but for today it may suffice to implement munmap and remove such mappings. If we really want to be lazy, we can just unmap the space, return the physical allocation and leave the virtual / rangemap allocation in place rather than try to undo it. (I'm going to start here to see if it's good enough for a ruby demo.)

Of course, we might just run out of virtual address space then. To do the right thing:

  1. Fix up the vmap / range search so we can safely remove arbitrary intersections with existing vmaps.
  2. Fix id_dealloc() and/or bitmap_dealloc() so that areas that aren't aligned or sized to a power of 2 can still be removed.

Although we'd still need to do #2 for physical id heap deallocations...

We could also discuss doing a sort of poor man's demand paging just for anonymous, private mappings. This is possible now that we have the vmaps structure in place.

wjhun commented 5 years ago

I'm able to get a ruby hello world using the munmap branch. This branch isn't quite ready for merging - the implementation is kind of a hack to just get ruby unstuck. Because of some idiosyncrasies with the bitmap allocator, the existing code could be potentially unsafe in certain situations. I have a plan for fixing the bitmap allocator logic (also allowing unaligned allocations - would be good for tfs) to make these unmaps more straightforward. Prob 1-2 add'l days to fix.

A few notes about this ruby/sinatra test:

Possibilities for the short term:

For the longer term:

$ ops load ruby_2.3.1 -p 4567 -c config.json
Extracting /home/wjhun/.ops/packages/ruby_2.3.1.tar.gz to /home/wjhun/.ops/.staging/ruby_2.3.1
[ruby myapp.rb -o 0.0.0.0]
booting /home/wjhun/.ops/images/ruby.img ...
assigned: 10.0.2.15
[2019-03-10 06:48:50] INFO  WEBrick 1.3.1
[2019-03-10 06:48:50] INFO  ruby 2.3.1 (2016-04-26) [x86_64-linux-gnu]
== Sinatra (v2.0.5) has taken the stage on 4567 for development with backup from WEBrick
[2019-03-10 06:48:50] INFO  WEBrick::HTTPServer#start: pid=1 port=4567
10.0.2.2 - - [10/Mar/2019:06:49:20 +0000] "GET / HTTP/1.1" 200 12 14.7117
10.0.2.2 - - [10/Mar/2019:06:49:05 UTC] "GET / HTTP/1.1" 200 12
- -> /
10.0.2.2 - - [10/Mar/2019:07:04:08 +0000] "GET / HTTP/1.1" 200 12 14.0485
10.0.2.2 - - [10/Mar/2019:07:03:54 UTC] "GET / HTTP/1.1" 200 12
- -> /
eyberg commented 5 years ago

might be worth breaking these out into separate issues so we can track them

wjhun commented 5 years ago

Various issues here have been resolved, including demand paging for anonymous mmaps (#607), mmap and munmap updates (#804, #811). With #830, the ruby/sinatra webserver test above runs:

$ cat myapp.rb 
# myapp.rb
require 'sinatra'

get '/' do
  'Hello world!'
end
$ ops load ruby_2.3.1 -c config.json -p 4567
warning: overwriting existing file /lib/x86_64-linux-gnu/libnss_dns.so.2 hostpath old: /home/wjhun/.ops/.staging/ruby_2.3.1/sysroot/lib/x86_64-linux-gnu/libnss_dns.so.2 new: lib/x86_64-linux-gnu/libnss_dns.so.2
[ruby myapp.rb -o 0.0.0.0]
booting /home/wjhun/.ops/images/ruby.img ...
assigned: 10.0.2.15
[2019-05-24 18:12:42] INFO  WEBrick 1.3.1
[2019-05-24 18:12:42] INFO  ruby 2.3.1 (2016-04-26) [x86_64-linux-gnu]
== Sinatra (v2.0.5) has taken the stage on 4567 for development with backup from WEBrick
[2019-05-24 18:12:42] INFO  WEBrick::HTTPServer#start: pid=1 port=4567
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.1212
10.0.2.2 - - [24/May/2019:18:12:45 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0051
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0040
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0045
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0039
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0041
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0037
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0042
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0039
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
10.0.2.2 - - [24/May/2019:18:12:46 +0000] "GET / HTTP/1.1" 200 12 0.0043
10.0.2.2 - - [24/May/2019:18:12:46 UTC] "GET / HTTP/1.0" 200 12
- -> /
$ ab -n 10 http://127.0.0.1:4567/
This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient).....done

Server Software:        WEBrick/1.3.1
Server Hostname:        127.0.0.1
Server Port:            4567

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      1
Time taken for tests:   0.514 seconds
Complete requests:      10
Failed requests:        0
Total transferred:      2890 bytes
HTML transferred:       120 bytes
Requests per second:    19.45 [#/sec] (mean)
Time per request:       51.412 [ms] (mean)
Time per request:       51.412 [ms] (mean, across all concurrent requests)
Transfer rate:          5.49 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    24   51  71.6     26     254
Waiting:       23   48  71.6     24     251
Total:         24   51  71.6     27     254

Percentage of the requests served within a certain time (ms)
  50%     27
  66%     27
  75%     34
  80%     43
  90%    254
  95%    254
  98%    254
  99%    254
 100%    254 (longest request)