Skip to content

Commit 17e0e29

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 3365362 commit 17e0e29

2 files changed

Lines changed: 67 additions & 83 deletions

File tree

drivers/gpu/drm/asahi/gpu.rs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,6 @@ pub(crate) struct GpuManager {
206206
event_manager: Arc<event::EventManager>,
207207
buffer_mgr: buffer::BufferManager::ver,
208208
ids: SequenceIDs,
209-
#[pin]
210-
garbage_work: Mutex<Vec<Box<dyn workqueue::GenSubmittedWork>>>,
211209
#[allow(clippy::vec_box)]
212210
#[pin]
213211
garbage_contexts: Mutex<KVec<KBox<fw::types::GpuObject<fw::workqueue::GpuContextData>>>>,
@@ -273,8 +271,6 @@ pub(crate) trait GpuManager: Send + Sync {
273271
fn get_cfg(&self) -> &'static hw::HwConfig;
274272
/// Get the dynamic GPU configuration for this SoC.
275273
fn get_dyncfg(&self) -> &hw::DynConfig;
276-
/// Register completed work as garbage
277-
fn add_completed_work(&self, work: Vec<Box<dyn workqueue::GenSubmittedWork>>);
278274
/// Register an unused context as garbage
279275
fn free_context(&self, data: Box<fw::types::GpuObject<fw::workqueue::GpuContextData>>);
280276
/// Check whether the GPU is crashed
@@ -717,7 +713,6 @@ impl GpuManager::ver {
717713
pipes,
718714
buffer_mgr,
719715
ids: Default::default(),
720-
garbage_work <- Mutex::new_named(Vec::new(), c_str!("garbage_work")),
721716
garbage_contexts <- Mutex::new_named(KVec::new(), c_str!("garbage_contexts")),
722717
}),
723718
GFP_KERNEL,
@@ -1165,12 +1160,6 @@ impl GpuManager for GpuManager::ver {
11651160
}
11661161

11671162
fn alloc(&self) -> Guard<'_, KernelAllocators, MutexBackend> {
1168-
/*
1169-
* TODO: This should be done in a workqueue or something.
1170-
* Clean up completed jobs
1171-
*/
1172-
self.garbage_work.lock().clear();
1173-
11741163
/* Clean up idle contexts */
11751164
let mut garbage_ctx = KVec::new();
11761165
core::mem::swap(&mut *self.garbage_contexts.lock(), &mut garbage_ctx);
@@ -1506,24 +1495,6 @@ impl GpuManager for GpuManager::ver {
15061495
&self.dyncfg
15071496
}
15081497

1509-
fn add_completed_work(&self, work: Vec<Box<dyn workqueue::GenSubmittedWork>>) {
1510-
let mut garbage = self.garbage_work.lock();
1511-
1512-
if garbage.reserve(work.len(), GFP_KERNEL).is_err() {
1513-
dev_err!(
1514-
self.dev,
1515-
"Failed to reserve space for completed work, deadlock possible.\n"
1516-
);
1517-
return;
1518-
}
1519-
1520-
for i in work {
1521-
garbage
1522-
.push(i, GFP_KERNEL)
1523-
.expect("push() failed after reserve()");
1524-
}
1525-
}
1526-
15271498
fn free_context(&self, ctx: KBox<fw::types::GpuObject<fw::workqueue::GpuContextData>>) {
15281499
let mut garbage = self.garbage_contexts.lock();
15291500

drivers/gpu/drm/asahi/workqueue.rs

Lines changed: 67 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use kernel::{
3333
Arc, Mutex,
3434
},
3535
uapi,
36+
workqueue::{self, impl_has_work, new_work, Work, WorkItem},
3637
};
3738

3839
const DEBUG_CLASS: DebugFlags = DebugFlags::WorkQueue;
@@ -172,6 +173,32 @@ pub(crate) trait GenSubmittedWork: Send + Sync {
172173
fn get_fence(&self) -> dma_fence::Fence;
173174
}
174175

176+
#[pin_data]
177+
struct SubmittedWorkContainer {
178+
#[pin]
179+
work: Work<Self>,
180+
inner: Box<dyn GenSubmittedWork>,
181+
}
182+
183+
impl_has_work! {
184+
impl HasWork<Self> for SubmittedWorkContainer { self.work }
185+
}
186+
187+
impl WorkItem for SubmittedWorkContainer {
188+
type Pointer = Pin<Box<SubmittedWorkContainer>>;
189+
190+
fn run(this: Pin<Box<SubmittedWorkContainer>>) {
191+
mod_pr_debug!("WorkQueue: Freeing command @ {:?}\n", this.inner.gpu_va());
192+
}
193+
}
194+
195+
impl SubmittedWorkContainer {
196+
fn inner_mut(self: Pin<&mut Self>) -> &mut Box<dyn GenSubmittedWork> {
197+
// SAFETY: inner does not require structural pinning.
198+
unsafe { &mut self.get_unchecked_mut().inner }
199+
}
200+
}
201+
175202
impl<O: OpaqueGpuObject, C: FnOnce(&mut O, Option<WorkError>) + Send + Sync> GenSubmittedWork
176203
for SubmittedWork<O, C>
177204
{
@@ -220,7 +247,7 @@ struct WorkQueueInner {
220247
pipe_type: PipeType,
221248
size: u32,
222249
wptr: u32,
223-
pending: Vec<Box<dyn GenSubmittedWork>>,
250+
pending: Vec<Pin<Box<SubmittedWorkContainer>>>,
224251
last_token: Option<event::Token>,
225252
pending_jobs: usize,
226253
last_submitted: Option<event::EventValue>,
@@ -269,7 +296,7 @@ pub(crate) struct Job {
269296
wq: Arc<WorkQueue::ver>,
270297
event_info: QueueEventInfo::ver,
271298
start_value: EventValue,
272-
pending: Vec<Box<dyn GenSubmittedWork>>,
299+
pending: Vec<Pin<Box<SubmittedWorkContainer>>>,
273300
committed: bool,
274301
submitted: bool,
275302
event_count: usize,
@@ -318,17 +345,23 @@ impl Job::ver {
318345
return Err(EINVAL);
319346
}
320347

348+
let fence = self.fence.clone();
349+
let value = self.event_info.value.next();
350+
321351
self.pending.push(
322-
Box::new(
323-
SubmittedWork::<_, _> {
324-
object: command,
325-
value: self.event_info.value.next(),
326-
error: None,
327-
callback: Some(callback),
328-
wptr: 0,
329-
vm_slot,
330-
fence: self.fence.clone(),
331-
},
352+
Box::try_pin_init(
353+
try_pin_init!(SubmittedWorkContainer {
354+
work <- new_work!("SubmittedWorkWrapper::work"),
355+
inner: Box::new(SubmittedWork::<_, _> {
356+
object: command,
357+
value,
358+
error: None,
359+
callback: Some(callback),
360+
wptr: 0,
361+
vm_slot,
362+
fence,
363+
}, GFP_KERNEL)?
364+
}),
332365
GFP_KERNEL,
333366
)?,
334367
GFP_KERNEL,
@@ -375,7 +408,7 @@ impl Job::ver {
375408
if inner.free_slots() > self.event_count && inner.free_space() > self.pending.len() {
376409
None
377410
} else if let Some(work) = inner.pending.first() {
378-
Some(work.get_fence())
411+
Some(work.inner.get_fence())
379412
} else {
380413
pr_err!(
381414
"WorkQueue: Cannot submit, but queue is empty? {} > {}, {} > {} (pend={} ls={:#x?} lc={:#x?}) ev={:#x?} cur={:#x?} slot {:?}\n",
@@ -453,11 +486,11 @@ impl Job::ver {
453486
);
454487

455488
for mut command in self.pending.drain(..) {
456-
command.set_wptr(wptr);
489+
command.as_mut().inner_mut().set_wptr(wptr);
457490

458491
let next_wptr = (wptr + 1) % inner.size;
459492
assert!(inner.doneptr() != next_wptr);
460-
inner.info.ring[wptr as usize] = command.gpu_va().get();
493+
inner.info.ring[wptr as usize] = command.inner.gpu_va().get();
461494
wptr = next_wptr;
462495

463496
// Cannot fail, since we did a reserve(1) above
@@ -862,11 +895,11 @@ impl WorkQueue for WorkQueue::ver {
862895
let mut completed_commands: usize = 0;
863896

864897
for cmd in inner.pending.iter() {
865-
if cmd.value() <= value {
898+
if cmd.inner.value() <= value {
866899
mod_pr_debug!(
867900
"WorkQueue({:?}): Command at value {:#x?} complete\n",
868901
inner.pipe_type,
869-
cmd.value()
902+
cmd.inner.value()
870903
);
871904
completed_commands += 1;
872905
} else {
@@ -878,25 +911,17 @@ impl WorkQueue for WorkQueue::ver {
878911
return inner.pending.is_empty();
879912
}
880913

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

893-
for cmd in inner.pending.drain(..completed_commands) {
894-
if completed.push(cmd, GFP_KERNEL).is_err() {
895-
pr_crit!(
896-
"WorkQueue({:?}): Failed to signal a completed command\n",
897-
pipe_type,
898-
);
899-
}
917+
for mut cmd in inner.pending.drain(..completed_commands) {
918+
mod_pr_debug!(
919+
"WorkQueue({:?}): Queueing command @ {:?} for cleanup\n",
920+
pipe_type,
921+
cmd.inner.gpu_va()
922+
);
923+
cmd.as_mut().inner_mut().complete();
924+
workqueue::system().enqueue(cmd);
900925
}
901926

902927
mod_pr_debug!(
@@ -908,12 +933,10 @@ impl WorkQueue for WorkQueue::ver {
908933
inner.last_completed,
909934
);
910935

911-
if let Some(i) = completed.last() {
912-
inner
913-
.info
914-
.state
915-
.with(|raw, _inner| raw.cpu_freeptr.store(i.wptr(), Ordering::Release));
916-
}
936+
inner
937+
.info
938+
.state
939+
.with(|raw, _inner| raw.cpu_freeptr.store(last_wptr, Ordering::Release));
917940

918941
let empty = inner.pending.is_empty();
919942
if empty && inner.pending_jobs == 0 {
@@ -922,16 +945,6 @@ impl WorkQueue for WorkQueue::ver {
922945
inner.last_completed = None;
923946
}
924947

925-
let dev = inner.dev.clone();
926-
core::mem::drop(inner);
927-
928-
for cmd in completed.iter_mut() {
929-
cmd.complete();
930-
}
931-
932-
let gpu = &dev.data().gpu;
933-
gpu.add_completed_work(completed);
934-
935948
empty
936949
}
937950

@@ -960,8 +973,8 @@ impl WorkQueue for WorkQueue::ver {
960973
);
961974

962975
for cmd in inner.pending.iter_mut() {
963-
if cmd.value() <= value {
964-
cmd.mark_error(error);
976+
if cmd.inner.value() <= value {
977+
cmd.as_mut().inner_mut().mark_error(error);
965978
} else {
966979
break;
967980
}
@@ -1002,8 +1015,8 @@ impl WorkQueue for WorkQueue::ver {
10021015
core::mem::drop(inner);
10031016

10041017
for mut cmd in cmds {
1005-
cmd.mark_error(error);
1006-
cmd.complete();
1018+
cmd.as_mut().inner_mut().mark_error(error);
1019+
cmd.as_mut().inner_mut().complete();
10071020
}
10081021
}
10091022
}

0 commit comments

Comments
 (0)