diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index ec2eca7db..36e7b8880 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -1,13 +1,16 @@ name: Benchmarks Test on: + push: + branches: + - 1.0.0-preview schedule: # Schedule to run everyday at 10PM UTC (6AM CST) - cron: '0 22 * * *' jobs: Sysbench_Test: - timeout-minutes: 20 + timeout-minutes: 40 runs-on: ${{ matrix.self_runner }} strategy: matrix: @@ -57,7 +60,7 @@ jobs: run: docker stop ${{ env.CONTAINER_NAME }} Iperf3_Test: - timeout-minutes: 20 + timeout-minutes: 40 runs-on: ${{ matrix.self_runner }} strategy: matrix: diff --git a/.github/workflows/demo_test.yml b/.github/workflows/demo_test.yml index 0073fad85..6eeb70c26 100644 --- a/.github/workflows/demo_test.yml +++ b/.github/workflows/demo_test.yml @@ -553,11 +553,11 @@ jobs: - name: Download and build Enclave TLS run: docker exec ${{ github.job }} bash -c "cd /root/occlum/demos/enclave_tls && ./download_and_build_enclave_tls.sh" - - name: Run the encalve tls server on Occlum + - name: Run the enclave tls server on Occlum run: docker exec ${{ github.job }} bash -c "cd /root/occlum/demos/enclave_tls && SGX_MODE=SIM ./run_enclave_tls_server_in_occlum.sh" # Ignore the result here as simulation mode doesn't have RA capabilities - - name: Run the encalve tls client + - name: Run the enclave tls client run: | sleep ${{ env.nap_time }}; docker exec ${{ github.job }} bash -c "/usr/share/enclave-tls/samples/enclave-tls-client" || true diff --git a/.github/workflows/hw_mode_test.yml b/.github/workflows/hw_mode_test.yml index cd7538cbb..300866bd5 100644 --- a/.github/workflows/hw_mode_test.yml +++ b/.github/workflows/hw_mode_test.yml @@ -914,6 +914,52 @@ jobs: if: ${{ always() }} run: docker stop ${{ env.CONTAINER_NAME }} + MySQL_test: + timeout-minutes: 180 + if: github.event_name == 'push' || ${{ contains(github.event.pull_request.labels.*.name, 'SGX-hardware-test-required') }} + runs-on: ${{ matrix.self_runner }} + strategy: + matrix: + self_runner: [[self-hosted, SGX2-HW]] + + steps: + - name: Clean before running + run: | + sudo chown -R ${{ secrets.CI_ADMIN }} "${{ github.workspace }}" + + - name: Checkout code + if: github.event_name == 'push' + uses: actions/checkout@v2 + with: + submodules: true + + - name: Checkout code from fork + if: ${{ contains(github.event.pull_request.labels.*.name, 'SGX-hardware-test-required') }} + uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge + submodules: true + + - uses: ./.github/workflows/composite_action/hw + with: + container-name: ${{ github.job }} + build-envs: 'OCCLUM_RELEASE_BUILD=1' + + - name: Download and build mysql + run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/mysql && ./dl_and_build_mysql.sh" + + - name: Run mysql server + run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/mysql && ./run_mysql_server.sh" & + + - name: Run mysql benchmarks + run: | + sleep 120; + docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/mysql && ./run_benchmarks.sh" + + - name: Clean the environment + if: ${{ always() }} + run: docker stop ${{ env.CONTAINER_NAME }} + Stress_test_with_musl: timeout-minutes: 500 if: github.event_name == 'schedule' diff --git a/demos/benchmarks/README.md b/demos/benchmarks/README.md index b06f90317..77f589c8c 100644 --- a/demos/benchmarks/README.md +++ b/demos/benchmarks/README.md @@ -10,6 +10,6 @@ This set of demos shows how commonly used benchmarking tools can be run inside S ## Benchmarks Data -There is a enabled [`benchmarks CI`](https://github.com/occlum/ngo/blob/master/.github/workflows/benchmarks.yml) for continuous benchmarking. It utilizes the [`github-action-benchmark`](https://github.com/benchmark-action/github-action-benchmark) to provide a chart view for visualized historical benchmarks data on the GitHub pages. +There is a enabled [`benchmarks CI`](https://github.com/occlum/occlum/blob/1.0.0-preview/.github/workflows/benchmarks.yml) for continuous benchmarking. It utilizes the [`github-action-benchmark`](https://github.com/benchmark-action/github-action-benchmark) to provide a chart view for visualized historical benchmarks data on the GitHub pages. -[**History Data**](https://occlum.io/ngo/dev/benchmarks/) +[**History Data**](https://occlum.io/occlum/dev/benchmarks/) diff --git a/demos/mysql/dl_and_build_mysql.sh b/demos/mysql/dl_and_build_mysql.sh index 542e47dd5..e58f8c27e 100755 --- a/demos/mysql/dl_and_build_mysql.sh +++ b/demos/mysql/dl_and_build_mysql.sh @@ -39,10 +39,36 @@ patch -s -p0 < apply-mysql-to-occlum.patch pushd mysql_src mkdir bld && cd bld -cmake -j$(nproc) .. -DCMAKE_CXX_FLAGS="-fpic -pie" -DCMAKE_C_FLAGS="-fpic -pie" - -CC="-fpic -pie" CXX="-fpic -pie" make -j$(nproc) +cmake -j$(nproc) .. \ + -DCMAKE_CXX_FLAGS="-fpic -pie" -DCMAKE_C_FLAGS="-fpic -pie" \ + -DWITH_ARCHIVE_STORAGE_ENGINE=0 \ + -DWITH_EXAMPLE_STORAGE_ENGINE=0 \ + -DWITH_FEDERATED_STORAGE_ENGINE=0 \ + -DDISABLE_PSI_COND=1 \ + -DDISABLE_PSI_DATA_LOCK=1 \ + -DDISABLE_PSI_ERROR=1 \ + -DDISABLE_PSI_FILE=1 \ + -DDISABLE_PSI_IDLE=1 \ + -DDISABLE_PSI_MEMORY=1 \ + -DDISABLE_PSI_METADATA=1 \ + -DDISABLE_PSI_MUTEX=1 \ + -DDISABLE_PSI_PS=1 \ + -DDISABLE_PSI_RWLOCK=1 \ + -DDISABLE_PSI_SOCKET=1 \ + -DDISABLE_PSI_SP=1 \ + -DDISABLE_PSI_STAGE=0 \ + -DDISABLE_PSI_STATEMENT=1 \ + -DDISABLE_PSI_STATEMENT_DIGEST=1 \ + -DDISABLE_PSI_TABLE=1 \ + -DDISABLE_PSI_THREAD=0 \ + -DDISABLE_PSI_TRANSACTION=1 \ + -DWITH_MYSQLX=0 \ + -DWITH_NDB_JAVA=0 \ + -DWITH_RAPID=0 \ + -DWITH_ROUTER=0 \ + -DWITH_UNIT_TESTS=0 +make -j4 make install -j$(nproc) cd .. diff --git a/demos/mysql/my.cnf b/demos/mysql/my.cnf index 6b905c628..d7ea8cc85 100644 --- a/demos/mysql/my.cnf +++ b/demos/mysql/my.cnf @@ -11,7 +11,6 @@ port = 3306 bind-address = 127.0.0.1 skip-networking = 0 skip_ssl = 0 -mysqlx = 0 wait_timeout = 60 interactive_timeout = 120 diff --git a/demos/mysql/mysql.yaml b/demos/mysql/mysql.yaml index ce524c6bd..50c9cc400 100644 --- a/demos/mysql/mysql.yaml +++ b/demos/mysql/mysql.yaml @@ -27,7 +27,7 @@ targets: - target: /opt/occlum/glibc/lib copy: - files: - - /usr/local/mysql/lib/mysqlrouter/private/libprotobuf-lite.so.3.19.4 + - ../mysql_src/bld/library_output_directory/libprotobuf-lite.so.3.19.4 - target: / copy: - files: diff --git a/demos/mysql/run_benchmarks.sh b/demos/mysql/run_benchmarks.sh new file mode 100755 index 000000000..a48645ce1 --- /dev/null +++ b/demos/mysql/run_benchmarks.sh @@ -0,0 +1,63 @@ +#!/bin/bash +set -e + +GREEN='\033[1;32m' +NC='\033[0m' + +echo -e "${GREEN}Install sysbench first${NC}" + +# We use sysbench to test mysql, need to install it first +apt-get install -y sysbench + +function run_benchmarks() +{ + WORKLOADS=("oltp_point_select" "oltp_write_only" "oltp_read_write") + for item in ${WORKLOADS[@]} + do + echo "start to prepare for $item" + sleep 3 + sysbench /usr/share/sysbench/$item.lua\ + --mysql-host='127.0.0.1'\ + --mysql-user=root\ + --time=60\ + --mysql-db=mysql\ + --tables=3\ + --table_size=100000\ + --rand-type=pareto\ + prepare + + echo "start to run $item" + sleep 3 + sysbench /usr/share/sysbench/$item.lua\ + --mysql-host='127.0.0.1'\ + --mysql-user=root\ + --time=60\ + --mysql-db=mysql\ + --tables=3\ + --table_size=100000\ + --rand-type=pareto\ + --threads=2\ + --report-interval=10\ + run + + echo "start to cleanup $item" + sleep 3 + sysbench /usr/share/sysbench/$item.lua\ + --mysql-host='127.0.0.1'\ + --mysql-user=root\ + --time=60\ + --mysql-db=mysql\ + --tables=3\ + --table_size=100000\ + --rand-type=pareto\ + --threads=2\ + --report-interval=10\ + cleanup + done + + echo "all done" +} + +echo -e "${GREEN}Run benchmarks using sysbench${NC}" + +run_benchmarks diff --git a/demos/mysql/run_mysql_server.sh b/demos/mysql/run_mysql_server.sh index 2bda14065..5869c470e 100755 --- a/demos/mysql/run_mysql_server.sh +++ b/demos/mysql/run_mysql_server.sh @@ -14,8 +14,9 @@ MYSQLD=mysqld rm -rf occlum_instance && occlum new occlum_instance pushd occlum_instance -yq '.resource_limits.user_space_size = "8000MB" | - .resource_limits.kernel_space_heap_size ="1000MB" ' -i Occlum.yaml +yq '.resource_limits.user_space_size.init = "8000MB" | + .resource_limits.kernel_space_heap_size.init = "1000MB" | + .resource_limits.kernel_space_heap_size.max = "2000MB" ' -i Occlum.yaml # 2. Copy files into Occlum instance and build rm -rf image diff --git a/demos/python/flask/install_python_with_conda.sh b/demos/python/flask/install_python_with_conda.sh index e900603e2..fb9f33e91 100755 --- a/demos/python/flask/install_python_with_conda.sh +++ b/demos/python/flask/install_python_with_conda.sh @@ -8,4 +8,4 @@ script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # 2. Install python and dependencies to specified position [ -f Miniconda3-latest-Linux-x86_64.sh ] || wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh [ -d miniconda ] || bash ./Miniconda3-latest-Linux-x86_64.sh -b -p $script_dir/miniconda -$script_dir/miniconda/bin/conda create --prefix $script_dir/python-occlum -y python=3.7.11 flask=1.1.2 flask-restful=0.3.8 jinja2=3.0 +$script_dir/miniconda/bin/conda create --prefix $script_dir/python-occlum -y python=3.7.11 flask=1.1.2 flask-restful=0.3.8 jinja2=3.0 werkzeug=2.0.3 diff --git a/deps/io-uring b/deps/io-uring index 7b421d95d..c654c4925 160000 --- a/deps/io-uring +++ b/deps/io-uring @@ -1 +1 @@ -Subproject commit 7b421d95d2fe00a28f97b573080c2bea9a169886 +Subproject commit c654c4925bb0b013d3eec736015f8ac4888722be diff --git a/docs/demo_contribution_guide.md b/docs/demo_contribution_guide.md index e985d79b4..7c7a048ce 100644 --- a/docs/demo_contribution_guide.md +++ b/docs/demo_contribution_guide.md @@ -42,7 +42,7 @@ To test and reproduce the new demo, it is better to "script-ize" the work. For e ## Step 3: Add New Demo to CI -It is the last one step before submitting but very important, which can definityly boost the speed of review and merge. And it also makes sure that this demo is tested for every PR, commit and release. So that it won't get broken in a new version. +It is the last one step before submitting but very important, which can definitely boost the speed of review and merge. And it also makes sure that this demo is tested for every PR, commit and release. So that it won't get broken in a new version. ### 3.1 - Simple dependency diff --git a/docs/install_occlum_packages.md b/docs/install_occlum_packages.md index eff04f9b1..91d1fb32a 100644 --- a/docs/install_occlum_packages.md +++ b/docs/install_occlum_packages.md @@ -63,7 +63,7 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-cer 2. Install Intel® SGX Driver and Intel® SGX PSW -Please follow [Intel SGX Installation Guide](https://download.01.org/intel-sgx/sgx-linux/2.13/docs/Intel_SGX_Installation_Guide_Linux_2.13_Open_Source.pdf) to install SGX driver and SGX PSW. SGX SDK is not required. Using PSW installer is recommanded. +Please follow [Intel SGX Installation Guide](https://download.01.org/intel-sgx/sgx-linux/2.13/docs/Intel_SGX_Installation_Guide_Linux_2.13_Open_Source.pdf) to install SGX driver and SGX PSW. SGX SDK is not required. Using PSW installer is recommended. To install PSW, follow the guide to add Intel® SGX repository to APT source. And then run: ``` @@ -99,11 +99,11 @@ occlum run /bin/hello_world ``` -## Version Compatability Matrix +## Version Compatibility Matrix When version is not specified, Occlum with the latest version will be installed. If a user would like to evaluate an older version, please make sure the corresponding Intel® SGX PSW is installed. -The matrix below shows the version compatability since Occlum `0.16.0`. Please check before installing or upgrading. +The matrix below shows the version compatibility since Occlum `0.16.0`. Please check before installing or upgrading. | Occlum Version | SGX PSW Version | Tested under Ubuntu | Tested under CentOS | | --------------- | ----------------- | ------------------- | ------------------- | diff --git a/docs/readthedocs/docs/source/quickstart.md b/docs/readthedocs/docs/source/quickstart.md index b3488bc9c..1ac1e4bef 100644 --- a/docs/readthedocs/docs/source/quickstart.md +++ b/docs/readthedocs/docs/source/quickstart.md @@ -4,7 +4,7 @@ ### Supported HW -First, please make sure the basemetal or VM machine support SGX. Otherwise, users can only try SW simulation mode. +First, please make sure the baremetal or VM machine support SGX. Otherwise, users can only try SW simulation mode. From Occlum v1.0, only SGX2 or SGX1 with [FLC](https://www.intel.com/content/www/us/en/developer/articles/technical/an-update-on-3rd-party-attestation.html)(Flexible Launch Control) feature are supported. diff --git a/docs/readthedocs/docs/source/tests/benchmark.md b/docs/readthedocs/docs/source/tests/benchmark.md index c0521674d..45e99fcac 100644 --- a/docs/readthedocs/docs/source/tests/benchmark.md +++ b/docs/readthedocs/docs/source/tests/benchmark.md @@ -10,10 +10,10 @@ This set of demos shows how commonly used benchmarking tools can be run inside S ## Benchmarks Data -There is a enabled [benchmarks CI](https://github.com/occlum/ngo/blob/master/.github/workflows/benchmarks.yml) for continuous benchmarking. It utilizes the [github-action-benchmark](https://github.com/benchmark-action/github-action-benchmark) to provide a chart view for visualized historical benchmarks data on the GitHub pages. +There is a enabled [benchmarks CI](https://github.com/occlum/occlum/blob/1.0.0-preview/.github/workflows/benchmarks.yml) for continuous benchmarking. It utilizes the [github-action-benchmark](https://github.com/benchmark-action/github-action-benchmark) to provide a chart view for visualized historical benchmarks data on the GitHub pages. The CI runs periodically. For example, **sysbench** has the historical benchmarks chart as below. ![sysbench_chart](../images/benchmark.png) -[**History Data**](https://occlum.io/ngo/dev/benchmarks/) \ No newline at end of file +[**History Data**](https://occlum.io/occlum/dev/benchmarks/) diff --git a/src/exec/occlum_exec.proto b/src/exec/occlum_exec.proto index f84e41754..566942bb7 100644 --- a/src/exec/occlum_exec.proto +++ b/src/exec/occlum_exec.proto @@ -44,7 +44,7 @@ message ExecCommRequest { string sockpath = 2; string command = 3; repeated string parameters = 4; - repeated string enviroments = 5; + repeated string environments = 5; } message ExecCommResponse { diff --git a/src/exec/src/bin/occlum_exec_client.rs b/src/exec/src/bin/occlum_exec_client.rs index 370f2622c..5944498ac 100644 --- a/src/exec/src/bin/occlum_exec_client.rs +++ b/src/exec/src/bin/occlum_exec_client.rs @@ -56,9 +56,9 @@ fn exec_command( parameter_list.push(p.to_string()); } - let mut enviroments_list = RepeatedField::default(); + let mut environments_list = RepeatedField::default(); for env in envs { - enviroments_list.push(env.to_string()); + environments_list.push(env.to_string()); } let tmp_dir = TempDir::new("occlum_tmp").expect("create temp dir"); @@ -91,7 +91,7 @@ fn exec_command( process_id: process::id(), command: command.to_string(), parameters: parameter_list, - enviroments: enviroments_list, + environments: environments_list, sockpath: String::from(sockpath.as_path().to_str().unwrap()), ..Default::default() }, diff --git a/src/exec/src/bin/occlum_exec_server.rs b/src/exec/src/bin/occlum_exec_server.rs index 4c9d99e8c..626de58ee 100644 --- a/src/exec/src/bin/occlum_exec_server.rs +++ b/src/exec/src/bin/occlum_exec_server.rs @@ -114,7 +114,7 @@ fn main() { while !*server_stopped { server_stopped = cvar.wait(server_stopped).unwrap(); } - rust_occlum_pal_destroy().expect("Destory occlum image failed"); + rust_occlum_pal_destroy().expect("Destroy occlum image failed"); println!("server stopped"); } } @@ -186,7 +186,7 @@ fn rust_occlum_pal_init(num_vcpus: u32) -> Result<(), i32> { } } -///Destroyes the Occlum enclave image +///Destroys the Occlum enclave image fn rust_occlum_pal_destroy() -> Result<(), i32> { let ret = unsafe { occlum_pal_destroy() }; match ret { diff --git a/src/exec/src/server.rs b/src/exec/src/server.rs index 3b92bde88..96f92fc58 100644 --- a/src/exec/src/server.rs +++ b/src/exec/src/server.rs @@ -193,7 +193,7 @@ impl OcclumExec for OcclumExecImpl { let cmd = req.command.clone(); let args = req.parameters.into_vec().clone(); - let envs = req.enviroments.into_vec().clone(); + let envs = req.environments.into_vec().clone(); let client_process_id = req.process_id; let exit_status = Box::new(Futex::::new(-1)); diff --git a/src/libos/Cargo.lock b/src/libos/Cargo.lock index 4b0ef94cd..dd45bcde0 100644 --- a/src/libos/Cargo.lock +++ b/src/libos/Cargo.lock @@ -177,7 +177,9 @@ dependencies = [ "byteorder", "cfg-if", "errno", + "io-uring", "io-uring-callback", + "lazy_static", "log", "memoffset 0.6.1", "num_enum", @@ -560,15 +562,6 @@ dependencies = [ "syn", ] -[[package]] -name = "instant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" -dependencies = [ - "cfg-if", -] - [[package]] name = "intrusive-collections" version = "0.9.0" @@ -580,11 +573,10 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.5.0-alpha" +version = "0.5.9" dependencies = [ "bitflags", "libc", - "parking_lot", "sgx_libc", "sgx_trts", "sgx_tstd", @@ -601,6 +593,7 @@ dependencies = [ "io-uring", "lazy_static", "libc", + "log", "sgx_libc", "sgx_tstd", "slab", @@ -637,9 +630,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.73" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "lock_api" @@ -735,31 +728,6 @@ dependencies = [ "spin 0.7.1", ] -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - [[package]] name = "pin-project-lite" version = "0.2.9" @@ -983,15 +951,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "redox_syscall" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.3.1" @@ -1231,12 +1190,6 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - [[package]] name = "spin" version = "0.5.2" diff --git a/src/libos/crates/async-rt/src/executor.rs b/src/libos/crates/async-rt/src/executor.rs index 4a367fad3..e4888c081 100644 --- a/src/libos/crates/async-rt/src/executor.rs +++ b/src/libos/crates/async-rt/src/executor.rs @@ -14,9 +14,14 @@ pub fn num_vcpus() -> u32 { EXECUTOR.num_vcpus() } +/// Create new vcpu id +pub fn new_vcpu() -> u32 { + EXECUTOR.new_vcpu() +} + /// Start running tasks in this vcpu of executor -pub fn run_tasks() -> u32 { - EXECUTOR.run_tasks() +pub fn run_tasks(this_vcpu: u32) -> u32 { + EXECUTOR.run_tasks(this_vcpu) } /// Shutdown the executor @@ -83,9 +88,13 @@ impl Executor { self.num_vcpus } + /// Return the new vcpu id + pub fn new_vcpu(&self) -> u32 { + self.running_vcpus.fetch_add(1, Ordering::Relaxed) + } + /// Start running tasks in this vcpu of executor - pub fn run_tasks(&self) -> u32 { - let this_vcpu = self.running_vcpus.fetch_add(1, Ordering::Relaxed); + pub fn run_tasks(&self, this_vcpu: u32) -> u32 { debug_assert!(this_vcpu < self.num_vcpus); vcpu::set_current(this_vcpu); diff --git a/src/libos/crates/async-rt/src/scheduler/mod.rs b/src/libos/crates/async-rt/src/scheduler/mod.rs index 046edf575..c9095a3cd 100644 --- a/src/libos/crates/async-rt/src/scheduler/mod.rs +++ b/src/libos/crates/async-rt/src/scheduler/mod.rs @@ -1,7 +1,6 @@ //! Scheduler. use crate::prelude::*; -use crate::util::AtomicBits; use crate::vcpu; use std::sync::Arc; diff --git a/src/libos/crates/async-rt/src/task/mod.rs b/src/libos/crates/async-rt/src/task/mod.rs index 46c55a004..ae2935491 100644 --- a/src/libos/crates/async-rt/src/task/mod.rs +++ b/src/libos/crates/async-rt/src/task/mod.rs @@ -66,7 +66,8 @@ fn init_runner_threads() { for _ in 0..crate::executor::num_vcpus() { std::thread::spawn(|| { - crate::executor::run_tasks(); + let this_vcpu = crate::executor::new_vcpu(); + crate::executor::run_tasks(this_vcpu); }); } }); diff --git a/src/libos/crates/async-rt/src/task/task.rs b/src/libos/crates/async-rt/src/task/task.rs index a18ec4d13..396e52599 100644 --- a/src/libos/crates/async-rt/src/task/task.rs +++ b/src/libos/crates/async-rt/src/task/task.rs @@ -34,6 +34,10 @@ impl Task { &self.tirqs } + pub fn vcpu(&self) -> Option { + self.sched_state.vcpu() + } + /// Get the task that a given tirqs is associated to. /// /// # Safety diff --git a/src/libos/crates/async-sfs/src/metadata.rs b/src/libos/crates/async-sfs/src/metadata.rs index bbf5ac1b9..b62c7bee5 100644 --- a/src/libos/crates/async-sfs/src/metadata.rs +++ b/src/libos/crates/async-sfs/src/metadata.rs @@ -302,9 +302,9 @@ pub const FS_MAGIC: u32 = 0x2f8d_be2b; pub const INODE_CACHE_CAP: usize = 256; /// number of direct blocks in inode pub const NDIRECT: usize = 12; -/// default fs infomation string +/// default fs information string pub const FS_INFO: &str = "async simple file system"; -/// max length of infomation +/// max length of information pub const MAX_INFO_LEN: usize = 31; /// max length of filename pub const MAX_FNAME_LEN: usize = 255; diff --git a/src/libos/crates/async-sfs/src/utils.rs b/src/libos/crates/async-sfs/src/utils.rs index 453edbd0c..e8dc23f4b 100644 --- a/src/libos/crates/async-sfs/src/utils.rs +++ b/src/libos/crates/async-sfs/src/utils.rs @@ -20,7 +20,7 @@ pub unsafe trait AsBuf { } } -/// Dirty wraps a value of type T with functions similiar to that of a Read/Write +/// Dirty wraps a value of type T with functions similar to that of a Read/Write /// lock but simply sets a dirty flag on write(), reset on read() pub struct Dirty { value: T, diff --git a/src/libos/crates/async-socket/Cargo.toml b/src/libos/crates/async-socket/Cargo.toml index af17499f9..ccbf468ac 100644 --- a/src/libos/crates/async-socket/Cargo.toml +++ b/src/libos/crates/async-socket/Cargo.toml @@ -19,8 +19,10 @@ byteorder = { version = "1.3.2", default-features = false } errno = { path = "../errno" } num_enum = { version = "0.5", default-features = false } slab = { version = "0.4.5", default-features = false } +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } libc = { version = "0.2", optional = true } log = "0.4" +io-uring = { path = "../../../../deps/io-uring", features = ["unstable"]} io-uring-callback = { path = "../io-uring-callback" } memoffset = "0.6" sgx_types = { path = "../../../../deps/rust-sgx-sdk/sgx_types", optional = true } @@ -30,6 +32,5 @@ sgx_libc = { path = "../../../../deps/rust-sgx-sdk/sgx_libc", optional = true } sgx-untrusted-alloc = { path = "../sgx-untrusted-alloc", default-features = false } [dev-dependencies] -lazy_static = { version = "1.4.0", features = ["spin_no_std"] } futures = { version = "0.3", default-features = false, features = ["alloc"] } async-rt = { path = "../async-rt", features = ["auto_run"] } diff --git a/src/libos/crates/async-socket/src/common/common.rs b/src/libos/crates/async-socket/src/common/common.rs index ff53cae6a..e7da87898 100644 --- a/src/libos/crates/async-socket/src/common/common.rs +++ b/src/libos/crates/async-socket/src/common/common.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use async_io::socket::Timeout; -use io_uring_callback::IoUring; +use io_uring_callback::IoUringRef; cfg_if::cfg_if! { if #[cfg(feature = "sgx")] { use libc::ocall::socket as do_socket; @@ -30,6 +30,7 @@ pub struct Common { inner: Mutex>, timeout: Mutex, phantom_data: PhantomData<(A, R)>, + io_uring: IoUringRef, } impl Common { @@ -43,6 +44,7 @@ impl Common { let pollee = Pollee::new(Events::empty()); let inner = Mutex::new(Inner::new()); let timeout = Mutex::new(Timeout::new()); + let io_uring = R::io_uring(); Ok(Self { host_fd, type_, @@ -52,6 +54,7 @@ impl Common { inner, timeout, phantom_data: PhantomData, + io_uring, }) } @@ -86,6 +89,7 @@ impl Common { let pollee = Pollee::new(Events::empty()); let inner = Mutex::new(Inner::new()); let timeout = Mutex::new(Timeout::new()); + let io_uring = R::io_uring(); Self { host_fd, type_, @@ -94,12 +98,13 @@ impl Common { pollee, inner, timeout, + io_uring, phantom_data: PhantomData, } } - pub fn io_uring(&self) -> &IoUring { - R::io_uring() + pub fn io_uring(&self) -> &IoUringRef { + &self.io_uring } pub fn host_fd(&self) -> HostFd { diff --git a/src/libos/crates/async-socket/src/datagram/sender.rs b/src/libos/crates/async-socket/src/datagram/sender.rs index e4b31795a..4ab8b3e11 100644 --- a/src/libos/crates/async-socket/src/datagram/sender.rs +++ b/src/libos/crates/async-socket/src/datagram/sender.rs @@ -95,7 +95,9 @@ impl Sender { flags: SendFlags, control: Option<&[u8]>, ) -> Result { - if !flags.is_empty() && flags != SendFlags::MSG_DONTWAIT { + if !flags.is_empty() + && flags.intersects(!(SendFlags::MSG_DONTWAIT | SendFlags::MSG_NOSIGNAL)) + { error!("Not supported flags: {:?}", flags); return_errno!(EINVAL, "not supported flags"); } @@ -360,19 +362,16 @@ impl DataMsg { #[inline(always)] fn copy_buf(&mut self, bufs: &[&[u8]]) -> Result { - // let total_len: usize = bufs.iter().map(|buf| buf.len()).sum(); let total_len = self.send_buf.len(); if total_len > super::MAX_BUF_SIZE { return_errno!(EMSGSIZE, "the message is too large") } // Copy data from the bufs to the send buffer let mut total_copied = 0; - let mut send_buf = UntrustedBox::new_uninit_slice(total_len); for buf in bufs { - send_buf[total_copied..(total_copied + buf.len())].copy_from_slice(buf); + self.send_buf[total_copied..(total_copied + buf.len())].copy_from_slice(buf); total_copied += buf.len(); } - self.send_buf = send_buf; Ok(total_copied) } diff --git a/src/libos/crates/async-socket/src/ioctl/get_ifconf.rs b/src/libos/crates/async-socket/src/ioctl/get_ifconf.rs index b41ba245a..0e4a7bd20 100644 --- a/src/libos/crates/async-socket/src/ioctl/get_ifconf.rs +++ b/src/libos/crates/async-socket/src/ioctl/get_ifconf.rs @@ -89,7 +89,7 @@ fn get_ifconf_by_host(fd: HostFd, if_conf: &mut IfConf) -> Result<()> { // Used to ioctl arguments with pointer members. // // Before the call the area the pointers points to should be assembled into - // one continous memory block. Then the block is repacked to ioctl arguments + // one continuous memory block. Then the block is repacked to ioctl arguments // in the ocall implementation in host. // // ret: holds the return value of ioctl in host diff --git a/src/libos/crates/async-socket/src/lib.rs b/src/libos/crates/async-socket/src/lib.rs index ef20a968e..51d4b5d7f 100644 --- a/src/libos/crates/async-socket/src/lib.rs +++ b/src/libos/crates/async-socket/src/lib.rs @@ -16,6 +16,8 @@ extern crate sgx_libc as libc; extern crate sgx_tstd as std; #[cfg(feature = "sgx")] extern crate sgx_types; +#[macro_use] +extern crate lazy_static; #[macro_use] mod prelude; diff --git a/src/libos/crates/async-socket/src/prelude.rs b/src/libos/crates/async-socket/src/prelude.rs index da7217837..0713ee119 100644 --- a/src/libos/crates/async-socket/src/prelude.rs +++ b/src/libos/crates/async-socket/src/prelude.rs @@ -12,7 +12,7 @@ cfg_if::cfg_if! { } } -// Convenient type alises for internal uses. +// Convenient type alias for internal uses. pub(crate) type HostFd = u32; pub(crate) use async_io::event::{Events, Observer, Pollee, Poller}; diff --git a/src/libos/crates/async-socket/src/runtime.rs b/src/libos/crates/async-socket/src/runtime.rs index 9d79bd00e..f41e225ac 100644 --- a/src/libos/crates/async-socket/src/runtime.rs +++ b/src/libos/crates/async-socket/src/runtime.rs @@ -1,4 +1,4 @@ -use io_uring_callback::IoUring; +use io_uring_callback::IoUringRef; /// The runtime support for HostSocket. /// @@ -6,5 +6,5 @@ use io_uring_callback::IoUring; /// that support HostSocket. Currently, the only dependency is a singleton /// of IoUring instance. pub trait Runtime: Send + Sync + 'static { - fn io_uring() -> &'static IoUring; + fn io_uring() -> IoUringRef; } diff --git a/src/libos/crates/async-socket/src/sockopt/get_buf_size.rs b/src/libos/crates/async-socket/src/sockopt/get_buf_size.rs new file mode 100644 index 000000000..04d069334 --- /dev/null +++ b/src/libos/crates/async-socket/src/sockopt/get_buf_size.rs @@ -0,0 +1,7 @@ +async_io::impl_ioctl_cmd! { + pub struct GetSndBufSizeCmd {} +} + +async_io::impl_ioctl_cmd! { + pub struct GetRcvBufSizeCmd {} +} diff --git a/src/libos/crates/async-socket/src/sockopt/mod.rs b/src/libos/crates/async-socket/src/sockopt/mod.rs index a5f3291c3..a710bfb80 100644 --- a/src/libos/crates/async-socket/src/sockopt/mod.rs +++ b/src/libos/crates/async-socket/src/sockopt/mod.rs @@ -1,16 +1,20 @@ mod get; mod get_acceptconn; +mod get_buf_size; mod get_domain; mod get_peername; mod get_type; mod set; +mod set_buf_size; pub use get::GetSockOptRawCmd; pub use get_acceptconn::GetAcceptConnCmd; +pub use get_buf_size::{GetRcvBufSizeCmd, GetSndBufSizeCmd}; pub use get_domain::GetDomainCmd; pub use get_peername::{AddrStorage, GetPeerNameCmd}; pub use get_type::GetTypeCmd; pub use set::SetSockOptRawCmd; +pub use set_buf_size::{SetRcvBufSizeCmd, SetSndBufSizeCmd}; use num_enum::{IntoPrimitive, TryFromPrimitive}; diff --git a/src/libos/crates/async-socket/src/sockopt/set.rs b/src/libos/crates/async-socket/src/sockopt/set.rs index 9629557ac..e82b7185e 100644 --- a/src/libos/crates/async-socket/src/sockopt/set.rs +++ b/src/libos/crates/async-socket/src/sockopt/set.rs @@ -33,7 +33,12 @@ impl SetSockOptRawCmd { impl IoctlCmd for SetSockOptRawCmd {} -fn setsockopt_by_host(fd: HostFd, level: i32, optname: i32, optval: &[u8]) -> Result<()> { +pub(crate) fn setsockopt_by_host( + fd: HostFd, + level: i32, + optname: i32, + optval: &[u8], +) -> Result<()> { try_libc!(do_setsockopt( fd as _, level as _, diff --git a/src/libos/crates/async-socket/src/sockopt/set_buf_size.rs b/src/libos/crates/async-socket/src/sockopt/set_buf_size.rs new file mode 100644 index 000000000..05dba8afb --- /dev/null +++ b/src/libos/crates/async-socket/src/sockopt/set_buf_size.rs @@ -0,0 +1,62 @@ +use super::set::setsockopt_by_host; +use crate::prelude::*; + +#[derive(Debug)] +pub struct SetSndBufSizeCmd { + buf_size: usize, +} + +impl SetSndBufSizeCmd { + pub fn new(buf_size: usize) -> Self { + Self { buf_size } + } + + pub fn buf_size(&self) -> usize { + self.buf_size + } + + pub fn update_host(&self, fd: HostFd) -> Result<()> { + // The buf size for host call should be divided by 2 because the value will be doubled by host kernel. + let host_call_buf_size = (self.buf_size / 2).to_ne_bytes(); + + // Setting SO_SNDBUF for host socket needs to respect /proc/sys/net/core/wmem_max. Thus, the value might be different on host, but it is fine. + setsockopt_by_host( + fd, + libc::SOL_SOCKET, + super::SockOptName::SO_SNDBUF.into(), + &host_call_buf_size, + ) + } +} + +impl IoctlCmd for SetSndBufSizeCmd {} + +#[derive(Debug)] +pub struct SetRcvBufSizeCmd { + buf_size: usize, +} + +impl SetRcvBufSizeCmd { + pub fn new(buf_size: usize) -> Self { + Self { buf_size } + } + + pub fn buf_size(&self) -> usize { + self.buf_size + } + + pub fn update_host(&self, fd: HostFd) -> Result<()> { + // The buf size for host call should be divided by 2 because the value will be doubled by host kernel. + let host_call_buf_size = (self.buf_size / 2).to_ne_bytes(); + + // Setting SO_RCVBUF for host socket needs to respect /proc/sys/net/core/rmem_max. Thus, the value might be different on host, but it is fine. + setsockopt_by_host( + fd, + libc::SOL_SOCKET, + super::SockOptName::SO_RCVBUF.into(), + &host_call_buf_size, + ) + } +} + +impl IoctlCmd for SetRcvBufSizeCmd {} diff --git a/src/libos/crates/async-socket/src/stream/mod.rs b/src/libos/crates/async-socket/src/stream/mod.rs index b9878ff30..047c12776 100644 --- a/src/libos/crates/async-socket/src/stream/mod.rs +++ b/src/libos/crates/async-socket/src/stream/mod.rs @@ -6,12 +6,18 @@ use crate::ioctl::*; use crate::prelude::*; use crate::runtime::Runtime; use crate::sockopt::*; +use std::sync::atomic::{AtomicUsize, Ordering}; use async_io::socket::{ timeout_to_timeval, GetRecvTimeoutCmd, GetSendTimeoutCmd, MsgFlags, SetRecvTimeoutCmd, SetSendTimeoutCmd, }; +lazy_static! { + pub static ref SEND_BUF_SIZE: AtomicUsize = AtomicUsize::new(2565 * 1024); // Default Linux send buffer size is 2.5MB. + pub static ref RECV_BUF_SIZE: AtomicUsize = AtomicUsize::new(128 * 1024); +} + pub struct StreamSocket { state: RwLock>, } @@ -397,6 +403,24 @@ impl StreamSocket { cmd: GetIfConf => { cmd.execute(self.host_fd())?; }, + cmd: SetSndBufSizeCmd => { + cmd.update_host(self.host_fd())?; + let buf_size = cmd.buf_size(); + self.set_kernel_send_buf_size(buf_size); + }, + cmd: SetRcvBufSizeCmd => { + cmd.update_host(self.host_fd())?; + let buf_size = cmd.buf_size(); + self.set_kernel_recv_buf_size(buf_size); + }, + cmd: GetSndBufSizeCmd => { + let buf_size = SEND_BUF_SIZE.load(Ordering::Relaxed); + cmd.set_output(buf_size); + }, + cmd: GetRcvBufSizeCmd => { + let buf_size = RECV_BUF_SIZE.load(Ordering::Relaxed); + cmd.set_output(buf_size); + }, _ => { return_errno!(EINVAL, "Not supported yet"); } @@ -484,6 +508,32 @@ impl StreamSocket { state.common().set_recv_timeout(timeout); } + fn set_kernel_send_buf_size(&self, buf_size: usize) { + let state = self.state.read().unwrap(); + match &*state { + State::Init(_) | State::Listen(_) | State::Connect(_) => { + // The kernel buffer is only created when the socket is connected. Just update the static variable. + SEND_BUF_SIZE.store(buf_size, Ordering::Relaxed); + } + State::Connected(connected_stream) => { + connected_stream.try_update_send_buf_size(buf_size); + } + } + } + + fn set_kernel_recv_buf_size(&self, buf_size: usize) { + let state = self.state.read().unwrap(); + match &*state { + State::Init(_) | State::Listen(_) | State::Connect(_) => { + // The kernel buffer is only created when the socket is connected. Just update the static variable. + RECV_BUF_SIZE.store(buf_size, Ordering::Relaxed); + } + State::Connected(connected_stream) => { + connected_stream.try_update_recv_buf_size(buf_size); + } + } + } + /* pub fn poll_by(&self, mask: Events, mut poller: Option<&mut Poller>) -> Events { let state = self.state.read(); diff --git a/src/libos/crates/async-socket/src/stream/states/connected/mod.rs b/src/libos/crates/async-socket/src/stream/states/connected/mod.rs index 1a18546ba..ba5548756 100644 --- a/src/libos/crates/async-socket/src/stream/states/connected/mod.rs +++ b/src/libos/crates/async-socket/src/stream/states/connected/mod.rs @@ -7,9 +7,6 @@ use crate::runtime::Runtime; mod recv; mod send; -pub const SEND_BUF_SIZE: usize = 128 * 1024; -pub const RECV_BUF_SIZE: usize = 128 * 1024; - pub struct ConnectedStream { common: Arc>, sender: Sender, diff --git a/src/libos/crates/async-socket/src/stream/states/connected/recv.rs b/src/libos/crates/async-socket/src/stream/states/connected/recv.rs index 4ce5cc037..aac173508 100644 --- a/src/libos/crates/async-socket/src/stream/states/connected/recv.rs +++ b/src/libos/crates/async-socket/src/stream/states/connected/recv.rs @@ -7,7 +7,21 @@ use sgx_untrusted_alloc::{MaybeUntrusted, UntrustedBox}; use super::ConnectedStream; use crate::prelude::*; use crate::runtime::Runtime; +use crate::stream::RECV_BUF_SIZE; use crate::util::UntrustedCircularBuf; +use io_uring::squeue::Flags; +use std::sync::atomic::{AtomicBool, Ordering}; + +/// Normal operation for io_uring is to try and issue an sqe as non-blocking first, +/// and if that fails, execute it in an async manner. +/// +/// To support more efficient overlapped operation of requests +/// that the application knows/assumes will always (or most of the time) block, +/// the application can ask for an sqe to be issued async from the start. +/// +/// For recvmsg, since we always issue the io_uring request ahead of time, use this flag could save +/// some resources. +const RECV_FLAGS: Flags = Flags::ASYNC; impl ConnectedStream { pub async fn recvmsg( @@ -129,6 +143,14 @@ impl ConnectedStream { (total_consumed, iov_buffer_index, iov_buffer_offset) }; + if self.receiver.need_update() { + // Only update the recv buf when it is empty and there is no pending recv request + if inner.recv_buf.is_empty() && inner.io_handle.is_none() { + self.receiver.set_need_update(false); + inner.update_buf_size(RECV_BUF_SIZE.load(Ordering::Relaxed)); + } + } + if inner.end_of_file { return Ok(res); } @@ -222,7 +244,8 @@ impl ConnectedStream { // Submit the async recv to io_uring let io_uring = self.common.io_uring(); let host_fd = Fd(self.common.host_fd() as _); - let handle = unsafe { io_uring.recvmsg(host_fd, msghdr_ptr, 0, complete_fn) }; + let handle = + unsafe { io_uring.recvmsg(host_fd, msghdr_ptr, RECV_FLAGS.bits() as u32, complete_fn) }; inner.io_handle.replace(handle); } @@ -271,22 +294,44 @@ impl ConnectedStream { let inner = self.receiver.inner.lock().unwrap(); inner.recv_buf.consumable() } + + // This function will try to update the kernel recv buf size. + // For socket recv, there will always be a pending request in advance. Thus,we can only update the kernel + // buffer when a recv request is done and the kernel buffer is empty. Here, we just set the update flag. + pub fn try_update_recv_buf_size(&self, buf_size: usize) { + let pre_buf_size = RECV_BUF_SIZE.swap(buf_size, Ordering::Relaxed); + if buf_size == pre_buf_size { + return; + } + + self.receiver.set_need_update(true); + } } pub struct Receiver { inner: Mutex, + need_update: AtomicBool, } impl Receiver { pub fn new() -> Self { let inner = Mutex::new(Inner::new()); - Self { inner } + let need_update = AtomicBool::new(false); + Self { inner, need_update } } pub fn shutdown(&self) { let mut inner = self.inner.lock().unwrap(); inner.is_shutdown = true; } + + pub fn set_need_update(&self, need_update: bool) { + self.need_update.store(need_update, Ordering::Relaxed) + } + + pub fn need_update(&self) -> bool { + self.need_update.load(Ordering::Relaxed) + } } impl std::fmt::Debug for Receiver { @@ -315,7 +360,7 @@ unsafe impl Send for Inner {} impl Inner { pub fn new() -> Self { Self { - recv_buf: UntrustedCircularBuf::with_capacity(super::RECV_BUF_SIZE), + recv_buf: UntrustedCircularBuf::with_capacity(RECV_BUF_SIZE.load(Ordering::Relaxed)), recv_req: UntrustedBox::new_uninit(), io_handle: None, is_shutdown: false, @@ -376,6 +421,12 @@ impl Inner { debug_assert!(iovecs_len > 0); (iovecs, iovecs_len) } + + fn update_buf_size(&mut self, buf_size: usize) { + debug_assert!(self.recv_buf.is_empty() && self.io_handle.is_none()); + let new_recv_buf = UntrustedCircularBuf::with_capacity(buf_size); + self.recv_buf = new_recv_buf; + } } impl std::fmt::Debug for Inner { diff --git a/src/libos/crates/async-socket/src/stream/states/connected/send.rs b/src/libos/crates/async-socket/src/stream/states/connected/send.rs index c9e163157..e7fbffe1b 100644 --- a/src/libos/crates/async-socket/src/stream/states/connected/send.rs +++ b/src/libos/crates/async-socket/src/stream/states/connected/send.rs @@ -8,7 +8,9 @@ use sgx_untrusted_alloc::{MaybeUntrusted, UntrustedBox}; use super::ConnectedStream; use crate::prelude::*; use crate::runtime::Runtime; +use crate::stream::SEND_BUF_SIZE; use crate::util::UntrustedCircularBuf; +use std::sync::atomic::{AtomicBool, Ordering}; impl ConnectedStream { // We make sure the all the buffer contents are buffered in kernel and then return. @@ -89,7 +91,9 @@ impl ConnectedStream { let mut inner = self.sender.inner.lock().unwrap(); if !flags.is_empty() - && flags.intersects(!(SendFlags::MSG_DONTWAIT | SendFlags::MSG_NOSIGNAL)) + && flags.intersects( + !(SendFlags::MSG_DONTWAIT | SendFlags::MSG_NOSIGNAL | SendFlags::MSG_MORE), + ) { error!("Not supported flags: {:?}", flags); return_errno!(EINVAL, "not supported flags"); @@ -194,6 +198,10 @@ impl ConnectedStream { // The buffer is empty and the write side is shutdown by the user. We can safely shutdown host file here. let _ = stream.common.host_shutdown(Shutdown::Write); inner.is_shutdown = ShutdownStatus::PostShutdown + } else if stream.sender.need_update() { + // send_buf is empty. We can try to update the send_buf + stream.sender.set_need_update(false); + inner.update_buf_size(SEND_BUF_SIZE.load(Ordering::Relaxed)); } }; @@ -261,16 +269,40 @@ impl ConnectedStream { } } } + + // This function will try to update the kernel buf size. + // If the kernel buf is currently empty, the size will be updated immediately. + // If the kernel buf is not empty, update the flag in Sender and update the kernel buf after send. + pub fn try_update_send_buf_size(&self, buf_size: usize) { + let pre_buf_size = SEND_BUF_SIZE.swap(buf_size, Ordering::Relaxed); + if pre_buf_size == buf_size { + return; + } + + // Try to acquire the lock. If success, try directly update here. + // If failure, don't wait because there is pending send request. + if let Ok(mut inner) = self.sender.inner.try_lock() { + if inner.send_buf.is_empty() && inner.io_handle.is_none() { + inner.update_buf_size(buf_size); + return; + } + } + + // Can't easily aquire lock or the sendbuf is not empty. Update the flag only + self.sender.set_need_update(true); + } } pub struct Sender { inner: Mutex, + need_update: AtomicBool, } impl Sender { pub fn new() -> Self { let inner = Mutex::new(Inner::new()); - Self { inner } + let need_update = AtomicBool::new(false); + Self { inner, need_update } } pub fn shutdown(&self) { @@ -282,6 +314,14 @@ impl Sender { let inner = self.inner.lock().unwrap(); inner.send_buf.is_empty() } + + pub fn set_need_update(&self, need_update: bool) { + self.need_update.store(need_update, Ordering::Relaxed) + } + + pub fn need_update(&self) -> bool { + self.need_update.load(Ordering::Relaxed) + } } impl std::fmt::Debug for Sender { @@ -309,7 +349,7 @@ unsafe impl Send for Inner {} impl Inner { pub fn new() -> Self { Self { - send_buf: UntrustedCircularBuf::with_capacity(super::SEND_BUF_SIZE), + send_buf: UntrustedCircularBuf::with_capacity(SEND_BUF_SIZE.load(Ordering::Relaxed)), send_req: UntrustedBox::new_uninit(), io_handle: None, is_shutdown: ShutdownStatus::Running, @@ -374,6 +414,12 @@ impl Inner { debug_assert!(iovecs_len > 0); (iovecs, iovecs_len) } + + fn update_buf_size(&mut self, buf_size: usize) { + debug_assert!(self.send_buf.is_empty() && self.io_handle.is_none()); + let new_send_buf = UntrustedCircularBuf::with_capacity(buf_size); + self.send_buf = new_send_buf; + } } impl std::fmt::Debug for Inner { diff --git a/src/libos/crates/block-device/src/block_buf.rs b/src/libos/crates/block-device/src/block_buf.rs index 0b829fc4f..383ed6ba5 100644 --- a/src/libos/crates/block-device/src/block_buf.rs +++ b/src/libos/crates/block-device/src/block_buf.rs @@ -50,7 +50,7 @@ impl BlockBuf { } } - /// Convert the block buffer innto a boxed slice. + /// Convert the block buffer into a boxed slice. /// /// # Safety /// diff --git a/src/libos/crates/block-device/src/block_device_as_file.rs b/src/libos/crates/block-device/src/block_device_as_file.rs index 4969ff74c..9e2b2354e 100644 --- a/src/libos/crates/block-device/src/block_device_as_file.rs +++ b/src/libos/crates/block-device/src/block_device_as_file.rs @@ -71,7 +71,7 @@ impl BlockDeviceAsFile for B { } } -// TODO: The following implementation does not gurantee the atomicity of concurrent +// TODO: The following implementation does not guarantee the atomicity of concurrent // reads and writes when their offsets or lengths are not block aligned. // Is this a problem? Should the interface promise such properties? // @@ -200,8 +200,8 @@ impl<'a> Impl<'a> { let ptr = NonNull::new_unchecked(whole_block_buf.as_mut_ptr()); let len = whole_block_buf.len(); debug_assert!(len % BLOCK_SIZE == 0); - // Safety. The memory refered to by the pair of pointer and - // length is valid during the entire life cyle of the request. + // Safety. The memory referred to by the pair of pointer and + // length is valid during the entire life cycle of the request. BlockBuf::from_raw_parts(ptr, len) }; bufs.push(buf); @@ -394,8 +394,8 @@ impl<'a> Impl<'a> { let ptr = NonNull::new_unchecked(whole_block_buf.as_ptr() as _); let len = whole_block_buf.len(); debug_assert!(len % BLOCK_SIZE == 0); - // Safety. The memory refered to by the pair of pointer and - // length is valid during the entire life cyle of the request. + // Safety. The memory referred to by the pair of pointer and + // length is valid during the entire life cycle of the request. BlockBuf::from_raw_parts(ptr, len) }; bufs.push(buf); diff --git a/src/libos/crates/block-device/src/block_io.rs b/src/libos/crates/block-device/src/block_io.rs index db6c25294..ff62b69e6 100644 --- a/src/libos/crates/block-device/src/block_io.rs +++ b/src/libos/crates/block-device/src/block_io.rs @@ -246,7 +246,7 @@ impl BioReq { /// The extensions of a request is a set of objects that may be added, removed, /// or accessed by block devices and their users. Implemented with `AnyMap`, /// each of the extension objects must have a different type. To avoid - /// conflicts, it is recommened to use only private types for the extension + /// conflicts, it is recommended to use only private types for the extension /// objects. pub fn ext(&self) -> MutexGuard { self.ext.lock() diff --git a/src/libos/crates/block-device/src/lib.rs b/src/libos/crates/block-device/src/lib.rs index e5031143c..8e1cfa8a5 100644 --- a/src/libos/crates/block-device/src/lib.rs +++ b/src/libos/crates/block-device/src/lib.rs @@ -28,7 +28,7 @@ pub use self::block_io::{ pub use self::util::anymap::{Any, AnyMap}; pub use self::util::block_range::{BlockRange, BlockRangeIter}; -// This crate assumes the machine is 64-bit to use u64 and usize interchangably. +// This crate assumes the machine is 64-bit to use u64 and usize interchangeably. use static_assertions::assert_eq_size; assert_eq_size!(usize, u64); diff --git a/src/libos/crates/block-device/src/mem_disk.rs b/src/libos/crates/block-device/src/mem_disk.rs index bc2131f2f..a4eabf042 100644 --- a/src/libos/crates/block-device/src/mem_disk.rs +++ b/src/libos/crates/block-device/src/mem_disk.rs @@ -80,7 +80,7 @@ impl BlockDevice for MemDisk { } fn submit(&self, req: Arc) -> BioSubmission { - // Update the status of req to submittted + // Update the status of req to submitted let submission = BioSubmission::new(req); let req = submission.req(); diff --git a/src/libos/crates/block-device/src/util/anymap.rs b/src/libos/crates/block-device/src/util/anymap.rs index 55753f0eb..73c94ee11 100644 --- a/src/libos/crates/block-device/src/util/anymap.rs +++ b/src/libos/crates/block-device/src/util/anymap.rs @@ -13,7 +13,7 @@ pub trait Any: CoreAny + Debug + Send {} impl Any for T {} /// A collection of heterogeneous types, storing one value of each type. The -/// only requiremet for the types is to implement `Any`. +/// only requirement for the types is to implement `Any`. /// /// This is a simplified and specialized implementation of the `anymap` crate. /// We do not choose to use the crate because 1) it is no longer maintained and diff --git a/src/libos/crates/io-uring-callback/Cargo.toml b/src/libos/crates/io-uring-callback/Cargo.toml index 350430638..af1e81df7 100644 --- a/src/libos/crates/io-uring-callback/Cargo.toml +++ b/src/libos/crates/io-uring-callback/Cargo.toml @@ -13,12 +13,13 @@ sgx = ["sgx_tstd", "sgx_libc", "io-uring/sgx"] [dependencies] atomic = "0.5.0" cfg-if = "1.0.0" +log = "0.4" futures = { version = "0.3", default-features = false, features = ["alloc"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] } slab = { version = "0.4.5", default-features = false } libc = { version = "0.2", optional = true } -io-uring = { path = "../../../../deps/io-uring", features = ["concurrent"] } +io-uring = { path = "../../../../deps/io-uring", features = ["unstable"] } sgx_tstd = { path = "../../../../deps/rust-sgx-sdk/sgx_tstd", optional = true, features = ["backtrace"] } sgx_libc = { path = "../../../../deps/rust-sgx-sdk/sgx_libc", optional = true } diff --git a/src/libos/crates/io-uring-callback/examples/tcp_echo.rs b/src/libos/crates/io-uring-callback/examples/tcp_echo.rs index 8d3dec674..576f25156 100644 --- a/src/libos/crates/io-uring-callback/examples/tcp_echo.rs +++ b/src/libos/crates/io-uring-callback/examples/tcp_echo.rs @@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::ptr; use std::sync::Mutex; -use io_uring::opcode::types; +use io_uring::types; use io_uring_callback::{Builder, IoHandle, IoUring}; use lazy_static::lazy_static; @@ -70,7 +70,7 @@ impl AcceptCount { fn main() { let ring = Builder::new() - .setup_sqpoll(Some(500 /* ms */)) + .setup_sqpoll(500 /* ms */) .build(256) .unwrap(); let listener = TcpListener::bind(("127.0.0.1", 3456)).unwrap(); diff --git a/src/libos/crates/io-uring-callback/src/lib.rs b/src/libos/crates/io-uring-callback/src/lib.rs index 148992739..1b7228896 100644 --- a/src/libos/crates/io-uring-callback/src/lib.rs +++ b/src/libos/crates/io-uring-callback/src/lib.rs @@ -93,7 +93,7 @@ //! use io_uring_callback::{Timespec, TimeoutFlags}; //! //! # let io_uring = Builder::new().build(256).unwrap(); -//! let tp = Timespec { tv_sec: 1, tv_nsec: 0, }; +//! let tp = Timespec::new().sec(1); //! let completion_callback = move |_retval: i32| {}; //! let handle = unsafe { //! io_uring.timeout(&tp as *const _, 0, TimeoutFlags::empty(), completion_callback) @@ -105,6 +105,7 @@ #![feature(get_mut_unchecked)] #![cfg_attr(feature = "sgx", no_std)] +extern crate log; #[cfg(feature = "sgx")] extern crate sgx_libc as libc; #[cfg(feature = "sgx")] @@ -121,16 +122,19 @@ cfg_if::cfg_if! { } } -use io_uring::opcode::{self, types}; +use io_uring::opcode; use io_uring::squeue::Entry as SqEntry; +use io_uring::types; use slab::Slab; +use std::os::unix::prelude::RawFd; use crate::io_handle::IoToken; mod io_handle; pub use crate::io_handle::{IoHandle, IoState}; -pub use io_uring::opcode::types::{Fd, RwFlags, TimeoutFlags, Timespec}; +pub use io_uring::types::{Fd, RwFlags, TimeoutFlags, Timespec}; +pub type IoUringRef = Arc; /// An io_uring instance. /// @@ -139,8 +143,9 @@ pub use io_uring::opcode::types::{Fd, RwFlags, TimeoutFlags, Timespec}; /// All I/O methods are based on the assumption that the resources (e.g., file descriptors, pointers, etc.) /// given in their arguments are valid before the completion of the async I/O. pub struct IoUring { - ring: io_uring::concurrent::IoUring, + ring: io_uring::IoUring, token_table: Mutex>>, + sq_lock: Mutex<()>, // For submission queue synchronization } impl Drop for IoUring { @@ -163,9 +168,18 @@ impl IoUring { /// /// Users should use `Builder` instead. pub(crate) fn new(ring: io_uring::IoUring) -> Self { - let ring = ring.concurrent(); let token_table = Mutex::new(Slab::new()); - Self { ring, token_table } + let sq_lock = Mutex::new(()); + Self { + ring, + token_table, + sq_lock, + } + } + + /// Get the raw io_uring instance for advanced usage. + pub fn raw(&self) -> &io_uring::IoUring { + &self.ring } /// Push an accept request into the submission queue of the io_uring. @@ -181,7 +195,9 @@ impl IoUring { flags: u32, callback: impl FnOnce(i32) + Send + 'static, ) -> IoHandle { - let entry = opcode::Accept::new(fd, addr, addrlen).flags(flags).build(); + let entry = opcode::Accept::new(fd, addr, addrlen) + .flags(flags as i32) + .build(); self.push_entry(entry, callback) } @@ -390,13 +406,16 @@ impl IoUring { /// to 0. If the user does not want to the method to busy polling, set /// `polling_retries` to 0. pub fn poll_completions(&self, min_complete: usize, polling_retries: usize) -> usize { - let cq = self.ring.completion(); + let mut cq = unsafe { self.ring.completion_shared() }; // Safety: Only polling thread is using the completion queue let mut nr_complete = 0; loop { // Polling for at most a specified number of times let mut nr_retries = 0; while nr_retries <= polling_retries { - if let Some(cqe) = cq.pop() { + // completetion queue must be synchoronized when loop for next entry. + cq.sync(); + + if let Some(cqe) = cq.next() { let retval = cqe.result(); let token_key = cqe.user_data(); if token_key != IoUring::CANCEL_TOKEN_KEY { @@ -429,10 +448,13 @@ impl IoUring { self.poll_completions(min_complete, 10) } + // Push the entry into the submission queue unsafe fn push(&self, entry: SqEntry) { - // Push the entry into the submission queue + // No other `SubmissionQueue`s may exist when calling submission_shared(). Thus must lock here. + // Since the loop below should be very quick, acquire lock here. + let sq_guard = self.sq_lock.lock(); loop { - if self.ring.submission().push(entry.clone()).is_err() { + if self.ring.submission_shared().push(&entry).is_err() { if self.ring.enter(1, 1, 0, None).is_err() { panic!("sq broken"); } @@ -440,6 +462,7 @@ impl IoUring { break; } } + drop(sq_guard); // Make sure Linux is aware of the new submission if let Err(e) = self.ring.submit() { @@ -497,7 +520,6 @@ impl IoUring { } /// A builder for `IoUring`. -#[derive(Default)] pub struct Builder { inner: io_uring::Builder, } @@ -505,13 +527,14 @@ pub struct Builder { impl Builder { /// Creates a `IoUring` builder. pub fn new() -> Self { - Default::default() + let inner = io_uring::IoUring::builder(); + Self { inner } } /// When this flag is specified, a kernel thread is created to perform submission queue polling. /// An io_uring instance configured in this way enables an application to issue I/O /// without ever context switching into the kernel. - pub fn setup_sqpoll(&mut self, idle: impl Into>) -> &mut Self { + pub fn setup_sqpoll(&mut self, idle: u32) -> &mut Self { self.inner.setup_sqpoll(idle); self } @@ -531,6 +554,11 @@ impl Builder { self } + pub fn setup_attach_wq(&mut self, fd: RawFd) -> &mut Self { + self.inner.setup_attach_wq(fd); + self + } + /// Build a [IoUring]. #[inline] pub fn build(&self, entries: u32) -> io::Result { @@ -688,10 +716,7 @@ mod tests { let start = Instant::now(); let secs = 1; - let timespec = types::Timespec { - tv_sec: secs, - tv_nsec: 0, - }; + let timespec = types::Timespec::new().sec(secs); let complete_fn = move |_retval: i32| {}; let handle = unsafe { @@ -714,10 +739,7 @@ mod tests { let start = Instant::now(); let secs = 1; - let timespec = types::Timespec { - tv_sec: secs, - tv_nsec: 0, - }; + let timespec = types::Timespec::new().sec(secs); let complete_fn = move |_retval: i32| {}; diff --git a/src/libos/crates/page-cache/src/cached_disk.rs b/src/libos/crates/page-cache/src/cached_disk.rs index 863a4c78f..366b3ee9a 100644 --- a/src/libos/crates/page-cache/src/cached_disk.rs +++ b/src/libos/crates/page-cache/src/cached_disk.rs @@ -37,7 +37,7 @@ struct Inner { // and writers. This policy is important to implement // the semantic of the flush operation correctly. arw_lock: AsyncRwLock<()>, - // Whether CachedDisk is droppedd + // Whether CachedDisk is dropped is_dropped: AtomicBool, } diff --git a/src/libos/crates/sgx-disk/src/crypt_disk.rs b/src/libos/crates/sgx-disk/src/crypt_disk.rs index 09bb9ae68..f51cb30b3 100644 --- a/src/libos/crates/sgx-disk/src/crypt_disk.rs +++ b/src/libos/crates/sgx-disk/src/crypt_disk.rs @@ -105,7 +105,7 @@ impl CryptDisk { origin_req.access_bufs_with(|bufs| { let total_len = bufs.iter().map(|buf| buf.len()).sum(); let uninit_slice = Box::new_uninit_slice(total_len); - // Safety. The initiail content is not important now. + // Safety. The initial content is not important now. let boxed_slice = unsafe { uninit_slice.assume_init() }; BlockBuf::from_boxed(boxed_slice) }) @@ -139,7 +139,7 @@ impl BlockDevice for CryptDisk { // submissions out of one request). let type_ = req.type_(); if type_ != BioType::Flush { - // Update the status of req to submittted + // Update the status of req to submitted let submission = BioSubmission::new(req); let req = submission.req().clone(); diff --git a/src/libos/crates/sgx-disk/src/host_disk/io_uring_disk.rs b/src/libos/crates/sgx-disk/src/host_disk/io_uring_disk.rs index 67f02dd40..081f281ba 100644 --- a/src/libos/crates/sgx-disk/src/host_disk/io_uring_disk.rs +++ b/src/libos/crates/sgx-disk/src/host_disk/io_uring_disk.rs @@ -1,6 +1,6 @@ use block_device::{BioReq, BioSubmission, BioType, BlockDevice}; use fs::File; -use io_uring_callback::{Fd, IoHandle, IoUring}; +use io_uring_callback::{Fd, IoHandle, IoUringRef}; use new_self_ref_arc::new_self_ref_arc; use std::fmt; use std::io::prelude::*; @@ -21,7 +21,7 @@ use crate::HostDisk; /// This trait is introduced to decouple the creation of io_uring from /// its users. pub trait IoUringProvider: Send + Sync + 'static { - fn io_uring() -> &'static IoUring; + fn io_uring() -> IoUringRef; } /// A type of host disk that implements a block device interface by performing @@ -35,11 +35,16 @@ struct Inner { total_blocks: usize, can_read: bool, can_write: bool, + io_uring: IoUringRef, phantom: PhantomData

, weak_self: Weak>, } impl IoUringDisk

{ + fn io_uring(&self) -> &IoUringRef { + &self.0.io_uring + } + fn do_read(&self, req: &Arc) -> Result<()> { if !self.0.can_read { return Err(errno!(EACCES, "read is not allowed")); @@ -135,7 +140,7 @@ impl IoUringDisk

{ } } }; - let io_uring = P::io_uring(); + let io_uring = self.io_uring(); let io_handle = unsafe { io_uring.readv( fd, @@ -236,7 +241,7 @@ impl IoUringDisk

{ } } }; - let io_uring = P::io_uring(); + let io_uring = self.io_uring(); let io_handle = unsafe { io_uring.writev( fd, @@ -281,7 +286,7 @@ impl IoUringDisk

{ } } }; - let io_uring = P::io_uring(); + let io_uring = self.io_uring(); let io_handle = unsafe { io_uring.fsync(fd, is_datasync, complete_fn) }; // We don't need to keep the handle IoHandle::release(io_handle); @@ -317,7 +322,7 @@ impl BlockDevice for IoUringDisk

{ } fn submit(&self, req: Arc) -> BioSubmission { - // Update the status of req to submittted + // Update the status of req to submitted let submission = BioSubmission::new(req); // Try to initiate the I/O @@ -352,6 +357,7 @@ impl HostDisk for IoUringDisk

{ let can_read = options.read; let can_write = options.write; let path = path.to_owned(); + let io_uring = P::io_uring(); let inner = Inner { fd, file: Mutex::new(file), @@ -359,6 +365,7 @@ impl HostDisk for IoUringDisk

{ total_blocks, can_read, can_write, + io_uring, phantom: PhantomData, weak_self: Weak::new(), }; @@ -384,7 +391,7 @@ impl fmt::Debug for IoUringDisk

{ impl Drop for IoUringDisk

{ fn drop(&mut self) { - // Ensure all data are peristed before the disk is dropped + // Ensure all data are persisted before the disk is dropped let mut file = self.0.file.lock().unwrap(); let _ = file.flush(); } @@ -441,22 +448,22 @@ mod test { mod runtime { use super::*; - use io_uring_callback::{Builder, IoUring}; + use io_uring_callback::{Builder, IoUringRef}; use lazy_static::lazy_static; pub struct IoUringSingleton; impl IoUringProvider for IoUringSingleton { - fn io_uring() -> &'static IoUring { - &*IO_URING + fn io_uring() -> IoUringRef { + IO_URING.clone() } } lazy_static! { - static ref IO_URING: Arc = { + static ref IO_URING: IoUringRef = { let ring = Arc::new( Builder::new() - .setup_sqpoll(Some(500/* ms */)) + .setup_sqpoll(500/* ms */) .build(256) .unwrap()); std::thread::spawn({ diff --git a/src/libos/crates/sgx-disk/src/host_disk/mod.rs b/src/libos/crates/sgx-disk/src/host_disk/mod.rs index 39fa4062d..c419d2bdc 100644 --- a/src/libos/crates/sgx-disk/src/host_disk/mod.rs +++ b/src/libos/crates/sgx-disk/src/host_disk/mod.rs @@ -1,4 +1,4 @@ -//! Host disks are untrusted hvirtual disks that are backed by a file on the host. +//! Host disks are untrusted virtual disks that are backed by a file on the host. //! //! There are two types of host disks. //! * `SyncIoDisk` is a disk that uses normal sync I/O operations. diff --git a/src/libos/crates/sgx-disk/src/host_disk/open_options.rs b/src/libos/crates/sgx-disk/src/host_disk/open_options.rs index ce4f6b59a..5b29f6c93 100644 --- a/src/libos/crates/sgx-disk/src/host_disk/open_options.rs +++ b/src/libos/crates/sgx-disk/src/host_disk/open_options.rs @@ -96,7 +96,7 @@ impl OpenOptions { .truncate(self.clear) .open(path.as_ref())?; - // If the size of the disk is specified, we set the length regradless + // If the size of the disk is specified, we set the length regardless // of the file is new or existing. if let Some(total_blocks) = self.total_blocks { let file_len = total_blocks * block_device::BLOCK_SIZE; diff --git a/src/libos/crates/sgx-disk/src/host_disk/sync_io_disk.rs b/src/libos/crates/sgx-disk/src/host_disk/sync_io_disk.rs index 83078d167..589b68057 100644 --- a/src/libos/crates/sgx-disk/src/host_disk/sync_io_disk.rs +++ b/src/libos/crates/sgx-disk/src/host_disk/sync_io_disk.rs @@ -162,7 +162,7 @@ impl BlockDevice for SyncIoDisk { } fn submit(&self, req: Arc) -> BioSubmission { - // Update the status of req to submittted + // Update the status of req to submitted let submission = BioSubmission::new(req); let req = submission.req(); @@ -219,7 +219,7 @@ impl fmt::Debug for SyncIoDisk { impl Drop for SyncIoDisk { fn drop(&mut self) { - // Ensure all data are peristed before the disk is dropped + // Ensure all data are persisted before the disk is dropped let _ = self.do_flush(); } } diff --git a/src/libos/crates/sgx-disk/src/pfs_disk/mod.rs b/src/libos/crates/sgx-disk/src/pfs_disk/mod.rs index 32b5f5894..06acac9ee 100644 --- a/src/libos/crates/sgx-disk/src/pfs_disk/mod.rs +++ b/src/libos/crates/sgx-disk/src/pfs_disk/mod.rs @@ -132,7 +132,7 @@ impl BlockDevice for PfsDisk { } fn submit(&self, req: Arc) -> BioSubmission { - // Update the status of req to submittted + // Update the status of req to submitted let submission = BioSubmission::new(req); let req = submission.req(); diff --git a/src/libos/crates/sgx-disk/src/pfs_disk/open_options.rs b/src/libos/crates/sgx-disk/src/pfs_disk/open_options.rs index 85693e627..4e9274d7f 100644 --- a/src/libos/crates/sgx-disk/src/pfs_disk/open_options.rs +++ b/src/libos/crates/sgx-disk/src/pfs_disk/open_options.rs @@ -1,4 +1,4 @@ -use std::io::{ErrorKind, Seek, SeekFrom, Write}; +use std::io::{Seek, SeekFrom, Write}; use std::path::Path; use std::sgxfs::{OpenOptions as PfsOpenOptions, SgxFile as PfsFile}; diff --git a/src/libos/crates/sgx-untrusted-alloc/src/maybe_untrusted.rs b/src/libos/crates/sgx-untrusted-alloc/src/maybe_untrusted.rs index ddace6960..d44240967 100644 --- a/src/libos/crates/sgx-untrusted-alloc/src/maybe_untrusted.rs +++ b/src/libos/crates/sgx-untrusted-alloc/src/maybe_untrusted.rs @@ -6,7 +6,7 @@ use std::cell::Cell; /// # Overview /// /// Not all types are created equal: some of them are difficult--- -/// if not impossible---to be used safey when residing in untrusted memory. +/// if not impossible---to be used safely when residing in untrusted memory. /// One obvious class is heap-backed Rust containers, like `Vec`. /// If such types were put into untrusted memory, then an attacker could manipulate /// the internal pointer. Enums, in general, cannot be used safely in untrusted diff --git a/src/libos/crates/sgx-untrusted-alloc/src/untrusted_allocator/free_space_manager.rs b/src/libos/crates/sgx-untrusted-alloc/src/untrusted_allocator/free_space_manager.rs index 5e85074b2..07857fcaf 100644 --- a/src/libos/crates/sgx-untrusted-alloc/src/untrusted_allocator/free_space_manager.rs +++ b/src/libos/crates/sgx-untrusted-alloc/src/untrusted_allocator/free_space_manager.rs @@ -29,7 +29,7 @@ impl VMFreeSpaceManager { } pub fn find_free_range_internal(&mut self, size: usize, align: usize) -> Result { - // Record the minimal free range that satisfies the contraints + // Record the minimal free range that satisfies the constraints let mut result_free_range: Option = None; let mut result_idx: Option = None; let free_list = &self.free_manager; diff --git a/src/libos/crates/sgx-untrusted-alloc/src/untrusted_allocator/vm_chunk_manager.rs b/src/libos/crates/sgx-untrusted-alloc/src/untrusted_allocator/vm_chunk_manager.rs index efafa723d..36bb2ab89 100644 --- a/src/libos/crates/sgx-untrusted-alloc/src/untrusted_allocator/vm_chunk_manager.rs +++ b/src/libos/crates/sgx-untrusted-alloc/src/untrusted_allocator/vm_chunk_manager.rs @@ -128,13 +128,13 @@ impl ChunkManager { } pub fn usage_percentage(&self) -> f32 { - let totol_size = self.range.size(); + let total_size = self.range.size(); let mut used_size = 0; self.vmas .iter() .for_each(|vma_obj| used_size += vma_obj.vma().size()); - return used_size as f32 / totol_size as f32; + return used_size as f32 / total_size as f32; } // Returns whether the requested range is free diff --git a/src/libos/crates/vdso-time/src/lib.rs b/src/libos/crates/vdso-time/src/lib.rs index 03d5b5d19..72b77704b 100644 --- a/src/libos/crates/vdso-time/src/lib.rs +++ b/src/libos/crates/vdso-time/src/lib.rs @@ -216,7 +216,7 @@ impl Vdso { }; if estimated_inaccuracy > MAX_INACCURACY { if retry_num == MAX_RETRY_NUM - 1 { - return_errno!(EOPNOTSUPP, "Vdso reached max retey number"); + return_errno!(EOPNOTSUPP, "Vdso reached max retry number"); } continue; } diff --git a/src/libos/src/entry/context_switch/cpu_context.rs b/src/libos/src/entry/context_switch/cpu_context.rs index 48c046a2c..f67d08d66 100644 --- a/src/libos/src/entry/context_switch/cpu_context.rs +++ b/src/libos/src/entry/context_switch/cpu_context.rs @@ -1,7 +1,7 @@ use super::{FpRegs, GpRegs}; use crate::prelude::*; -/// Cpu context, including both general-pupose registers and floating-point registers. +/// Cpu context, including both general-purpose registers and floating-point registers. /// /// Note. The Rust definition of this struct must be kept in sync with assembly code. #[derive(Clone, Default)] diff --git a/src/libos/src/entry/enclave.rs b/src/libos/src/entry/enclave.rs index 5a7972399..e27bfe165 100644 --- a/src/libos/src/entry/enclave.rs +++ b/src/libos/src/entry/enclave.rs @@ -8,6 +8,7 @@ use std::sync::Once; use sgx_tse::*; use crate::fs::HostStdioFds; +use crate::io_uring::{IoUringInstance, IO_URING_MANAGER}; use crate::misc; use crate::prelude::*; use crate::process::{self, table, ProcessFilter, SpawnAttr}; @@ -136,15 +137,6 @@ pub extern "C" fn occlum_ecall_init( async_rt::vcpu::set_total(num_vcpus); - std::thread::spawn(move || { - let io_uring = &crate::io_uring::SINGLETON; - loop { - let min_complete = 1; - let polling_retries = 10000; - io_uring.poll_completions(min_complete, polling_retries); - } - }); - // Init boot up time stamp here. crate::time::up_time::init(); @@ -243,7 +235,12 @@ pub extern "C" fn occlum_ecall_run_vcpu(pal_data_ptr: *const occlum_pal_vcpu_dat assert!(check_ptr(pal_data_ptr).is_ok()); // Make sure the ptr is outside the enclave set_pal_data_addr(pal_data_ptr); - let running_vcpu_num = async_rt::executor::run_tasks(); + // Init an io_uring instance for this vcpu + let this_vcpu = async_rt::executor::new_vcpu(); + IO_URING_MANAGER.assign_io_uring_instance_for_vcpu(this_vcpu); + + let running_vcpu_num = async_rt::executor::run_tasks(this_vcpu); + if running_vcpu_num == 0 { // It is the last vcpu for the executor. We can perform some check to make sure there is no resource leakage assert!( @@ -251,6 +248,7 @@ pub extern "C" fn occlum_ecall_run_vcpu(pal_data_ptr: *const occlum_pal_vcpu_dat && table::get_all_processes().len() == 0 && table::get_all_threads().len() == 0 ); + IO_URING_MANAGER.clear(); } 0 @@ -441,7 +439,7 @@ fn validate_program_path(target_path: &PathBuf) -> Result<()> { return_errno!(EINVAL, "program path must be absolute"); } - // Forbid paths like /bin/../root, which may circument our prefix-based path matching + // Forbid paths like /bin/../root, which may circumvent our prefix-based path matching let has_parent_component = { target_path .components() diff --git a/src/libos/src/entry/exception/syscall.rs b/src/libos/src/entry/exception/syscall.rs index 066680b30..b8081d746 100644 --- a/src/libos/src/entry/exception/syscall.rs +++ b/src/libos/src/entry/exception/syscall.rs @@ -12,7 +12,7 @@ pub async fn handle_syscall_exception() -> Result<()> { let gp_regs = &mut context.gp_regs; // SYSCALL instruction saves RIP into RCX and RFLAGS into R11. This is to - // comply with hardware's behavoir. Not useful for us. + // comply with hardware's behavior. Not useful for us. gp_regs.rcx = gp_regs.rip; gp_regs.r11 = gp_regs.rflags; diff --git a/src/libos/src/entry/interrupt/mod.rs b/src/libos/src/entry/interrupt/mod.rs index b448a0a2a..2ce8ef994 100644 --- a/src/libos/src/entry/interrupt/mod.rs +++ b/src/libos/src/entry/interrupt/mod.rs @@ -38,7 +38,7 @@ extern "C" fn interrupt_entrypoint(sgx_interrupt_info: *mut sgx_interrupt_info_t pub async fn handle_interrupt() -> Result<()> { debug!("handle interrupt"); - // We use the interrupt as a chance to do preemptivee sceduling + // We use the interrupt as a chance to do preemptive scheduling async_rt::scheduler::yield_now().await; Ok(()) } diff --git a/src/libos/src/entry/syscall.rs b/src/libos/src/entry/syscall.rs index 38eb7fabf..b791670e1 100644 --- a/src/libos/src/entry/syscall.rs +++ b/src/libos/src/entry/syscall.rs @@ -236,7 +236,7 @@ macro_rules! process_syscall_table_with_callback { (Lchown = 94) => do_lchown(path: *const i8, uid: u32, gid: u32), (Umask = 95) => do_umask(mask: u16), (Gettimeofday = 96) => do_gettimeofday(tv_u: *mut timeval_t), - (Getrlimit = 97) => handle_unsupported(), + (Getrlimit = 97) => do_gettrlimit(resource: u32, rlim: *mut rlimit_t), (Getrusage = 98) => handle_unsupported(), (SysInfo = 99) => do_sysinfo(info: *mut sysinfo_t), (Times = 100) => handle_unsupported(), @@ -299,7 +299,7 @@ macro_rules! process_syscall_table_with_callback { (Prctl = 157) => do_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64), (ArchPrctl = 158) => do_arch_prctl(code: u32, addr: *mut usize), (Adjtimex = 159) => handle_unsupported(), - (Setrlimit = 160) => handle_unsupported(), + (Setrlimit = 160) => do_settrlimit(resource: u32, rlim: *const rlimit_t), (Chroot = 161) => handle_unsupported(), (Sync = 162) => do_sync(), (Acct = 163) => handle_unsupported(), @@ -806,6 +806,14 @@ async fn do_uname(name: *mut utsname_t) -> Result { crate::misc::do_uname(name).map(|_| 0) } +async fn do_gettrlimit(resource: u32, rlim: *mut rlimit_t) -> Result { + do_prlimit(0, resource, ptr::null(), rlim).await +} + +async fn do_settrlimit(resource: u32, rlim: *const rlimit_t) -> Result { + do_prlimit(0, resource, rlim, ptr::null_mut()).await +} + async fn do_prlimit( pid: pid_t, resource: u32, diff --git a/src/libos/src/fs/async_file_handle.rs b/src/libos/src/fs/async_file_handle.rs index 731a34f05..272cc776d 100644 --- a/src/libos/src/fs/async_file_handle.rs +++ b/src/libos/src/fs/async_file_handle.rs @@ -197,7 +197,7 @@ impl AsyncFileHandle { let ext = match self.dentry().inode().ext() { Some(ext) => ext, None => { - warn!("Inode extension is not supportted, the lock could be placed"); + warn!("Inode extension is not supported, the lock could be placed"); lock.set_type(RangeLockType::F_UNLCK); return Ok(()); } diff --git a/src/libos/src/fs/builtin_disk.rs b/src/libos/src/fs/builtin_disk.rs index e37f4d8a8..f1df024c5 100644 --- a/src/libos/src/fs/builtin_disk.rs +++ b/src/libos/src/fs/builtin_disk.rs @@ -70,14 +70,24 @@ const MB: usize = 1024 * 1024; const GB: usize = 1024 * 1024 * 1024; mod runtime { - use io_uring_callback::IoUring; + use crate::io_uring::IO_URING_MANAGER; + use io_uring_callback::IoUringRef; use sgx_disk::IoUringProvider; pub struct IoUringRuntime; impl IoUringProvider for IoUringRuntime { - fn io_uring() -> &'static IoUring { - &*crate::io_uring::SINGLETON + fn io_uring() -> IoUringRef { + let current = current!(); + let vcpu_id = current + .task() + .unwrap() + .vcpu() + .expect("This task must be running"); + + IO_URING_MANAGER + .get_io_uring_ref(vcpu_id) + .expect("io_uring instance should be initialized") } } } diff --git a/src/libos/src/fs/dev_fs/dev_sgx/consts.rs b/src/libos/src/fs/dev_fs/dev_sgx/consts.rs index a5a943226..114d62f18 100644 --- a/src/libos/src/fs/dev_fs/dev_sgx/consts.rs +++ b/src/libos/src/fs/dev_fs/dev_sgx/consts.rs @@ -70,5 +70,13 @@ pub const SGX_CMD_NUM_VER_DCAP_QUOTE: u32 = StructuredIoctlNum::new::( + 11, + SGX_MAGIC_CHAR, + StructuredIoctlArgType::InputOutput, +) +.as_u32(); + /// A magical number that distinguishes SGX ioctls for other ioctls const SGX_MAGIC_CHAR: u8 = 's' as u8; diff --git a/src/libos/src/fs/dev_fs/dev_sgx/mod.rs b/src/libos/src/fs/dev_fs/dev_sgx/mod.rs index 685bbe527..dc1ac7614 100644 --- a/src/libos/src/fs/dev_fs/dev_sgx/mod.rs +++ b/src/libos/src/fs/dev_fs/dev_sgx/mod.rs @@ -256,6 +256,24 @@ impl DevSgx { slice.copy_from_slice(&supplemental_data); } } + SGX_CMD_NUM_KEY => { + // Prepare the arguments + let arg = nonbuiltin_cmd.arg_mut::()?; + let key_request = { + if arg.key_request.is_null() { + return_errno!(EINVAL, "key_request must not be null"); + } + unsafe { &*arg.key_request } + }; + + let key = { + if arg.key.is_null() { + return_errno!(EINVAL, "output pointer for key must not be null"); + } + unsafe { &mut *arg.key } + }; + *key = get_key(key_request)?; + } _ => { return_errno!(ENOSYS, "unknown ioctl cmd for /dev/sgx"); } @@ -314,3 +332,9 @@ struct IoctlVerDCAPQuoteArg { supplemental_data_size: u32, // Input (optional) supplemental_data: *mut u8, // Output (optional) } + +#[repr(C)] +struct IoctlGetKeyArg { + key_request: *const sgx_key_request_t, // Input + key: *mut sgx_key_128bit_t, // Output +} diff --git a/src/libos/src/fs/file_handle.rs b/src/libos/src/fs/file_handle.rs index 50a209f13..d9a2cc18b 100644 --- a/src/libos/src/fs/file_handle.rs +++ b/src/libos/src/fs/file_handle.rs @@ -20,7 +20,7 @@ use crate::time::TimerFile; /// `FileLike: Any` that abstracts the common characteristics of any file type. But we choose /// not to do so. The primary reason is that `FileHandle` needs to have async methods, but /// Rust does not support async methods in trait unless you are ok with incurring -/// an overhead of one heap allocationn per call (I am not). +/// an overhead of one heap allocation per call (I am not). /// For more info, check out [the async-trait crate](https://crates.io/crates/async-trait). /// /// Internally, `FileHandle` is implemented with an enum. Using enums is sufficient to achieve @@ -225,7 +225,7 @@ impl FileHandle { // // Currently, disk files are the only types of files // that may have internal caches for updates and - // requires explict flushes to ensure the persist of the + // requires explicit flushes to ensure the persist of the // updates. // // TODO: add a general-purpose mechanism to do async drop. diff --git a/src/libos/src/fs/file_ops/ioctl/macros.rs b/src/libos/src/fs/file_ops/ioctl/macros.rs index 258a7ff88..8703ddff1 100644 --- a/src/libos/src/fs/file_ops/ioctl/macros.rs +++ b/src/libos/src/fs/file_ops/ioctl/macros.rs @@ -83,8 +83,8 @@ macro_rules! impl_ioctl_cmds { if cmd_num.require_arg() && arg_ptr.is_null() { return_errno!(EINVAL, "arg_ptr cannot be null"); } - // Note that we do allow the caller to give an non-enull arg even - // when the ioctl cmd does not take an arguement + // Note that we do allow the caller to give an non-null arg even + // when the ioctl cmd does not take an argument let cmd = match cmd_num { $( diff --git a/src/libos/src/fs/host_fd.rs b/src/libos/src/fs/host_fd.rs index 3a795b3a3..48259bc27 100644 --- a/src/libos/src/fs/host_fd.rs +++ b/src/libos/src/fs/host_fd.rs @@ -45,7 +45,7 @@ lazy_static! { { SgxMutex::new(HostFdRegistry::new()) }; } -/// A registery for host fds to ensure that they are unique. +/// A registry for host fds to ensure that they are unique. struct HostFdRegistry { set: HashSet, } diff --git a/src/libos/src/fs/procfs/pid/mod.rs b/src/libos/src/fs/procfs/pid/mod.rs index ab82d214e..35a31c47b 100644 --- a/src/libos/src/fs/procfs/pid/mod.rs +++ b/src/libos/src/fs/procfs/pid/mod.rs @@ -80,7 +80,7 @@ impl DirProcINode for LockedPidDirINode { return Ok(Arc::clone(&file.parent)); } // The 'fd' entry holds 1 Arc of LockedPidDirINode, so the LockedPidDirINode - // ifself will hold 2 Arcs. This makes it cannot be dropped automatically. + // itself will hold 2 Arcs. This makes it cannot be dropped automatically. // We initialize the 'fd' here to avoid this. // TODO:: Try to find a better solution. if name == "fd" { diff --git a/src/libos/src/fs/rootfs.rs b/src/libos/src/fs/rootfs.rs index f4896f63d..bbc2c95cd 100644 --- a/src/libos/src/fs/rootfs.rs +++ b/src/libos/src/fs/rootfs.rs @@ -52,7 +52,7 @@ pub async fn rootfs() -> &'static Arc { unsafe { ROOT_FS.as_ref().unwrap() } } -/// Update the rootfs, must be called after initilized +/// Update the rootfs, must be called after initialized pub async fn update_rootfs(new_rootfs: Arc) -> Result<()> { loop { match STATE.compare_exchange( diff --git a/src/libos/src/fs/sefs/mod.rs b/src/libos/src/fs/sefs/mod.rs index 463278dfc..67fd54f64 100644 --- a/src/libos/src/fs/sefs/mod.rs +++ b/src/libos/src/fs/sefs/mod.rs @@ -5,3 +5,7 @@ pub use self::sgx_uuid_provider::SgxUuidProvider; mod sgx_storage; mod sgx_uuid_provider; + +// Cache size of underlying SGX-PFS of SEFS +// Default cache size: 0x1000 * 48 +const SEFS_CACHE_SIZE: u64 = 0x1000 * 256; diff --git a/src/libos/src/fs/sefs/sgx_storage.rs b/src/libos/src/fs/sefs/sgx_storage.rs index e9d1038d3..5f64ed38f 100644 --- a/src/libos/src/fs/sefs/sgx_storage.rs +++ b/src/libos/src/fs/sefs/sgx_storage.rs @@ -100,11 +100,13 @@ impl Storage for SgxStorage { let file = match self.encrypt_mode { EncryptMode::IntegrityOnly(_) => options.open_integrity_only(path)?, EncryptMode::EncryptWithIntegrity(key, _) | EncryptMode::Encrypt(key) => { - options.open_ex(path, &key)? + options.open_with(path, Some(&key), None, Some(SEFS_CACHE_SIZE))? } EncryptMode::EncryptAutoKey(key_policy) => match key_policy { None => options.open(path)?, - Some(policy) => options.open_with(path, None, Some(policy.bits()), None)?, + Some(policy) => { + options.open_with(path, None, Some(policy.bits()), Some(SEFS_CACHE_SIZE))? + } }, }; @@ -138,11 +140,13 @@ impl Storage for SgxStorage { let file = match self.encrypt_mode { EncryptMode::IntegrityOnly(_) => options.open_integrity_only(path)?, EncryptMode::EncryptWithIntegrity(key, _) | EncryptMode::Encrypt(key) => { - options.open_ex(path, &key)? + options.open_with(path, Some(&key), None, Some(SEFS_CACHE_SIZE))? } EncryptMode::EncryptAutoKey(key_policy) => match key_policy { None => options.open(path)?, - Some(policy) => options.open_with(path, None, Some(policy.bits()), None)?, + Some(policy) => { + options.open_with(path, None, Some(policy.bits()), Some(SEFS_CACHE_SIZE))? + } }, }; Ok(LockedFile(Arc::new(Mutex::new(file)))) diff --git a/src/libos/src/fs/sync_fs_wrapper.rs b/src/libos/src/fs/sync_fs_wrapper.rs index 259cfd2f2..16b2536e1 100644 --- a/src/libos/src/fs/sync_fs_wrapper.rs +++ b/src/libos/src/fs/sync_fs_wrapper.rs @@ -1,5 +1,5 @@ //! It is infeasible to implement AsyncInode for INode and AsyncFilesystem for FileSystem, -//! because doing upcasting and downcasting between traits is not allowd. +//! because doing upcasting and downcasting between traits is not allowed. //! //! The SyncFS and SyncInode are very special structs to wrap any FileSystems and INodes of rcore-fs. //! It is straightforward to upcast struct to trait or downcast trait to struct, so the sync FileSystem diff --git a/src/libos/src/io_uring.rs b/src/libos/src/io_uring.rs index ddee58377..fb00da554 100644 --- a/src/libos/src/io_uring.rs +++ b/src/libos/src/io_uring.rs @@ -1,11 +1,80 @@ -use io_uring_callback::{Builder, IoUring}; +use io_uring_callback::{Builder, IoState, IoUring, TimeoutFlags, Timespec}; +use std::collections::HashMap; +use std::sync::Arc; +use std::sync::SgxRwLock; + +pub use io_uring_callback::IoUringRef; lazy_static::lazy_static! { - pub static ref SINGLETON: IoUring = { + pub static ref IO_URING_MANAGER: IoUringManager = IoUringManager::new(); +} + +// A number of vcpu can share the same io_uring instance. +// +// Currently, based on iperf2 benchmarks, we assign each vcpu a single io_uring instance to achieve best performance. +// However, when there are more than 4 vcpus, the performance will drop. Further investigation is needed. +const NUM_OF_VCPU_SHARING_SINGLE_IO_URING: u32 = 1; + +pub struct IoUringManager(SgxRwLock>); // Key: io_uring_uid, Value: IoUringInstance + +impl IoUringManager { + fn new() -> Self { + Self(SgxRwLock::new(HashMap::new())) + } + + pub fn assign_io_uring_instance_for_vcpu(&self, vcpu_id: u32) { + let io_uring_uid = Self::get_io_uring_uid(vcpu_id); + let mut manager = self.0.write().unwrap(); + if manager.get(&io_uring_uid).is_none() { + let instance = IoUringInstance::new(); + manager.insert(io_uring_uid, instance); + } + } + + pub fn get_io_uring_ref(&self, vcpu_id: u32) -> Option { + let io_uring_uid = Self::get_io_uring_uid(vcpu_id); + let manager = self.0.read().unwrap(); + if let Some(io_uring) = manager.get(&io_uring_uid) { + Some(io_uring.inner.clone()) + } else { + None + } + } + + pub fn clear(&self) { + let mut manager = self.0.write().unwrap(); + manager.clear(); + } + + fn get_io_uring_uid(vcpu_id: u32) -> u32 { + vcpu_id / NUM_OF_VCPU_SHARING_SINGLE_IO_URING + } +} + +pub struct IoUringInstance { + inner: IoUringRef, +} + +impl IoUringInstance { + pub fn new() -> Self { let io_uring = Builder::new() - .setup_sqpoll(Some(500/* ms */)) + .setup_sqpoll(1000 /* ms */) .build(256) .unwrap(); - io_uring - }; + + let inner = Arc::new(io_uring); + + let inner_copy = inner.clone(); + + std::thread::spawn(move || loop { + let min_complete = 1; + let polling_retries = 10000; + let ret = inner_copy.poll_completions(min_complete, polling_retries); + if ret == 0 { + break; + } + }); + + Self { inner } + } } diff --git a/src/libos/src/misc/rlimit.rs b/src/libos/src/misc/rlimit.rs index 2b6db58e4..0f2074c0b 100644 --- a/src/libos/src/misc/rlimit.rs +++ b/src/libos/src/misc/rlimit.rs @@ -132,7 +132,7 @@ impl resource_t { /// and the input argument pid can only be process ID, not thread ID. This /// (unnecessary) restriction is lifted by our implementation. Nevertheless, /// since the rlimits object is shared between threads in a process, the -/// semantic of limiting resource usage on a per-process basisi is preserved. +/// semantic of limiting resource usage on a per-process basis is preserved. /// /// Limitation: Current implementation only takes effect on child processes. pub fn do_prlimit( diff --git a/src/libos/src/net/socket_file.rs b/src/libos/src/net/socket_file.rs index 10813b4da..3b36d534d 100644 --- a/src/libos/src/net/socket_file.rs +++ b/src/libos/src/net/socket_file.rs @@ -626,7 +626,7 @@ impl SocketFile { mod impls { use super::*; - use io_uring_callback::IoUring; + use crate::io_uring::{IoUringRef, IO_URING_MANAGER}; pub type Ipv4Stream = async_socket::StreamSocket; pub type Ipv6Stream = async_socket::StreamSocket; @@ -644,8 +644,17 @@ mod impls { pub struct SocketRuntime; impl async_socket::Runtime for SocketRuntime { - fn io_uring() -> &'static IoUring { - &*crate::io_uring::SINGLETON + fn io_uring() -> IoUringRef { + let current = current!(); + let vcpu_id = current + .task() + .unwrap() + .vcpu() + .expect("This task must be running"); + + IO_URING_MANAGER + .get_io_uring_ref(vcpu_id) + .expect("io_uring instance should be initialized") } } } diff --git a/src/libos/src/net/sockopt.rs b/src/libos/src/net/sockopt.rs index 437190c03..7d76050a1 100644 --- a/src/libos/src/net/sockopt.rs +++ b/src/libos/src/net/sockopt.rs @@ -1,6 +1,7 @@ use async_io::socket::{GetRecvTimeoutCmd, GetSendTimeoutCmd}; use async_socket::sockopt::{ - GetAcceptConnCmd, GetDomainCmd, GetPeerNameCmd, GetSockOptRawCmd, GetTypeCmd, + GetAcceptConnCmd, GetDomainCmd, GetPeerNameCmd, GetRcvBufSizeCmd, GetSndBufSizeCmd, + GetSockOptRawCmd, GetTypeCmd, }; use libc::timeval; use std::time::Duration; @@ -70,3 +71,25 @@ impl GetOutputAsBytes for GetSendTimeoutCmd { }) } } + +impl GetOutputAsBytes for GetSndBufSizeCmd { + fn get_output_as_bytes(&self) -> Option<&[u8]> { + self.output().map(|val_ref| unsafe { + std::slice::from_raw_parts( + val_ref as *const _ as *const u8, + std::mem::size_of::(), + ) + }) + } +} + +impl GetOutputAsBytes for GetRcvBufSizeCmd { + fn get_output_as_bytes(&self) -> Option<&[u8]> { + self.output().map(|val_ref| unsafe { + std::slice::from_raw_parts( + val_ref as *const _ as *const u8, + std::mem::size_of::(), + ) + }) + } +} diff --git a/src/libos/src/net/syscalls.rs b/src/libos/src/net/syscalls.rs index 6f76e5adb..c90cc11a2 100644 --- a/src/libos/src/net/syscalls.rs +++ b/src/libos/src/net/syscalls.rs @@ -9,7 +9,8 @@ use async_io::socket::{ }; use async_io::socket::{RecvFlags, SendFlags, Shutdown, Type}; use async_socket::sockopt::{ - GetAcceptConnCmd, GetDomainCmd, GetPeerNameCmd, GetSockOptRawCmd, GetTypeCmd, SetSockOptRawCmd, + GetAcceptConnCmd, GetDomainCmd, GetPeerNameCmd, GetRcvBufSizeCmd, GetSndBufSizeCmd, + GetSockOptRawCmd, GetTypeCmd, SetRcvBufSizeCmd, SetSndBufSizeCmd, SetSockOptRawCmd, SockOptName, }; use num_enum::TryFromPrimitive; @@ -502,7 +503,7 @@ fn copy_sock_addr_from_user( // Safety. The content will be initialized before function returns. let mut sockaddr_storage = unsafe { MaybeUninit::::uninit().assume_init() }; - // Safety. The dst slice is the only mutable reference to the sockaddr_storge + // Safety. The dst slice is the only mutable reference to the sockaddr_storage let sockaddr_dst_buf = unsafe { let ptr = &mut sockaddr_storage as *mut _ as *mut u8; let len = addr_len; @@ -562,6 +563,8 @@ fn new_getsockopt_cmd(level: i32, optname: i32, optlen: u32) -> Result Box::new(GetRecvTimeoutCmd::new(())), SockOptName::SO_SNDTIMEO_OLD => Box::new(GetSendTimeoutCmd::new(())), SockOptName::SO_CNX_ADVICE => return_errno!(ENOPROTOOPT, "it's a write-only option"), + SockOptName::SO_SNDBUF => Box::new(GetSndBufSizeCmd::new(())), + SockOptName::SO_RCVBUF => Box::new(GetRcvBufSizeCmd::new(())), _ => Box::new(GetSockOptRawCmd::new(level, optname, optlen)), }) } @@ -634,6 +637,28 @@ fn new_setsockopt_cmd(level: i32, optname: i32, optval: &[u8]) -> Result { + let send_buf_size = unsafe { *(optval as *const _ as *const usize) }; + if send_buf_size < 1024 { + // Based on the man page: The minimum (doubled) value for this option is 2048. + return_errno!(EINVAL, "invalid send buffer size"); + } + // Based on man page: The kernel doubles this value (to allow space for bookkeeping overhead) + // when it is set using setsockopt(2), and this doubled value is returned by getsockopt(2). + let send_buf_size = send_buf_size * 2; + Box::new(SetSndBufSizeCmd::new(send_buf_size)) + } + SockOptName::SO_RCVBUF => { + let recv_buf_size = unsafe { *(optval as *const _ as *const usize) }; + if recv_buf_size < 128 { + // Based on the man page: The minimum (doubled) value for this option is 256. + return_errno!(EINVAL, "invalid send buffer size"); + } + // Based on man page: The kernel doubles this value (to allow space for bookkeeping overhead) + // when it is set using setsockopt(2), and this doubled value is returned by getsockopt(2). + let recv_buf_size = recv_buf_size * 2; + Box::new(SetRcvBufSizeCmd::new(recv_buf_size)) + } _ => Box::new(SetSockOptRawCmd::new(level, optname, optval)), }) } @@ -661,6 +686,12 @@ fn get_optval(cmd: &dyn IoctlCmd) -> Result<&[u8]> { cmd : GetSendTimeoutCmd => { cmd.get_output_as_bytes() }, + cmd : GetSndBufSizeCmd => { + cmd.get_output_as_bytes() + }, + cmd : GetRcvBufSizeCmd => { + cmd.get_output_as_bytes() + }, _ => { return_errno!(EINVAL, "invalid sockopt command"); } diff --git a/src/libos/src/net/unix/trusted/stream/stream.rs b/src/libos/src/net/unix/trusted/stream/stream.rs index cc11e396d..55817031f 100644 --- a/src/libos/src/net/unix/trusted/stream/stream.rs +++ b/src/libos/src/net/unix/trusted/stream/stream.rs @@ -253,7 +253,7 @@ impl Stream { return_errno!(EINVAL, "the socket is already bound"); } - // check the global address space to see if the address is avaiable before bind + // check the global address space to see if the address is available before bind ADDRESS_SPACE.add_binder(addr)?; info.set_addr(addr); } @@ -271,7 +271,7 @@ impl Stream { } pub fn listen(&self, backlog: u32) -> Result<()> { - //TODO: restrict backlog accroding to /proc/sys/net/core/somaxconn + //TODO: restrict backlog according to /proc/sys/net/core/somaxconn let capacity = backlog as usize; let mut inner = self.inner(); @@ -416,7 +416,7 @@ impl Drop for Stream { pub enum Status { Idle(Info), // The listeners are stored in a global data structure indexed by the address. - // The consitency of Status with that data structure should be carefully maintained. + // The consistency of Status with that data structure should be carefully maintained. Listening(TrustedAddr), Connected(SockEnd), } diff --git a/src/libos/src/poll/do_epoll/epoll_file.rs b/src/libos/src/poll/do_epoll/epoll_file.rs index 4c8e729c5..64dc22f6b 100644 --- a/src/libos/src/poll/do_epoll/epoll_file.rs +++ b/src/libos/src/poll/do_epoll/epoll_file.rs @@ -18,7 +18,7 @@ use crate::prelude::*; /// /// To maintain the ready list, we need to monitor interesting events that happen /// on the files. To do so, the `EpollFile` registers itself as an `Observer` to -/// the monotored files. Thus, we can add a file to the ready list when an interesting +/// the monitored files. Thus, we can add a file to the ready list when an interesting /// event happens on the file. pub struct EpollFile { // All interesting entries. @@ -69,7 +69,7 @@ impl EpollFile { let mask = ep_event.events; let entry = EpollEntry::new(fd, weak_file, ep_event, ep_flags, self.weak_self.clone()); - // Add the new entry to the interest list and start monitering its events + // Add the new entry to the interest list and start monitoring its events let mut interest = self.interest.lock().unwrap(); if interest.contains_key(&fd) { return_errno!(EEXIST, "the fd has been added"); @@ -102,7 +102,7 @@ impl EpollFile { // // To optimize the performance, we only mark the epoll entry as // deleted at this moment. The real deletion happens when the ready list - // is scanned in EpolFile::wait. + // is scanned in EpollFile::wait. entry.set_deleted(); let file = match entry.file() { diff --git a/src/libos/src/poll/do_select.rs b/src/libos/src/poll/do_select.rs index 9958ff1ba..220f8638e 100644 --- a/src/libos/src/poll/do_select.rs +++ b/src/libos/src/poll/do_select.rs @@ -77,7 +77,7 @@ pub async fn do_select( Ok(num_events) } -// Convert select's rwe input to poll's IoEvents input accordingg to Linux's +// Convert select's rwe input to poll's IoEvents input according to Linux's // behavior. fn convert_rwe_to_events(readable: bool, writable: bool, except: bool) -> Events { let mut events = Events::empty(); diff --git a/src/libos/src/process/do_exit.rs b/src/libos/src/process/do_exit.rs index 44e22beed..8700e8979 100644 --- a/src/libos/src/process/do_exit.rs +++ b/src/libos/src/process/do_exit.rs @@ -115,7 +115,7 @@ fn exit_process(thread: &ThreadRef, term_status: TermStatus, new_parent_ref: Opt let parent_inner = parent.inner(); // To prevent the race condition that parent is changed after `parent()`, - // but before `parent().innner()`, we need to check again here. + // but before `parent().inner()`, we need to check again here. if parent.pid() != process.parent().pid() { continue; } @@ -204,7 +204,7 @@ pub fn exit_old_process_for_execve(term_status: TermStatus, new_parent_ref: Proc let num_remaining_threads = thread.exit(term_status); if thread.tid() != thread.process().pid() { // Keep the main thread's tid available as long as the process is not destroyed. - // Main thread doesn't need to delete here. It will be repalced later. + // Main thread doesn't need to delete here. It will be replaced later. table::del_thread(thread.tid()).expect("tid must be in the table"); } diff --git a/src/libos/src/process/do_futex.rs b/src/libos/src/process/do_futex.rs index 827a1f63a..e9f4eb6d3 100644 --- a/src/libos/src/process/do_futex.rs +++ b/src/libos/src/process/do_futex.rs @@ -133,7 +133,7 @@ pub async fn futex_wait_bitset( // wait(); // } // } - // If the waiter on CPU 0 does not lock the bucket before check the futex velue, + // If the waiter on CPU 0 does not lock the bucket before check the futex value, // it cannot find the transition of futex value from val to new_val and enqueue // to the bucket, which will cause the waiter to wait forever. diff --git a/src/libos/src/process/do_robust_list.rs b/src/libos/src/process/do_robust_list.rs index babb087dc..836847f4b 100644 --- a/src/libos/src/process/do_robust_list.rs +++ b/src/libos/src/process/do_robust_list.rs @@ -71,7 +71,7 @@ pub struct RobustListHead { impl RobustListHead { /// Return an iterator for all futexes in the robust list. /// - /// The futex refered to by `list_op_pending`, if any, will be returned as + /// The futex referred to by `list_op_pending`, if any, will be returned as /// the last item. pub fn futexes<'a>(&'a self) -> FutexIter<'a> { FutexIter::new(self) diff --git a/src/libos/src/process/do_spawn/exec_loader.rs b/src/libos/src/process/do_spawn/exec_loader.rs index fd15709f8..1f0082848 100644 --- a/src/libos/src/process/do_spawn/exec_loader.rs +++ b/src/libos/src/process/do_spawn/exec_loader.rs @@ -97,7 +97,7 @@ pub async fn load_file_hdr_to_vec( } if file_mode.has_set_uid() || file_mode.has_set_gid() { warn!( - "set-user-ID and set-group-ID are not supportted, FileMode:{:?}", + "set-user-ID and set-group-ID are not supported, FileMode:{:?}", file_mode ); } diff --git a/src/libos/src/process/do_spawn/init_stack.rs b/src/libos/src/process/do_spawn/init_stack.rs index e5bdcb475..b8f90b88d 100644 --- a/src/libos/src/process/do_spawn/init_stack.rs +++ b/src/libos/src/process/do_spawn/init_stack.rs @@ -178,7 +178,7 @@ fn adjust_alignment( envp: &[&CStr], argv: &[&CStr], ) -> Result<()> { - // Put 8 byte to make the postion of stack 8-byte aligned + // Put 8 byte to make the position of stack 8-byte aligned stack.put(0 as u64)?; let current_pos = stack.get_pos(); let to_alloc_size = { @@ -189,7 +189,7 @@ fn adjust_alignment( auxtbl_size + envp_size + argv_size + argc_size }; // Libc ABI requires 16-byte alignment of the stack entrypoint. - // Current postion of the stack is 8-byte aligned already, insert 8 byte + // Current position of the stack is 8-byte aligned already, insert 8 byte // to meet the requirement if necessary. if (current_pos - to_alloc_size) % 16 != 0 { stack.put(0 as u64)?; diff --git a/src/libos/src/process/do_spawn/mod.rs b/src/libos/src/process/do_spawn/mod.rs index 2661851e2..c13a3558c 100644 --- a/src/libos/src/process/do_spawn/mod.rs +++ b/src/libos/src/process/do_spawn/mod.rs @@ -183,7 +183,7 @@ async fn new_process_common( load_exec_file_hdr_to_vec(file_path, current_ref).await?; // elf_path might be different from file_path because file_path could lead to a script text file. - // And intepreter will be the loaded ELF. + // And interpreter will be the loaded ELF. let elf_path = if let Some(interpreter_path) = is_script { if argv.len() == 0 { return_errno!(EINVAL, "argv[0] not found"); @@ -356,7 +356,7 @@ async fn new_process_common( .sig_dispositions(sig_dispositions); let new_process = builder.build()?; - // This is done here becuase if we want to create a new process group, we must have a new process first. + // This is done here because if we want to create a new process group, we must have a new process first. // So we can't set "pgrp" during the build above. update_pgrp_for_new_process(&new_process, new_pgid)?; (new_process, init_cpu_state) diff --git a/src/libos/src/process/process/idle.rs b/src/libos/src/process/process/idle.rs index 361441f9d..6c79302f2 100644 --- a/src/libos/src/process/process/idle.rs +++ b/src/libos/src/process/process/idle.rs @@ -5,7 +5,7 @@ use super::{ProcessBuilder, ThreadRef}; use crate::misc::ResourceLimits; /// Process 0, a.k.a, the idle process. /// -/// The idle process has no practical use except making process 1 (a.k.a, the init proess) +/// The idle process has no practical use except making process 1 (a.k.a, the init process) /// having a parent. use crate::prelude::*; use crate::vm::ProcessVM; diff --git a/src/libos/src/process/process/mod.rs b/src/libos/src/process/process/mod.rs index a7ae13212..60bab2ce9 100644 --- a/src/libos/src/process/process/mod.rs +++ b/src/libos/src/process/process/mod.rs @@ -360,7 +360,7 @@ impl PartialEq for Process { // Why manual implementation of Debug trait? // -// An explict implementation of Debug trait is required since Process and Thread +// An explicit implementation of Debug trait is required since Process and Thread // structs refer to each other. Thus, the automatically-derived implementation // of Debug trait for the two structs may lead to infinite loop. diff --git a/src/libos/src/process/spawn_attribute.rs b/src/libos/src/process/spawn_attribute.rs index 1c2abad8f..fba098f90 100644 --- a/src/libos/src/process/spawn_attribute.rs +++ b/src/libos/src/process/spawn_attribute.rs @@ -50,7 +50,7 @@ pub struct SpawnAttr { pub sig_default: Option, } -pub fn clone_spawn_atrributes_safely( +pub fn clone_spawn_attributes_safely( attr_ptr: *const posix_spawnattr_t, ) -> Result> { if attr_ptr != std::ptr::null() { diff --git a/src/libos/src/process/syscalls.rs b/src/libos/src/process/syscalls.rs index 5923cf999..2d73fc483 100644 --- a/src/libos/src/process/syscalls.rs +++ b/src/libos/src/process/syscalls.rs @@ -11,7 +11,7 @@ use super::do_wait4::WaitOptions; use super::pgrp::*; use super::prctl::PrctlCmd; use super::process::ProcessFilter; -use super::spawn_attribute::{clone_spawn_atrributes_safely, posix_spawnattr_t, SpawnAttr}; +use super::spawn_attribute::{clone_spawn_attributes_safely, posix_spawnattr_t, SpawnAttr}; use crate::prelude::*; use crate::time::{timespec_t, ClockId}; use crate::util::mem_util::from_user::*; @@ -29,7 +29,7 @@ pub async fn do_spawn_for_musl( let argv = clone_cstrings_safely(argv)?; let envp = clone_cstrings_safely(envp)?; let file_actions = clone_file_actions_safely(fdop_list)?; - let spawn_attrs = clone_spawn_atrributes_safely(attribute_list)?; + let spawn_attrs = clone_spawn_attributes_safely(attribute_list)?; let current = current!(); debug!( "spawn: path: {:?}, argv: {:?}, envp: {:?}, fdop: {:?}, spawn_attr: {:?}", @@ -109,7 +109,7 @@ pub async fn do_spawn_for_glibc( let argv = clone_cstrings_safely(argv)?; let envp = clone_cstrings_safely(envp)?; let file_actions = clone_file_actions_from_fa_safely(fa)?; - let spawn_attrs = clone_spawn_atrributes_safely(attribute_list)?; + let spawn_attrs = clone_spawn_attributes_safely(attribute_list)?; let current = current!(); debug!( "spawn: path: {:?}, argv: {:?}, envp: {:?}, actions: {:?}, attributes: {:?}", diff --git a/src/libos/src/process/thread/id.rs b/src/libos/src/process/thread/id.rs index 569d9b92c..24eacf469 100644 --- a/src/libos/src/process/thread/id.rs +++ b/src/libos/src/process/thread/id.rs @@ -90,7 +90,7 @@ impl IdAlloc { } pub fn free(&mut self, id: u32) -> Option { - // Note: When enableing "execve", there is situation that the ThreadId is reused. + // Note: When enabling "execve", there is situation that the ThreadId is reused. // And thus when exit, it may free twice. // debug_assert!(self.used_ids.contains(&id)); if self.used_ids.remove(&id) { diff --git a/src/libos/src/process/thread/mod.rs b/src/libos/src/process/thread/mod.rs index e9b6626a5..b9f6fb7c9 100644 --- a/src/libos/src/process/thread/mod.rs +++ b/src/libos/src/process/thread/mod.rs @@ -181,7 +181,7 @@ impl Thread { } Some(robust_list) => robust_list.as_ptr(), }; - debug!("wake the rubust_list: {:?}", list_head_ptr); + debug!("wake the robust_list: {:?}", list_head_ptr); let robust_list = { // Invalid pointer, stop scanning the list further if crate::util::mem_util::from_user::check_ptr(list_head_ptr).is_err() { @@ -314,7 +314,7 @@ impl PartialEq for Thread { // Why manual implementation of Debug trait? // -// An explict implementation of Debug trait is required since Process and Thread +// An explicit implementation of Debug trait is required since Process and Thread // structs refer to each other. Thus, the automatically-derived implementation // of Debug trait for the two structs may lead to infinite loop. diff --git a/src/libos/src/signal/signals/fault.rs b/src/libos/src/signal/signals/fault.rs index 7c734da1b..a4ee6764d 100644 --- a/src/libos/src/signal/signals/fault.rs +++ b/src/libos/src/signal/signals/fault.rs @@ -15,7 +15,7 @@ impl FaultSignal { pub fn new(exception: &Exception) -> Self { // TODO: the current mapping from exception to signal is only a first // order approximation. The resulting signum or siginfo may not be - // idential to Linux's behavior. + // identical to Linux's behavior. use sgx_exception_vector_t::*; let (num, code, addr) = match exception.vector { // Divider exception diff --git a/src/libos/src/util/mem_util.rs b/src/libos/src/util/mem_util.rs index e247da824..0c0154618 100644 --- a/src/libos/src/util/mem_util.rs +++ b/src/libos/src/util/mem_util.rs @@ -69,7 +69,7 @@ pub mod from_user { return_errno!(EINVAL, "NULL address is invalid"); } - // confirm that at least the fisrt byte of the string is from user + // confirm that at least the first byte of the string is from user check_ptr(out_ptr)?; let cstr = unsafe { CStr::from_ptr(out_ptr) }; @@ -152,7 +152,7 @@ pub mod from_untrusted { return_errno!(EINVAL, "NULL address is invalid"); } - // confirm that at least the fisrt byte of the string is out side of enclave + // confirm that at least the first byte of the string is out side of enclave check_ptr(out_ptr)?; let cstr = unsafe { CStr::from_ptr(out_ptr) }; diff --git a/src/libos/src/util/sgx/epid/attestation_agent.rs b/src/libos/src/util/sgx/epid/attestation_agent.rs index eb261b5db..1ebcd8abf 100644 --- a/src/libos/src/util/sgx/epid/attestation_agent.rs +++ b/src/libos/src/util/sgx/epid/attestation_agent.rs @@ -120,7 +120,7 @@ impl InnerAgent { ) -> sgx_status_t; } - // Prepare argments for OCall + // Prepare arguments for OCall let (sigrl_ptr, sigrl_size): (*const u8, u32) = { match sigrl { Some(sigrl) => { diff --git a/src/libos/src/util/sgx/mod.rs b/src/libos/src/util/sgx/mod.rs index 78350749b..934343af0 100644 --- a/src/libos/src/util/sgx/mod.rs +++ b/src/libos/src/util/sgx/mod.rs @@ -8,6 +8,7 @@ use sgx_types::*; #[cfg(feature = "dcap")] mod dcap; mod epid; +mod sgx_key; mod sgx_report; pub use sgx_types::{ @@ -20,6 +21,7 @@ pub use self::dcap::{ QuoteGenerator as SgxDCAPQuoteGenerator, QuoteVerifier as SgxDCAPQuoteVerifier, }; pub use self::epid::AttestationAgent as SgxEPIDAttestationAgent; +pub use self::sgx_key::get_key; pub use self::sgx_report::{create_report, get_self_target, verify_report}; pub fn allow_debug() -> bool { diff --git a/src/libos/src/util/sgx/sgx_key.rs b/src/libos/src/util/sgx/sgx_key.rs new file mode 100644 index 000000000..8c9bb74aa --- /dev/null +++ b/src/libos/src/util/sgx/sgx_key.rs @@ -0,0 +1,16 @@ +use super::*; + +use std::ptr; + +pub fn get_key(key_request: &sgx_key_request_t) -> Result { + let mut key = sgx_key_128bit_t::default(); + let sgx_status = unsafe { sgx_get_key(key_request, &mut key as *mut sgx_key_128bit_t) }; + match sgx_status { + sgx_status_t::SGX_SUCCESS => Ok(key), + sgx_status_t::SGX_ERROR_INVALID_PARAMETER => return_errno!(EINVAL, "invalid paramters"), + _ => { + error!("sgx_get_key return {:?}", sgx_status); + return_errno!(EINVAL, "unexpected SGX error") + } + } +} diff --git a/src/libos/src/util/sgx/sgx_report.rs b/src/libos/src/util/sgx/sgx_report.rs index c378ce738..a342665fb 100644 --- a/src/libos/src/util/sgx/sgx_report.rs +++ b/src/libos/src/util/sgx/sgx_report.rs @@ -25,7 +25,7 @@ pub fn create_report( }; match sgx_status { sgx_status_t::SGX_SUCCESS => Ok(report), - sgx_status_t::SGX_ERROR_INVALID_PARAMETER => return_errno!(EINVAL, "invalid paramters"), + sgx_status_t::SGX_ERROR_INVALID_PARAMETER => return_errno!(EINVAL, "invalid parameters"), _ => return_errno!(EINVAL, "unexpected SGX error"), } } diff --git a/src/libos/src/vm/process_vm.rs b/src/libos/src/vm/process_vm.rs index e4fc6bd84..ef241f920 100644 --- a/src/libos/src/vm/process_vm.rs +++ b/src/libos/src/vm/process_vm.rs @@ -10,7 +10,6 @@ use super::vm_util::{ FileBacked, VMInitializer, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRemapOptions, }; use std::collections::HashSet; -use std::sync::atomic::{AtomicUsize, Ordering}; #[derive(Debug, Clone)] pub struct ProcessVMBuilder<'a, 'b> { @@ -48,7 +47,7 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> { .stack_size .unwrap_or(config::LIBOS_CONFIG.process.default_stack_size); - // Before allocating memory, let's first calcualte how much memory + // Before allocating memory, let's first calculate how much memory // we need in total by iterating the memory layouts required by // all the memory regions let elf_layouts: Vec = self @@ -138,7 +137,7 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> { })?; debug_assert!(heap_range.start() % heap_layout.align() == 0); trace!("heap range = {:?}", heap_range); - let brk = AtomicUsize::new(heap_range.start()); + let brk = RwLock::new(heap_range.start()); chunks.insert(chunk_ref); // Init the stack memory in the process @@ -202,7 +201,7 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> { let mut empty_start_offset = 0; let mut empty_end_offset = 0; - // Init all loadable segements + // Init all loadable segments let elf_file_handle = elf_file .file_ref() .as_async_file_handle() @@ -261,7 +260,7 @@ pub struct ProcessVM { elf_ranges: Vec, heap_range: VMRange, stack_range: VMRange, - brk: AtomicUsize, + brk: RwLock, // Memory safety notes: the mem_chunks field must be the last one. // // Rust drops fields in the same order as they are declared. So by making @@ -377,14 +376,15 @@ impl ProcessVM { // Collect merged vmas which will be the output of this function let mut merged_vmas = Vec::new(); - // Insert the merged chunks or unchanged chunks back to mem_chunk list + // Insert unchanged chunks back to mem_chunks list and collect merged vmas for output for chunk in single_vma_chunks.into_iter().filter_map(|chunk| { if !chunk.is_single_dummy_vma() { if chunk.is_single_vma_with_conflict_size() { let new_vma = chunk.get_vma_for_single_vma_chunk(); - merged_vmas.push(new_vma.clone()); + merged_vmas.push(new_vma); - Some(Arc::new(Chunk::new_chunk_with_vma(new_vma))) + // Don't insert the merged chunks to mem_chunk list here. It should be updated later. + None } else { Some(chunk) } @@ -427,25 +427,40 @@ impl ProcessVM { } pub fn get_brk(&self) -> usize { - self.brk.load(Ordering::SeqCst) + *self.brk.read().unwrap() } - pub fn brk(&self, new_brk: usize) -> Result { + pub fn brk(&self, brk: usize) -> Result { let heap_start = self.heap_range.start(); let heap_end = self.heap_range.end(); - if new_brk >= heap_start && new_brk <= heap_end { - self.brk - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old_brk| Some(new_brk)); - Ok(new_brk) + // Acquire lock first to avoid data-race. + let mut brk_guard = self.brk.write().unwrap(); + + if brk >= heap_start && brk <= heap_end { + // Get page-aligned brk address. + let new_brk = align_up(brk, PAGE_SIZE); + // Get page-aligned old brk address. + let old_brk = align_up(*brk_guard, PAGE_SIZE); + + // Reset the memory when brk shrinks. + if new_brk < old_brk { + let shrink_brk_range = + VMRange::new(new_brk, old_brk).expect("shrink brk range must be valid"); + USER_SPACE_VM_MANAGER.reset_memory(shrink_brk_range)?; + } + + // Return the user-specified brk address without page aligned. This is same as Linux. + *brk_guard = brk; + Ok(brk) } else { - if new_brk < heap_start { + if brk < heap_start { error!("New brk address is too low"); - } else if new_brk > heap_end { + } else if brk > heap_end { error!("New brk address is too high"); } - Ok(self.get_brk()) + Ok(*brk_guard) } } diff --git a/src/libos/src/vm/user_space_vm.rs b/src/libos/src/vm/user_space_vm.rs index 58a95e96a..24c5466a9 100644 --- a/src/libos/src/vm/user_space_vm.rs +++ b/src/libos/src/vm/user_space_vm.rs @@ -17,7 +17,7 @@ impl UserSpaceVMManager { // TODO: Use reserved memory API for init space and use EDMM API for max space. let rsrv_mem_size = LIBOS_CONFIG.resource_limits.user_space_init_size; let vm_range = unsafe { - // TODO: Current sgx_alloc_rsrv_mem implmentation will commit all the pages of the desired size, which will consume + // TODO: Current sgx_alloc_rsrv_mem implementation will commit all the pages of the desired size, which will consume // a lot of time. When EDMM is supported, there is no need to commit all the pages at the initialization stage. A function // which reserves memory but not commit pages should be provided then. let ptr = sgx_alloc_rsrv_mem(rsrv_mem_size); diff --git a/src/libos/src/vm/vm_chunk_manager.rs b/src/libos/src/vm/vm_chunk_manager.rs index cc31f5b0c..527b0bbfe 100644 --- a/src/libos/src/vm/vm_chunk_manager.rs +++ b/src/libos/src/vm/vm_chunk_manager.rs @@ -14,7 +14,7 @@ use intrusive_collections::{intrusive_adapter, KeyAdapter}; /// Memory chunk manager. /// /// Chunk is the memory unit for Occlum. For chunks with `default` size, every chunk is managed by a ChunkManager which provides -/// usedful memory management APIs such as mmap, munmap, mremap, mprotect, etc. +/// useful memory management APIs such as mmap, munmap, mremap, mprotect, etc. /// ChunkManager is implemented basically with two data structures: a red-black tree to track vmas in use and a FreeRangeManager to track /// ranges which are free. /// For vmas-in-use, there are two sentry vmas with zero length at the front and end of the red-black tree. @@ -449,13 +449,13 @@ impl ChunkManager { } pub fn usage_percentage(&self) -> f32 { - let totol_size = self.range.size(); + let total_size = self.range.size(); let mut used_size = 0; self.vmas .iter() .for_each(|vma_obj| used_size += vma_obj.vma().size()); - return used_size as f32 / totol_size as f32; + return used_size as f32 / total_size as f32; } fn merge_all_vmas(&mut self) { diff --git a/src/libos/src/vm/vm_manager.rs b/src/libos/src/vm/vm_manager.rs index 22259e146..4d990f0d5 100644 --- a/src/libos/src/vm/vm_manager.rs +++ b/src/libos/src/vm/vm_manager.rs @@ -62,8 +62,6 @@ impl VMManager { // Allocate single VMA chunk for new process whose process VM is not ready yet pub fn alloc(&self, options: &VMMapOptions) -> Result<(VMRange, ChunkRef)> { - let addr = *options.addr(); - let size = *options.size(); if let Ok(new_chunk) = self.internal().mmap_chunk(options) { return Ok((new_chunk.range().clone(), new_chunk)); } @@ -368,6 +366,48 @@ impl VMManager { } } + // Reset memory permission to default (R/W) and reset the memory contents to zero. Currently only used by brk. + pub fn reset_memory(&self, reset_range: VMRange) -> Result<()> { + let intersect_chunks = { + let chunks = self + .internal() + .chunks + .iter() + .filter(|&chunk| chunk.range().intersect(&reset_range).is_some()) + .map(|chunk| chunk.clone()) + .collect::>(); + + // In the heap area, there shouldn't be any default chunks or chunks owned by other process. + if chunks + .iter() + .any(|chunk| !chunk.is_owned_by_current_process() || !chunk.is_single_vma()) + { + return_errno!(EINVAL, "There is something wrong with the intersect chunks"); + } + chunks + }; + + intersect_chunks.iter().for_each(|chunk| { + if let ChunkType::SingleVMA(vma) = chunk.internal() { + if let Some(intersection_range) = chunk.range().intersect(&reset_range) { + let mut internal_manager = self.internal(); + internal_manager.mprotect_single_vma_chunk( + &chunk, + intersection_range, + VMPerms::DEFAULT, + ); + + unsafe { + let buf = intersection_range.as_slice_mut(); + buf.iter_mut().for_each(|b| *b = 0) + } + } + } + }); + + Ok(()) + } + pub fn msync(&self, addr: usize, size: usize) -> Result<()> { let sync_range = VMRange::new_with_size(addr, size)?; let chunk = { @@ -432,14 +472,14 @@ impl VMManager { // Must lock the internal manager first here in case the chunk's range and vma are conflict when other threads are operating the VM let mut internal_manager = self.internal.lock().unwrap(); let mut merged_vmas = current.vm().merge_all_single_vma_chunks()?; + internal_manager.clean_single_vma_chunks(); while merged_vmas.len() != 0 { let merged_vma = merged_vmas.pop().unwrap(); internal_manager.add_new_chunk(¤t, merged_vma); } - internal_manager.clean_single_vma_chunks(); } - // Deternmine the chunk of the old range + // Determine the chunk of the old range let chunk = { let process_mem_chunks = current.vm().mem_chunks().read().unwrap(); let chunk = process_mem_chunks @@ -468,7 +508,7 @@ impl VMManager { let ret_addr = if let Some(mmap_options) = remap_result_option.mmap_options() { let mmap_addr = self.mmap(mmap_options); - // FIXME: For MRemapFlags::MayMove flag, we checked if the prefered range is free when parsing the options. + // FIXME: For MRemapFlags::MayMove flag, we checked if the preferred range is free when parsing the options. // But there is no lock after the checking, thus the mmap might fail. In this case, we should try mmap again. if mmap_addr.is_err() && remap_result_option.may_move() == true { return_errno!( @@ -488,7 +528,7 @@ impl VMManager { if let Some((munmap_addr, munmap_size)) = remap_result_option.munmap_args() { self.munmap(*munmap_addr, *munmap_size) - .expect("Shouln't fail"); + .expect("Shouldn't fail"); } return Ok(ret_addr); @@ -616,7 +656,7 @@ impl VMManager { } } -// Modification on this structure must aquire the global lock. +// Modification on this structure must acquire the global lock. // TODO: Enable fast_default_chunks for faster chunk allocation #[derive(Debug)] pub struct InternalVMManager { @@ -779,12 +819,14 @@ impl InternalVMManager { self.chunks.insert(new_chunk); } + // protect_range should a sub-range of the chunk range pub fn mprotect_single_vma_chunk( &mut self, chunk: &ChunkRef, protect_range: VMRange, new_perms: VMPerms, ) -> Result<()> { + debug_assert!(chunk.range().is_superset_of(&protect_range)); let vma = match chunk.internal() { ChunkType::MultiVMA(_) => { unreachable!(); @@ -843,15 +885,16 @@ impl InternalVMManager { ) }; - let updated_vmas = vec![containing_vma.clone(), new_vma, remaining_old_vma]; + // Put containing_vma at last to be updated first. + let updated_vmas = vec![new_vma, remaining_old_vma, containing_vma.clone()]; updated_vmas } _ => { if same_start { - // Protect range is at left side of the cotaining vma + // Protect range is at left side of the containing vma containing_vma.set_start(protect_range.end()); } else { - // Protect range is at right side of the cotaining vma + // Protect range is at right side of the containing vma containing_vma.set_end(protect_range.start()); } @@ -863,28 +906,33 @@ impl InternalVMManager { ); VMPerms::apply_perms(&new_vma, new_vma.perms()); - let updated_vmas = vec![containing_vma.clone(), new_vma]; + // Put containing_vma at last to be updated first. + let updated_vmas = vec![new_vma, containing_vma.clone()]; updated_vmas } } }; let current = current!(); - while updated_vmas.len() > 1 { - let vma = updated_vmas.pop().unwrap(); - self.add_new_chunk(¤t, vma); + // First update current vma chunk + if updated_vmas.len() > 1 { + let update_vma = updated_vmas.pop().unwrap(); + self.update_single_vma_chunk(¤t, &chunk, update_vma); } - debug_assert!(updated_vmas.len() == 1); - let vma = updated_vmas.pop().unwrap(); - self.update_single_vma_chunk(¤t, &chunk, vma); + // Then add new chunks if any + updated_vmas.into_iter().for_each(|vma| { + self.add_new_chunk(¤t, vma); + }); Ok(()) } + // Must make sure that all the chunks are valid before adding new chunks fn add_new_chunk(&mut self, current_thread: &ThreadRef, new_vma: VMArea) { let new_vma_chunk = Arc::new(Chunk::new_chunk_with_vma(new_vma)); - self.chunks.insert(new_vma_chunk.clone()); + let success = self.chunks.insert(new_vma_chunk.clone()); + debug_assert!(success); current_thread.vm().add_mem_chunk(new_vma_chunk); } diff --git a/src/libos/src/vm/vm_range.rs b/src/libos/src/vm/vm_range.rs index 17c88481b..65f356457 100644 --- a/src/libos/src/vm/vm_range.rs +++ b/src/libos/src/vm/vm_range.rs @@ -89,7 +89,7 @@ impl VMRange { self.start() <= addr && addr < self.end() } - // Returns whether two ranges have non-empty interesection. + // Returns whether two ranges have non-empty intersection. pub fn overlap_with(&self, other: &VMRange) -> bool { let intersection_start = self.start().max(other.start()); let intersection_end = self.end().min(other.end()); diff --git a/src/libos/src/vm/vm_util.rs b/src/libos/src/vm/vm_util.rs index 3743ece37..96692ed5d 100644 --- a/src/libos/src/vm/vm_util.rs +++ b/src/libos/src/vm/vm_util.rs @@ -20,7 +20,7 @@ pub enum VMInitializer { FileBacked { file: FileBacked, }, - // For ELF files, there is specical handling to not copy all the contents of the file. This is only used for tracking. + // For ELF files, there is special handling to not copy all the contents of the file. This is only used for tracking. ElfSpecific { elf_file: FileRef, }, @@ -446,7 +446,7 @@ pub trait VMRemapParser { // For Linux, writing to either memory vma or the file will update the other two equally. But we won't be able to support this before // we really have paging. Thus, if the old_range is not equal to a recorded vma, we will just return with error. if writeback_file.is_some() && &old_range != vma.range() { - return_errno!(EINVAL, "Known limition") + return_errno!(EINVAL, "Known limitation") } // Implement mremap as one optional mmap followed by one optional munmap. @@ -636,7 +636,7 @@ pub trait VMRemapParser { } // Generate a random address within [0, range] -// Note: This function doesn't gurantee alignment +// Note: This function doesn't guarantee alignment pub fn get_randomize_offset(range: usize) -> usize { if cfg!(debug_assertions) { return range; diff --git a/src/pal/include/occlum_pal_api.h b/src/pal/include/occlum_pal_api.h index b227d869a..8ce9b0105 100644 --- a/src/pal/include/occlum_pal_api.h +++ b/src/pal/include/occlum_pal_api.h @@ -72,7 +72,7 @@ struct occlum_pal_create_process_args { // Mandatory field. Must not be NULL. const char *path; - // Argments array pass to new process. + // Arguments array pass to new process. // // The arguments to the command. By convention, the argv[0] should be the program name. // And the array must be NULL terminated. diff --git a/src/pal/src/pal_enclave.c b/src/pal/src/pal_enclave.c index 115264b36..8ebdcadd1 100644 --- a/src/pal/src/pal_enclave.c +++ b/src/pal/src/pal_enclave.c @@ -161,7 +161,7 @@ int pal_init_enclave(const char *instance_dir) { return 0; } - /* reopen the file with write capablity */ + /* reopen the file with write capability */ fp = freopen(token_path, "wb", fp); if (fp == NULL) { return 0; } size_t write_num = fwrite(&token, 1, sizeof(sgx_launch_token_t), fp); diff --git a/src/pal/src/pal_sig_handler.c b/src/pal/src/pal_sig_handler.c index 467424ef2..cb3223693 100644 --- a/src/pal/src/pal_sig_handler.c +++ b/src/pal/src/pal_sig_handler.c @@ -8,12 +8,12 @@ int pal_register_sig_handlers(void) { if (signal(SIGRT_INTERRUPT, SIG_IGN) == SIG_ERR) { - PAL_ERROR("Failed to regiter the SIG64 handler"); + PAL_ERROR("Failed to register the SIG64 handler"); return -1; } if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - PAL_ERROR("Failed to regiter the SIGPIPE handler"); + PAL_ERROR("Failed to register the SIGPIPE handler"); return -1; } return 0; diff --git a/src/pal/src/pal_vcpu_thread.c b/src/pal/src/pal_vcpu_thread.c index 2d2520f4c..4286fa603 100644 --- a/src/pal/src/pal_vcpu_thread.c +++ b/src/pal/src/pal_vcpu_thread.c @@ -28,7 +28,7 @@ static void *thread_func(void *_data) { } if (ret < 0) { int errno_ = -ret; - PAL_ERROR("Unexpcted error from occlum_ecall_run_vcpu: %s", errno2str(errno_)); + PAL_ERROR("Unexpected error from occlum_ecall_run_vcpu: %s", errno2str(errno_)); exit(EXIT_FAILURE); } diff --git a/test/Makefile b/test/Makefile index 9e8502213..4685a7975 100644 --- a/test/Makefile +++ b/test/Makefile @@ -23,7 +23,7 @@ TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info rlimit \ server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group posix_flock \ ioctl fcntl eventfd emulate_syscall access signal prctl rename procfs wait flock async_sfs \ - spawn_attribute exec statfs random umask pgrp vfork mount sysinfo timerfd utimes shm epoll netlink + spawn_attribute exec statfs random umask pgrp vfork mount sysinfo timerfd utimes shm epoll netlink brk # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/brk/Makefile b/test/brk/Makefile new file mode 100644 index 000000000..9e1b6dec9 --- /dev/null +++ b/test/brk/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/brk/main.c b/test/brk/main.c new file mode 100644 index 000000000..7a4ea30ed --- /dev/null +++ b/test/brk/main.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include "test.h" + +// ============================================================================ +// Helper function +// ============================================================================ + +#define PAGE_SIZE 4096 + +const static uint8_t magic_num_01 = 0xFF; +typedef struct syscall_args { + int num; + unsigned long arg0; +} syscall_args_t; + +static inline uint64_t native_syscall(syscall_args_t *p) { + uint64_t ret; + register int num asm ("rax") = p->num; + register unsigned long arg0 asm ("rdi") = p->arg0; + + asm volatile("syscall" + : "=a" (ret) + : "r" (num), "r" (arg0)); + return ret; +} + +static uint64_t brk_syscall(uint64_t brk) { + syscall_args_t brk_arg = { + .num = __NR_brk, + .arg0 = brk, + }; + + return native_syscall(&brk_arg); +} + +// ============================================================================ +// Test cases for access +// ============================================================================ + +static int test_brk_shrinks() { + char *zero_buf = malloc(PAGE_SIZE * 2); + if (zero_buf == NULL) { + THROW_ERROR("malloc failed"); + } + memset(zero_buf, 0, PAGE_SIZE * 2); + + uint64_t original_brk = brk_syscall(0); + if (original_brk == 0) { + THROW_ERROR("sbrk failed"); + } + printf("original brk = %lx\n", original_brk); + + // increase brk + printf("increase brk\n"); + uint64_t ret = brk_syscall(original_brk + PAGE_SIZE * 4); + if (ret == 0) { + THROW_ERROR("extend brk failed"); + } + + // set some values to the brk memory + uint64_t test_range_start = original_brk + PAGE_SIZE * 2; + for (int i = 0; i < PAGE_SIZE; i++) { + *(int *)test_range_start = magic_num_01; + } + + // decrease brk + printf("decrease brk\n"); + ret = brk_syscall(original_brk + PAGE_SIZE * 2); + if (ret != test_range_start) { + THROW_ERROR("shrink brk failed"); + } + printf("test range start = %lx\n", test_range_start); + + // increase brk + uint64_t test_range_end = brk_syscall(original_brk + PAGE_SIZE * 4); + if (test_range_end != original_brk + PAGE_SIZE * 4) { + THROW_ERROR("extend brk failed"); + } + + if ( memcmp((const void *)test_range_start, zero_buf, PAGE_SIZE * 2) != 0) { + THROW_ERROR("sbrk not reset memory"); + } + + free(zero_buf); + + return 0; +} + +#ifdef SGX_MODE_HW +// This test case will fail in simulation mode. Because the raw syscall interface are not handled by Occlum +// in simulation mode. +// +// Use brk to allocate 4 pages and test brk and mprotect +// original brk +// | page 00 page 02 +// | page 01 page 03 +// ...---|-------|-------|-------|-------| +static int test_brk_shrinks_spans_multiple_chunks() { + const static uint8_t magic_num_02 = 0xFE; + char *zero_buf = malloc(PAGE_SIZE * 4); + if (zero_buf == NULL) { + THROW_ERROR("malloc failed"); + } + memset(zero_buf, 0, PAGE_SIZE * 4); + + size_t original_brk = brk_syscall(0); + if (original_brk == 0) { + THROW_ERROR("brk failed"); + } + printf("original brk = %lx\n", original_brk); + + // increase brk to the end of page 03 + size_t ret = brk_syscall(original_brk + PAGE_SIZE * 4); + if (ret != original_brk + PAGE_SIZE * 4) { + THROW_ERROR("extend brk failed"); + } + + // set some values to the brk memory page 02 + size_t test_range_start = original_brk + PAGE_SIZE * 2; + for (int i = 0; i < PAGE_SIZE; i++) { + *(int *)test_range_start = magic_num_01; + } + + // mprotect page 01 - 03 to PROT_NONE and decrease brk to the end of page 00 + int rc = mprotect((void *)(original_brk + PAGE_SIZE * 1), PAGE_SIZE * 3, PROT_NONE); + if (rc < 0) { + THROW_ERROR("mprotect failure"); + } + ret = brk_syscall(original_brk + PAGE_SIZE * 1); + if (ret != original_brk + PAGE_SIZE * 1) { + THROW_ERROR("shrink brk failed"); + } + + // increase brk to the end of page 02 + ret = brk_syscall(original_brk + PAGE_SIZE * 3); + if (ret != original_brk + PAGE_SIZE * 3) { + THROW_ERROR("extend brk failed"); + } + + // set some values to the brk memory page 01 + test_range_start = original_brk + PAGE_SIZE * 1; + for (int i = 0; i < PAGE_SIZE; i++) { + *(int *)test_range_start = magic_num_02; + } + + // decrease brk again to the end of page 00 + rc = mprotect((void *)(original_brk + PAGE_SIZE * 1), PAGE_SIZE * 2, PROT_NONE); + if (rc < 0) { + THROW_ERROR("mprotect failure"); + } + ret = brk_syscall(original_brk + PAGE_SIZE * 1); + if (ret != original_brk + PAGE_SIZE * 1) { + THROW_ERROR("shrink brk failed"); + } + + // increase brk to the end of page 03 + ret = brk_syscall(original_brk + PAGE_SIZE * 4); + if (ret != original_brk + PAGE_SIZE * 4) { + THROW_ERROR("extend brk failed"); + } + + if ( memcmp((const void *)original_brk, zero_buf, PAGE_SIZE * 4) != 0) { + THROW_ERROR("brk not reset memory"); + } + + // decrease brk to the original brk + ret = brk_syscall(original_brk); + if (ret != original_brk) { + THROW_ERROR("shrink brk failed"); + } + + free(zero_buf); + + return 0; +} +#endif + +// ============================================================================ +// Test suite main +// ============================================================================ + +static test_case_t test_cases[] = { + TEST_CASE(test_brk_shrinks), +#ifdef SGX_MODE_HW + TEST_CASE(test_brk_shrinks_spans_multiple_chunks), +#endif +}; + +int main(int argc, const char *argv[]) { + return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); +} diff --git a/test/ioctl/main.c b/test/ioctl/main.c index 7c5061b68..a4ee16992 100644 --- a/test/ioctl/main.c +++ b/test/ioctl/main.c @@ -15,6 +15,7 @@ #include #include #include +#include #ifndef OCCLUM_DISABLE_DCAP #include #include @@ -142,6 +143,11 @@ typedef struct { sgx_report_t *report; // output } sgxioc_create_report_arg_t; +typedef struct { + const sgx_key_request_t *key_request; // Input + sgx_key_128bit_t *key; // Output +} sgxioc_get_key_arg_t; + #ifndef OCCLUM_DISABLE_DCAP typedef struct { sgx_report_data_t *report_data; // input @@ -174,6 +180,8 @@ typedef struct { #define SGXIOC_VER_DCAP_QUOTE _IOWR('s', 10, sgxioc_ver_dcap_quote_arg_t) #endif +#define SGXIOC_GET_KEY _IOWR('s', 11, sgxioc_get_key_arg_t) + // The max number of retries if ioctl returns EBUSY #define IOCTL_MAX_RETRIES 20 @@ -309,6 +317,30 @@ static int do_SGXIOC_CREATE_AND_VERIFY_REPORT(int sgx_fd) { return 0; } +static int do_SGXIOC_GET_KEY(int sgx_fd) { + sgx_key_request_t key_request = { 0 }; + sgx_key_128bit_t key = { 0 }; + + key_request.key_name = SGX_KEYSELECT_SEAL; // SGX_KEYSELECT_REPORT + key_request.key_policy = SGX_KEYPOLICY_MRENCLAVE; // SGX_KEYPOLICY_MRSIGNER + + sgxioc_get_key_arg_t args = { + .key_request = (const sgx_key_request_t *) &key_request, + .key = &key, + }; + if (ioctl(sgx_fd, SGXIOC_GET_KEY, &args) < 0) { + THROW_ERROR("failed to ioctl /dev/sgx"); + } + + printf("key: \n"); + for (int i = 0; i < 16; i++) { + printf("%x ", key[i]); + } + printf("\n"); + + return 0; +} + #ifndef OCCLUM_DISABLE_DCAP #define REPORT_BODY_OFFSET 48 static int generate_and_verify_dcap_quote(int sgx_fd) { @@ -462,6 +494,10 @@ int test_sgx_ioctl_SGXIOC_CREATE_AND_VERIFY_REPORT(void) { return do_sgx_ioctl_test(do_SGXIOC_CREATE_AND_VERIFY_REPORT); } +int test_sgx_ioctl_SGXIOC_GET_KEY(void) { + return do_sgx_ioctl_test(do_SGXIOC_GET_KEY); +} + #define CONFIG_SIZE 512 int test_ioctl_SIOCGIFCONF(void) { struct ifreq *req; @@ -625,6 +661,7 @@ static test_case_t test_cases[] = { TEST_CASE(test_sgx_ioctl_SGXIOC_GEN_EPID_QUOTE), TEST_CASE(test_sgx_ioctl_SGXIOC_SELF_TARGET), TEST_CASE(test_sgx_ioctl_SGXIOC_CREATE_AND_VERIFY_REPORT), + TEST_CASE(test_sgx_ioctl_SGXIOC_GET_KEY), #ifndef OCCLUM_DISABLE_DCAP TEST_CASE(test_sgx_ioctl_SGXIOC_GENERATE_AND_VERIFY_DCAP_QUOTE), #endif diff --git a/test/server/main.c b/test/server/main.c index 6e2050cca..8a1fa2d6c 100644 --- a/test/server/main.c +++ b/test/server/main.c @@ -545,7 +545,7 @@ int test_sockopt() { THROW_ERROR("create socket error"); } int reuse = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, (socklen_t)sizeof(reuse)) < 0) { THROW_ERROR("setsockopt port to reuse failed"); } @@ -562,8 +562,59 @@ int test_sockopt() { optval != AF_INET) { THROW_ERROR("getsockopt(SO_DOMAIN) failed"); } - close(fd); + + // Test SO_SNDBUF and SO_RCVBUF option. + int child_pid = 0; + int client_fd = connect_with_child(8806, &child_pid); + if (client_fd < 0) { + THROW_ERROR("connect failed"); + } + + size_t test_buf_size = 32 * 1024; // 32K + size_t send_buf_size = 0; + size_t recv_buf_size = 0; + optlen = sizeof(size_t); + if (getsockopt(client_fd, SOL_SOCKET, SO_SNDBUF, &send_buf_size, &optlen) < 0) { + THROW_ERROR("getsockopt(SO_SNDBUF) failed"); + } + + if (getsockopt(client_fd, SOL_SOCKET, SO_RCVBUF, &recv_buf_size, &optlen) < 0) { + THROW_ERROR("getsockopt(SO_RCVBUF) failed"); + } + printf("default send buf size = %ld, recv buf size = %ld\n", send_buf_size, + recv_buf_size); + + // Set the buf size to test_buf_size + send_buf_size = test_buf_size; + recv_buf_size = test_buf_size; + if (setsockopt(client_fd, SOL_SOCKET, SO_SNDBUF, &send_buf_size, optlen) < 0) { + THROW_ERROR("setsockopt(SO_SNDBUF) failed"); + } + + if (setsockopt(client_fd, SOL_SOCKET, SO_RCVBUF, &recv_buf_size, optlen) < 0) { + THROW_ERROR("setsockopt(SO_RCVBUF) failed"); + } + + // Get buffer size and should be the double size of the set value + if (getsockopt(client_fd, SOL_SOCKET, SO_SNDBUF, &send_buf_size, &optlen) < 0) { + THROW_ERROR("getsockopt(SO_SNDBUF) failed"); + } + + if (send_buf_size != 2 * test_buf_size) { + THROW_ERROR("send buffer size error"); + } + + if (getsockopt(client_fd, SOL_SOCKET, SO_RCVBUF, &recv_buf_size, &optlen) < 0) { + THROW_ERROR("getsockopt(SO_RCVBUF) failed"); + } + + if (recv_buf_size != 2 * test_buf_size) { + THROW_ERROR("send buffer size error"); + } + + wait_for_child_exit(child_pid); + close(client_fd); return 0; } diff --git a/tools/occlum b/tools/occlum index 88d511bd4..c685476ae 100755 --- a/tools/occlum +++ b/tools/occlum @@ -511,7 +511,7 @@ cmd_package() { if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" && "$debug" != "true" ]]; then echo '"occlum package" command should only be used for an Occlum instance of SGX hardware mode, not the simulation mode.' echo 'Please run "occlum build --sgx-mode HW" and then use "occlum package"' - echo 'Or, use "occlum package --debug" to support similation mode package' + echo 'Or, use "occlum package --debug" to support simulation mode package' exit 1 fi