Skip to content

Commit f3841ab

Browse files
committed
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 4af79b7 commit f3841ab

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
@@ -207,8 +207,6 @@ pub(crate) struct GpuManager {
207207
event_manager: Arc<event::EventManager>,
208208
buffer_mgr: buffer::BufferManager::ver,
209209
ids: SequenceIDs,
210-
#[pin]
211-
garbage_work: Mutex<Vec<Box<dyn workqueue::GenSubmittedWork>>>,
212210
#[allow(clippy::vec_box)]
213211
#[pin]
214212
garbage_contexts: Mutex<Vec<Box<fw::types::GpuObject<fw::workqueue::GpuContextData>>>>,
@@ -274,8 +272,6 @@ pub(crate) trait GpuManager: Send + Sync {
274272
fn get_cfg(&self) -> &'static hw::HwConfig;
275273
/// Get the dynamic GPU configuration for this SoC.
276274
fn get_dyncfg(&self) -> &hw::DynConfig;
277-
/// Register completed work as garbage
278-
fn add_completed_work(&self, work: Vec<Box<dyn workqueue::GenSubmittedWork>>);
279275
/// Register an unused context as garbage
280276
fn free_context(&self, data: Box<fw::types::GpuObject<fw::workqueue::GpuContextData>>);
281277
/// Check whether the GPU is crashed
@@ -718,7 +714,6 @@ impl GpuManager::ver {
718714
pipes,
719715
buffer_mgr,
720716
ids: Default::default(),
721-
garbage_work <- Mutex::new_named(Vec::new(), c_str!("garbage_work")),
722717
garbage_contexts <- Mutex::new_named(Vec::new(), c_str!("garbage_contexts")),
723718
}),
724719
GFP_KERNEL,
@@ -1155,12 +1150,6 @@ impl GpuManager for GpuManager::ver {
11551150
}
11561151

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

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

drivers/gpu/drm/asahi/workqueue.rs

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

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

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

349+
let fence = self.fence.clone();
350+
let value = self.event_info.value.next();
351+
322352
self.pending.push(
323-
Box::new(
324-
SubmittedWork::<_, _> {
325-
object: command,
326-
value: self.event_info.value.next(),
327-
error: None,
328-
callback: Some(callback),
329-
wptr: 0,
330-
vm_slot,
331-
fence: self.fence.clone(),
332-
},
353+
Box::try_pin_init(
354+
try_pin_init!(SubmittedWorkContainer {
355+
work <- new_work!("SubmittedWorkWrapper::work"),
356+
inner: Box::new(SubmittedWork::<_, _> {
357+
object: command,
358+
value,
359+
error: None,
360+
callback: Some(callback),
361+
wptr: 0,
362+
vm_slot,
363+
fence,
364+
}, GFP_KERNEL)?
365+
}),
333366
GFP_KERNEL,
334367
)?,
335368
GFP_KERNEL,
@@ -376,7 +409,7 @@ impl Job::ver {
376409
if inner.free_slots() > self.event_count && inner.free_space() > self.pending.len() {
377410
None
378411
} else if let Some(work) = inner.pending.first() {
379-
Some(work.get_fence())
412+
Some(work.inner.get_fence())
380413
} else {
381414
pr_err!(
382415
"WorkQueue: Cannot submit, but queue is empty? {} > {}, {} > {} (pend={} ls={:#x?} lc={:#x?}) ev={:#x?} cur={:#x?} slot {:?}\n",
@@ -454,11 +487,11 @@ impl Job::ver {
454487
);
455488

456489
for mut command in self.pending.drain(..) {
457-
command.set_wptr(wptr);
490+
command.as_mut().inner_mut().set_wptr(wptr);
458491

459492
let next_wptr = (wptr + 1) % inner.size;
460493
assert!(inner.doneptr() != next_wptr);
461-
inner.info.ring[wptr as usize] = command.gpu_va().get();
494+
inner.info.ring[wptr as usize] = command.inner.gpu_va().get();
462495
wptr = next_wptr;
463496

464497
// Cannot fail, since we did a reserve(1) above
@@ -863,11 +896,11 @@ impl WorkQueue for WorkQueue::ver {
863896
let mut completed_commands: usize = 0;
864897

865898
for cmd in inner.pending.iter() {
866-
if cmd.value() <= value {
899+
if cmd.inner.value() <= value {
867900
mod_pr_debug!(
868901
"WorkQueue({:?}): Command at value {:#x?} complete\n",
869902
inner.pipe_type,
870-
cmd.value()
903+
cmd.inner.value()
871904
);
872905
completed_commands += 1;
873906
} else {
@@ -879,25 +912,17 @@ impl WorkQueue for WorkQueue::ver {
879912
return inner.pending.is_empty();
880913
}
881914

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

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

903928
mod_pr_debug!(
@@ -909,12 +934,10 @@ impl WorkQueue for WorkQueue::ver {
909934
inner.last_completed,
910935
);
911936

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

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

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

@@ -961,8 +974,8 @@ impl WorkQueue for WorkQueue::ver {
961974
);
962975

963976
for cmd in inner.pending.iter_mut() {
964-
if cmd.value() <= value {
965-
cmd.mark_error(error);
977+
if cmd.inner.value() <= value {
978+
cmd.as_mut().inner_mut().mark_error(error);
966979
} else {
967980
break;
968981
}
@@ -1003,8 +1016,8 @@ impl WorkQueue for WorkQueue::ver {
10031016
core::mem::drop(inner);
10041017

10051018
for mut cmd in cmds {
1006-
cmd.mark_error(error);
1007-
cmd.complete();
1019+
cmd.as_mut().inner_mut().mark_error(error);
1020+
cmd.as_mut().inner_mut().complete();
10081021
}
10091022
}
10101023
}

0 commit comments

Comments
 (0)