The file drivers/dma-buf/dma-fence-unwrap.c was incorrectly added to
the 5.15.y stable branch in commit 4e82b9c11d3c ("dma-buf: add
dma_fence_timestamp helper") as a new file, but it was never enabled in
the Makefile, and its header include/linux/dma-fence-unwrap.h was not
present, making it uncompilable.
A full revert of commit 4e82b9c11d3c ("dma-buf: add dma_fence_timestamp
helper") is not desirable because that commit also introduced the valid
dma_fence_timestamp() helper and fixed legitimate timestamp race
windows in drivers/dma-buf/sync_file.c and
drivers/gpu/drm/scheduler/sched_main.c.
Since there are no users of dma-fence-unwrap in the 5.15.y branch,
remove the unused file to clean up the tree and avoid confusion.
Fixes: 4e82b9c11d3c ("dma-buf: add dma_fence_timestamp helper")
Signed-off-by: Tudor Ambarus <tudor.ambarus(a)linaro.org>
---
drivers/dma-buf/dma-fence-unwrap.c | 176 -------------------------------------
1 file changed, 176 deletions(-)
diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c
deleted file mode 100644
index 628af51c81af..000000000000
--- a/drivers/dma-buf/dma-fence-unwrap.c
+++ /dev/null
@@ -1,176 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * dma-fence-util: misc functions for dma_fence objects
- *
- * Copyright (C) 2022 Advanced Micro Devices, Inc.
- * Authors:
- * Christian König <christian.koenig(a)amd.com>
- */
-
-#include <linux/dma-fence.h>
-#include <linux/dma-fence-array.h>
-#include <linux/dma-fence-chain.h>
-#include <linux/dma-fence-unwrap.h>
-#include <linux/slab.h>
-
-/* Internal helper to start new array iteration, don't use directly */
-static struct dma_fence *
-__dma_fence_unwrap_array(struct dma_fence_unwrap *cursor)
-{
- cursor->array = dma_fence_chain_contained(cursor->chain);
- cursor->index = 0;
- return dma_fence_array_first(cursor->array);
-}
-
-/**
- * dma_fence_unwrap_first - return the first fence from fence containers
- * @head: the entrypoint into the containers
- * @cursor: current position inside the containers
- *
- * Unwraps potential dma_fence_chain/dma_fence_array containers and return the
- * first fence.
- */
-struct dma_fence *dma_fence_unwrap_first(struct dma_fence *head,
- struct dma_fence_unwrap *cursor)
-{
- cursor->chain = dma_fence_get(head);
- return __dma_fence_unwrap_array(cursor);
-}
-EXPORT_SYMBOL_GPL(dma_fence_unwrap_first);
-
-/**
- * dma_fence_unwrap_next - return the next fence from a fence containers
- * @cursor: current position inside the containers
- *
- * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return
- * the next fence from them.
- */
-struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor)
-{
- struct dma_fence *tmp;
-
- ++cursor->index;
- tmp = dma_fence_array_next(cursor->array, cursor->index);
- if (tmp)
- return tmp;
-
- cursor->chain = dma_fence_chain_walk(cursor->chain);
- return __dma_fence_unwrap_array(cursor);
-}
-EXPORT_SYMBOL_GPL(dma_fence_unwrap_next);
-
-/* Implementation for the dma_fence_merge() marco, don't use directly */
-struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
- struct dma_fence **fences,
- struct dma_fence_unwrap *iter)
-{
- struct dma_fence_array *result;
- struct dma_fence *tmp, **array;
- ktime_t timestamp;
- unsigned int i;
- size_t count;
-
- count = 0;
- timestamp = ns_to_ktime(0);
- for (i = 0; i < num_fences; ++i) {
- dma_fence_unwrap_for_each(tmp, &iter[i], fences[i]) {
- if (!dma_fence_is_signaled(tmp)) {
- ++count;
- } else {
- ktime_t t = dma_fence_timestamp(tmp);
-
- if (ktime_after(t, timestamp))
- timestamp = t;
- }
- }
- }
-
- /*
- * If we couldn't find a pending fence just return a private signaled
- * fence with the timestamp of the last signaled one.
- */
- if (count == 0)
- return dma_fence_allocate_private_stub(timestamp);
-
- array = kmalloc_array(count, sizeof(*array), GFP_KERNEL);
- if (!array)
- return NULL;
-
- /*
- * This trashes the input fence array and uses it as position for the
- * following merge loop. This works because the dma_fence_merge()
- * wrapper macro is creating this temporary array on the stack together
- * with the iterators.
- */
- for (i = 0; i < num_fences; ++i)
- fences[i] = dma_fence_unwrap_first(fences[i], &iter[i]);
-
- count = 0;
- do {
- unsigned int sel;
-
-restart:
- tmp = NULL;
- for (i = 0; i < num_fences; ++i) {
- struct dma_fence *next;
-
- while (fences[i] && dma_fence_is_signaled(fences[i]))
- fences[i] = dma_fence_unwrap_next(&iter[i]);
-
- next = fences[i];
- if (!next)
- continue;
-
- /*
- * We can't guarantee that inpute fences are ordered by
- * context, but it is still quite likely when this
- * function is used multiple times. So attempt to order
- * the fences by context as we pass over them and merge
- * fences with the same context.
- */
- if (!tmp || tmp->context > next->context) {
- tmp = next;
- sel = i;
-
- } else if (tmp->context < next->context) {
- continue;
-
- } else if (dma_fence_is_later(tmp, next)) {
- fences[i] = dma_fence_unwrap_next(&iter[i]);
- goto restart;
- } else {
- fences[sel] = dma_fence_unwrap_next(&iter[sel]);
- goto restart;
- }
- }
-
- if (tmp) {
- array[count++] = dma_fence_get(tmp);
- fences[sel] = dma_fence_unwrap_next(&iter[sel]);
- }
- } while (tmp);
-
- if (count == 0) {
- tmp = dma_fence_allocate_private_stub(ktime_get());
- goto return_tmp;
- }
-
- if (count == 1) {
- tmp = array[0];
- goto return_tmp;
- }
-
- result = dma_fence_array_create(count, array,
- dma_fence_context_alloc(1),
- 1, false);
- if (!result) {
- tmp = NULL;
- goto return_tmp;
- }
- return &result->base;
-
-return_tmp:
- kfree(array);
- return tmp;
-}
-EXPORT_SYMBOL_GPL(__dma_fence_unwrap_merge);
---
base-commit: eceeec79dbc646d6dace49ed1ba2f656683d5537
change-id: 20260703-5-15-dma-fence-unwrap-0f4a6c03daf2
Best regards,
--
Tudor Ambarus <tudor.ambarus(a)linaro.org>
In the structs dma_fence_array and dma_fence_chain, the field 'lock'
has been removed, but its documentation comment remained. Remove the
stale descriptions to clear up the following kernel-doc warnings:
WARNING: ./include/linux/dma-fence-array.h:47 Excess struct member 'lock' description in 'dma_fence_array'
WARNING: ./include/linux/dma-fence-array.h:47 Excess struct member 'lock' description in 'dma_fence_array'
WARNING: ./include/linux/dma-fence-chain.h:48 Excess struct member 'lock' description in 'dma_fence_chain'
WARNING: ./include/linux/dma-fence-chain.h:48 Excess struct member 'lock' description in 'dma_fence_chain'
Fixes: 5943243914b9 ("dma-buf: use inline lock for the dma-fence-array")
Fixes: a408c0ca0c41 ("dma-buf: use inline lock for the dma-fence-chain")
Signed-off-by: Nicolás Antinori <nico.antinori.7(a)gmail.com>
---
include/linux/dma-fence-array.h | 1 -
include/linux/dma-fence-chain.h | 1 -
2 files changed, 2 deletions(-)
diff --git a/include/linux/dma-fence-array.h b/include/linux/dma-fence-array.h
index 1b1d87579c38..0c49d7ccefb6 100644
--- a/include/linux/dma-fence-array.h
+++ b/include/linux/dma-fence-array.h
@@ -28,7 +28,6 @@ struct dma_fence_array_cb {
/**
* struct dma_fence_array - fence to represent an array of fences
* @base: fence base class
- * @lock: spinlock for fence handling
* @num_fences: number of fences in the array
* @num_pending: fences in the array still pending
* @fences: array of the fences
diff --git a/include/linux/dma-fence-chain.h b/include/linux/dma-fence-chain.h
index df3beadf1515..42289f505164 100644
--- a/include/linux/dma-fence-chain.h
+++ b/include/linux/dma-fence-chain.h
@@ -20,7 +20,6 @@
* @prev: previous fence of the chain
* @prev_seqno: original previous seqno before garbage collection
* @fence: encapsulated fence
- * @lock: spinlock for fence handling
*/
struct dma_fence_chain {
struct dma_fence base;
--
2.47.3
Currently, `fill_sg_entry()` splits the scatterlist using `UINT_MAX`.
This creates a non-page-aligned DMA length (`0xFFFFFFFF`) for the
first entry, resulting in non-page-aligned DMA addresses for all
subsequent entries.
While the underlying IOMMU mapping may be contiguous, hardware
DMA engines often require explicit address alignment (e.g., page,
cacheline, or storage sector boundaries). Passing unaligned
addresses and lengths can cause explicit failures in DMA descriptor
creation or silent data corruption if lower unaligned bits are
truncated.
Fix this by splitting the scatterlist by the largest possible page
aligned chunk within `UINT_MAX` (`ALIGN_DOWN(UINT_MAX, PAGE_SIZE)`).
This ensures all scatterlist DMA addresses and lengths remain page
aligned and satisfy hardware constraints.
Page-aligned entries allow the system to cleanly chunk payloads into
PCIe MaxPayloadSize (MPS) (e.g., 128 bytes, 256 bytes, 512 bytes).
As a result, this may help reduce TLP fragmentation in P2P transfers
and alleviate potential congestion within a logical PCIe switch
partition, especially when Relaxed Ordering is not possible due to
hardware constraints.
Reported-by: sashiko-bot <sashiko-bot(a)kernel.org>
Closes: https://lore.kernel.org/all/20260609165431.778061F00893@smtp.kernel.org/
Fixes: 3aa31a8bb11e ("dma-buf: provide phys_vec to scatter-gather mapping routine")
Cc: stable(a)vger.kernel.org
Signed-off-by: David Hu <xuehaohu(a)google.com>
---
drivers/dma-buf/dma-buf-mapping.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/dma-buf/dma-buf-mapping.c b/drivers/dma-buf/dma-buf-mapping.c
index 794acff2546a..f2bde38fdb1f 100644
--- a/drivers/dma-buf/dma-buf-mapping.c
+++ b/drivers/dma-buf/dma-buf-mapping.c
@@ -5,6 +5,9 @@
*/
#include <linux/dma-buf-mapping.h>
#include <linux/dma-resv.h>
+#include <linux/align.h>
+
+#define MAX_ENT_SZ ALIGN_DOWN(UINT_MAX, PAGE_SIZE)
static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
dma_addr_t addr)
@@ -12,9 +15,9 @@ static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
unsigned int len, nents;
int i;
- nents = DIV_ROUND_UP(length, UINT_MAX);
+ nents = DIV_ROUND_UP(length, MAX_ENT_SZ);
for (i = 0; i < nents; i++) {
- len = min_t(size_t, length, UINT_MAX);
+ len = min_t(size_t, length, MAX_ENT_SZ);
length -= len;
/*
* DMABUF abuses scatterlist to create a scatterlist
@@ -24,7 +27,7 @@ static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
* does not require the CPU list for mapping or unmapping.
*/
sg_set_page(sgl, NULL, 0, 0);
- sg_dma_address(sgl) = addr + (dma_addr_t)i * UINT_MAX;
+ sg_dma_address(sgl) = addr + (dma_addr_t)i * MAX_ENT_SZ;
sg_dma_len(sgl) = len;
sgl = sg_next(sgl);
}
@@ -41,14 +44,14 @@ static unsigned int calc_sg_nents(struct dma_iova_state *state,
if (!state || !dma_use_iova(state)) {
for (i = 0; i < nr_ranges; i++)
- nents += DIV_ROUND_UP(phys_vec[i].len, UINT_MAX);
+ nents += DIV_ROUND_UP(phys_vec[i].len, MAX_ENT_SZ);
} else {
/*
* In IOVA case, there is only one SG entry which spans
* for whole IOVA address space, but we need to make sure
* that it fits sg->length, maybe we need more.
*/
- nents = DIV_ROUND_UP(size, UINT_MAX);
+ nents = DIV_ROUND_UP(size, MAX_ENT_SZ);
}
return nents;
--
2.55.0.rc0.738.g0c8ab3ebcc-goog
The current name of `as_ptr` is very generic, and if you attempt to
invoke `foo.as_ptr()` on a type for which this method is missing, then
an error along these lines will be printed:
error[E0599]: no method named `as_ptr` found for reference `&DmaBuf` in the current scope
--> linux/rust/kernel/dma_buf/buf.rs:54:38
|
54 | ptr::eq(self.as_ptr(), other.as_ptr())
| ^^^^^^ method not found in `&DmaBuf`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `device_id::IdTable` defines an item `as_ptr`, perhaps you need to implement it
--> linux/rust/kernel/device_id.rs:165:1
|
165 | pub trait IdTable<T: RawDeviceId, U> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Suggesting the IdTable trait when an as_ptr() method is missing is not
useful. Renaming it to `as_raw_id_table` makes the method name unique to
this trait and avoids these bad suggestions.
Assisted-by: Antigravity:Gemini
Signed-off-by: Alice Ryhl <aliceryhl(a)google.com>
---
rust/kernel/auxiliary.rs | 2 +-
rust/kernel/device_id.rs | 4 ++--
rust/kernel/driver.rs | 8 +++++---
rust/kernel/i2c.rs | 8 ++++----
rust/kernel/pci.rs | 2 +-
rust/kernel/platform.rs | 4 ++--
rust/kernel/usb.rs | 2 +-
7 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index c42928d5a239..fd6940577e0d 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -64,7 +64,7 @@ unsafe fn register(
(*adrv.get()).name = name.as_char_ptr();
(*adrv.get()).probe = Some(Self::probe_callback);
(*adrv.get()).remove = Some(Self::remove_callback);
- (*adrv.get()).id_table = T::ID_TABLE.as_ptr();
+ (*adrv.get()).id_table = T::ID_TABLE.as_raw_id_table();
}
// SAFETY: `adrv` is guaranteed to be a valid `DriverType`.
diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
index 8e9721446014..821da02540b1 100644
--- a/rust/kernel/device_id.rs
+++ b/rust/kernel/device_id.rs
@@ -164,7 +164,7 @@ impl<T: RawDeviceId + RawDeviceIdIndex, U, const N: usize> IdArray<T, U, N> {
/// `IdArray` doesn't matter.
pub trait IdTable<T: RawDeviceId, U> {
/// Obtain the pointer to the ID table.
- fn as_ptr(&self) -> *const T::RawType;
+ fn as_raw_id_table(&self) -> *const T::RawType;
/// Obtain the pointer to the bus specific device ID from an index.
fn id(&self, index: usize) -> &T::RawType;
@@ -174,7 +174,7 @@ pub trait IdTable<T: RawDeviceId, U> {
}
impl<T: RawDeviceId, U, const N: usize> IdTable<T, U> for IdArray<T, U, N> {
- fn as_ptr(&self) -> *const T::RawType {
+ fn as_raw_id_table(&self) -> *const T::RawType {
// This cannot be `self.ids.as_ptr()`, as the return pointer must have correct provenance
// to access the sentinel.
core::ptr::from_ref(self).cast()
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index bf5ba0d27553..69068adcbdae 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -341,7 +341,8 @@ fn acpi_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
// SAFETY:
// - `table` has static lifetime, hence it's valid for read,
// - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`.
- let raw_id = unsafe { bindings::acpi_match_device(table.as_ptr(), dev.as_raw()) };
+ let raw_id =
+ unsafe { bindings::acpi_match_device(table.as_raw_id_table(), dev.as_raw()) };
if raw_id.is_null() {
None
@@ -374,7 +375,8 @@ fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
// SAFETY:
// - `table` has static lifetime, hence it's valid for read,
// - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`.
- let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) };
+ let raw_id =
+ unsafe { bindings::of_match_device(table.as_raw_id_table(), dev.as_raw()) };
if !raw_id.is_null() {
// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id`
@@ -404,7 +406,7 @@ fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
// - `adev` is a valid pointer to `acpi_device` or is null. It is guaranteed to be
// valid as long as `dev` is alive.
// - `table` has static lifetime, hence it's valid for read.
- if unsafe { acpi_of_match_device(adev, table.as_ptr(), &raw mut raw_id) } {
+ if unsafe { acpi_of_match_device(adev, table.as_raw_id_table(), &raw mut raw_id) } {
// SAFETY:
// - the function returns true, therefore `raw_id` has been set to a pointer to a
// valid `of_device_id`.
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 624b971ca8b0..920794d4089d 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -116,17 +116,17 @@ unsafe fn register(
);
let i2c_table = match T::I2C_ID_TABLE {
- Some(table) => table.as_ptr(),
+ Some(table) => table.as_raw_id_table(),
None => core::ptr::null(),
};
let of_table = match T::OF_ID_TABLE {
- Some(table) => table.as_ptr(),
+ Some(table) => table.as_raw_id_table(),
None => core::ptr::null(),
};
let acpi_table = match T::ACPI_ID_TABLE {
- Some(table) => table.as_ptr(),
+ Some(table) => table.as_raw_id_table(),
None => core::ptr::null(),
};
@@ -208,7 +208,7 @@ fn i2c_id_info(dev: &I2cClient) -> Option<&'static <Self as driver::Adapter>::Id
// SAFETY:
// - `table` has static lifetime, hence it's valid for reads
// - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`.
- let raw_id = unsafe { bindings::i2c_match_id(table.as_ptr(), dev.as_raw()) };
+ let raw_id = unsafe { bindings::i2c_match_id(table.as_raw_id_table(), dev.as_raw()) };
if raw_id.is_null() {
return None;
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 5071cae6543f..311b4716800b 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -81,7 +81,7 @@ unsafe fn register(
(*pdrv.get()).name = name.as_char_ptr();
(*pdrv.get()).probe = Some(Self::probe_callback);
(*pdrv.get()).remove = Some(Self::remove_callback);
- (*pdrv.get()).id_table = T::ID_TABLE.as_ptr();
+ (*pdrv.get()).id_table = T::ID_TABLE.as_raw_id_table();
}
// SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 9b362e0495d3..f6a48f7750da 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -63,12 +63,12 @@ unsafe fn register(
module: &'static ThisModule,
) -> Result {
let of_table = match T::OF_ID_TABLE {
- Some(table) => table.as_ptr(),
+ Some(table) => table.as_raw_id_table(),
None => core::ptr::null(),
};
let acpi_table = match T::ACPI_ID_TABLE {
- Some(table) => table.as_ptr(),
+ Some(table) => table.as_raw_id_table(),
None => core::ptr::null(),
};
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index 7aff0c82d0af..14e0602c3f03 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -58,7 +58,7 @@ unsafe fn register(
(*udrv.get()).name = name.as_char_ptr();
(*udrv.get()).probe = Some(Self::probe_callback);
(*udrv.get()).disconnect = Some(Self::disconnect_callback);
- (*udrv.get()).id_table = T::ID_TABLE.as_ptr();
+ (*udrv.get()).id_table = T::ID_TABLE.as_raw_id_table();
}
// SAFETY: `udrv` is guaranteed to be a valid `DriverType`.
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260702-idtable-rename-asptr-3ea4d9e38287
Best regards,
--
Alice Ryhl <aliceryhl(a)google.com>
In a recent discussion with Philip and Danilo the question came up what
was already tried and never finished to cleanup the dma_fence framework.
So here are the different ideas I came with but never fully finished,
with the patches itself modernized and rebased on top of drm-misc-next.
The main goal of those changes is to make it easier to implement dma_fence
backends and don't enforce unnecessary constrains on implementations.
As first step the locking around the dma_fence_ops.signaled callback is
made consistent by removing the dma_fence_is_signaled_locked() function.
This was mostly used by backends itself, but if polling the HW is desired
the backends can call their own functions for this directly without going
through the dma-fence layer.
XE actually seems to be the only driver which make use of that for a bit
more handling. For all other cases testing the signaled flag should be enough.
Then forcefully calling dma_fence_signaled() is removed from the dma-fence
layer and moved into the backend implementations.
This allows the backend implementations to cleanup after they have
signaled the fence. Such cleanup can include removing now signaled fences
from lists, dropping references, starting work etc....
Especially nouveau seems to have some really messy workaround because of
that involving the DMA_FENCE_FLAG_USER_BITS and installing callbacks
because the reference to the context couldn't be dropped directly after
signaling. This can now be cleaned up as far as I can see.
In the long term this should also allow reworking the error handling, e.g.
removing dma_fence_set_error() and instead giving the error as mandatory
parameter to dma_fence_signal().
Then the last piece is dropping calling enable_signaling callback with the
dma_fence lock held. This makes it possible for backends to acquire locks
which are semantically ordered outside of the dma_fence lock.
This is necessary to allows using the dma_fence inline lock in more cases,
previously backends used some common external lock for their dma_fences to
for example make it possible remove fences from linked lists.
Please comment and review,
Christian.
The entity->last_scheduled field has always been set and read with
special RCU functions in addition to memory barriers. There is no
obvious reason for that, since the entity lock is available and taken at
all places that evaluate the last_scheduled field. The only exception is
drm_sched_entity_error(), which is not performance critical in any way.
Improve robustness, readability and maintainability by replacing RCU and
barriers with the lock.
As a preparational step, while at it, also guard spsc_queue_pop() with
the lock, since spsc_queue is deprecated and supposed to be replaced
with a locked list.
Signed-off-by: Philipp Stanner <phasta(a)kernel.org>
---
Changes since v1:
- Add a helper variable to drop the last_scheduled reference without
the entity lock being held; just to be more robust.
- Write additional comment to detail the WRITE_ONCE().
---
drivers/gpu/drm/scheduler/sched_entity.c | 58 +++++++++++++-----------
include/drm/gpu_scheduler.h | 9 ++--
2 files changed, 35 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
index c51101ec70c1..12fd695c6d46 100644
--- a/drivers/gpu/drm/scheduler/sched_entity.c
+++ b/drivers/gpu/drm/scheduler/sched_entity.c
@@ -135,7 +135,6 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
entity->num_sched_list = num_sched_list;
entity->sched_list = num_sched_list > 1 ? sched_list : NULL;
entity->rq = &sched_list[0]->rq;
- RCU_INIT_POINTER(entity->last_scheduled, NULL);
RB_CLEAR_NODE(&entity->rb_tree_node);
init_completion(&entity->entity_idle);
@@ -201,10 +200,10 @@ int drm_sched_entity_error(struct drm_sched_entity *entity)
struct dma_fence *fence;
int r;
- rcu_read_lock();
- fence = rcu_dereference(entity->last_scheduled);
+ spin_lock(&entity->lock);
+ fence = entity->last_scheduled;
r = fence ? fence->error : 0;
- rcu_read_unlock();
+ spin_unlock(&entity->lock);
return r;
}
@@ -288,8 +287,10 @@ void drm_sched_entity_kill(struct drm_sched_entity *entity)
wait_for_completion(&entity->entity_idle);
/* The entity is guaranteed to not be used by the scheduler */
- prev = rcu_dereference_check(entity->last_scheduled, true);
+ spin_lock(&entity->lock);
+ prev = entity->last_scheduled;
dma_fence_get(prev);
+ spin_unlock(&entity->lock);
while ((job = drm_sched_entity_queue_pop(entity))) {
struct drm_sched_fence *s_fence = job->s_fence;
@@ -381,8 +382,12 @@ void drm_sched_entity_fini(struct drm_sched_entity *entity)
entity->dependency = NULL;
}
- dma_fence_put(rcu_dereference_check(entity->last_scheduled, true));
- RCU_INIT_POINTER(entity->last_scheduled, NULL);
+ dma_fence_put(entity->last_scheduled);
+ /*
+ * Normally all users should be gone now, but since drm_sched has
+ * experienced many layering violations in the past, better be safe.
+ */
+ WRITE_ONCE(entity->last_scheduled, NULL);
drm_sched_entity_stats_put(entity->stats);
}
EXPORT_SYMBOL(drm_sched_entity_fini);
@@ -507,6 +512,10 @@ drm_sched_job_dependency(struct drm_sched_job *job,
struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity)
{
+ /* Helper to avoid dropping the reference while the entity lock is held,
+ * just to have some more robustness.
+ */
+ struct dma_fence *prev_last_scheduled;
struct drm_sched_job *sched_job;
sched_job = drm_sched_entity_queue_peek(entity);
@@ -523,19 +532,20 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity)
if (entity->guilty && atomic_read(entity->guilty))
dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED);
- dma_fence_put(rcu_dereference_check(entity->last_scheduled, true));
- rcu_assign_pointer(entity->last_scheduled,
- dma_fence_get(&sched_job->s_fence->finished));
+ spin_lock(&entity->lock);
+ prev_last_scheduled = entity->last_scheduled;
+ entity->last_scheduled = dma_fence_get(&sched_job->s_fence->finished);
- /*
- * If the queue is empty we allow drm_sched_entity_select_rq() to
- * locklessly access ->last_scheduled. This only works if we set the
- * pointer before we dequeue and if we a write barrier here.
+ /* A recent rework required taking the spinlock above. Since spsc_queue
+ * is scheduled for removal as per the DRM-TODO-list, we access it here
+ * locked already to prepare for that cleanup.
+ *
+ * TODO: Fully replace spsc_queue with a locked (h)list.
*/
- smp_wmb();
-
spsc_queue_pop(&entity->job_queue);
+ spin_unlock(&entity->lock);
+ dma_fence_put(prev_last_scheduled);
drm_sched_rq_pop_entity(entity);
/* Jobs and entities might have different lifecycles. Since we're
@@ -561,21 +571,15 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
if (spsc_queue_count(&entity->job_queue))
return;
- /*
- * Only when the queue is empty are we guaranteed that
- * drm_sched_run_job_work() cannot change entity->last_scheduled. To
- * enforce ordering we need a read barrier here. See
- * drm_sched_entity_pop_job() for the other side.
- */
- smp_rmb();
-
- fence = rcu_dereference_check(entity->last_scheduled, true);
+ spin_lock(&entity->lock);
+ fence = entity->last_scheduled;
/* stay on the same engine if the previous job hasn't finished */
- if (fence && !dma_fence_is_signaled(fence))
+ if (fence && !dma_fence_is_signaled(fence)) {
+ spin_unlock(&entity->lock);
return;
+ }
- spin_lock(&entity->lock);
sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list);
rq = sched ? &sched->rq : NULL;
if (rq != entity->rq) {
diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h
index d61c19e78182..176ff1f936cd 100644
--- a/include/drm/gpu_scheduler.h
+++ b/include/drm/gpu_scheduler.h
@@ -100,7 +100,8 @@ struct drm_sched_entity {
* @lock:
*
* Lock protecting the run-queue (@rq) to which this entity belongs,
- * @priority and the list of schedulers (@sched_list, @num_sched_list).
+ * @priority, @last_scheduled and the list of schedulers (@sched_list,
+ * @num_sched_list).
*/
spinlock_t lock;
@@ -202,11 +203,9 @@ struct drm_sched_entity {
/**
* @last_scheduled:
*
- * Points to the finished fence of the last scheduled job. Only written
- * by drm_sched_entity_pop_job(). Can be accessed locklessly from
- * drm_sched_job_arm() if the queue is empty.
+ * Points to the finished fence of the last scheduled job.
*/
- struct dma_fence __rcu *last_scheduled;
+ struct dma_fence *last_scheduled;
/**
* @last_user: last group leader pushing a job into the entity.
--
2.54.0
Ever found yourself yearning for a game that’s easy to pick up but incredibly challenging to master? A game that tests your reflexes, your rhythm, and your patience, all while delivering a surprisingly addictive experience? Look no further than geometry dash lite. This free-to-play mobile sensation, a trimmed-down version of the full Geometry Dash game, offers a fantastic entry point into a world of pulsing electronic music, geometric obstacles, and an almost zen-like focus on timing. Whether you're a seasoned gamer or just looking for a fun way to pass the time, Geometry Dash Lite provides an exhilarating ride.
https://geometrylitepc.com/
The Rhythmic Heartbeat: What is Geometry Dash Lite?
At its core, Geometry Dash Lite is a rhythm-based platformer. You control a small, customizable icon (usually a square, but you can unlock others) that automatically moves forward through a series of levels. Your sole objective is to navigate these levels, avoiding an onslaught of spikes, sawblades, and other cleverly designed traps, all perfectly synced to an energetic soundtrack. The game's beauty lies in its simplicity: tap to jump. That's it. But within this simple mechanic lies a world of nuanced timing, precise execution, and a surprising amount of strategic thinking.
The "Lite" version of the game offers a selection of levels from the full Geometry Dash experience, allowing players to get a taste of what the full game has to offer without any initial commitment. It's a perfect sampler, showcasing the core mechanics, the diverse level design, and the infectious music that defines the series. For those curious to explore further or even play on their PC, you can find more information about Geometry Dash Lite at Geometry Dash Lite
Gameplay: A Symphony of Taps and Jumps
The gameplay loop of Geometry Dash Lite is incredibly straightforward, yet it offers a satisfying and often frustrating challenge.
Your Icon: Your main character is a small geometric shape that continuously moves forward. Its movement speed is constant within a given segment of a level.
The Obstacles: Levels are littered with various obstacles, primarily spikes and sawblades, designed to instantly destroy your icon upon contact.
The Tap to Jump: Your only control is to tap the screen. A single tap initiates a small jump. Holding down your finger allows for a longer jump. This simple interaction is the key to navigating the complex layouts.
Portals and Gravity: As you progress, you'll encounter various portals that change your icon's behavior. These can include:
Gravity Portals: These flip your icon's gravity, making it move on the ceiling instead of the floor, or vice versa.
Ship Portals: Your icon transforms into a ship, allowing you to fly freely by holding down your finger to ascend and releasing to descend. This introduces a whole new dimension of control.
Ball Portals: Your icon becomes a ball, which rolls and can stick to surfaces when you tap, creating unique platforming opportunities.
UFO Portals: These transform your icon into a UFO, allowing for a "flappy bird" like tapping mechanic to ascend.
Wave Portals: Your icon becomes a wave, moving diagonally up and down with taps, requiring very precise timing.
Music Synchronization: Every jump, every obstacle, and every portal change is meticulously synchronized with the background music. This is not just an aesthetic choice; it’s a crucial gameplay element. Learning the rhythm of each level is as important as memorizing its layout.
Practice Mode: One of the most player-friendly features is the practice mode. In this mode, you can place checkpoints anywhere in the level, allowing you to repeatedly attempt difficult sections without having to restart from the beginning. This is invaluable for learning and improving.
Attempts Counter: A relentless counter at the top of the screen tracks how many attempts it takes you to complete a level. This often fuels a competitive spirit and a desire to achieve fewer attempts.
Tips for Conquering the Geometric Gauntlet
While Geometry Dash Lite can feel overwhelming at first, a few key strategies can significantly improve your experience and help you conquer even the most daunting levels.
1. Start Slow and Steady: Don't jump into the hardest level immediately. Begin with the introductory levels to get a feel for the controls, the different portal mechanics, and the rhythm. "Stereo Madness" is a classic starting point.
2. Embrace Practice Mode: This cannot be stressed enough. When you hit a wall, switch to practice mode. Break down the level into smaller, manageable segments. Practice each difficult section until you can consistently clear it.
3. Listen to the Music: The music isn't just background noise; it's your guide. Pay close attention to the beats, the drops, and the changes in tempo. Often, the timing of your jumps will align perfectly with the musical cues.
4. Memorization is Key: While reflexes are important, Geometry Dash Lite is also a memory game. As you repeatedly play a level, you'll start to internalize the layout and the sequence of obstacles.
5. Identify Patterns: Many levels feature recurring patterns of obstacles. Once you recognize these patterns, you can apply the same strategy to clear them each time they appear.
6. Don't Get Frustrated (Easily): This game will test your patience. You will die countless times on the same obstacle. Take a break if you feel yourself getting angry. Come back with a fresh perspective. Sometimes, a short break is all it takes to clear a section you were stuck on.
7. Watch Others Play: If you're truly stuck, watching videos of experienced players completing the level can provide valuable insights into optimal timing and strategies.
8. Customize Your Icon: While purely aesthetic, customizing your icon can make the experience feel more personal and enjoyable. Unlock new colors and shapes as you progress.
9. Focus on Small Victories: Don't aim to complete the entire level in one go initially. Celebrate clearing a new section, reaching a further point, or successfully navigating a tricky portal sequence.
Conclusion: A Rewarding Test of Skill and Rhythm
Geometry Dash Lite is more than just a simple mobile game; it's a testament to the power of straightforward mechanics combined with clever design. It offers a rewarding challenge that, while demanding, never feels truly unfair. The satisfaction of finally completing a difficult level, feeling your fingers dance across the screen in perfect sync with the music, is an experience few other games can replicate. So, if you're looking for a game that will push your reflexes and your rhythm to their limits, all while delivering a surprisingly addictive and fun experience, dive into the world of Geometry Dash Lite. You might just find your next obsession.
Assignment help provides students with guidance, resources, and support to complete academic tasks effectively. It improves understanding of subjects, enhances research and writing skills, and helps meet deadlines. Professional assistance can reduce stress, improve grades, and encourage learning by offering clear explanations, structured solutions, and valuable academic insights.
https://allassignmenthelper.com/management-assignment-help/