* [PATCH v3 1/5] gpu: nova-core: gsp: fix incorrect advancing of write pointer
2026-01-29 7:44 [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Eliot Courtney
@ 2026-01-29 7:44 ` Eliot Courtney
2026-01-29 7:45 ` [PATCH v3 2/5] gpu: nova-core: gsp: clarify comments about invariants and pointer roles Eliot Courtney
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-01-29 7:44 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter, Alistair Popple
Cc: nouveau, rust-for-linux, dri-devel, linux-kernel, Eliot Courtney
We should modulo not bitwise-and here. The current code could, for
example, set wptr to MSGQ_NUM_PAGES which is not valid.
Fixes: 75f6b1de8133 ("gpu: nova-core: gsp: Add GSP command queue bindings and handling")
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 46819a82a51a..f139aad7af3f 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -384,7 +384,7 @@ fn cpu_write_ptr(&self) -> u32 {
// Informs the GSP that it can process `elem_count` new pages from the command queue.
fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
- let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES;
+ let wptr = self.cpu_write_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES;
let gsp_mem = self.0.start_ptr_mut();
// SAFETY:
--
2.52.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v3 2/5] gpu: nova-core: gsp: clarify comments about invariants and pointer roles
2026-01-29 7:44 [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Eliot Courtney
2026-01-29 7:44 ` [PATCH v3 1/5] gpu: nova-core: gsp: fix incorrect advancing of write pointer Eliot Courtney
@ 2026-01-29 7:45 ` Eliot Courtney
2026-01-29 7:45 ` [PATCH v3 3/5] gpu: nova-core: gsp: use empty slices instead of [0..0] ranges Eliot Courtney
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-01-29 7:45 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter, Alistair Popple
Cc: nouveau, rust-for-linux, dri-devel, linux-kernel, Eliot Courtney
Disambiguate a few things in comments in cmdq.rs.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index f139aad7af3f..0743597779f1 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -161,12 +161,14 @@ struct GspMem {
/// Self-mapping page table entries.
ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>,
/// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
- /// write and read pointers that the CPU updates.
+ /// write and read pointers that the CPU updates. This means that the read pointer here is an
+ /// index into the GSP queue.
///
/// This member is read-only for the GSP.
cpuq: Msgq,
/// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
- /// write and read pointers that the GSP updates.
+ /// write and read pointers that the GSP updates. This means that the read pointer here is an
+ /// index into the CPU queue.
///
/// This member is read-only for the driver.
gspq: Msgq,
@@ -222,7 +224,7 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
// - We will only access the driver-owned part of the shared memory.
// - Per the safety statement of the function, no concurrent access will be performed.
let gsp_mem = &mut unsafe { self.0.as_slice_mut(0, 1) }.unwrap()[0];
- // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `<= MSGQ_NUM_PAGES`.
+ // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `< MSGQ_NUM_PAGES`.
let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx);
if rx <= tx {
@@ -257,7 +259,7 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
// - We will only access the driver-owned part of the shared memory.
// - Per the safety statement of the function, no concurrent access will be performed.
let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0];
- // PANIC: per the invariant of `cpu_read_ptr`, `xx` is `<= MSGQ_NUM_PAGES`.
+ // PANIC: per the invariant of `cpu_read_ptr`, `rx` is `< MSGQ_NUM_PAGES`.
let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx);
match tx.cmp(&rx) {
@@ -315,7 +317,7 @@ fn allocate_command(&mut self, size: usize) -> Result<GspCommand<'_>> {
//
// # Invariants
//
- // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ // - The returned value is within `0..MSGQ_NUM_PAGES`.
fn gsp_write_ptr(&self) -> u32 {
let gsp_mem = self.0.start_ptr();
@@ -329,7 +331,7 @@ fn gsp_write_ptr(&self) -> u32 {
//
// # Invariants
//
- // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ // - The returned value is within `0..MSGQ_NUM_PAGES`.
fn gsp_read_ptr(&self) -> u32 {
let gsp_mem = self.0.start_ptr();
@@ -343,7 +345,7 @@ fn gsp_read_ptr(&self) -> u32 {
//
// # Invariants
//
- // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ // - The returned value is within `0..MSGQ_NUM_PAGES`.
fn cpu_read_ptr(&self) -> u32 {
let gsp_mem = self.0.start_ptr();
@@ -372,7 +374,7 @@ fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
//
// # Invariants
//
- // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ // - The returned value is within `0..MSGQ_NUM_PAGES`.
fn cpu_write_ptr(&self) -> u32 {
let gsp_mem = self.0.start_ptr();
--
2.52.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v3 3/5] gpu: nova-core: gsp: use empty slices instead of [0..0] ranges
2026-01-29 7:44 [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Eliot Courtney
2026-01-29 7:44 ` [PATCH v3 1/5] gpu: nova-core: gsp: fix incorrect advancing of write pointer Eliot Courtney
2026-01-29 7:45 ` [PATCH v3 2/5] gpu: nova-core: gsp: clarify comments about invariants and pointer roles Eliot Courtney
@ 2026-01-29 7:45 ` Eliot Courtney
2026-01-29 7:45 ` [PATCH v3 4/5] gpu: nova-core: gsp: fix improper handling of empty slot in cmdq Eliot Courtney
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-01-29 7:45 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter, Alistair Popple
Cc: nouveau, rust-for-linux, dri-devel, linux-kernel, Eliot Courtney
The current code unnecessarily uses, for example, &before_rx[0..0] to
return an empty slice. Instead, just use an empty slice.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 0743597779f1..b88ff8ebc098 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -232,7 +232,7 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
// to `rx`, minus one unit, belongs to the driver.
if rx == 0 {
let last = after_tx.len() - 1;
- (&mut after_tx[..last], &mut before_tx[0..0])
+ (&mut after_tx[..last], &mut [])
} else {
(after_tx, &mut before_tx[..rx])
}
@@ -241,7 +241,7 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
//
// PANIC: per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, `rx` and `tx` are
// `<= MSGQ_NUM_PAGES`, and the test above ensured that `rx > tx`.
- (after_tx.split_at_mut(rx - tx).0, &mut before_tx[0..0])
+ (after_tx.split_at_mut(rx - tx).0, &mut [])
}
}
@@ -263,8 +263,8 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx);
match tx.cmp(&rx) {
- cmp::Ordering::Equal => (&after_rx[0..0], &after_rx[0..0]),
- cmp::Ordering::Greater => (&after_rx[..tx], &before_rx[0..0]),
+ cmp::Ordering::Equal => (&[], &[]),
+ cmp::Ordering::Greater => (&after_rx[..tx], &[]),
cmp::Ordering::Less => (after_rx, &before_rx[..tx]),
}
}
--
2.52.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v3 4/5] gpu: nova-core: gsp: fix improper handling of empty slot in cmdq
2026-01-29 7:44 [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Eliot Courtney
` (2 preceding siblings ...)
2026-01-29 7:45 ` [PATCH v3 3/5] gpu: nova-core: gsp: use empty slices instead of [0..0] ranges Eliot Courtney
@ 2026-01-29 7:45 ` Eliot Courtney
2026-01-29 7:45 ` [PATCH v3 5/5] gpu: nova-core: gsp: fix improper indexing in driver_read_area Eliot Courtney
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-01-29 7:45 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter, Alistair Popple
Cc: nouveau, rust-for-linux, dri-devel, linux-kernel, Eliot Courtney
The current code hands out buffers that go all the way up to and
including `rx - 1`, but we need to maintain an empty slot to prevent the
ring buffer from wrapping around into having 'tx == rx', which means
empty.
Also add more rigorous no-panic proofs.
Fixes: 75f6b1de8133 ("gpu: nova-core: gsp: Add GSP command queue bindings and handling")
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 34 ++++++++++++++++++++--------------
1 file changed, 20 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index b88ff8ebc098..333bf0125d74 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -227,21 +227,27 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
// PANIC: per the invariant of `cpu_write_ptr`, `tx` is `< MSGQ_NUM_PAGES`.
let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx);
- if rx <= tx {
- // The area from `tx` up to the end of the ring, and from the beginning of the ring up
- // to `rx`, minus one unit, belongs to the driver.
- if rx == 0 {
- let last = after_tx.len() - 1;
- (&mut after_tx[..last], &mut [])
- } else {
- (after_tx, &mut before_tx[..rx])
- }
+ // The area starting at `tx` and ending at `rx - 2` modulo MSGQ_NUM_PAGES, inclusive,
+ // belongs to the driver for writing.
+
+ if rx == 0 {
+ // Since `rx` is zero, leave an empty slot at end of the buffer.
+ let last = after_tx.len() - 1;
+ (&mut after_tx[..last], &mut [])
+ } else if rx <= tx {
+ // The area is discontiguous and we leave an empty slot before `rx`.
+ // PANIC:
+ // - The index `rx - 1` is non-negative because `rx != 0` in this branch.
+ // - The index does not exceed `before_tx.len()` (which equals `tx`) because
+ // `rx <= tx` in this branch.
+ (after_tx, &mut before_tx[..(rx - 1)])
} else {
- // The area from `tx` to `rx`, minus one unit, belongs to the driver.
- //
- // PANIC: per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, `rx` and `tx` are
- // `<= MSGQ_NUM_PAGES`, and the test above ensured that `rx > tx`.
- (after_tx.split_at_mut(rx - tx).0, &mut [])
+ // The area is contiguous and we leave an empty slot before `rx`.
+ // PANIC:
+ // - The index `rx - tx - 1` is non-negative because `rx > tx` in this branch.
+ // - The index does not exceed `after_tx.len()` (which is `MSGQ_NUM_PAGES - tx`)
+ // because `rx < MSGQ_NUM_PAGES` by the `gsp_read_ptr` invariant.
+ (&mut after_tx[..(rx - tx - 1)], &mut [])
}
}
--
2.52.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v3 5/5] gpu: nova-core: gsp: fix improper indexing in driver_read_area
2026-01-29 7:44 [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Eliot Courtney
` (3 preceding siblings ...)
2026-01-29 7:45 ` [PATCH v3 4/5] gpu: nova-core: gsp: fix improper handling of empty slot in cmdq Eliot Courtney
@ 2026-01-29 7:45 ` Eliot Courtney
2026-01-29 14:21 ` [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Gary Guo
2026-02-06 7:29 ` Alexandre Courbot
6 siblings, 0 replies; 9+ messages in thread
From: Eliot Courtney @ 2026-01-29 7:45 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Alice Ryhl, David Airlie,
Simona Vetter, Alistair Popple
Cc: nouveau, rust-for-linux, dri-devel, linux-kernel, Eliot Courtney
The current code indexes into `after_rx` using `tx` which is an index
for the whole buffer, not the split buffer `after_rx`.
Also add more rigorous no-panic proofs.
Fixes: 75f6b1de8133 ("gpu: nova-core: gsp: Add GSP command queue bindings and handling")
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/cmdq.rs | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 333bf0125d74..16895f5281b7 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
use core::{
- cmp,
mem,
sync::atomic::{
fence,
@@ -265,13 +264,19 @@ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
// - We will only access the driver-owned part of the shared memory.
// - Per the safety statement of the function, no concurrent access will be performed.
let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0];
- // PANIC: per the invariant of `cpu_read_ptr`, `rx` is `< MSGQ_NUM_PAGES`.
- let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx);
-
- match tx.cmp(&rx) {
- cmp::Ordering::Equal => (&[], &[]),
- cmp::Ordering::Greater => (&after_rx[..tx], &[]),
- cmp::Ordering::Less => (after_rx, &before_rx[..tx]),
+ let data = &gsp_mem.gspq.msgq.data;
+
+ // The area starting at `rx` and ending at `tx - 1` modulo MSGQ_NUM_PAGES, inclusive,
+ // belongs to the driver for reading.
+ // PANIC:
+ // - per the invariant of `cpu_read_ptr`, `rx < MSGQ_NUM_PAGES`
+ // - per the invariant of `gsp_write_ptr`, `tx < MSGQ_NUM_PAGES`
+ if rx <= tx {
+ // The area is contiguous.
+ (&data[rx..tx], &[])
+ } else {
+ // The area is discontiguous.
+ (&data[rx..], &data[..tx])
}
}
--
2.52.0
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs
2026-01-29 7:44 [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Eliot Courtney
` (4 preceding siblings ...)
2026-01-29 7:45 ` [PATCH v3 5/5] gpu: nova-core: gsp: fix improper indexing in driver_read_area Eliot Courtney
@ 2026-01-29 14:21 ` Gary Guo
2026-02-06 7:29 ` Alexandre Courbot
6 siblings, 0 replies; 9+ messages in thread
From: Gary Guo @ 2026-01-29 14:21 UTC (permalink / raw)
To: Eliot Courtney, Danilo Krummrich, Alexandre Courbot, Alice Ryhl,
David Airlie, Simona Vetter, Alistair Popple
Cc: nouveau, rust-for-linux, dri-devel, linux-kernel
On Thu Jan 29, 2026 at 7:44 AM GMT, Eliot Courtney wrote:
> This series fixes a few bugs in the GSP command queue ring buffer
> implementation in nova-core and also clarifies some of the comments.
>
> The ring buffer uses read and write pointers (rx/tx) to track which areas
> are available for the CPU vs the GSP to read/write into.
>
> In the ring buffers there were some indexing issues which could end up
> causing panics, so I fixed those and added more rigorous proofs of
> correctness in the panic comments.
>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
For the whole series:
Reviewed-by: Gary Guo <gary@garyguo.net>
Best,
Gary
> ---
> Changes in v3:
> - Use `&mut []` instead of `&mut xyz[0..0]` for empty slices.
> - Simplify driver_read_area by removing unnecessary split_at.
> - Use range notation for comments (e.g., `is within 0..MSGQ_NUM_PAGES`).
> - Fix up branch order to mirror existing code.
> - Link to v2: https://lore.kernel.org/r/20260123-nova-core-cmdq1-v2-0-e797ec1b714c@nvidia.com
>
> Changes in v2:
> - Modified PANIC comments to match existing style.
> - Link to v1: https://lore.kernel.org/r/20260122-nova-core-cmdq1-v1-0-7f8fe4683f11@nvidia.com
>
> ---
> Eliot Courtney (5):
> gpu: nova-core: gsp: fix incorrect advancing of write pointer
> gpu: nova-core: gsp: clarify comments about invariants and pointer roles
> gpu: nova-core: gsp: use empty slices instead of [0..0] ranges
> gpu: nova-core: gsp: fix improper handling of empty slot in cmdq
> gpu: nova-core: gsp: fix improper indexing in driver_read_area
>
> drivers/gpu/nova-core/gsp/cmdq.rs | 71 +++++++++++++++++++++++----------------
> 1 file changed, 42 insertions(+), 29 deletions(-)
> ---
> base-commit: 58d26d42818c0f8c9b334cc7cf318b43046e675f
> change-id: 20260121-nova-core-cmdq1-6aaa369824c4
>
> Best regards,
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs
2026-01-29 7:44 [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Eliot Courtney
` (5 preceding siblings ...)
2026-01-29 14:21 ` [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs Gary Guo
@ 2026-02-06 7:29 ` Alexandre Courbot
2026-02-25 1:00 ` Alexandre Courbot
6 siblings, 1 reply; 9+ messages in thread
From: Alexandre Courbot @ 2026-02-06 7:29 UTC (permalink / raw)
To: Eliot Courtney
Cc: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Alistair Popple, nouveau, rust-for-linux, dri-devel,
linux-kernel
On Thu Jan 29, 2026 at 4:44 PM JST, Eliot Courtney wrote:
> This series fixes a few bugs in the GSP command queue ring buffer
> implementation in nova-core and also clarifies some of the comments.
>
> The ring buffer uses read and write pointers (rx/tx) to track which areas
> are available for the CPU vs the GSP to read/write into.
>
> In the ring buffers there were some indexing issues which could end up
> causing panics, so I fixed those and added more rigorous proofs of
> correctness in the panic comments.
>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
Staged the series for pushing into `drm-rust-next` as soon as it
reopens. Thanks for these critical fixes!
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3 0/5] gpu: nova-core: gsp: fix command queue ring buffer bugs
2026-02-06 7:29 ` Alexandre Courbot
@ 2026-02-25 1:00 ` Alexandre Courbot
0 siblings, 0 replies; 9+ messages in thread
From: Alexandre Courbot @ 2026-02-25 1:00 UTC (permalink / raw)
To: Eliot Courtney
Cc: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Alistair Popple, nouveau, rust-for-linux, dri-devel,
linux-kernel
On Fri Feb 6, 2026 at 4:29 PM JST, Alexandre Courbot wrote:
> On Thu Jan 29, 2026 at 4:44 PM JST, Eliot Courtney wrote:
>> This series fixes a few bugs in the GSP command queue ring buffer
>> implementation in nova-core and also clarifies some of the comments.
>>
>> The ring buffer uses read and write pointers (rx/tx) to track which areas
>> are available for the CPU vs the GSP to read/write into.
>>
>> In the ring buffers there were some indexing issues which could end up
>> causing panics, so I fixed those and added more rigorous proofs of
>> correctness in the panic comments.
>>
>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>
> Staged the series for pushing into `drm-rust-next` as soon as it
> reopens. Thanks for these critical fixes!
Pushed into `drm-rust-next`.
^ permalink raw reply [flat|nested] 9+ messages in thread