Skip to content

Commit 86a8fca

Browse files
committed
device: Add devctl device notifications
1 parent 1020d2f commit 86a8fca

13 files changed

Lines changed: 415 additions & 210 deletions

File tree

kernel/src/device/block/mod.rs

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@ use crate::{
77
memory::{IovecIter, VirtAddr},
88
posix::errno::{EResult, Errno},
99
process::Identity,
10-
vfs::{
11-
self, File,
12-
file::{FileOps, PollFlags},
13-
fs::devtmpfs,
14-
inode::{MknodTarget, Mode},
15-
},
10+
vfs::{self, File, file::FileOps, fs::devtmpfs, inode::Mode},
1611
};
1712
use alloc::{format, sync::Arc};
1813

@@ -168,15 +163,10 @@ pub fn BLOCK_STAGE() {
168163
/// Registers a block device by name and scans for partitions.
169164
pub fn register_block_device(name: &str, device: Arc<dyn BlockDevice>) -> EResult<()> {
170165
// Register in devtmpfs as well.
171-
let root = devtmpfs::get_root();
172-
173-
vfs::mknod(
174-
root.clone(),
175-
root,
166+
crate::device::register_block_node(
176167
format!("block/{}", name).as_bytes(),
168+
device.clone(),
177169
Mode::from_bits_truncate(0o660),
178-
Some(MknodTarget::BlockDevice(device.clone())),
179-
&Identity::get_kernel(),
180170
)?;
181171

182172
log!("Registered block device: \"{}\"", name);
@@ -202,17 +192,13 @@ fn scan_partitions(parent_name: &str, device: Arc<dyn BlockDevice>) -> EResult<(
202192
part.end_lba - part.start_lba + 1,
203193
));
204194

205-
let root = devtmpfs::get_root();
206-
207-
vfs::mknod(
208-
root.clone(),
209-
root.clone(),
195+
crate::device::register_block_node(
210196
format!("block/{}", part_name).as_bytes(),
197+
part_dev,
211198
Mode::from_bits_truncate(0o660),
212-
Some(MknodTarget::BlockDevice(part_dev)),
213-
&Identity::get_kernel(),
214199
)?;
215200

201+
let root = devtmpfs::get_root();
216202
let uuid_str = part.unique_guid.to_string();
217203
let type_str = part.type_guid.to_string();
218204

@@ -367,9 +353,4 @@ impl<T: BlockDevice> FileOps for T {
367353
fn ioctl(&self, file: &File, request: usize, arg: VirtAddr) -> EResult<usize> {
368354
self.handle_ioctl(file, request, arg)
369355
}
370-
371-
fn poll(&self, file: &File, mask: PollFlags) -> EResult<PollFlags> {
372-
_ = (file, mask);
373-
Ok(mask)
374-
}
375356
}

kernel/src/device/cmdline.rs

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,8 @@ use crate::{
33
device,
44
memory::IovecIter,
55
posix::errno::{EResult, Errno},
6-
process::{Identity, PROCESS_STAGE},
7-
vfs::{
8-
self, File,
9-
file::FileOps,
10-
fs::devtmpfs::{self, DEVTMPFS_STAGE},
11-
inode::{MknodTarget, Mode},
12-
},
6+
process::PROCESS_STAGE,
7+
vfs::{File, file::FileOps, fs::devtmpfs::DEVTMPFS_STAGE, inode::Mode},
138
};
149
use alloc::sync::Arc;
1510

@@ -30,19 +25,10 @@ impl FileOps for CmdlineFile {
3025
depends = [PROCESS_STAGE, DEVTMPFS_STAGE]
3126
)]
3227
fn CMDLINE_STAGE() {
33-
let root = devtmpfs::get_root();
34-
35-
vfs::mknod(
36-
root.clone(),
37-
root.clone(),
28+
device::register_char_node(
3829
b"cmdline",
30+
device::make_shared(Arc::new(CmdlineFile), 1, 12),
3931
Mode::from_bits_truncate(0o666),
40-
Some(MknodTarget::CharacterDevice(device::make_shared(
41-
Arc::new(CmdlineFile),
42-
1,
43-
12,
44-
))),
45-
&Identity::get_kernel(),
4632
)
4733
.expect("Unable to create /dev/cmdline");
4834
}

kernel/src/device/devctl.rs

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
use crate::{
2+
device::{self, Device},
3+
memory::IovecIter,
4+
posix::errno::{EResult, Errno},
5+
sched::Scheduler,
6+
util::{event::Event, mutex::spin::SpinMutex, once::Once},
7+
vfs::{
8+
File,
9+
file::{FileOps, OpenFlags, PollEventSet, PollFlags},
10+
fs::devtmpfs::DEVTMPFS_STAGE,
11+
inode::Mode,
12+
},
13+
};
14+
use alloc::{
15+
collections::{btree_map::BTreeMap, vec_deque::VecDeque},
16+
sync::Arc,
17+
vec::Vec,
18+
};
19+
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
20+
21+
/// Maximum number of buffered lines per reader before the oldest is dropped.
22+
const QUEUE_CAPACITY: usize = 256;
23+
/// Maximum number of lines buffered before any reader has connected.
24+
const BACKLOG_CAPACITY: usize = 256;
25+
26+
static READER_COUNTER: AtomicU32 = AtomicU32::new(0);
27+
static DEVCTL: Once<Arc<DevCtl>> = Once::new();
28+
static DEVCTL_READY: AtomicBool = AtomicBool::new(false);
29+
30+
/// The global `/dev/devctl` device.
31+
struct DevCtl {
32+
readers: SpinMutex<BTreeMap<u32, Arc<SpinMutex<VecDeque<Vec<u8>>>>>>,
33+
/// Lines emitted before any reader connects (so boot-time nodes aren't lost).
34+
backlog: SpinMutex<VecDeque<Vec<u8>>>,
35+
had_reader: AtomicBool,
36+
rd_event: Event,
37+
}
38+
39+
/// A single open handle on `/dev/devctl`.
40+
struct DevCtlFile {
41+
device: Arc<DevCtl>,
42+
reader_id: u32,
43+
queue: Arc<SpinMutex<VecDeque<Vec<u8>>>>,
44+
}
45+
46+
impl DevCtl {
47+
fn new() -> Self {
48+
Self {
49+
readers: SpinMutex::new(BTreeMap::new()),
50+
backlog: SpinMutex::new(VecDeque::new()),
51+
had_reader: AtomicBool::new(false),
52+
rd_event: Event::new(),
53+
}
54+
}
55+
56+
/// Enqueue a single notification line and wake any blocked readers.
57+
fn enqueue(&self, line: Vec<u8>) {
58+
let readers = self.readers.lock();
59+
if readers.is_empty() {
60+
drop(readers);
61+
// No reader yet: buffer until the first one connects, unless one has
62+
// already come and gone (then drop, like FreeBSD without devd).
63+
if !self.had_reader.load(Ordering::Acquire) {
64+
let mut backlog = self.backlog.lock();
65+
if backlog.len() >= BACKLOG_CAPACITY {
66+
backlog.pop_front();
67+
}
68+
backlog.push_back(line);
69+
}
70+
return;
71+
}
72+
73+
for buf in readers.values() {
74+
let mut buf = buf.lock();
75+
if buf.len() >= QUEUE_CAPACITY {
76+
buf.pop_front();
77+
}
78+
buf.push_back(line.clone());
79+
}
80+
drop(readers);
81+
self.rd_event.wake_all();
82+
}
83+
}
84+
85+
impl Device for DevCtl {
86+
fn open(self: Arc<Self>, _flags: OpenFlags) -> EResult<Arc<dyn FileOps>> {
87+
let reader_id = READER_COUNTER.fetch_add(1, Ordering::Relaxed);
88+
let queue = Arc::try_new(SpinMutex::new(VecDeque::new()))?;
89+
90+
// On the first ever open, drain the boot backlog into this reader.
91+
if !self.had_reader.swap(true, Ordering::AcqRel) {
92+
let mut backlog = self.backlog.lock();
93+
let mut q = queue.lock();
94+
q.extend(backlog.drain(..));
95+
drop(q);
96+
drop(backlog);
97+
}
98+
99+
self.readers.lock().insert(reader_id, queue.clone());
100+
101+
Ok(Arc::try_new(DevCtlFile {
102+
device: self,
103+
reader_id,
104+
queue,
105+
})?)
106+
}
107+
108+
fn major(&self) -> u32 {
109+
5
110+
}
111+
112+
fn minor(&self) -> u32 {
113+
3
114+
}
115+
}
116+
117+
impl Drop for DevCtlFile {
118+
fn drop(&mut self) {
119+
self.device.readers.lock().remove(&self.reader_id);
120+
}
121+
}
122+
123+
impl FileOps for DevCtlFile {
124+
fn read(&self, file: &File, buffer: &mut IovecIter, _offset: u64) -> EResult<isize> {
125+
let non_blocking = file.flags.lock().contains(OpenFlags::NonBlocking);
126+
127+
loop {
128+
let guard = self.device.rd_event.guard();
129+
{
130+
let mut queue = self.queue.lock();
131+
if !queue.is_empty() {
132+
return Self::drain_lines(&mut queue, buffer);
133+
}
134+
}
135+
if non_blocking {
136+
return Err(Errno::EAGAIN);
137+
}
138+
if Scheduler::get_current().has_pending_signals() {
139+
return Err(Errno::EINTR);
140+
}
141+
guard.wait();
142+
if Scheduler::get_current().has_pending_signals() {
143+
return Err(Errno::EINTR);
144+
}
145+
}
146+
}
147+
148+
fn write(&self, _file: &File, _buffer: &mut IovecIter, _offset: u64) -> EResult<isize> {
149+
Err(Errno::EBADF)
150+
}
151+
152+
fn poll(&self, _file: &File, mask: PollFlags) -> EResult<PollFlags> {
153+
let mut revents = PollFlags::empty();
154+
if mask.contains(PollFlags::In) && !self.queue.lock().is_empty() {
155+
revents |= PollFlags::In;
156+
}
157+
Ok(revents)
158+
}
159+
160+
fn poll_events(&self, _file: &File, mask: PollFlags) -> PollEventSet<'_> {
161+
if mask.intersects(PollFlags::Read) {
162+
PollEventSet::one(&self.device.rd_event)
163+
} else {
164+
PollEventSet::new()
165+
}
166+
}
167+
}
168+
169+
impl DevCtlFile {
170+
/// Copy as many whole lines as fit into the user buffer. Returns bytes copied.
171+
fn drain_lines(queue: &mut VecDeque<Vec<u8>>, buffer: &mut IovecIter) -> EResult<isize> {
172+
let mut total = 0isize;
173+
while let Some(line) = queue.front() {
174+
if line.len() > buffer.len() {
175+
// A single line larger than the whole buffer can never be read.
176+
if total == 0 {
177+
return Err(Errno::EINVAL);
178+
}
179+
break;
180+
}
181+
let line = queue.pop_front().unwrap();
182+
total += buffer.copy_from_slice(&line)?;
183+
}
184+
Ok(total)
185+
}
186+
}
187+
188+
fn format_line(typ: &str, relpath: &[u8]) -> Vec<u8> {
189+
let mut line = Vec::new();
190+
line.extend_from_slice(b"!system=DEVFS subsystem=CDEV type=");
191+
line.extend_from_slice(typ.as_bytes());
192+
line.extend_from_slice(b" cdev=");
193+
line.extend_from_slice(relpath);
194+
line.push(b'\n');
195+
line
196+
}
197+
198+
pub fn notify_create(relpath: &[u8]) {
199+
if !DEVCTL_READY.load(Ordering::Acquire) {
200+
return;
201+
}
202+
DEVCTL.get().enqueue(format_line("CREATE", relpath));
203+
}
204+
205+
pub fn notify_destroy(relpath: &[u8]) {
206+
if !DEVCTL_READY.load(Ordering::Acquire) {
207+
return;
208+
}
209+
DEVCTL.get().enqueue(format_line("DESTROY", relpath));
210+
}
211+
212+
#[initgraph::task(
213+
name = "generic.device.devctl",
214+
depends = [DEVTMPFS_STAGE],
215+
)]
216+
pub fn DEVCTL_STAGE() {
217+
let dev = Arc::new(DevCtl::new());
218+
unsafe { DEVCTL.init(dev.clone()) };
219+
DEVCTL_READY.store(true, Ordering::Release);
220+
221+
device::register_char_node(b"devctl", dev, Mode::from_bits_truncate(0o600))
222+
.expect("Unable to create /dev/devctl");
223+
}

kernel/src/device/drm/mod.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -476,11 +476,16 @@ impl FileOps for DrmFile {
476476

477477
val.crtc_id = encoder.active_crtc.id() as _;
478478

479-
// Create a bit mask using the IDs as indices.
480-
let mut possible_crtcs = 0;
479+
// possible_crtcs is indexed by each CRTC's position in the global
480+
// CRTC list (drm_crtc_index), not by its object id.
481+
let crtcs = state.crtcs.lock();
482+
let mut possible_crtcs = 0u32;
481483
for crtc in encoder.possible_crtcs.iter() {
482-
possible_crtcs |= 1 << crtc.id();
484+
if let Some(idx) = crtcs.iter().position(|c| c.id() == crtc.id()) {
485+
possible_crtcs |= 1 << idx;
486+
}
483487
}
488+
drop(crtcs);
484489
val.possible_crtcs = possible_crtcs;
485490

486491
ptr.write(val).ok_or(Errno::EFAULT)?;
@@ -1148,18 +1153,12 @@ static CARD_COUNTER: AtomicU32 = AtomicU32::new(0);
11481153
pub fn register(device: Arc<dyn Device>) -> EResult<()> {
11491154
log!("Registering new DRM card");
11501155

1151-
let root = devtmpfs::get_root();
11521156
let minor = CARD_COUNTER.fetch_add(1, Ordering::SeqCst);
11531157

1154-
vfs::mknod(
1155-
root.clone(),
1156-
root.clone(),
1158+
crate::device::register_char_node(
11571159
format!("drm/card{}", minor).as_bytes(),
1160+
Arc::new(DrmDeviceNode { device, minor }),
11581161
Mode::from_bits_truncate(0o660),
1159-
Some(crate::vfs::inode::MknodTarget::CharacterDevice(Arc::new(
1160-
DrmDeviceNode { device, minor },
1161-
))),
1162-
&Identity::get_kernel(),
11631162
)
11641163
}
11651164

0 commit comments

Comments
 (0)