During the snapshot shown, interrupts would be _disabled_ on both CPUs and both
would have have the _same_ address space (i.e.
`PercpuBlock::current().current_addrsp`).
```rs
// CPU 0 CPU 1 (inside `context::switch()`)
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// let guard = current_addrsp.acquire_write()
// let flusher = Flusher::with_cpu_set(&addrsp.used_by, ¤t_addrsp.tlb_ack);
// ...
// drop(flusher); // Flusher::flush()
// // send IPI to CPU 1 as it is in the `active_set`
// shootdown_tlb_ipi(Some(1));
// percpu::switch_arch_hook();
// // spin until TLB shootdown IPI has been acknowledged by CPU 1 // acquire_read() to remove the CPU from the `used_by` set in `prev_addrsp`
// while ackword < affected_cpu_count prev_addrsp.acquire_read()
// PercpuBlock::current().maybe_handle_tlb_shootdown();
// spin_loop();
// }
// drop(guard);
```
Now CPU 0 will spin _until_ CPU 1 has acknowledged the IPI. However, CPU 1 has
interrupts disabled and is in the middle of a context switch. To complete the
context switch it needs to remove itself from the `used_by` set in
`prev_addrsp` (which is the current address space atm). To do that it needs to
`acquire_read()` lock but it cannot since it is held by CPU 0 and won't release
it until CPU 1 has acknowledged the IPI; creating a deadlock.
Also note:
```rs
// src/context/memory.rs
// NOTE: Lock must be held, which must be guaranteed by the caller.
pub fn flush(&mut self) {
```
Here is some output from GDB to back this up:
```
pwndbg> info threads
Id Target Id Frame
1 Thread 1.1 (CPU#0 [running]) core::sync::atomic::atomic_compare_exchange_weak<usize> (dst=0xffffff7f801972f0, old=0, new=1, success=core::sync::atomic::Ordering::Acquire, failure=core::sync::atomic::Ordering::Relaxed)
at /mnt/redox/prefix/x86_64-unknown-redox/sysroot/lib/rustlib/src/rust/library/core/src/sync/atomic.rs:4100
2 Thread 1.2 (CPU#1 [running]) kernel::context::memory::Flusher::flush (self=0xffff80004212f4b8) at src/context/memory.rs:2827
3 Thread 1.3 (CPU#2 [running]) spin::rwlock::RwLock<kernel::context::context::Context, spin::relax::Spin>::write<kernel::context::context::Context, spin::relax::Spin> (self=0xffffff7f801a99c0)
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spin-0.9.8/src/rwlock.rs:239
* 4 Thread 1.4 (CPU#3 [running]) 0xffffffff80059cf9 in kernel::context::memory::AddrSpaceWrapper::acquire_read (self=0xffffff7f80188110)
at /mnt/redox/prefix/x86_64-unknown-redox/sysroot/lib/rustlib/src/rust/library/core/src/../../stdarch/crates/core_arch/src/x86/sse2.rs:25
pwndbg> p kernel::context::arch::CONTEXT_SWITCH_LOCK
$1 = core::sync::atomic::AtomicBool {
v: core::cell::UnsafeCell<u8> {
value: 1
}
}
pwndbg> p (*kernel::percpu::ALL_PERCPU_BLOCKS[0].p.value).wants_tlb_shootdown.v.value
$2 = 0
pwndbg> p (*kernel::percpu::ALL_PERCPU_BLOCKS[1].p.value).wants_tlb_shootdown.v.value
$3 = 0
pwndbg> p (*kernel::percpu::ALL_PERCPU_BLOCKS[2].p.value).wants_tlb_shootdown.v.value
$4 = 1
pwndbg> p (*kernel::percpu::ALL_PERCPU_BLOCKS[3].p.value).wants_tlb_shootdown.v.value
$5 = 0
pwndbg> p (*kernel::percpu::ALL_PERCPU_BLOCKS[3].p.value).current_addrsp
$6 = core::cell::RefCell<core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>> {
borrow: core::cell::Cell<isize> {
value: core::cell::UnsafeCell<isize> {
value: 0
}
},
value: core::cell::UnsafeCell<core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>> {
value: core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>::Some(alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global> {
ptr: core::ptr::non_null::NonNull<alloc::sync::ArcInner<kernel::context::memory::AddrSpaceWrapper>> {
pointer: 0xffffff7f80188100
},
phantom: core::marker::PhantomData<alloc::sync::ArcInner<kernel::context::memory::AddrSpaceWrapper>>,
alloc: alloc::alloc::Global
})
}
}
pwndbg> p (*kernel::percpu::ALL_PERCPU_BLOCKS[2].p.value).current_addrsp
$7 = core::cell::RefCell<core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>> {
borrow: core::cell::Cell<isize> {
value: core::cell::UnsafeCell<isize> {
value: 0
}
},
value: core::cell::UnsafeCell<core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>> {
value: core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>::Some(alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global> {
ptr: core::ptr::non_null::NonNull<alloc::sync::ArcInner<kernel::context::memory::AddrSpaceWrapper>> {
pointer: 0xffffff7f80188100
},
phantom: core::marker::PhantomData<alloc::sync::ArcInner<kernel::context::memory::AddrSpaceWrapper>>,
alloc: alloc::alloc::Global
})
}
}
pwndbg> p (*kernel::percpu::ALL_PERCPU_BLOCKS[1].p.value).current_addrsp
$8 = core::cell::RefCell<core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>> {
borrow: core::cell::Cell<isize> {
value: core::cell::UnsafeCell<isize> {
value: 0
}
},
value: core::cell::UnsafeCell<core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>> {
value: core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>::Some(alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global> {
ptr: core::ptr::non_null::NonNull<alloc::sync::ArcInner<kernel::context::memory::AddrSpaceWrapper>> {
pointer: 0xffffff7f80188100
},
phantom: core::marker::PhantomData<alloc::sync::ArcInner<kernel::context::memory::AddrSpaceWrapper>>,
alloc: alloc::alloc::Global
})
}
}
pwndbg> p (*kernel::percpu::ALL_PERCPU_BLOCKS[0].p.value).current_addrsp
$9 = core::cell::RefCell<core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>> {
borrow: core::cell::Cell<isize> {
value: core::cell::UnsafeCell<isize> {
value: 0
}
},
value: core::cell::UnsafeCell<core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>> {
value: core::option::Option<alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global>>::Some(alloc::sync::Arc<kernel::context::memory::AddrSpaceWrapper, alloc::alloc::Global> {
ptr: core::ptr::non_null::NonNull<alloc::sync::ArcInner<kernel::context::memory::AddrSpaceWrapper>> {
pointer: 0xffffff7f80133ac8
},
phantom: core::marker::PhantomData<alloc::sync::ArcInner<kernel::context::memory::AddrSpaceWrapper>>,
alloc: alloc::alloc::Global
})
}
}
```
```
pwndbg> thread 4
[Switching to thread 4 (Thread 1.4)]
25 in /mnt/redox/prefix/x86_64-unknown-redox/sysroot/lib/rustlib/src/rust/library/core/src/../../stdarch/crates/core_arch/src/x86/sse2.rs
pwndbg> info threads
Id Target Id Frame
1 Thread 1.1 (CPU#0 [running]) core::sync::atomic::atomic_compare_exchange_weak<usize> (dst=0xffffff7f801972f0, old=0, new=1, success=core::sync::atomic::Ordering::Acquire, failure=core::sync::atomic::Ordering::Relaxed)
at /mnt/redox/prefix/x86_64-unknown-redox/sysroot/lib/rustlib/src/rust/library/core/src/sync/atomic.rs:4100
2 Thread 1.2 (CPU#1 [running]) kernel::context::memory::Flusher::flush (self=0xffff80004212f4b8) at src/context/memory.rs:2827
3 Thread 1.3 (CPU#2 [running]) spin::rwlock::RwLock<kernel::context::context::Context, spin::relax::Spin>::write<kernel::context::context::Context, spin::relax::Spin> (self=0xffffff7f801a99c0)
at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spin-0.9.8/src/rwlock.rs:239
* 4 Thread 1.4 (CPU#3 [running]) 0xffffffff80059cf9 in kernel::context::memory::AddrSpaceWrapper::acquire_read (self=0xffffff7f80188110)
at /mnt/redox/prefix/x86_64-unknown-redox/sysroot/lib/rustlib/src/rust/library/core/src/../../stdarch/crates/core_arch/src/x86/sse2.rs:25
pwndbg> thread 4
[Switching to thread 4 (Thread 1.4)]
25 in /mnt/redox/prefix/x86_64-unknown-redox/sysroot/lib/rustlib/src/rust/library/core/src/../../stdarch/crates/core_arch/src/x86/sse2.rs
pwndbg> bt
```
This is *after* everything freezes.
For relibc tests, if I run `while true; do
/root/relibc-tests/pthread/once; done` it eventually does freeze up and
does so at the same location:
```
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ THREADS (4 TOTAL) ]──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
► 1 "" stopped: 0xffffffff800750a2 <kernel::context::switch::switch+1282>
4 "" stopped: 0xffffffff80074d5a <kernel::context::switch::switch+442>
3 "" stopped: 0xffffffff80059cf9 <kernel::context::context::Context::set_addr_space+217>
2 "" stopped: 0xffffffff80020839 <kernel::context::memory::Flusher::flush+361>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
```
Related: https://gitlab.redox-os.org/redox-os/kernel/-/issues/175
Signed-off-by: Anhad Singh <andypython@protonmail.com>
Previously, if a futex with a timeout was woken up (even via
`futex_wake`), it was treated as though the timeout had expired. When
the timeout expires, the scheduler sets `wake` to `None` and unblocks
the process. Hence if `wake` is `None` and if a timeout was given to
futex, it has expired. Otherwise the process was woken up by
`futex_wake`.
Signed-off-by: Anhad Singh <andypython@protonmail.com>
The `AddrSpaceWrapper::r#move` is used by `mremap` to move, expand or
shrink existing mappings.
`src_span.count`: Size of old region in pages
`new_page_count`: Size of new region in pages
Fixes the following panic when running `cargo`:
```
KERNEL PANIC: panicked at src/context/memory.rs:1969:9:
assertion failed: self.info.can_extract(false)
FP ffff80000cbbf2c0: PC ffffffff8004ced5
FFFFFFFF8004CD50+0185
kernel::panic::panic_handler_inner
FP ffff80000cbbf3a0: PC ffffffff8004ac99
FP ffff80000cbbf3b0: PC ffffffff800a34df
FP ffff80000cbbf3e0: PC ffffffff800a34b4
FP ffff80000cbbf430: PC ffffffff8001c029
FFFFFFFF8001BF10+0119
kernel::context::memory::Grant::extract
FP ffff80000cbbf4a0: PC ffffffff800181ac
FFFFFFFF80016D30+147C
kernel::context::memory::AddrSpaceWrapper::move
FP ffff80000cbbfd30: PC ffffffff80037c08
FFFFFFFF80035660+25A8
kernel::syscall::syscall
FP ffff80000cbbfe90: PC ffffffff8008efbf
FFFFFFFF8008EF10+00AF
__inner_syscall_instruction
FP ffff80000cbbff50: PC ffffffff80085d33
FFFFFFFF80085CF0+0043
kernel::arch::x86_64::interrupt::syscall::syscall_instruction
0000000000004455: GUARD PAGE
CPU #2, CID 0xffffff7f80133690
NAME: /scheme/initfs/bin/redoxfs, DEBUG ID: 43
SYSCALL: mremap(0x2177F000, 0x8000, 0x0, 0xC7000, 0x60000)
HALT
```
```
mremap_flags = MremapFlags(
0x0,
)
prot_flags = MapFlags(
PROT_WRITE | PROT_READ,
)
```
Signed-off-by: Anhad Singh <andypython@protonmail.com>
Instead of using a simple switch to determine if preemption is enabled
(`is_preemptable: bool`), a counter is used instead. This handles the
case where a function holding a `PreemptGuard` calls another function
that creates a new `PreemptGuard`.
Signed-off-by: Anhad Singh <andypython@protonmail.com>
This is a lot easier to parse in no_alloc scenarios than the current
textual format. This would help with moving the cloexec handling from
relibc to redox-rt.
The global allocator panics when out of memory rather than returning an
errors. In addition there are plenty of other places where we don't
handle allocation failure anyway. At some point in the future we should
systematically handle out of memory conditions, but until then let's not
pretend we do and get rid of the usage of the unstable allocator_api
feature that is unlikely to get stabilized any time soon.