use alloc::{
borrow::Cow,
collections::btree_map::BTreeMap,
sync::{Arc, Weak},
};
use core::{
sync::atomic::{AtomicU64, Ordering},
task::Context,
};
use ax_errno::AxResult;
use ax_fs_ng::vfs::{FileBackend, FileFlags, MountNamespace};
use ax_kspin::SpinNoIrq;
use axpoll::{IoEvents, PollSet, Pollable};
use spin::Once;
use super::{File, FileLike, IoDst, IoSrc, Kstat};
const MOUNT_CHANGE_EVENTS: IoEvents = IoEvents::PRI.union(IoEvents::ERR);
static MOUNT_NAMESPACE_EVENTS: Once<SpinNoIrq<BTreeMap<u64, Weak<MountNamespaceEvent>>>> =
Once::new();
fn event_registry() -> &'static SpinNoIrq<BTreeMap<u64, Weak<MountNamespaceEvent>>> {
MOUNT_NAMESPACE_EVENTS.call_once(|| SpinNoIrq::new(BTreeMap::new()))
}
fn event_for_open(namespace: &MountNamespace) -> Arc<MountNamespaceEvent> {
let mut registry = event_registry().lock();
registry.retain(|_, event| event.strong_count() != 0);
if let Some(event) = registry.get(&namespace.id()).and_then(Weak::upgrade) {
return event;
}
let event = Arc::new(MountNamespaceEvent::new());
registry.insert(namespace.id(), Arc::downgrade(&event));
event
}
pub(crate) fn notify_mount_namespace_changed(namespace: &MountNamespace) {
let event = event_registry()
.lock()
.get(&namespace.id())
.and_then(Weak::upgrade);
if let Some(event) = event {
event.notify();
}
}
struct MountNamespaceEvent {
generation: AtomicU64,
waiters: PollSet,
}
impl MountNamespaceEvent {
const fn new() -> Self {
Self {
generation: AtomicU64::new(0),
waiters: PollSet::new(),
}
}
fn generation(&self) -> u64 {
self.generation.load(Ordering::Acquire)
}
fn notify(&self) {
self.generation.fetch_add(1, Ordering::Release);
unsafe {
self.waiters.wake(MOUNT_CHANGE_EVENTS);
}
}
fn register(&self, context: &mut Context<'_>, events: IoEvents) {
let interests = events & MOUNT_CHANGE_EVENTS;
if interests.is_empty() {
return;
}
unsafe {
self.waiters.register(context.waker(), interests);
}
}
}
pub(crate) struct MountTableFile {
file: Arc<File>,
event: Arc<MountNamespaceEvent>,
observed_generation: AtomicU64,
}
impl MountTableFile {
pub(crate) fn new(file: Arc<File>, namespace: &MountNamespace) -> Arc<Self> {
let event = event_for_open(namespace);
let observed_generation = AtomicU64::new(event.generation());
Arc::new(Self {
file,
event,
observed_generation,
})
}
pub(crate) fn inner(&self) -> &Arc<File> {
&self.file
}
}
impl FileLike for MountTableFile {
fn read(&self, dst: &mut IoDst) -> AxResult<usize> {
self.file.read(dst)
}
fn write(&self, src: &mut IoSrc) -> AxResult<usize> {
self.file.write(src)
}
fn stat(&self) -> AxResult<Kstat> {
self.file.stat()
}
fn inode_key(&self) -> Option<(u64, u64)> {
self.file.inode_key()
}
fn file_mmap(&self) -> AxResult<(FileBackend, FileFlags)> {
self.file.file_mmap()
}
fn ioctl(&self, cmd: u32, arg: usize) -> AxResult<usize> {
self.file.ioctl(cmd, arg)
}
fn open_flags(&self) -> u32 {
self.file.open_flags()
}
fn nonblocking(&self) -> bool {
self.file.nonblocking()
}
fn set_nonblocking(&self, nonblocking: bool) -> AxResult {
self.file.set_nonblocking(nonblocking)
}
fn append(&self) -> bool {
self.file.append()
}
fn set_append(&self, append: bool) -> AxResult {
self.file.set_append(append)
}
fn path(&self) -> Cow<'_, str> {
self.file.path()
}
}
impl Pollable for MountTableFile {
fn poll(&self) -> IoEvents {
let generation = self.event.generation();
let observed = self.observed_generation.swap(generation, Ordering::AcqRel);
let changed = if generation != observed {
MOUNT_CHANGE_EVENTS
} else {
IoEvents::empty()
};
self.file.poll() | changed
}
fn register(&self, context: &mut Context<'_>, events: IoEvents) {
self.event.register(context, events);
self.file.register(context, events);
}
}