Skip to content

Commit 6008001

Browse files
hoshinolinajannau
authored andcommitted
drm/asahi: Clean up jobs in a workqueue
This eliminates a potential deadlock under load and improves the fence signaling situation (for when we have a shrinker). Signed-off-by: Asahi Lina <lina@asahilina.net>
1 parent 61c11f3 commit 6008001

2 files changed

Lines changed: 68 additions & 84 deletions

File tree

drivers/gpu/drm/asahi/gpu.rs

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,6 @@ pub(crate) struct GpuManager {
205205
event_manager: Arc<event::EventManager>,
206206
buffer_mgr: buffer::BufferManager::ver,
207207
ids: SequenceIDs,
208-
#[pin]
209-
garbage_work: Mutex<Vec<Box<dyn workqueue::GenSubmittedWork>>>,
210208
#[allow(clippy::vec_box)]
211209
#[pin]
212210
garbage_contexts: Mutex<KVec<KBox<fw::types::GpuObject<fw::workqueue::GpuContextData>>>>,
@@ -270,10 +268,8 @@ pub(crate) trait GpuManager: Send + Sync {
270268
fn get_cfg(&self) -> &'static hw::HwConfig;
271269
/// Get the dynamic GPU configuration for this SoC.
272270
fn get_dyncfg(&self) -> &hw::DynConfig;
273-
/// Register completed work as garbage
274-
fn add_completed_work(&self, work: Vec<Box<dyn workqueue::GenSubmittedWork>>);
275271
/// Register an unused context as garbage
276-
fn free_context(&self, data: Box<fw::types::GpuObject<fw::workqueue::GpuContextData>>);
272+
fn free_context(&self, data: KBox<fw::types::GpuObject<fw::workqueue::GpuContextData>>);
277273
/// Check whether the GPU is crashed
278274
fn is_crashed(&self) -> bool;
279275
}
@@ -714,7 +710,6 @@ impl GpuManager::ver {
714710
pipes,
715711
buffer_mgr,
716712
ids: Default::default(),
717-
garbage_work <- Mutex::new_named(Vec::new(), c_str!("garbage_work")),
718713
garbage_contexts <- Mutex::new_named(KVec::new(), c_str!("garbage_contexts")),
719714
}),
720715
GFP_KERNEL,
@@ -1156,12 +1151,6 @@ impl GpuManager for GpuManager::ver {
11561151
}
11571152

11581153
fn alloc(&self) -> Guard<'_, KernelAllocators, MutexBackend> {
1159-
/*
1160-
* TODO: This should be done in a workqueue or something.
1161-
* Clean up completed jobs
1162-
*/
1163-
self.garbage_work.lock().clear();
1164-
11651154
/* Clean up idle contexts */
11661155
let mut garbage_ctx = KVec::new();
11671156
core::mem::swap(&mut *self.garbage_contexts.lock(), &mut garbage_ctx);
@@ -1485,24 +1474,6 @@ impl GpuManager for GpuManager::ver {
14851474
&self.dyncfg
14861475
}
14871476

1488-
fn add_completed_work(&self, work: Vec<Box<dyn workqueue::GenSubmittedWork>>) {
1489-
let mut garbage = self.garbage_work.lock();
1490-
1491-
if garbage.reserve(work.len(), GFP_KERNEL).is_err() {
1492-
dev_err!(
1493-
self.dev,
1494-
"Failed to reserve space for completed work, deadlock possible.\n"
1495-
);
1496-
return;
1497-
}
1498-
1499-
for i in work {
1500-
garbage
1501-
.push(i, GFP_KERNEL)
1502-
.expect("push() failed after reserve()");
1503-
}
1504-
}
1505-
15061477
fn free_context(&self, ctx: KBox<fw::types::GpuObject<fw::workqueue::GpuContextData>>) {
15071478
let mut garbage = self.garbage_contexts.lock();
15081479

drivers/gpu/drm/asahi/workqueue.rs

Lines changed: 67 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use kernel::{
3535
},
3636
types::ForeignOwnable,
3737
uapi,
38+
workqueue::{self, impl_has_work, new_work, Work, WorkItem},
3839
};
3940

4041
const DEBUG_CLASS: DebugFlags = DebugFlags::WorkQueue;
@@ -175,6 +176,32 @@ pub(crate) trait GenSubmittedWork: Send + Sync {
175176
fn get_fence(&self) -> dma_fence::Fence;
176177
}
177178

179+
#[pin_data]
180+
struct SubmittedWorkContainer {
181+
#[pin]
182+
work: Work<Self>,
183+
inner: KBox<dyn GenSubmittedWork>,
184+
}
185+
186+
impl_has_work! {
187+
impl HasWork<Self> for SubmittedWorkContainer { self.work }
188+
}
189+
190+
impl WorkItem for SubmittedWorkContainer {
191+
type Pointer = Pin<KBox<SubmittedWorkContainer>>;
192+
193+
fn run(this: Pin<KBox<SubmittedWorkContainer>>) {
194+
mod_pr_debug!("WorkQueue: Freeing command @ {:?}\n", this.inner.gpu_va());
195+
}
196+
}
197+
198+
impl SubmittedWorkContainer {
199+
fn inner_mut(self: Pin<&mut Self>) -> &mut KBox<dyn GenSubmittedWork> {
200+
// SAFETY: inner does not require structural pinning.
201+
unsafe { &mut self.get_unchecked_mut().inner }
202+
}
203+
}
204+
178205
impl<O: OpaqueGpuObject, C: FnOnce(&mut O, Option<WorkError>) + Send + Sync> GenSubmittedWork
179206
for SubmittedWork<O, C>
180207
{
@@ -223,7 +250,7 @@ struct WorkQueueInner {
223250
pipe_type: PipeType,
224251
size: u32,
225252
wptr: u32,
226-
pending: Vec<Box<dyn GenSubmittedWork>>,
253+
pending: KVec<Pin<KBox<SubmittedWorkContainer>>>,
227254
last_token: Option<event::Token>,
228255
pending_jobs: usize,
229256
last_submitted: Option<event::EventValue>,
@@ -272,7 +299,7 @@ pub(crate) struct Job {
272299
wq: Arc<WorkQueue::ver>,
273300
event_info: QueueEventInfo::ver,
274301
start_value: EventValue,
275-
pending: Vec<Box<dyn GenSubmittedWork>>,
302+
pending: KVec<Pin<KBox<SubmittedWorkContainer>>>,
276303
committed: bool,
277304
submitted: bool,
278305
event_count: usize,
@@ -321,17 +348,23 @@ impl Job::ver {
321348
return Err(EINVAL);
322349
}
323350

351+
let fence = self.fence.clone();
352+
let value = self.event_info.value.next();
353+
324354
self.pending.push(
325-
Box::new(
326-
SubmittedWork::<_, _> {
327-
object: command,
328-
value: self.event_info.value.next(),
329-
error: None,
330-
callback: Some(callback),
331-
wptr: 0,
332-
vm_slot,
333-
fence: self.fence.clone(),
334-
},
355+
KBox::try_pin_init(
356+
try_pin_init!(SubmittedWorkContainer {
357+
work <- new_work!("SubmittedWorkWrapper::work"),
358+
inner: KBox::new(SubmittedWork::<_, _> {
359+
object: command,
360+
value,
361+
error: None,
362+
callback: Some(callback),
363+
wptr: 0,
364+
vm_slot,
365+
fence,
366+
}, GFP_KERNEL)?
367+
}),
335368
GFP_KERNEL,
336369
)?,
337370
GFP_KERNEL,
@@ -378,7 +411,7 @@ impl Job::ver {
378411
if inner.free_slots() > self.event_count && inner.free_space() > self.pending.len() {
379412
None
380413
} else if let Some(work) = inner.pending.first() {
381-
Some(work.get_fence())
414+
Some(work.inner.get_fence())
382415
} else {
383416
pr_err!(
384417
"WorkQueue: Cannot submit, but queue is empty? {} > {}, {} > {} (pend={} ls={:#x?} lc={:#x?}) ev={:#x?} cur={:#x?} slot {:?}\n",
@@ -456,11 +489,11 @@ impl Job::ver {
456489
);
457490

458491
for mut command in self.pending.drain(..) {
459-
command.set_wptr(wptr);
492+
command.as_mut().inner_mut().set_wptr(wptr);
460493

461494
let next_wptr = (wptr + 1) % inner.size;
462495
assert!(inner.doneptr() != next_wptr);
463-
inner.info.ring[wptr as usize] = command.gpu_va().get();
496+
inner.info.ring[wptr as usize] = command.inner.gpu_va().get();
464497
wptr = next_wptr;
465498

466499
// Cannot fail, since we did a reserve(1) above
@@ -865,11 +898,11 @@ impl WorkQueue for WorkQueue::ver {
865898
let mut completed_commands: usize = 0;
866899

867900
for cmd in inner.pending.iter() {
868-
if cmd.value() <= value {
901+
if cmd.inner.value() <= value {
869902
mod_pr_debug!(
870903
"WorkQueue({:?}): Command at value {:#x?} complete\n",
871904
inner.pipe_type,
872-
cmd.value()
905+
cmd.inner.value()
873906
);
874907
completed_commands += 1;
875908
} else {
@@ -881,25 +914,17 @@ impl WorkQueue for WorkQueue::ver {
881914
return inner.pending.is_empty();
882915
}
883916

884-
let mut completed = Vec::new();
885-
886-
if completed.reserve(completed_commands, GFP_KERNEL).is_err() {
887-
pr_crit!(
888-
"WorkQueue({:?}): Failed to allocate space for {} completed commands\n",
889-
inner.pipe_type,
890-
completed_commands
891-
);
892-
}
893-
917+
let last_wptr = inner.pending[completed_commands - 1].inner.wptr();
894918
let pipe_type = inner.pipe_type;
895919

896-
for cmd in inner.pending.drain(..completed_commands) {
897-
if completed.push(cmd, GFP_KERNEL).is_err() {
898-
pr_crit!(
899-
"WorkQueue({:?}): Failed to signal a completed command\n",
900-
pipe_type,
901-
);
902-
}
920+
for mut cmd in inner.pending.drain(..completed_commands) {
921+
mod_pr_debug!(
922+
"WorkQueue({:?}): Queueing command @ {:?} for cleanup\n",
923+
pipe_type,
924+
cmd.inner.gpu_va()
925+
);
926+
cmd.as_mut().inner_mut().complete();
927+
workqueue::system().enqueue(cmd);
903928
}
904929

905930
mod_pr_debug!(
@@ -911,12 +936,10 @@ impl WorkQueue for WorkQueue::ver {
911936
inner.last_completed,
912937
);
913938

914-
if let Some(i) = completed.last() {
915-
inner
916-
.info
917-
.state
918-
.with(|raw, _inner| raw.cpu_freeptr.store(i.wptr(), Ordering::Release));
919-
}
939+
inner
940+
.info
941+
.state
942+
.with(|raw, _inner| raw.cpu_freeptr.store(last_wptr, Ordering::Release));
920943

921944
let empty = inner.pending.is_empty();
922945
if empty && inner.pending_jobs == 0 {
@@ -925,16 +948,6 @@ impl WorkQueue for WorkQueue::ver {
925948
inner.last_completed = None;
926949
}
927950

928-
let dev = inner.dev.clone();
929-
core::mem::drop(inner);
930-
931-
for cmd in completed.iter_mut() {
932-
cmd.complete();
933-
}
934-
935-
let gpu = &dev.data().gpu;
936-
gpu.add_completed_work(completed);
937-
938951
empty
939952
}
940953

@@ -963,8 +976,8 @@ impl WorkQueue for WorkQueue::ver {
963976
);
964977

965978
for cmd in inner.pending.iter_mut() {
966-
if cmd.value() <= value {
967-
cmd.mark_error(error);
979+
if cmd.inner.value() <= value {
980+
cmd.as_mut().inner_mut().mark_error(error);
968981
} else {
969982
break;
970983
}
@@ -1005,8 +1018,8 @@ impl WorkQueue for WorkQueue::ver {
10051018
core::mem::drop(inner);
10061019

10071020
for mut cmd in cmds {
1008-
cmd.mark_error(error);
1009-
cmd.complete();
1021+
cmd.as_mut().inner_mut().mark_error(error);
1022+
cmd.as_mut().inner_mut().complete();
10101023
}
10111024
}
10121025
}

0 commit comments

Comments
 (0)