mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 07:03:03 +02:00
Merge 6.14-rc4 into usb-next
We need the USB fixes in here as well for testing. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
commit
d3571faa1b
1
.mailmap
1
.mailmap
|
|
@ -735,6 +735,7 @@ Wolfram Sang <wsa@kernel.org> <w.sang@pengutronix.de>
|
|||
Wolfram Sang <wsa@kernel.org> <wsa@the-dreams.de>
|
||||
Yakir Yang <kuankuan.y@gmail.com> <ykk@rock-chips.com>
|
||||
Yanteng Si <si.yanteng@linux.dev> <siyanteng@loongson.cn>
|
||||
Ying Huang <huang.ying.caritas@gmail.com> <ying.huang@intel.com>
|
||||
Yusuke Goda <goda.yusuke@renesas.com>
|
||||
Zack Rusin <zack.rusin@broadcom.com> <zackr@vmware.com>
|
||||
Zhu Yanjun <zyjzyj2000@gmail.com> <yanjunz@nvidia.com>
|
||||
|
|
|
|||
|
|
@ -251,9 +251,7 @@ performance supported in `AMD CPPC Performance Capability <perf_cap_>`_).
|
|||
In some ASICs, the highest CPPC performance is not the one in the ``_CPC``
|
||||
table, so we need to expose it to sysfs. If boost is not active, but
|
||||
still supported, this maximum frequency will be larger than the one in
|
||||
``cpuinfo``. On systems that support preferred core, the driver will have
|
||||
different values for some cores than others and this will reflect the values
|
||||
advertised by the platform at bootup.
|
||||
``cpuinfo``.
|
||||
This attribute is read-only.
|
||||
|
||||
``amd_pstate_lowest_nonlinear_freq``
|
||||
|
|
|
|||
|
|
@ -114,8 +114,9 @@ patternProperties:
|
|||
table that specifies the PPID to LIODN mapping. Needed if the PAMU is
|
||||
used. Value is a 12 bit value where value is a LIODN ID for this JR.
|
||||
This property is normally set by boot firmware.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 0xfff
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- maximum: 0xfff
|
||||
|
||||
'^rtic@[0-9a-f]+$':
|
||||
type: object
|
||||
|
|
@ -186,8 +187,9 @@ patternProperties:
|
|||
Needed if the PAMU is used. Value is a 12 bit value where value
|
||||
is a LIODN ID for this JR. This property is normally set by boot
|
||||
firmware.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 0xfff
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- maximum: 0xfff
|
||||
|
||||
fsl,rtic-region:
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ examples:
|
|||
|
||||
uimage@100000 {
|
||||
reg = <0x0100000 0x200000>;
|
||||
compress = "lzma";
|
||||
compression = "lzma";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ properties:
|
|||
|
||||
fsl,liodn:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
maxItems: 2
|
||||
description: See pamu.txt. Two LIODN(s). DQRR LIODN (DLIODN) and Frame LIODN
|
||||
(FLIODN)
|
||||
|
||||
|
|
@ -69,6 +70,7 @@ patternProperties:
|
|||
type: object
|
||||
properties:
|
||||
fsl,liodn:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: See pamu.txt, PAMU property used for static LIODN assignment
|
||||
|
||||
fsl,iommu-parent:
|
||||
|
|
|
|||
|
|
@ -3,3 +3,853 @@
|
|||
=================
|
||||
Process Addresses
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
|
||||
Userland memory ranges are tracked by the kernel via Virtual Memory Areas or
|
||||
'VMA's of type :c:struct:`!struct vm_area_struct`.
|
||||
|
||||
Each VMA describes a virtually contiguous memory range with identical
|
||||
attributes, each described by a :c:struct:`!struct vm_area_struct`
|
||||
object. Userland access outside of VMAs is invalid except in the case where an
|
||||
adjacent stack VMA could be extended to contain the accessed address.
|
||||
|
||||
All VMAs are contained within one and only one virtual address space, described
|
||||
by a :c:struct:`!struct mm_struct` object which is referenced by all tasks (that is,
|
||||
threads) which share the virtual address space. We refer to this as the
|
||||
:c:struct:`!mm`.
|
||||
|
||||
Each mm object contains a maple tree data structure which describes all VMAs
|
||||
within the virtual address space.
|
||||
|
||||
.. note:: An exception to this is the 'gate' VMA which is provided by
|
||||
architectures which use :c:struct:`!vsyscall` and is a global static
|
||||
object which does not belong to any specific mm.
|
||||
|
||||
-------
|
||||
Locking
|
||||
-------
|
||||
|
||||
The kernel is designed to be highly scalable against concurrent read operations
|
||||
on VMA **metadata** so a complicated set of locks are required to ensure memory
|
||||
corruption does not occur.
|
||||
|
||||
.. note:: Locking VMAs for their metadata does not have any impact on the memory
|
||||
they describe nor the page tables that map them.
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
|
||||
* **mmap locks** - Each MM has a read/write semaphore :c:member:`!mmap_lock`
|
||||
which locks at a process address space granularity which can be acquired via
|
||||
:c:func:`!mmap_read_lock`, :c:func:`!mmap_write_lock` and variants.
|
||||
* **VMA locks** - The VMA lock is at VMA granularity (of course) which behaves
|
||||
as a read/write semaphore in practice. A VMA read lock is obtained via
|
||||
:c:func:`!lock_vma_under_rcu` (and unlocked via :c:func:`!vma_end_read`) and a
|
||||
write lock via :c:func:`!vma_start_write` (all VMA write locks are unlocked
|
||||
automatically when the mmap write lock is released). To take a VMA write lock
|
||||
you **must** have already acquired an :c:func:`!mmap_write_lock`.
|
||||
* **rmap locks** - When trying to access VMAs through the reverse mapping via a
|
||||
:c:struct:`!struct address_space` or :c:struct:`!struct anon_vma` object
|
||||
(reachable from a folio via :c:member:`!folio->mapping`). VMAs must be stabilised via
|
||||
:c:func:`!anon_vma_[try]lock_read` or :c:func:`!anon_vma_[try]lock_write` for
|
||||
anonymous memory and :c:func:`!i_mmap_[try]lock_read` or
|
||||
:c:func:`!i_mmap_[try]lock_write` for file-backed memory. We refer to these
|
||||
locks as the reverse mapping locks, or 'rmap locks' for brevity.
|
||||
|
||||
We discuss page table locks separately in the dedicated section below.
|
||||
|
||||
The first thing **any** of these locks achieve is to **stabilise** the VMA
|
||||
within the MM tree. That is, guaranteeing that the VMA object will not be
|
||||
deleted from under you nor modified (except for some specific fields
|
||||
described below).
|
||||
|
||||
Stabilising a VMA also keeps the address space described by it around.
|
||||
|
||||
Lock usage
|
||||
----------
|
||||
|
||||
If you want to **read** VMA metadata fields or just keep the VMA stable, you
|
||||
must do one of the following:
|
||||
|
||||
* Obtain an mmap read lock at the MM granularity via :c:func:`!mmap_read_lock` (or a
|
||||
suitable variant), unlocking it with a matching :c:func:`!mmap_read_unlock` when
|
||||
you're done with the VMA, *or*
|
||||
* Try to obtain a VMA read lock via :c:func:`!lock_vma_under_rcu`. This tries to
|
||||
acquire the lock atomically so might fail, in which case fall-back logic is
|
||||
required to instead obtain an mmap read lock if this returns :c:macro:`!NULL`,
|
||||
*or*
|
||||
* Acquire an rmap lock before traversing the locked interval tree (whether
|
||||
anonymous or file-backed) to obtain the required VMA.
|
||||
|
||||
If you want to **write** VMA metadata fields, then things vary depending on the
|
||||
field (we explore each VMA field in detail below). For the majority you must:
|
||||
|
||||
* Obtain an mmap write lock at the MM granularity via :c:func:`!mmap_write_lock` (or a
|
||||
suitable variant), unlocking it with a matching :c:func:`!mmap_write_unlock` when
|
||||
you're done with the VMA, *and*
|
||||
* Obtain a VMA write lock via :c:func:`!vma_start_write` for each VMA you wish to
|
||||
modify, which will be released automatically when :c:func:`!mmap_write_unlock` is
|
||||
called.
|
||||
* If you want to be able to write to **any** field, you must also hide the VMA
|
||||
from the reverse mapping by obtaining an **rmap write lock**.
|
||||
|
||||
VMA locks are special in that you must obtain an mmap **write** lock **first**
|
||||
in order to obtain a VMA **write** lock. A VMA **read** lock however can be
|
||||
obtained without any other lock (:c:func:`!lock_vma_under_rcu` will acquire then
|
||||
release an RCU lock to lookup the VMA for you).
|
||||
|
||||
This constrains the impact of writers on readers, as a writer can interact with
|
||||
one VMA while a reader interacts with another simultaneously.
|
||||
|
||||
.. note:: The primary users of VMA read locks are page fault handlers, which
|
||||
means that without a VMA write lock, page faults will run concurrent with
|
||||
whatever you are doing.
|
||||
|
||||
Examining all valid lock states:
|
||||
|
||||
.. table::
|
||||
|
||||
========= ======== ========= ======= ===== =========== ==========
|
||||
mmap lock VMA lock rmap lock Stable? Read? Write most? Write all?
|
||||
========= ======== ========= ======= ===== =========== ==========
|
||||
\- \- \- N N N N
|
||||
\- R \- Y Y N N
|
||||
\- \- R/W Y Y N N
|
||||
R/W \-/R \-/R/W Y Y N N
|
||||
W W \-/R Y Y Y N
|
||||
W W W Y Y Y Y
|
||||
========= ======== ========= ======= ===== =========== ==========
|
||||
|
||||
.. warning:: While it's possible to obtain a VMA lock while holding an mmap read lock,
|
||||
attempting to do the reverse is invalid as it can result in deadlock - if
|
||||
another task already holds an mmap write lock and attempts to acquire a VMA
|
||||
write lock that will deadlock on the VMA read lock.
|
||||
|
||||
All of these locks behave as read/write semaphores in practice, so you can
|
||||
obtain either a read or a write lock for each of these.
|
||||
|
||||
.. note:: Generally speaking, a read/write semaphore is a class of lock which
|
||||
permits concurrent readers. However a write lock can only be obtained
|
||||
once all readers have left the critical region (and pending readers
|
||||
made to wait).
|
||||
|
||||
This renders read locks on a read/write semaphore concurrent with other
|
||||
readers and write locks exclusive against all others holding the semaphore.
|
||||
|
||||
VMA fields
|
||||
^^^^^^^^^^
|
||||
|
||||
We can subdivide :c:struct:`!struct vm_area_struct` fields by their purpose, which makes it
|
||||
easier to explore their locking characteristics:
|
||||
|
||||
.. note:: We exclude VMA lock-specific fields here to avoid confusion, as these
|
||||
are in effect an internal implementation detail.
|
||||
|
||||
.. table:: Virtual layout fields
|
||||
|
||||
===================== ======================================== ===========
|
||||
Field Description Write lock
|
||||
===================== ======================================== ===========
|
||||
:c:member:`!vm_start` Inclusive start virtual address of range mmap write,
|
||||
VMA describes. VMA write,
|
||||
rmap write.
|
||||
:c:member:`!vm_end` Exclusive end virtual address of range mmap write,
|
||||
VMA describes. VMA write,
|
||||
rmap write.
|
||||
:c:member:`!vm_pgoff` Describes the page offset into the file, mmap write,
|
||||
the original page offset within the VMA write,
|
||||
virtual address space (prior to any rmap write.
|
||||
:c:func:`!mremap`), or PFN if a PFN map
|
||||
and the architecture does not support
|
||||
:c:macro:`!CONFIG_ARCH_HAS_PTE_SPECIAL`.
|
||||
===================== ======================================== ===========
|
||||
|
||||
These fields describes the size, start and end of the VMA, and as such cannot be
|
||||
modified without first being hidden from the reverse mapping since these fields
|
||||
are used to locate VMAs within the reverse mapping interval trees.
|
||||
|
||||
.. table:: Core fields
|
||||
|
||||
============================ ======================================== =========================
|
||||
Field Description Write lock
|
||||
============================ ======================================== =========================
|
||||
:c:member:`!vm_mm` Containing mm_struct. None - written once on
|
||||
initial map.
|
||||
:c:member:`!vm_page_prot` Architecture-specific page table mmap write, VMA write.
|
||||
protection bits determined from VMA
|
||||
flags.
|
||||
:c:member:`!vm_flags` Read-only access to VMA flags describing N/A
|
||||
attributes of the VMA, in union with
|
||||
private writable
|
||||
:c:member:`!__vm_flags`.
|
||||
:c:member:`!__vm_flags` Private, writable access to VMA flags mmap write, VMA write.
|
||||
field, updated by
|
||||
:c:func:`!vm_flags_*` functions.
|
||||
:c:member:`!vm_file` If the VMA is file-backed, points to a None - written once on
|
||||
struct file object describing the initial map.
|
||||
underlying file, if anonymous then
|
||||
:c:macro:`!NULL`.
|
||||
:c:member:`!vm_ops` If the VMA is file-backed, then either None - Written once on
|
||||
the driver or file-system provides a initial map by
|
||||
:c:struct:`!struct vm_operations_struct` :c:func:`!f_ops->mmap()`.
|
||||
object describing callbacks to be
|
||||
invoked on VMA lifetime events.
|
||||
:c:member:`!vm_private_data` A :c:member:`!void *` field for Handled by driver.
|
||||
driver-specific metadata.
|
||||
============================ ======================================== =========================
|
||||
|
||||
These are the core fields which describe the MM the VMA belongs to and its attributes.
|
||||
|
||||
.. table:: Config-specific fields
|
||||
|
||||
================================= ===================== ======================================== ===============
|
||||
Field Configuration option Description Write lock
|
||||
================================= ===================== ======================================== ===============
|
||||
:c:member:`!anon_name` CONFIG_ANON_VMA_NAME A field for storing a mmap write,
|
||||
:c:struct:`!struct anon_vma_name` VMA write.
|
||||
object providing a name for anonymous
|
||||
mappings, or :c:macro:`!NULL` if none
|
||||
is set or the VMA is file-backed. The
|
||||
underlying object is reference counted
|
||||
and can be shared across multiple VMAs
|
||||
for scalability.
|
||||
:c:member:`!swap_readahead_info` CONFIG_SWAP Metadata used by the swap mechanism mmap read,
|
||||
to perform readahead. This field is swap-specific
|
||||
accessed atomically. lock.
|
||||
:c:member:`!vm_policy` CONFIG_NUMA :c:type:`!mempolicy` object which mmap write,
|
||||
describes the NUMA behaviour of the VMA write.
|
||||
VMA. The underlying object is reference
|
||||
counted.
|
||||
:c:member:`!numab_state` CONFIG_NUMA_BALANCING :c:type:`!vma_numab_state` object which mmap read,
|
||||
describes the current state of numab-specific
|
||||
NUMA balancing in relation to this VMA. lock.
|
||||
Updated under mmap read lock by
|
||||
:c:func:`!task_numa_work`.
|
||||
:c:member:`!vm_userfaultfd_ctx` CONFIG_USERFAULTFD Userfaultfd context wrapper object of mmap write,
|
||||
type :c:type:`!vm_userfaultfd_ctx`, VMA write.
|
||||
either of zero size if userfaultfd is
|
||||
disabled, or containing a pointer
|
||||
to an underlying
|
||||
:c:type:`!userfaultfd_ctx` object which
|
||||
describes userfaultfd metadata.
|
||||
================================= ===================== ======================================== ===============
|
||||
|
||||
These fields are present or not depending on whether the relevant kernel
|
||||
configuration option is set.
|
||||
|
||||
.. table:: Reverse mapping fields
|
||||
|
||||
=================================== ========================================= ============================
|
||||
Field Description Write lock
|
||||
=================================== ========================================= ============================
|
||||
:c:member:`!shared.rb` A red/black tree node used, if the mmap write, VMA write,
|
||||
mapping is file-backed, to place the VMA i_mmap write.
|
||||
in the
|
||||
:c:member:`!struct address_space->i_mmap`
|
||||
red/black interval tree.
|
||||
:c:member:`!shared.rb_subtree_last` Metadata used for management of the mmap write, VMA write,
|
||||
interval tree if the VMA is file-backed. i_mmap write.
|
||||
:c:member:`!anon_vma_chain` List of pointers to both forked/CoW’d mmap read, anon_vma write.
|
||||
:c:type:`!anon_vma` objects and
|
||||
:c:member:`!vma->anon_vma` if it is
|
||||
non-:c:macro:`!NULL`.
|
||||
:c:member:`!anon_vma` :c:type:`!anon_vma` object used by When :c:macro:`NULL` and
|
||||
anonymous folios mapped exclusively to setting non-:c:macro:`NULL`:
|
||||
this VMA. Initially set by mmap read, page_table_lock.
|
||||
:c:func:`!anon_vma_prepare` serialised
|
||||
by the :c:macro:`!page_table_lock`. This When non-:c:macro:`NULL` and
|
||||
is set as soon as any page is faulted in. setting :c:macro:`NULL`:
|
||||
mmap write, VMA write,
|
||||
anon_vma write.
|
||||
=================================== ========================================= ============================
|
||||
|
||||
These fields are used to both place the VMA within the reverse mapping, and for
|
||||
anonymous mappings, to be able to access both related :c:struct:`!struct anon_vma` objects
|
||||
and the :c:struct:`!struct anon_vma` in which folios mapped exclusively to this VMA should
|
||||
reside.
|
||||
|
||||
.. note:: If a file-backed mapping is mapped with :c:macro:`!MAP_PRIVATE` set
|
||||
then it can be in both the :c:type:`!anon_vma` and :c:type:`!i_mmap`
|
||||
trees at the same time, so all of these fields might be utilised at
|
||||
once.
|
||||
|
||||
Page tables
|
||||
-----------
|
||||
|
||||
We won't speak exhaustively on the subject but broadly speaking, page tables map
|
||||
virtual addresses to physical ones through a series of page tables, each of
|
||||
which contain entries with physical addresses for the next page table level
|
||||
(along with flags), and at the leaf level the physical addresses of the
|
||||
underlying physical data pages or a special entry such as a swap entry,
|
||||
migration entry or other special marker. Offsets into these pages are provided
|
||||
by the virtual address itself.
|
||||
|
||||
In Linux these are divided into five levels - PGD, P4D, PUD, PMD and PTE. Huge
|
||||
pages might eliminate one or two of these levels, but when this is the case we
|
||||
typically refer to the leaf level as the PTE level regardless.
|
||||
|
||||
.. note:: In instances where the architecture supports fewer page tables than
|
||||
five the kernel cleverly 'folds' page table levels, that is stubbing
|
||||
out functions related to the skipped levels. This allows us to
|
||||
conceptually act as if there were always five levels, even if the
|
||||
compiler might, in practice, eliminate any code relating to missing
|
||||
ones.
|
||||
|
||||
There are four key operations typically performed on page tables:
|
||||
|
||||
1. **Traversing** page tables - Simply reading page tables in order to traverse
|
||||
them. This only requires that the VMA is kept stable, so a lock which
|
||||
establishes this suffices for traversal (there are also lockless variants
|
||||
which eliminate even this requirement, such as :c:func:`!gup_fast`).
|
||||
2. **Installing** page table mappings - Whether creating a new mapping or
|
||||
modifying an existing one in such a way as to change its identity. This
|
||||
requires that the VMA is kept stable via an mmap or VMA lock (explicitly not
|
||||
rmap locks).
|
||||
3. **Zapping/unmapping** page table entries - This is what the kernel calls
|
||||
clearing page table mappings at the leaf level only, whilst leaving all page
|
||||
tables in place. This is a very common operation in the kernel performed on
|
||||
file truncation, the :c:macro:`!MADV_DONTNEED` operation via
|
||||
:c:func:`!madvise`, and others. This is performed by a number of functions
|
||||
including :c:func:`!unmap_mapping_range` and :c:func:`!unmap_mapping_pages`.
|
||||
The VMA need only be kept stable for this operation.
|
||||
4. **Freeing** page tables - When finally the kernel removes page tables from a
|
||||
userland process (typically via :c:func:`!free_pgtables`) extreme care must
|
||||
be taken to ensure this is done safely, as this logic finally frees all page
|
||||
tables in the specified range, ignoring existing leaf entries (it assumes the
|
||||
caller has both zapped the range and prevented any further faults or
|
||||
modifications within it).
|
||||
|
||||
.. note:: Modifying mappings for reclaim or migration is performed under rmap
|
||||
lock as it, like zapping, does not fundamentally modify the identity
|
||||
of what is being mapped.
|
||||
|
||||
**Traversing** and **zapping** ranges can be performed holding any one of the
|
||||
locks described in the terminology section above - that is the mmap lock, the
|
||||
VMA lock or either of the reverse mapping locks.
|
||||
|
||||
That is - as long as you keep the relevant VMA **stable** - you are good to go
|
||||
ahead and perform these operations on page tables (though internally, kernel
|
||||
operations that perform writes also acquire internal page table locks to
|
||||
serialise - see the page table implementation detail section for more details).
|
||||
|
||||
When **installing** page table entries, the mmap or VMA lock must be held to
|
||||
keep the VMA stable. We explore why this is in the page table locking details
|
||||
section below.
|
||||
|
||||
.. warning:: Page tables are normally only traversed in regions covered by VMAs.
|
||||
If you want to traverse page tables in areas that might not be
|
||||
covered by VMAs, heavier locking is required.
|
||||
See :c:func:`!walk_page_range_novma` for details.
|
||||
|
||||
**Freeing** page tables is an entirely internal memory management operation and
|
||||
has special requirements (see the page freeing section below for more details).
|
||||
|
||||
.. warning:: When **freeing** page tables, it must not be possible for VMAs
|
||||
containing the ranges those page tables map to be accessible via
|
||||
the reverse mapping.
|
||||
|
||||
The :c:func:`!free_pgtables` function removes the relevant VMAs
|
||||
from the reverse mappings, but no other VMAs can be permitted to be
|
||||
accessible and span the specified range.
|
||||
|
||||
Lock ordering
|
||||
-------------
|
||||
|
||||
As we have multiple locks across the kernel which may or may not be taken at the
|
||||
same time as explicit mm or VMA locks, we have to be wary of lock inversion, and
|
||||
the **order** in which locks are acquired and released becomes very important.
|
||||
|
||||
.. note:: Lock inversion occurs when two threads need to acquire multiple locks,
|
||||
but in doing so inadvertently cause a mutual deadlock.
|
||||
|
||||
For example, consider thread 1 which holds lock A and tries to acquire lock B,
|
||||
while thread 2 holds lock B and tries to acquire lock A.
|
||||
|
||||
Both threads are now deadlocked on each other. However, had they attempted to
|
||||
acquire locks in the same order, one would have waited for the other to
|
||||
complete its work and no deadlock would have occurred.
|
||||
|
||||
The opening comment in :c:macro:`!mm/rmap.c` describes in detail the required
|
||||
ordering of locks within memory management code:
|
||||
|
||||
.. code-block::
|
||||
|
||||
inode->i_rwsem (while writing or truncating, not reading or faulting)
|
||||
mm->mmap_lock
|
||||
mapping->invalidate_lock (in filemap_fault)
|
||||
folio_lock
|
||||
hugetlbfs_i_mmap_rwsem_key (in huge_pmd_share, see hugetlbfs below)
|
||||
vma_start_write
|
||||
mapping->i_mmap_rwsem
|
||||
anon_vma->rwsem
|
||||
mm->page_table_lock or pte_lock
|
||||
swap_lock (in swap_duplicate, swap_info_get)
|
||||
mmlist_lock (in mmput, drain_mmlist and others)
|
||||
mapping->private_lock (in block_dirty_folio)
|
||||
i_pages lock (widely used)
|
||||
lruvec->lru_lock (in folio_lruvec_lock_irq)
|
||||
inode->i_lock (in set_page_dirty's __mark_inode_dirty)
|
||||
bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty)
|
||||
sb_lock (within inode_lock in fs/fs-writeback.c)
|
||||
i_pages lock (widely used, in set_page_dirty,
|
||||
in arch-dependent flush_dcache_mmap_lock,
|
||||
within bdi.wb->list_lock in __sync_single_inode)
|
||||
|
||||
There is also a file-system specific lock ordering comment located at the top of
|
||||
:c:macro:`!mm/filemap.c`:
|
||||
|
||||
.. code-block::
|
||||
|
||||
->i_mmap_rwsem (truncate_pagecache)
|
||||
->private_lock (__free_pte->block_dirty_folio)
|
||||
->swap_lock (exclusive_swap_page, others)
|
||||
->i_pages lock
|
||||
|
||||
->i_rwsem
|
||||
->invalidate_lock (acquired by fs in truncate path)
|
||||
->i_mmap_rwsem (truncate->unmap_mapping_range)
|
||||
|
||||
->mmap_lock
|
||||
->i_mmap_rwsem
|
||||
->page_table_lock or pte_lock (various, mainly in memory.c)
|
||||
->i_pages lock (arch-dependent flush_dcache_mmap_lock)
|
||||
|
||||
->mmap_lock
|
||||
->invalidate_lock (filemap_fault)
|
||||
->lock_page (filemap_fault, access_process_vm)
|
||||
|
||||
->i_rwsem (generic_perform_write)
|
||||
->mmap_lock (fault_in_readable->do_page_fault)
|
||||
|
||||
bdi->wb.list_lock
|
||||
sb_lock (fs/fs-writeback.c)
|
||||
->i_pages lock (__sync_single_inode)
|
||||
|
||||
->i_mmap_rwsem
|
||||
->anon_vma.lock (vma_merge)
|
||||
|
||||
->anon_vma.lock
|
||||
->page_table_lock or pte_lock (anon_vma_prepare and various)
|
||||
|
||||
->page_table_lock or pte_lock
|
||||
->swap_lock (try_to_unmap_one)
|
||||
->private_lock (try_to_unmap_one)
|
||||
->i_pages lock (try_to_unmap_one)
|
||||
->lruvec->lru_lock (follow_page_mask->mark_page_accessed)
|
||||
->lruvec->lru_lock (check_pte_range->folio_isolate_lru)
|
||||
->private_lock (folio_remove_rmap_pte->set_page_dirty)
|
||||
->i_pages lock (folio_remove_rmap_pte->set_page_dirty)
|
||||
bdi.wb->list_lock (folio_remove_rmap_pte->set_page_dirty)
|
||||
->inode->i_lock (folio_remove_rmap_pte->set_page_dirty)
|
||||
bdi.wb->list_lock (zap_pte_range->set_page_dirty)
|
||||
->inode->i_lock (zap_pte_range->set_page_dirty)
|
||||
->private_lock (zap_pte_range->block_dirty_folio)
|
||||
|
||||
Please check the current state of these comments which may have changed since
|
||||
the time of writing of this document.
|
||||
|
||||
------------------------------
|
||||
Locking Implementation Details
|
||||
------------------------------
|
||||
|
||||
.. warning:: Locking rules for PTE-level page tables are very different from
|
||||
locking rules for page tables at other levels.
|
||||
|
||||
Page table locking details
|
||||
--------------------------
|
||||
|
||||
In addition to the locks described in the terminology section above, we have
|
||||
additional locks dedicated to page tables:
|
||||
|
||||
* **Higher level page table locks** - Higher level page tables, that is PGD, P4D
|
||||
and PUD each make use of the process address space granularity
|
||||
:c:member:`!mm->page_table_lock` lock when modified.
|
||||
|
||||
* **Fine-grained page table locks** - PMDs and PTEs each have fine-grained locks
|
||||
either kept within the folios describing the page tables or allocated
|
||||
separated and pointed at by the folios if :c:macro:`!ALLOC_SPLIT_PTLOCKS` is
|
||||
set. The PMD spin lock is obtained via :c:func:`!pmd_lock`, however PTEs are
|
||||
mapped into higher memory (if a 32-bit system) and carefully locked via
|
||||
:c:func:`!pte_offset_map_lock`.
|
||||
|
||||
These locks represent the minimum required to interact with each page table
|
||||
level, but there are further requirements.
|
||||
|
||||
Importantly, note that on a **traversal** of page tables, sometimes no such
|
||||
locks are taken. However, at the PTE level, at least concurrent page table
|
||||
deletion must be prevented (using RCU) and the page table must be mapped into
|
||||
high memory, see below.
|
||||
|
||||
Whether care is taken on reading the page table entries depends on the
|
||||
architecture, see the section on atomicity below.
|
||||
|
||||
Locking rules
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
We establish basic locking rules when interacting with page tables:
|
||||
|
||||
* When changing a page table entry the page table lock for that page table
|
||||
**must** be held, except if you can safely assume nobody can access the page
|
||||
tables concurrently (such as on invocation of :c:func:`!free_pgtables`).
|
||||
* Reads from and writes to page table entries must be *appropriately*
|
||||
atomic. See the section on atomicity below for details.
|
||||
* Populating previously empty entries requires that the mmap or VMA locks are
|
||||
held (read or write), doing so with only rmap locks would be dangerous (see
|
||||
the warning below).
|
||||
* As mentioned previously, zapping can be performed while simply keeping the VMA
|
||||
stable, that is holding any one of the mmap, VMA or rmap locks.
|
||||
|
||||
.. warning:: Populating previously empty entries is dangerous as, when unmapping
|
||||
VMAs, :c:func:`!vms_clear_ptes` has a window of time between
|
||||
zapping (via :c:func:`!unmap_vmas`) and freeing page tables (via
|
||||
:c:func:`!free_pgtables`), where the VMA is still visible in the
|
||||
rmap tree. :c:func:`!free_pgtables` assumes that the zap has
|
||||
already been performed and removes PTEs unconditionally (along with
|
||||
all other page tables in the freed range), so installing new PTE
|
||||
entries could leak memory and also cause other unexpected and
|
||||
dangerous behaviour.
|
||||
|
||||
There are additional rules applicable when moving page tables, which we discuss
|
||||
in the section on this topic below.
|
||||
|
||||
PTE-level page tables are different from page tables at other levels, and there
|
||||
are extra requirements for accessing them:
|
||||
|
||||
* On 32-bit architectures, they may be in high memory (meaning they need to be
|
||||
mapped into kernel memory to be accessible).
|
||||
* When empty, they can be unlinked and RCU-freed while holding an mmap lock or
|
||||
rmap lock for reading in combination with the PTE and PMD page table locks.
|
||||
In particular, this happens in :c:func:`!retract_page_tables` when handling
|
||||
:c:macro:`!MADV_COLLAPSE`.
|
||||
So accessing PTE-level page tables requires at least holding an RCU read lock;
|
||||
but that only suffices for readers that can tolerate racing with concurrent
|
||||
page table updates such that an empty PTE is observed (in a page table that
|
||||
has actually already been detached and marked for RCU freeing) while another
|
||||
new page table has been installed in the same location and filled with
|
||||
entries. Writers normally need to take the PTE lock and revalidate that the
|
||||
PMD entry still refers to the same PTE-level page table.
|
||||
|
||||
To access PTE-level page tables, a helper like :c:func:`!pte_offset_map_lock` or
|
||||
:c:func:`!pte_offset_map` can be used depending on stability requirements.
|
||||
These map the page table into kernel memory if required, take the RCU lock, and
|
||||
depending on variant, may also look up or acquire the PTE lock.
|
||||
See the comment on :c:func:`!__pte_offset_map_lock`.
|
||||
|
||||
Atomicity
|
||||
^^^^^^^^^
|
||||
|
||||
Regardless of page table locks, the MMU hardware concurrently updates accessed
|
||||
and dirty bits (perhaps more, depending on architecture). Additionally, page
|
||||
table traversal operations in parallel (though holding the VMA stable) and
|
||||
functionality like GUP-fast locklessly traverses (that is reads) page tables,
|
||||
without even keeping the VMA stable at all.
|
||||
|
||||
When performing a page table traversal and keeping the VMA stable, whether a
|
||||
read must be performed once and only once or not depends on the architecture
|
||||
(for instance x86-64 does not require any special precautions).
|
||||
|
||||
If a write is being performed, or if a read informs whether a write takes place
|
||||
(on an installation of a page table entry say, for instance in
|
||||
:c:func:`!__pud_install`), special care must always be taken. In these cases we
|
||||
can never assume that page table locks give us entirely exclusive access, and
|
||||
must retrieve page table entries once and only once.
|
||||
|
||||
If we are reading page table entries, then we need only ensure that the compiler
|
||||
does not rearrange our loads. This is achieved via :c:func:`!pXXp_get`
|
||||
functions - :c:func:`!pgdp_get`, :c:func:`!p4dp_get`, :c:func:`!pudp_get`,
|
||||
:c:func:`!pmdp_get`, and :c:func:`!ptep_get`.
|
||||
|
||||
Each of these uses :c:func:`!READ_ONCE` to guarantee that the compiler reads
|
||||
the page table entry only once.
|
||||
|
||||
However, if we wish to manipulate an existing page table entry and care about
|
||||
the previously stored data, we must go further and use an hardware atomic
|
||||
operation as, for example, in :c:func:`!ptep_get_and_clear`.
|
||||
|
||||
Equally, operations that do not rely on the VMA being held stable, such as
|
||||
GUP-fast (see :c:func:`!gup_fast` and its various page table level handlers like
|
||||
:c:func:`!gup_fast_pte_range`), must very carefully interact with page table
|
||||
entries, using functions such as :c:func:`!ptep_get_lockless` and equivalent for
|
||||
higher level page table levels.
|
||||
|
||||
Writes to page table entries must also be appropriately atomic, as established
|
||||
by :c:func:`!set_pXX` functions - :c:func:`!set_pgd`, :c:func:`!set_p4d`,
|
||||
:c:func:`!set_pud`, :c:func:`!set_pmd`, and :c:func:`!set_pte`.
|
||||
|
||||
Equally functions which clear page table entries must be appropriately atomic,
|
||||
as in :c:func:`!pXX_clear` functions - :c:func:`!pgd_clear`,
|
||||
:c:func:`!p4d_clear`, :c:func:`!pud_clear`, :c:func:`!pmd_clear`, and
|
||||
:c:func:`!pte_clear`.
|
||||
|
||||
Page table installation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Page table installation is performed with the VMA held stable explicitly by an
|
||||
mmap or VMA lock in read or write mode (see the warning in the locking rules
|
||||
section for details as to why).
|
||||
|
||||
When allocating a P4D, PUD or PMD and setting the relevant entry in the above
|
||||
PGD, P4D or PUD, the :c:member:`!mm->page_table_lock` must be held. This is
|
||||
acquired in :c:func:`!__p4d_alloc`, :c:func:`!__pud_alloc` and
|
||||
:c:func:`!__pmd_alloc` respectively.
|
||||
|
||||
.. note:: :c:func:`!__pmd_alloc` actually invokes :c:func:`!pud_lock` and
|
||||
:c:func:`!pud_lockptr` in turn, however at the time of writing it ultimately
|
||||
references the :c:member:`!mm->page_table_lock`.
|
||||
|
||||
Allocating a PTE will either use the :c:member:`!mm->page_table_lock` or, if
|
||||
:c:macro:`!USE_SPLIT_PMD_PTLOCKS` is defined, a lock embedded in the PMD
|
||||
physical page metadata in the form of a :c:struct:`!struct ptdesc`, acquired by
|
||||
:c:func:`!pmd_ptdesc` called from :c:func:`!pmd_lock` and ultimately
|
||||
:c:func:`!__pte_alloc`.
|
||||
|
||||
Finally, modifying the contents of the PTE requires special treatment, as the
|
||||
PTE page table lock must be acquired whenever we want stable and exclusive
|
||||
access to entries contained within a PTE, especially when we wish to modify
|
||||
them.
|
||||
|
||||
This is performed via :c:func:`!pte_offset_map_lock` which carefully checks to
|
||||
ensure that the PTE hasn't changed from under us, ultimately invoking
|
||||
:c:func:`!pte_lockptr` to obtain a spin lock at PTE granularity contained within
|
||||
the :c:struct:`!struct ptdesc` associated with the physical PTE page. The lock
|
||||
must be released via :c:func:`!pte_unmap_unlock`.
|
||||
|
||||
.. note:: There are some variants on this, such as
|
||||
:c:func:`!pte_offset_map_rw_nolock` when we know we hold the PTE stable but
|
||||
for brevity we do not explore this. See the comment for
|
||||
:c:func:`!__pte_offset_map_lock` for more details.
|
||||
|
||||
When modifying data in ranges we typically only wish to allocate higher page
|
||||
tables as necessary, using these locks to avoid races or overwriting anything,
|
||||
and set/clear data at the PTE level as required (for instance when page faulting
|
||||
or zapping).
|
||||
|
||||
A typical pattern taken when traversing page table entries to install a new
|
||||
mapping is to optimistically determine whether the page table entry in the table
|
||||
above is empty, if so, only then acquiring the page table lock and checking
|
||||
again to see if it was allocated underneath us.
|
||||
|
||||
This allows for a traversal with page table locks only being taken when
|
||||
required. An example of this is :c:func:`!__pud_alloc`.
|
||||
|
||||
At the leaf page table, that is the PTE, we can't entirely rely on this pattern
|
||||
as we have separate PMD and PTE locks and a THP collapse for instance might have
|
||||
eliminated the PMD entry as well as the PTE from under us.
|
||||
|
||||
This is why :c:func:`!__pte_offset_map_lock` locklessly retrieves the PMD entry
|
||||
for the PTE, carefully checking it is as expected, before acquiring the
|
||||
PTE-specific lock, and then *again* checking that the PMD entry is as expected.
|
||||
|
||||
If a THP collapse (or similar) were to occur then the lock on both pages would
|
||||
be acquired, so we can ensure this is prevented while the PTE lock is held.
|
||||
|
||||
Installing entries this way ensures mutual exclusion on write.
|
||||
|
||||
Page table freeing
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Tearing down page tables themselves is something that requires significant
|
||||
care. There must be no way that page tables designated for removal can be
|
||||
traversed or referenced by concurrent tasks.
|
||||
|
||||
It is insufficient to simply hold an mmap write lock and VMA lock (which will
|
||||
prevent racing faults, and rmap operations), as a file-backed mapping can be
|
||||
truncated under the :c:struct:`!struct address_space->i_mmap_rwsem` alone.
|
||||
|
||||
As a result, no VMA which can be accessed via the reverse mapping (either
|
||||
through the :c:struct:`!struct anon_vma->rb_root` or the :c:member:`!struct
|
||||
address_space->i_mmap` interval trees) can have its page tables torn down.
|
||||
|
||||
The operation is typically performed via :c:func:`!free_pgtables`, which assumes
|
||||
either the mmap write lock has been taken (as specified by its
|
||||
:c:member:`!mm_wr_locked` parameter), or that the VMA is already unreachable.
|
||||
|
||||
It carefully removes the VMA from all reverse mappings, however it's important
|
||||
that no new ones overlap these or any route remain to permit access to addresses
|
||||
within the range whose page tables are being torn down.
|
||||
|
||||
Additionally, it assumes that a zap has already been performed and steps have
|
||||
been taken to ensure that no further page table entries can be installed between
|
||||
the zap and the invocation of :c:func:`!free_pgtables`.
|
||||
|
||||
Since it is assumed that all such steps have been taken, page table entries are
|
||||
cleared without page table locks (in the :c:func:`!pgd_clear`, :c:func:`!p4d_clear`,
|
||||
:c:func:`!pud_clear`, and :c:func:`!pmd_clear` functions.
|
||||
|
||||
.. note:: It is possible for leaf page tables to be torn down independent of
|
||||
the page tables above it as is done by
|
||||
:c:func:`!retract_page_tables`, which is performed under the i_mmap
|
||||
read lock, PMD, and PTE page table locks, without this level of care.
|
||||
|
||||
Page table moving
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some functions manipulate page table levels above PMD (that is PUD, P4D and PGD
|
||||
page tables). Most notable of these is :c:func:`!mremap`, which is capable of
|
||||
moving higher level page tables.
|
||||
|
||||
In these instances, it is required that **all** locks are taken, that is
|
||||
the mmap lock, the VMA lock and the relevant rmap locks.
|
||||
|
||||
You can observe this in the :c:func:`!mremap` implementation in the functions
|
||||
:c:func:`!take_rmap_locks` and :c:func:`!drop_rmap_locks` which perform the rmap
|
||||
side of lock acquisition, invoked ultimately by :c:func:`!move_page_tables`.
|
||||
|
||||
VMA lock internals
|
||||
------------------
|
||||
|
||||
Overview
|
||||
^^^^^^^^
|
||||
|
||||
VMA read locking is entirely optimistic - if the lock is contended or a competing
|
||||
write has started, then we do not obtain a read lock.
|
||||
|
||||
A VMA **read** lock is obtained by :c:func:`!lock_vma_under_rcu`, which first
|
||||
calls :c:func:`!rcu_read_lock` to ensure that the VMA is looked up in an RCU
|
||||
critical section, then attempts to VMA lock it via :c:func:`!vma_start_read`,
|
||||
before releasing the RCU lock via :c:func:`!rcu_read_unlock`.
|
||||
|
||||
VMA read locks hold the read lock on the :c:member:`!vma->vm_lock` semaphore for
|
||||
their duration and the caller of :c:func:`!lock_vma_under_rcu` must release it
|
||||
via :c:func:`!vma_end_read`.
|
||||
|
||||
VMA **write** locks are acquired via :c:func:`!vma_start_write` in instances where a
|
||||
VMA is about to be modified, unlike :c:func:`!vma_start_read` the lock is always
|
||||
acquired. An mmap write lock **must** be held for the duration of the VMA write
|
||||
lock, releasing or downgrading the mmap write lock also releases the VMA write
|
||||
lock so there is no :c:func:`!vma_end_write` function.
|
||||
|
||||
Note that a semaphore write lock is not held across a VMA lock. Rather, a
|
||||
sequence number is used for serialisation, and the write semaphore is only
|
||||
acquired at the point of write lock to update this.
|
||||
|
||||
This ensures the semantics we require - VMA write locks provide exclusive write
|
||||
access to the VMA.
|
||||
|
||||
Implementation details
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The VMA lock mechanism is designed to be a lightweight means of avoiding the use
|
||||
of the heavily contended mmap lock. It is implemented using a combination of a
|
||||
read/write semaphore and sequence numbers belonging to the containing
|
||||
:c:struct:`!struct mm_struct` and the VMA.
|
||||
|
||||
Read locks are acquired via :c:func:`!vma_start_read`, which is an optimistic
|
||||
operation, i.e. it tries to acquire a read lock but returns false if it is
|
||||
unable to do so. At the end of the read operation, :c:func:`!vma_end_read` is
|
||||
called to release the VMA read lock.
|
||||
|
||||
Invoking :c:func:`!vma_start_read` requires that :c:func:`!rcu_read_lock` has
|
||||
been called first, establishing that we are in an RCU critical section upon VMA
|
||||
read lock acquisition. Once acquired, the RCU lock can be released as it is only
|
||||
required for lookup. This is abstracted by :c:func:`!lock_vma_under_rcu` which
|
||||
is the interface a user should use.
|
||||
|
||||
Writing requires the mmap to be write-locked and the VMA lock to be acquired via
|
||||
:c:func:`!vma_start_write`, however the write lock is released by the termination or
|
||||
downgrade of the mmap write lock so no :c:func:`!vma_end_write` is required.
|
||||
|
||||
All this is achieved by the use of per-mm and per-VMA sequence counts, which are
|
||||
used in order to reduce complexity, especially for operations which write-lock
|
||||
multiple VMAs at once.
|
||||
|
||||
If the mm sequence count, :c:member:`!mm->mm_lock_seq` is equal to the VMA
|
||||
sequence count :c:member:`!vma->vm_lock_seq` then the VMA is write-locked. If
|
||||
they differ, then it is not.
|
||||
|
||||
Each time the mmap write lock is released in :c:func:`!mmap_write_unlock` or
|
||||
:c:func:`!mmap_write_downgrade`, :c:func:`!vma_end_write_all` is invoked which
|
||||
also increments :c:member:`!mm->mm_lock_seq` via
|
||||
:c:func:`!mm_lock_seqcount_end`.
|
||||
|
||||
This way, we ensure that, regardless of the VMA's sequence number, a write lock
|
||||
is never incorrectly indicated and that when we release an mmap write lock we
|
||||
efficiently release **all** VMA write locks contained within the mmap at the
|
||||
same time.
|
||||
|
||||
Since the mmap write lock is exclusive against others who hold it, the automatic
|
||||
release of any VMA locks on its release makes sense, as you would never want to
|
||||
keep VMAs locked across entirely separate write operations. It also maintains
|
||||
correct lock ordering.
|
||||
|
||||
Each time a VMA read lock is acquired, we acquire a read lock on the
|
||||
:c:member:`!vma->vm_lock` read/write semaphore and hold it, while checking that
|
||||
the sequence count of the VMA does not match that of the mm.
|
||||
|
||||
If it does, the read lock fails. If it does not, we hold the lock, excluding
|
||||
writers, but permitting other readers, who will also obtain this lock under RCU.
|
||||
|
||||
Importantly, maple tree operations performed in :c:func:`!lock_vma_under_rcu`
|
||||
are also RCU safe, so the whole read lock operation is guaranteed to function
|
||||
correctly.
|
||||
|
||||
On the write side, we acquire a write lock on the :c:member:`!vma->vm_lock`
|
||||
read/write semaphore, before setting the VMA's sequence number under this lock,
|
||||
also simultaneously holding the mmap write lock.
|
||||
|
||||
This way, if any read locks are in effect, :c:func:`!vma_start_write` will sleep
|
||||
until these are finished and mutual exclusion is achieved.
|
||||
|
||||
After setting the VMA's sequence number, the lock is released, avoiding
|
||||
complexity with a long-term held write lock.
|
||||
|
||||
This clever combination of a read/write semaphore and sequence count allows for
|
||||
fast RCU-based per-VMA lock acquisition (especially on page fault, though
|
||||
utilised elsewhere) with minimal complexity around lock ordering.
|
||||
|
||||
mmap write lock downgrading
|
||||
---------------------------
|
||||
|
||||
When an mmap write lock is held one has exclusive access to resources within the
|
||||
mmap (with the usual caveats about requiring VMA write locks to avoid races with
|
||||
tasks holding VMA read locks).
|
||||
|
||||
It is then possible to **downgrade** from a write lock to a read lock via
|
||||
:c:func:`!mmap_write_downgrade` which, similar to :c:func:`!mmap_write_unlock`,
|
||||
implicitly terminates all VMA write locks via :c:func:`!vma_end_write_all`, but
|
||||
importantly does not relinquish the mmap lock while downgrading, therefore
|
||||
keeping the locked virtual address space stable.
|
||||
|
||||
An interesting consequence of this is that downgraded locks are exclusive
|
||||
against any other task possessing a downgraded lock (since a racing task would
|
||||
have to acquire a write lock first to downgrade it, and the downgraded lock
|
||||
prevents a new write lock from being obtained until the original lock is
|
||||
released).
|
||||
|
||||
For clarity, we map read (R)/downgraded write (D)/write (W) locks against one
|
||||
another showing which locks exclude the others:
|
||||
|
||||
.. list-table:: Lock exclusivity
|
||||
:widths: 5 5 5 5
|
||||
:header-rows: 1
|
||||
:stub-columns: 1
|
||||
|
||||
* -
|
||||
- R
|
||||
- D
|
||||
- W
|
||||
* - R
|
||||
- N
|
||||
- N
|
||||
- Y
|
||||
* - D
|
||||
- N
|
||||
- Y
|
||||
- Y
|
||||
* - W
|
||||
- Y
|
||||
- Y
|
||||
- Y
|
||||
|
||||
Here a Y indicates the locks in the matching row/column are mutually exclusive,
|
||||
and N indicates that they are not.
|
||||
|
||||
Stack expansion
|
||||
---------------
|
||||
|
||||
Stack expansion throws up additional complexities in that we cannot permit there
|
||||
to be racing page faults, as a result we invoke :c:func:`!vma_start_write` to
|
||||
prevent this in :c:func:`!expand_downwards` or :c:func:`!expand_upwards`.
|
||||
|
|
|
|||
|
|
@ -7347,7 +7347,7 @@ F: drivers/gpu/drm/panel/panel-novatek-nt36672a.c
|
|||
DRM DRIVER FOR NVIDIA GEFORCE/QUADRO GPUS
|
||||
M: Karol Herbst <kherbst@redhat.com>
|
||||
M: Lyude Paul <lyude@redhat.com>
|
||||
M: Danilo Krummrich <dakr@redhat.com>
|
||||
M: Danilo Krummrich <dakr@kernel.org>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: nouveau@lists.freedesktop.org
|
||||
S: Supported
|
||||
|
|
@ -8453,7 +8453,7 @@ F: include/video/s1d13xxxfb.h
|
|||
EROFS FILE SYSTEM
|
||||
M: Gao Xiang <xiang@kernel.org>
|
||||
M: Chao Yu <chao@kernel.org>
|
||||
R: Yue Hu <huyue2@coolpad.com>
|
||||
R: Yue Hu <zbestahu@gmail.com>
|
||||
R: Jeffle Xu <jefflexu@linux.alibaba.com>
|
||||
R: Sandeep Dhavale <dhavale@google.com>
|
||||
L: linux-erofs@lists.ozlabs.org
|
||||
|
|
@ -8924,7 +8924,7 @@ F: include/linux/arm_ffa.h
|
|||
FIRMWARE LOADER (request_firmware)
|
||||
M: Luis Chamberlain <mcgrof@kernel.org>
|
||||
M: Russ Weight <russ.weight@linux.dev>
|
||||
M: Danilo Krummrich <dakr@redhat.com>
|
||||
M: Danilo Krummrich <dakr@kernel.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/firmware_class/
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -2,7 +2,7 @@
|
|||
VERSION = 6
|
||||
PATCHLEVEL = 13
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION = -rc3
|
||||
EXTRAVERSION = -rc4
|
||||
NAME = Baby Opossum Posse
|
||||
|
||||
# *DOCUMENTATION*
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
config ARC
|
||||
def_bool y
|
||||
select ARC_TIMERS
|
||||
select ARCH_HAS_CPU_CACHE_ALIASING
|
||||
select ARCH_HAS_CACHE_LINE_SIZE
|
||||
select ARCH_HAS_DEBUG_VM_PGTABLE
|
||||
select ARCH_HAS_DMA_PREP_COHERENT
|
||||
|
|
|
|||
8
arch/arc/include/asm/cachetype.h
Normal file
8
arch/arc/include/asm/cachetype.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __ASM_ARC_CACHETYPE_H
|
||||
#define __ASM_ARC_CACHETYPE_H
|
||||
|
||||
#define cpu_dcache_is_aliasing() false
|
||||
#define cpu_icache_is_aliasing() true
|
||||
|
||||
#endif
|
||||
|
|
@ -233,7 +233,7 @@ pci: pci@40000000 {
|
|||
#interrupt-cells = <0x1>;
|
||||
compatible = "pci-host-ecam-generic";
|
||||
device_type = "pci";
|
||||
bus-range = <0x0 0x1>;
|
||||
bus-range = <0x0 0xff>;
|
||||
reg = <0x0 0x40000000 0x0 0x10000000>;
|
||||
ranges = <0x2000000 0x0 0x50000000 0x0 0x50000000 0x0 0x10000000>;
|
||||
interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ cpu0: cpu@0 {
|
|||
l2_cache_l0: l2-cache-l0 {
|
||||
compatible = "cache";
|
||||
cache-size = <0x80000>;
|
||||
cache-line-size = <128>;
|
||||
cache-line-size = <64>;
|
||||
cache-sets = <1024>; //512KiB(size)/64(line-size)=8192ways/8-way set
|
||||
cache-level = <2>;
|
||||
cache-unified;
|
||||
|
|
@ -91,7 +91,7 @@ cpu1: cpu@1 {
|
|||
l2_cache_l1: l2-cache-l1 {
|
||||
compatible = "cache";
|
||||
cache-size = <0x80000>;
|
||||
cache-line-size = <128>;
|
||||
cache-line-size = <64>;
|
||||
cache-sets = <1024>; //512KiB(size)/64(line-size)=8192ways/8-way set
|
||||
cache-level = <2>;
|
||||
cache-unified;
|
||||
|
|
@ -115,7 +115,7 @@ cpu2: cpu@2 {
|
|||
l2_cache_l2: l2-cache-l2 {
|
||||
compatible = "cache";
|
||||
cache-size = <0x80000>;
|
||||
cache-line-size = <128>;
|
||||
cache-line-size = <64>;
|
||||
cache-sets = <1024>; //512KiB(size)/64(line-size)=8192ways/8-way set
|
||||
cache-level = <2>;
|
||||
cache-unified;
|
||||
|
|
@ -139,7 +139,7 @@ cpu3: cpu@3 {
|
|||
l2_cache_l3: l2-cache-l3 {
|
||||
compatible = "cache";
|
||||
cache-size = <0x80000>;
|
||||
cache-line-size = <128>;
|
||||
cache-line-size = <64>;
|
||||
cache-sets = <1024>; //512KiB(size)/64(line-size)=8192ways/8-way set
|
||||
cache-level = <2>;
|
||||
cache-unified;
|
||||
|
|
|
|||
|
|
@ -36,15 +36,8 @@
|
|||
#include <asm/traps.h>
|
||||
#include <asm/vdso.h>
|
||||
|
||||
#ifdef CONFIG_ARM64_GCS
|
||||
#define GCS_SIGNAL_CAP(addr) (((unsigned long)addr) & GCS_CAP_ADDR_MASK)
|
||||
|
||||
static bool gcs_signal_cap_valid(u64 addr, u64 val)
|
||||
{
|
||||
return val == GCS_SIGNAL_CAP(addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Do a signal return; undo the signal stack. These are aligned to 128-bit.
|
||||
*/
|
||||
|
|
@ -1062,8 +1055,7 @@ static int restore_sigframe(struct pt_regs *regs,
|
|||
#ifdef CONFIG_ARM64_GCS
|
||||
static int gcs_restore_signal(void)
|
||||
{
|
||||
unsigned long __user *gcspr_el0;
|
||||
u64 cap;
|
||||
u64 gcspr_el0, cap;
|
||||
int ret;
|
||||
|
||||
if (!system_supports_gcs())
|
||||
|
|
@ -1072,7 +1064,7 @@ static int gcs_restore_signal(void)
|
|||
if (!(current->thread.gcs_el0_mode & PR_SHADOW_STACK_ENABLE))
|
||||
return 0;
|
||||
|
||||
gcspr_el0 = (unsigned long __user *)read_sysreg_s(SYS_GCSPR_EL0);
|
||||
gcspr_el0 = read_sysreg_s(SYS_GCSPR_EL0);
|
||||
|
||||
/*
|
||||
* Ensure that any changes to the GCS done via GCS operations
|
||||
|
|
@ -1087,22 +1079,23 @@ static int gcs_restore_signal(void)
|
|||
* then faults will be generated on GCS operations - the main
|
||||
* concern is to protect GCS pages.
|
||||
*/
|
||||
ret = copy_from_user(&cap, gcspr_el0, sizeof(cap));
|
||||
ret = copy_from_user(&cap, (unsigned long __user *)gcspr_el0,
|
||||
sizeof(cap));
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Check that the cap is the actual GCS before replacing it.
|
||||
*/
|
||||
if (!gcs_signal_cap_valid((u64)gcspr_el0, cap))
|
||||
if (cap != GCS_SIGNAL_CAP(gcspr_el0))
|
||||
return -EINVAL;
|
||||
|
||||
/* Invalidate the token to prevent reuse */
|
||||
put_user_gcs(0, (__user void*)gcspr_el0, &ret);
|
||||
put_user_gcs(0, (unsigned long __user *)gcspr_el0, &ret);
|
||||
if (ret != 0)
|
||||
return -EFAULT;
|
||||
|
||||
write_sysreg_s(gcspr_el0 + 1, SYS_GCSPR_EL0);
|
||||
write_sysreg_s(gcspr_el0 + 8, SYS_GCSPR_EL0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1421,7 +1414,7 @@ static int get_sigframe(struct rt_sigframe_user_layout *user,
|
|||
|
||||
static int gcs_signal_entry(__sigrestore_t sigtramp, struct ksignal *ksig)
|
||||
{
|
||||
unsigned long __user *gcspr_el0;
|
||||
u64 gcspr_el0;
|
||||
int ret = 0;
|
||||
|
||||
if (!system_supports_gcs())
|
||||
|
|
@ -1434,18 +1427,20 @@ static int gcs_signal_entry(__sigrestore_t sigtramp, struct ksignal *ksig)
|
|||
* We are entering a signal handler, current register state is
|
||||
* active.
|
||||
*/
|
||||
gcspr_el0 = (unsigned long __user *)read_sysreg_s(SYS_GCSPR_EL0);
|
||||
gcspr_el0 = read_sysreg_s(SYS_GCSPR_EL0);
|
||||
|
||||
/*
|
||||
* Push a cap and the GCS entry for the trampoline onto the GCS.
|
||||
*/
|
||||
put_user_gcs((unsigned long)sigtramp, gcspr_el0 - 2, &ret);
|
||||
put_user_gcs(GCS_SIGNAL_CAP(gcspr_el0 - 1), gcspr_el0 - 1, &ret);
|
||||
put_user_gcs((unsigned long)sigtramp,
|
||||
(unsigned long __user *)(gcspr_el0 - 16), &ret);
|
||||
put_user_gcs(GCS_SIGNAL_CAP(gcspr_el0 - 8),
|
||||
(unsigned long __user *)(gcspr_el0 - 8), &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
gcspr_el0 -= 2;
|
||||
write_sysreg_s((unsigned long)gcspr_el0, SYS_GCSPR_EL0);
|
||||
gcspr_el0 -= 16;
|
||||
write_sysreg_s(gcspr_el0, SYS_GCSPR_EL0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,3 +32,9 @@ KBUILD_LDFLAGS += $(ldflags-y)
|
|||
TIR_NAME := r19
|
||||
KBUILD_CFLAGS += -ffixed-$(TIR_NAME) -DTHREADINFO_REG=$(TIR_NAME) -D__linux__
|
||||
KBUILD_AFLAGS += -DTHREADINFO_REG=$(TIR_NAME)
|
||||
|
||||
# Disable HexagonConstExtenders pass for LLVM versions prior to 19.1.0
|
||||
# https://github.com/llvm/llvm-project/issues/99714
|
||||
ifneq ($(call clang-min-version, 190100),y)
|
||||
KBUILD_CFLAGS += -mllvm -hexagon-cext=false
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ CONFIG_FB_ATY=y
|
|||
CONFIG_FB_ATY_CT=y
|
||||
CONFIG_FB_ATY_GX=y
|
||||
CONFIG_FB_3DFX=y
|
||||
CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||
# CONFIG_VGA_CONSOLE is not set
|
||||
CONFIG_FRAMEBUFFER_CONSOLE=y
|
||||
CONFIG_LOGO=y
|
||||
|
|
|
|||
|
|
@ -716,6 +716,7 @@ CONFIG_FB_TRIDENT=m
|
|||
CONFIG_FB_SM501=m
|
||||
CONFIG_FB_IBM_GXT4500=y
|
||||
CONFIG_LCD_PLATFORM=m
|
||||
CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||
CONFIG_FRAMEBUFFER_CONSOLE=y
|
||||
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
|
||||
CONFIG_LOGO=y
|
||||
|
|
|
|||
|
|
@ -234,6 +234,8 @@ static unsigned long get_vmem_size(unsigned long identity_size,
|
|||
vsize = round_up(SZ_2G + max_mappable, rte_size) +
|
||||
round_up(vmemmap_size, rte_size) +
|
||||
FIXMAP_SIZE + MODULES_LEN + KASLR_LEN;
|
||||
if (IS_ENABLED(CONFIG_KMSAN))
|
||||
vsize += MODULES_LEN * 2;
|
||||
return size_add(vsize, vmalloc_size);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long e
|
|||
pages++;
|
||||
}
|
||||
}
|
||||
if (mode == POPULATE_DIRECT)
|
||||
if (mode == POPULATE_IDENTITY)
|
||||
update_page_count(PG_DIRECT_MAP_4K, pages);
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +339,7 @@ static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long e
|
|||
}
|
||||
pgtable_pte_populate(pmd, addr, next, mode);
|
||||
}
|
||||
if (mode == POPULATE_DIRECT)
|
||||
if (mode == POPULATE_IDENTITY)
|
||||
update_page_count(PG_DIRECT_MAP_1M, pages);
|
||||
}
|
||||
|
||||
|
|
@ -372,7 +372,7 @@ static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long e
|
|||
}
|
||||
pgtable_pmd_populate(pud, addr, next, mode);
|
||||
}
|
||||
if (mode == POPULATE_DIRECT)
|
||||
if (mode == POPULATE_IDENTITY)
|
||||
update_page_count(PG_DIRECT_MAP_2G, pages);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
|
|||
if (len >= sizeof(_value)) \
|
||||
return -E2BIG; \
|
||||
len = strscpy(_value, buf, sizeof(_value)); \
|
||||
if (len < 0) \
|
||||
if ((ssize_t)len < 0) \
|
||||
return len; \
|
||||
strim(_value); \
|
||||
return len; \
|
||||
|
|
|
|||
|
|
@ -452,6 +452,7 @@
|
|||
#define X86_FEATURE_SME_COHERENT (19*32+10) /* AMD hardware-enforced cache coherency */
|
||||
#define X86_FEATURE_DEBUG_SWAP (19*32+14) /* "debug_swap" AMD SEV-ES full debug state swap support */
|
||||
#define X86_FEATURE_SVSM (19*32+28) /* "svsm" SVSM present */
|
||||
#define X86_FEATURE_HV_INUSE_WR_ALLOWED (19*32+30) /* Allow Write to in-use hypervisor-owned pages */
|
||||
|
||||
/* AMD-defined Extended Feature 2 EAX, CPUID level 0x80000021 (EAX), word 20 */
|
||||
#define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* No Nested Data Breakpoints */
|
||||
|
|
|
|||
|
|
@ -230,6 +230,8 @@ static inline unsigned long long l1tf_pfn_limit(void)
|
|||
return BIT_ULL(boot_cpu_data.x86_cache_bits - 1 - PAGE_SHIFT);
|
||||
}
|
||||
|
||||
void init_cpu_devs(void);
|
||||
void get_cpu_vendor(struct cpuinfo_x86 *c);
|
||||
extern void early_cpu_init(void);
|
||||
extern void identify_secondary_cpu(struct cpuinfo_x86 *);
|
||||
extern void print_cpu_info(struct cpuinfo_x86 *);
|
||||
|
|
|
|||
|
|
@ -65,4 +65,19 @@
|
|||
|
||||
extern bool __static_call_fixup(void *tramp, u8 op, void *dest);
|
||||
|
||||
extern void __static_call_update_early(void *tramp, void *func);
|
||||
|
||||
#define static_call_update_early(name, _func) \
|
||||
({ \
|
||||
typeof(&STATIC_CALL_TRAMP(name)) __F = (_func); \
|
||||
if (static_call_initialized) { \
|
||||
__static_call_update(&STATIC_CALL_KEY(name), \
|
||||
STATIC_CALL_TRAMP_ADDR(name), __F);\
|
||||
} else { \
|
||||
WRITE_ONCE(STATIC_CALL_KEY(name).func, _func); \
|
||||
__static_call_update_early(STATIC_CALL_TRAMP_ADDR(name),\
|
||||
__F); \
|
||||
} \
|
||||
})
|
||||
|
||||
#endif /* _ASM_STATIC_CALL_H */
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#include <asm/special_insns.h>
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
static inline void iret_to_self(void)
|
||||
static __always_inline void iret_to_self(void)
|
||||
{
|
||||
asm volatile (
|
||||
"pushfl\n\t"
|
||||
|
|
@ -19,7 +19,7 @@ static inline void iret_to_self(void)
|
|||
: ASM_CALL_CONSTRAINT : : "memory");
|
||||
}
|
||||
#else
|
||||
static inline void iret_to_self(void)
|
||||
static __always_inline void iret_to_self(void)
|
||||
{
|
||||
unsigned int tmp;
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ static inline void iret_to_self(void)
|
|||
* Like all of Linux's memory ordering operations, this is a
|
||||
* compiler barrier as well.
|
||||
*/
|
||||
static inline void sync_core(void)
|
||||
static __always_inline void sync_core(void)
|
||||
{
|
||||
/*
|
||||
* The SERIALIZE instruction is the most straightforward way to
|
||||
|
|
|
|||
|
|
@ -39,9 +39,11 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pgtable.h>
|
||||
#include <linux/instrumentation.h>
|
||||
|
||||
#include <trace/events/xen.h>
|
||||
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/smap.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
|
|
@ -86,11 +88,20 @@ struct xen_dm_op_buf;
|
|||
* there aren't more than 5 arguments...)
|
||||
*/
|
||||
|
||||
extern struct { char _entry[32]; } hypercall_page[];
|
||||
void xen_hypercall_func(void);
|
||||
DECLARE_STATIC_CALL(xen_hypercall, xen_hypercall_func);
|
||||
|
||||
#define __HYPERCALL "call hypercall_page+%c[offset]"
|
||||
#define __HYPERCALL_ENTRY(x) \
|
||||
[offset] "i" (__HYPERVISOR_##x * sizeof(hypercall_page[0]))
|
||||
#ifdef MODULE
|
||||
#define __ADDRESSABLE_xen_hypercall
|
||||
#else
|
||||
#define __ADDRESSABLE_xen_hypercall __ADDRESSABLE_ASM_STR(__SCK__xen_hypercall)
|
||||
#endif
|
||||
|
||||
#define __HYPERCALL \
|
||||
__ADDRESSABLE_xen_hypercall \
|
||||
"call __SCT__xen_hypercall"
|
||||
|
||||
#define __HYPERCALL_ENTRY(x) "a" (x)
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
#define __HYPERCALL_RETREG "eax"
|
||||
|
|
@ -148,7 +159,7 @@ extern struct { char _entry[32]; } hypercall_page[];
|
|||
__HYPERCALL_0ARG(); \
|
||||
asm volatile (__HYPERCALL \
|
||||
: __HYPERCALL_0PARAM \
|
||||
: __HYPERCALL_ENTRY(name) \
|
||||
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
|
||||
: __HYPERCALL_CLOBBER0); \
|
||||
(type)__res; \
|
||||
})
|
||||
|
|
@ -159,7 +170,7 @@ extern struct { char _entry[32]; } hypercall_page[];
|
|||
__HYPERCALL_1ARG(a1); \
|
||||
asm volatile (__HYPERCALL \
|
||||
: __HYPERCALL_1PARAM \
|
||||
: __HYPERCALL_ENTRY(name) \
|
||||
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
|
||||
: __HYPERCALL_CLOBBER1); \
|
||||
(type)__res; \
|
||||
})
|
||||
|
|
@ -170,7 +181,7 @@ extern struct { char _entry[32]; } hypercall_page[];
|
|||
__HYPERCALL_2ARG(a1, a2); \
|
||||
asm volatile (__HYPERCALL \
|
||||
: __HYPERCALL_2PARAM \
|
||||
: __HYPERCALL_ENTRY(name) \
|
||||
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
|
||||
: __HYPERCALL_CLOBBER2); \
|
||||
(type)__res; \
|
||||
})
|
||||
|
|
@ -181,7 +192,7 @@ extern struct { char _entry[32]; } hypercall_page[];
|
|||
__HYPERCALL_3ARG(a1, a2, a3); \
|
||||
asm volatile (__HYPERCALL \
|
||||
: __HYPERCALL_3PARAM \
|
||||
: __HYPERCALL_ENTRY(name) \
|
||||
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
|
||||
: __HYPERCALL_CLOBBER3); \
|
||||
(type)__res; \
|
||||
})
|
||||
|
|
@ -192,7 +203,7 @@ extern struct { char _entry[32]; } hypercall_page[];
|
|||
__HYPERCALL_4ARG(a1, a2, a3, a4); \
|
||||
asm volatile (__HYPERCALL \
|
||||
: __HYPERCALL_4PARAM \
|
||||
: __HYPERCALL_ENTRY(name) \
|
||||
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
|
||||
: __HYPERCALL_CLOBBER4); \
|
||||
(type)__res; \
|
||||
})
|
||||
|
|
@ -206,12 +217,9 @@ xen_single_call(unsigned int call,
|
|||
__HYPERCALL_DECLS;
|
||||
__HYPERCALL_5ARG(a1, a2, a3, a4, a5);
|
||||
|
||||
if (call >= PAGE_SIZE / sizeof(hypercall_page[0]))
|
||||
return -EINVAL;
|
||||
|
||||
asm volatile(CALL_NOSPEC
|
||||
asm volatile(__HYPERCALL
|
||||
: __HYPERCALL_5PARAM
|
||||
: [thunk_target] "a" (&hypercall_page[call])
|
||||
: __HYPERCALL_ENTRY(call)
|
||||
: __HYPERCALL_CLOBBER5);
|
||||
|
||||
return (long)__res;
|
||||
|
|
|
|||
|
|
@ -142,11 +142,6 @@ static bool skip_addr(void *dest)
|
|||
if (dest >= (void *)relocate_kernel &&
|
||||
dest < (void*)relocate_kernel + KEXEC_CONTROL_CODE_MAX_SIZE)
|
||||
return true;
|
||||
#endif
|
||||
#ifdef CONFIG_XEN
|
||||
if (dest >= (void *)hypercall_page &&
|
||||
dest < (void*)hypercall_page + PAGE_SIZE)
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -867,7 +867,7 @@ static void cpu_detect_tlb(struct cpuinfo_x86 *c)
|
|||
tlb_lld_4m[ENTRIES], tlb_lld_1g[ENTRIES]);
|
||||
}
|
||||
|
||||
static void get_cpu_vendor(struct cpuinfo_x86 *c)
|
||||
void get_cpu_vendor(struct cpuinfo_x86 *c)
|
||||
{
|
||||
char *v = c->x86_vendor_id;
|
||||
int i;
|
||||
|
|
@ -1649,15 +1649,11 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
|
|||
detect_nopl();
|
||||
}
|
||||
|
||||
void __init early_cpu_init(void)
|
||||
void __init init_cpu_devs(void)
|
||||
{
|
||||
const struct cpu_dev *const *cdev;
|
||||
int count = 0;
|
||||
|
||||
#ifdef CONFIG_PROCESSOR_SELECT
|
||||
pr_info("KERNEL supported cpus:\n");
|
||||
#endif
|
||||
|
||||
for (cdev = __x86_cpu_dev_start; cdev < __x86_cpu_dev_end; cdev++) {
|
||||
const struct cpu_dev *cpudev = *cdev;
|
||||
|
||||
|
|
@ -1665,20 +1661,30 @@ void __init early_cpu_init(void)
|
|||
break;
|
||||
cpu_devs[count] = cpudev;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
void __init early_cpu_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PROCESSOR_SELECT
|
||||
unsigned int i, j;
|
||||
|
||||
pr_info("KERNEL supported cpus:\n");
|
||||
#endif
|
||||
|
||||
init_cpu_devs();
|
||||
|
||||
#ifdef CONFIG_PROCESSOR_SELECT
|
||||
{
|
||||
unsigned int j;
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
if (!cpudev->c_ident[j])
|
||||
continue;
|
||||
pr_info(" %s %s\n", cpudev->c_vendor,
|
||||
cpudev->c_ident[j]);
|
||||
}
|
||||
for (i = 0; i < X86_VENDOR_NUM && cpu_devs[i]; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
if (!cpu_devs[i]->c_ident[j])
|
||||
continue;
|
||||
pr_info(" %s %s\n", cpu_devs[i]->c_vendor,
|
||||
cpu_devs[i]->c_ident[j]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
early_identify_cpu(&boot_cpu_data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -223,6 +223,63 @@ static void hv_machine_crash_shutdown(struct pt_regs *regs)
|
|||
hyperv_cleanup();
|
||||
}
|
||||
#endif /* CONFIG_CRASH_DUMP */
|
||||
|
||||
static u64 hv_ref_counter_at_suspend;
|
||||
static void (*old_save_sched_clock_state)(void);
|
||||
static void (*old_restore_sched_clock_state)(void);
|
||||
|
||||
/*
|
||||
* Hyper-V clock counter resets during hibernation. Save and restore clock
|
||||
* offset during suspend/resume, while also considering the time passed
|
||||
* before suspend. This is to make sure that sched_clock using hv tsc page
|
||||
* based clocksource, proceeds from where it left off during suspend and
|
||||
* it shows correct time for the timestamps of kernel messages after resume.
|
||||
*/
|
||||
static void save_hv_clock_tsc_state(void)
|
||||
{
|
||||
hv_ref_counter_at_suspend = hv_read_reference_counter();
|
||||
}
|
||||
|
||||
static void restore_hv_clock_tsc_state(void)
|
||||
{
|
||||
/*
|
||||
* Adjust the offsets used by hv tsc clocksource to
|
||||
* account for the time spent before hibernation.
|
||||
* adjusted value = reference counter (time) at suspend
|
||||
* - reference counter (time) now.
|
||||
*/
|
||||
hv_adj_sched_clock_offset(hv_ref_counter_at_suspend - hv_read_reference_counter());
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions to override save_sched_clock_state and restore_sched_clock_state
|
||||
* functions of x86_platform. The Hyper-V clock counter is reset during
|
||||
* suspend-resume and the offset used to measure time needs to be
|
||||
* corrected, post resume.
|
||||
*/
|
||||
static void hv_save_sched_clock_state(void)
|
||||
{
|
||||
old_save_sched_clock_state();
|
||||
save_hv_clock_tsc_state();
|
||||
}
|
||||
|
||||
static void hv_restore_sched_clock_state(void)
|
||||
{
|
||||
restore_hv_clock_tsc_state();
|
||||
old_restore_sched_clock_state();
|
||||
}
|
||||
|
||||
static void __init x86_setup_ops_for_tsc_pg_clock(void)
|
||||
{
|
||||
if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
|
||||
return;
|
||||
|
||||
old_save_sched_clock_state = x86_platform.save_sched_clock_state;
|
||||
x86_platform.save_sched_clock_state = hv_save_sched_clock_state;
|
||||
|
||||
old_restore_sched_clock_state = x86_platform.restore_sched_clock_state;
|
||||
x86_platform.restore_sched_clock_state = hv_restore_sched_clock_state;
|
||||
}
|
||||
#endif /* CONFIG_HYPERV */
|
||||
|
||||
static uint32_t __init ms_hyperv_platform(void)
|
||||
|
|
@ -579,6 +636,7 @@ static void __init ms_hyperv_init_platform(void)
|
|||
|
||||
/* Register Hyper-V specific clocksource */
|
||||
hv_init_clocksource();
|
||||
x86_setup_ops_for_tsc_pg_clock();
|
||||
hv_vtl_init_platform();
|
||||
#endif
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -172,6 +172,15 @@ void arch_static_call_transform(void *site, void *tramp, void *func, bool tail)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(arch_static_call_transform);
|
||||
|
||||
noinstr void __static_call_update_early(void *tramp, void *func)
|
||||
{
|
||||
BUG_ON(system_state != SYSTEM_BOOTING);
|
||||
BUG_ON(!early_boot_irqs_disabled);
|
||||
BUG_ON(static_call_initialized);
|
||||
__text_gen_insn(tramp, JMP32_INSN_OPCODE, tramp, func, JMP32_INSN_SIZE);
|
||||
sync_core();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MITIGATION_RETHUNK
|
||||
/*
|
||||
* This is called by apply_returns() to fix up static call trampolines,
|
||||
|
|
|
|||
|
|
@ -519,14 +519,10 @@ INIT_PER_CPU(irq_stack_backing_store);
|
|||
* linker will never mark as relocatable. (Using just ABSOLUTE() is not
|
||||
* sufficient for that).
|
||||
*/
|
||||
#ifdef CONFIG_XEN
|
||||
#ifdef CONFIG_XEN_PV
|
||||
xen_elfnote_entry_value =
|
||||
ABSOLUTE(xen_elfnote_entry) + ABSOLUTE(startup_xen);
|
||||
#endif
|
||||
xen_elfnote_hypercall_page_value =
|
||||
ABSOLUTE(xen_elfnote_hypercall_page) + ABSOLUTE(hypercall_page);
|
||||
#endif
|
||||
#ifdef CONFIG_PVH
|
||||
xen_elfnote_phys32_entry_value =
|
||||
ABSOLUTE(xen_elfnote_phys32_entry) + ABSOLUTE(pvh_start_xen - LOAD_OFFSET);
|
||||
|
|
|
|||
|
|
@ -3364,18 +3364,6 @@ static bool fast_pf_fix_direct_spte(struct kvm_vcpu *vcpu,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool is_access_allowed(struct kvm_page_fault *fault, u64 spte)
|
||||
{
|
||||
if (fault->exec)
|
||||
return is_executable_pte(spte);
|
||||
|
||||
if (fault->write)
|
||||
return is_writable_pte(spte);
|
||||
|
||||
/* Fault was on Read access */
|
||||
return spte & PT_PRESENT_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the last level spte pointer of the shadow page walk for the given
|
||||
* gpa, and sets *spte to the spte value. This spte may be non-preset. If no
|
||||
|
|
|
|||
|
|
@ -461,6 +461,23 @@ static inline bool is_mmu_writable_spte(u64 spte)
|
|||
return spte & shadow_mmu_writable_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the access indicated by @fault is allowed by the existing
|
||||
* SPTE protections. Note, the caller is responsible for checking that the
|
||||
* SPTE is a shadow-present, leaf SPTE (either before or after).
|
||||
*/
|
||||
static inline bool is_access_allowed(struct kvm_page_fault *fault, u64 spte)
|
||||
{
|
||||
if (fault->exec)
|
||||
return is_executable_pte(spte);
|
||||
|
||||
if (fault->write)
|
||||
return is_writable_pte(spte);
|
||||
|
||||
/* Fault was on Read access */
|
||||
return spte & PT_PRESENT_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the MMU-writable flag is cleared, i.e. the SPTE is write-protected for
|
||||
* write-tracking, remote TLBs must be flushed, even if the SPTE was read-only,
|
||||
|
|
|
|||
|
|
@ -985,6 +985,11 @@ static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu,
|
|||
if (fault->prefetch && is_shadow_present_pte(iter->old_spte))
|
||||
return RET_PF_SPURIOUS;
|
||||
|
||||
if (is_shadow_present_pte(iter->old_spte) &&
|
||||
is_access_allowed(fault, iter->old_spte) &&
|
||||
is_last_spte(iter->old_spte, iter->level))
|
||||
return RET_PF_SPURIOUS;
|
||||
|
||||
if (unlikely(!fault->slot))
|
||||
new_spte = make_mmio_spte(vcpu, iter->gfn, ACC_ALL);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1199,6 +1199,12 @@ bool avic_hardware_setup(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
|
||||
!boot_cpu_has(X86_FEATURE_HV_INUSE_WR_ALLOWED)) {
|
||||
pr_warn("AVIC disabled: missing HvInUseWrAllowed on SNP-enabled system\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_AVIC)) {
|
||||
pr_info("AVIC enabled\n");
|
||||
} else if (force_avic) {
|
||||
|
|
|
|||
|
|
@ -3201,15 +3201,6 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
|
|||
if (data & ~supported_de_cfg)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Don't let the guest change the host-programmed value. The
|
||||
* MSR is very model specific, i.e. contains multiple bits that
|
||||
* are completely unknown to KVM, and the one bit known to KVM
|
||||
* is simply a reflection of hardware capabilities.
|
||||
*/
|
||||
if (!msr->host_initiated && data != svm->msr_decfg)
|
||||
return 1;
|
||||
|
||||
svm->msr_decfg = data;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#ifndef __KVM_X86_VMX_POSTED_INTR_H
|
||||
#define __KVM_X86_VMX_POSTED_INTR_H
|
||||
|
||||
#include <linux/find.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <asm/posted_intr.h>
|
||||
|
||||
void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu);
|
||||
|
|
|
|||
|
|
@ -9976,7 +9976,7 @@ static int complete_hypercall_exit(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
u64 ret = vcpu->run->hypercall.ret;
|
||||
|
||||
if (!is_64_bit_mode(vcpu))
|
||||
if (!is_64_bit_hypercall(vcpu))
|
||||
ret = (u32)ret;
|
||||
kvm_rax_write(vcpu, ret);
|
||||
++vcpu->stat.hypercalls;
|
||||
|
|
@ -12724,6 +12724,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
kvm_hv_init_vm(kvm);
|
||||
kvm_xen_init_vm(kvm);
|
||||
|
||||
if (ignore_msrs && !report_ignored_msrs) {
|
||||
pr_warn_once("Running KVM with ignore_msrs=1 and report_ignored_msrs=0 is not a\n"
|
||||
"a supported configuration. Lying to the guest about the existence of MSRs\n"
|
||||
"may cause the guest operating system to hang or produce errors. If a guest\n"
|
||||
"does not run without ignore_msrs=1, please report it to kvm@vger.kernel.org.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_uninit_mmu:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <linux/console.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/instrumentation.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -21,7 +22,8 @@
|
|||
|
||||
#include "xen-ops.h"
|
||||
|
||||
EXPORT_SYMBOL_GPL(hypercall_page);
|
||||
DEFINE_STATIC_CALL(xen_hypercall, xen_hypercall_hvm);
|
||||
EXPORT_STATIC_CALL_TRAMP(xen_hypercall);
|
||||
|
||||
/*
|
||||
* Pointer to the xen_vcpu_info structure or
|
||||
|
|
@ -68,6 +70,67 @@ EXPORT_SYMBOL(xen_start_flags);
|
|||
*/
|
||||
struct shared_info *HYPERVISOR_shared_info = &xen_dummy_shared_info;
|
||||
|
||||
static __ref void xen_get_vendor(void)
|
||||
{
|
||||
init_cpu_devs();
|
||||
cpu_detect(&boot_cpu_data);
|
||||
get_cpu_vendor(&boot_cpu_data);
|
||||
}
|
||||
|
||||
void xen_hypercall_setfunc(void)
|
||||
{
|
||||
if (static_call_query(xen_hypercall) != xen_hypercall_hvm)
|
||||
return;
|
||||
|
||||
if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
|
||||
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON))
|
||||
static_call_update(xen_hypercall, xen_hypercall_amd);
|
||||
else
|
||||
static_call_update(xen_hypercall, xen_hypercall_intel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate processor vendor in order to select the correct hypercall
|
||||
* function for HVM/PVH guests.
|
||||
* Might be called very early in boot before vendor has been set by
|
||||
* early_cpu_init().
|
||||
*/
|
||||
noinstr void *__xen_hypercall_setfunc(void)
|
||||
{
|
||||
void (*func)(void);
|
||||
|
||||
/*
|
||||
* Xen is supported only on CPUs with CPUID, so testing for
|
||||
* X86_FEATURE_CPUID is a test for early_cpu_init() having been
|
||||
* run.
|
||||
*
|
||||
* Note that __xen_hypercall_setfunc() is noinstr only due to a nasty
|
||||
* dependency chain: it is being called via the xen_hypercall static
|
||||
* call when running as a PVH or HVM guest. Hypercalls need to be
|
||||
* noinstr due to PV guests using hypercalls in noinstr code. So we
|
||||
* can safely tag the function body as "instrumentation ok", since
|
||||
* the PV guest requirement is not of interest here (xen_get_vendor()
|
||||
* calls noinstr functions, and static_call_update_early() might do
|
||||
* so, too).
|
||||
*/
|
||||
instrumentation_begin();
|
||||
|
||||
if (!boot_cpu_has(X86_FEATURE_CPUID))
|
||||
xen_get_vendor();
|
||||
|
||||
if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
|
||||
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON))
|
||||
func = xen_hypercall_amd;
|
||||
else
|
||||
func = xen_hypercall_intel;
|
||||
|
||||
static_call_update_early(xen_hypercall, func);
|
||||
|
||||
instrumentation_end();
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static int xen_cpu_up_online(unsigned int cpu)
|
||||
{
|
||||
xen_init_lock_cpu(cpu);
|
||||
|
|
|
|||
|
|
@ -106,15 +106,8 @@ static void __init init_hvm_pv_info(void)
|
|||
/* PVH set up hypercall page in xen_prepare_pvh(). */
|
||||
if (xen_pvh_domain())
|
||||
pv_info.name = "Xen PVH";
|
||||
else {
|
||||
u64 pfn;
|
||||
uint32_t msr;
|
||||
|
||||
else
|
||||
pv_info.name = "Xen HVM";
|
||||
msr = cpuid_ebx(base + 2);
|
||||
pfn = __pa(hypercall_page);
|
||||
wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32));
|
||||
}
|
||||
|
||||
xen_setup_features();
|
||||
|
||||
|
|
@ -300,6 +293,10 @@ static uint32_t __init xen_platform_hvm(void)
|
|||
if (xen_pv_domain())
|
||||
return 0;
|
||||
|
||||
/* Set correct hypercall function. */
|
||||
if (xen_domain)
|
||||
xen_hypercall_setfunc();
|
||||
|
||||
if (xen_pvh_domain() && nopv) {
|
||||
/* Guest booting via the Xen-PVH boot entry goes here */
|
||||
pr_info("\"nopv\" parameter is ignored in PVH guest\n");
|
||||
|
|
|
|||
|
|
@ -1341,6 +1341,9 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
|
|||
|
||||
xen_domain_type = XEN_PV_DOMAIN;
|
||||
xen_start_flags = xen_start_info->flags;
|
||||
/* Interrupts are guaranteed to be off initially. */
|
||||
early_boot_irqs_disabled = true;
|
||||
static_call_update_early(xen_hypercall, xen_hypercall_pv);
|
||||
|
||||
xen_setup_features();
|
||||
|
||||
|
|
@ -1431,7 +1434,6 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
|
|||
WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_pv, xen_cpu_dead_pv));
|
||||
|
||||
local_irq_disable();
|
||||
early_boot_irqs_disabled = true;
|
||||
|
||||
xen_raw_console_write("mapping kernel into physical memory\n");
|
||||
xen_setup_kernel_pagetable((pgd_t *)xen_start_info->pt_base,
|
||||
|
|
|
|||
|
|
@ -129,17 +129,10 @@ static void __init pvh_arch_setup(void)
|
|||
|
||||
void __init xen_pvh_init(struct boot_params *boot_params)
|
||||
{
|
||||
u32 msr;
|
||||
u64 pfn;
|
||||
|
||||
xen_pvh = 1;
|
||||
xen_domain_type = XEN_HVM_DOMAIN;
|
||||
xen_start_flags = pvh_start_info.flags;
|
||||
|
||||
msr = cpuid_ebx(xen_cpuid_base() + 2);
|
||||
pfn = __pa(hypercall_page);
|
||||
wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32));
|
||||
|
||||
x86_init.oem.arch_setup = pvh_arch_setup;
|
||||
x86_init.oem.banner = xen_banner;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,9 +20,32 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <../entry/calling.h>
|
||||
|
||||
.pushsection .noinstr.text, "ax"
|
||||
/*
|
||||
* PV hypercall interface to the hypervisor.
|
||||
*
|
||||
* Called via inline asm(), so better preserve %rcx and %r11.
|
||||
*
|
||||
* Input:
|
||||
* %eax: hypercall number
|
||||
* %rdi, %rsi, %rdx, %r10, %r8: args 1..5 for the hypercall
|
||||
* Output: %rax
|
||||
*/
|
||||
SYM_FUNC_START(xen_hypercall_pv)
|
||||
ANNOTATE_NOENDBR
|
||||
push %rcx
|
||||
push %r11
|
||||
UNWIND_HINT_SAVE
|
||||
syscall
|
||||
UNWIND_HINT_RESTORE
|
||||
pop %r11
|
||||
pop %rcx
|
||||
RET
|
||||
SYM_FUNC_END(xen_hypercall_pv)
|
||||
|
||||
/*
|
||||
* Disabling events is simply a matter of making the event mask
|
||||
* non-zero.
|
||||
|
|
@ -176,7 +199,6 @@ SYM_CODE_START(xen_early_idt_handler_array)
|
|||
SYM_CODE_END(xen_early_idt_handler_array)
|
||||
__FINIT
|
||||
|
||||
hypercall_iret = hypercall_page + __HYPERVISOR_iret * 32
|
||||
/*
|
||||
* Xen64 iret frame:
|
||||
*
|
||||
|
|
@ -186,17 +208,28 @@ hypercall_iret = hypercall_page + __HYPERVISOR_iret * 32
|
|||
* cs
|
||||
* rip <-- standard iret frame
|
||||
*
|
||||
* flags
|
||||
* flags <-- xen_iret must push from here on
|
||||
*
|
||||
* rcx }
|
||||
* r11 }<-- pushed by hypercall page
|
||||
* rsp->rax }
|
||||
* rcx
|
||||
* r11
|
||||
* rsp->rax
|
||||
*/
|
||||
.macro xen_hypercall_iret
|
||||
pushq $0 /* Flags */
|
||||
push %rcx
|
||||
push %r11
|
||||
push %rax
|
||||
mov $__HYPERVISOR_iret, %eax
|
||||
syscall /* Do the IRET. */
|
||||
#ifdef CONFIG_MITIGATION_SLS
|
||||
int3
|
||||
#endif
|
||||
.endm
|
||||
|
||||
SYM_CODE_START(xen_iret)
|
||||
UNWIND_HINT_UNDEFINED
|
||||
ANNOTATE_NOENDBR
|
||||
pushq $0
|
||||
jmp hypercall_iret
|
||||
xen_hypercall_iret
|
||||
SYM_CODE_END(xen_iret)
|
||||
|
||||
/*
|
||||
|
|
@ -301,8 +334,7 @@ SYM_CODE_START(xen_entry_SYSENTER_compat)
|
|||
ENDBR
|
||||
lea 16(%rsp), %rsp /* strip %rcx, %r11 */
|
||||
mov $-ENOSYS, %rax
|
||||
pushq $0
|
||||
jmp hypercall_iret
|
||||
xen_hypercall_iret
|
||||
SYM_CODE_END(xen_entry_SYSENTER_compat)
|
||||
SYM_CODE_END(xen_entry_SYSCALL_compat)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@
|
|||
|
||||
#include <linux/elfnote.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/instrumentation.h>
|
||||
|
||||
#include <asm/boot.h>
|
||||
#include <asm/asm.h>
|
||||
#include <asm/frame.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/page_types.h>
|
||||
#include <asm/percpu.h>
|
||||
|
|
@ -20,28 +22,6 @@
|
|||
#include <xen/interface/xen-mca.h>
|
||||
#include <asm/xen/interface.h>
|
||||
|
||||
.pushsection .noinstr.text, "ax"
|
||||
.balign PAGE_SIZE
|
||||
SYM_CODE_START(hypercall_page)
|
||||
.rept (PAGE_SIZE / 32)
|
||||
UNWIND_HINT_FUNC
|
||||
ANNOTATE_NOENDBR
|
||||
ANNOTATE_UNRET_SAFE
|
||||
ret
|
||||
/*
|
||||
* Xen will write the hypercall page, and sort out ENDBR.
|
||||
*/
|
||||
.skip 31, 0xcc
|
||||
.endr
|
||||
|
||||
#define HYPERCALL(n) \
|
||||
.equ xen_hypercall_##n, hypercall_page + __HYPERVISOR_##n * 32; \
|
||||
.type xen_hypercall_##n, @function; .size xen_hypercall_##n, 32
|
||||
#include <asm/xen-hypercalls.h>
|
||||
#undef HYPERCALL
|
||||
SYM_CODE_END(hypercall_page)
|
||||
.popsection
|
||||
|
||||
#ifdef CONFIG_XEN_PV
|
||||
__INIT
|
||||
SYM_CODE_START(startup_xen)
|
||||
|
|
@ -87,6 +67,87 @@ SYM_CODE_END(xen_cpu_bringup_again)
|
|||
#endif
|
||||
#endif
|
||||
|
||||
.pushsection .noinstr.text, "ax"
|
||||
/*
|
||||
* Xen hypercall interface to the hypervisor.
|
||||
*
|
||||
* Input:
|
||||
* %eax: hypercall number
|
||||
* 32-bit:
|
||||
* %ebx, %ecx, %edx, %esi, %edi: args 1..5 for the hypercall
|
||||
* 64-bit:
|
||||
* %rdi, %rsi, %rdx, %r10, %r8: args 1..5 for the hypercall
|
||||
* Output: %[er]ax
|
||||
*/
|
||||
SYM_FUNC_START(xen_hypercall_hvm)
|
||||
ENDBR
|
||||
FRAME_BEGIN
|
||||
/* Save all relevant registers (caller save and arguments). */
|
||||
#ifdef CONFIG_X86_32
|
||||
push %eax
|
||||
push %ebx
|
||||
push %ecx
|
||||
push %edx
|
||||
push %esi
|
||||
push %edi
|
||||
#else
|
||||
push %rax
|
||||
push %rcx
|
||||
push %rdx
|
||||
push %rdi
|
||||
push %rsi
|
||||
push %r11
|
||||
push %r10
|
||||
push %r9
|
||||
push %r8
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
pushq $0 /* Dummy push for stack alignment. */
|
||||
#endif
|
||||
#endif
|
||||
/* Set the vendor specific function. */
|
||||
call __xen_hypercall_setfunc
|
||||
/* Set ZF = 1 if AMD, Restore saved registers. */
|
||||
#ifdef CONFIG_X86_32
|
||||
lea xen_hypercall_amd, %ebx
|
||||
cmp %eax, %ebx
|
||||
pop %edi
|
||||
pop %esi
|
||||
pop %edx
|
||||
pop %ecx
|
||||
pop %ebx
|
||||
pop %eax
|
||||
#else
|
||||
lea xen_hypercall_amd(%rip), %rbx
|
||||
cmp %rax, %rbx
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
pop %rax /* Dummy pop. */
|
||||
#endif
|
||||
pop %r8
|
||||
pop %r9
|
||||
pop %r10
|
||||
pop %r11
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
pop %rdx
|
||||
pop %rcx
|
||||
pop %rax
|
||||
#endif
|
||||
/* Use correct hypercall function. */
|
||||
jz xen_hypercall_amd
|
||||
jmp xen_hypercall_intel
|
||||
SYM_FUNC_END(xen_hypercall_hvm)
|
||||
|
||||
SYM_FUNC_START(xen_hypercall_amd)
|
||||
vmmcall
|
||||
RET
|
||||
SYM_FUNC_END(xen_hypercall_amd)
|
||||
|
||||
SYM_FUNC_START(xen_hypercall_intel)
|
||||
vmcall
|
||||
RET
|
||||
SYM_FUNC_END(xen_hypercall_intel)
|
||||
.popsection
|
||||
|
||||
ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz "linux")
|
||||
ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION, .asciz "2.6")
|
||||
ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION, .asciz "xen-3.0")
|
||||
|
|
@ -116,8 +177,6 @@ SYM_CODE_END(xen_cpu_bringup_again)
|
|||
#else
|
||||
# define FEATURES_DOM0 0
|
||||
#endif
|
||||
ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .globl xen_elfnote_hypercall_page;
|
||||
xen_elfnote_hypercall_page: _ASM_PTR xen_elfnote_hypercall_page_value - .)
|
||||
ELFNOTE(Xen, XEN_ELFNOTE_SUPPORTED_FEATURES,
|
||||
.long FEATURES_PV | FEATURES_PVH | FEATURES_DOM0)
|
||||
ELFNOTE(Xen, XEN_ELFNOTE_LOADER, .asciz "generic")
|
||||
|
|
|
|||
|
|
@ -326,4 +326,13 @@ static inline void xen_smp_intr_free_pv(unsigned int cpu) {}
|
|||
static inline void xen_smp_count_cpus(void) { }
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#ifdef CONFIG_XEN_PV
|
||||
void xen_hypercall_pv(void);
|
||||
#endif
|
||||
void xen_hypercall_hvm(void);
|
||||
void xen_hypercall_amd(void);
|
||||
void xen_hypercall_intel(void);
|
||||
void xen_hypercall_setfunc(void);
|
||||
void *__xen_hypercall_setfunc(void);
|
||||
|
||||
#endif /* XEN_OPS_H */
|
||||
|
|
|
|||
|
|
@ -155,8 +155,7 @@ int set_blocksize(struct file *file, int size)
|
|||
struct inode *inode = file->f_mapping->host;
|
||||
struct block_device *bdev = I_BDEV(inode);
|
||||
|
||||
/* Size must be a power of two, and between 512 and PAGE_SIZE */
|
||||
if (size > PAGE_SIZE || size < 512 || !is_power_of_2(size))
|
||||
if (blk_validate_block_size(size))
|
||||
return -EINVAL;
|
||||
|
||||
/* Size cannot be smaller than the size supported by the device */
|
||||
|
|
|
|||
|
|
@ -275,13 +275,15 @@ void blk_mq_sysfs_unregister_hctxs(struct request_queue *q)
|
|||
struct blk_mq_hw_ctx *hctx;
|
||||
unsigned long i;
|
||||
|
||||
lockdep_assert_held(&q->sysfs_dir_lock);
|
||||
|
||||
mutex_lock(&q->sysfs_dir_lock);
|
||||
if (!q->mq_sysfs_init_done)
|
||||
return;
|
||||
goto unlock;
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i)
|
||||
blk_mq_unregister_hctx(hctx);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&q->sysfs_dir_lock);
|
||||
}
|
||||
|
||||
int blk_mq_sysfs_register_hctxs(struct request_queue *q)
|
||||
|
|
@ -290,10 +292,9 @@ int blk_mq_sysfs_register_hctxs(struct request_queue *q)
|
|||
unsigned long i;
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(&q->sysfs_dir_lock);
|
||||
|
||||
mutex_lock(&q->sysfs_dir_lock);
|
||||
if (!q->mq_sysfs_init_done)
|
||||
return ret;
|
||||
goto unlock;
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
ret = blk_mq_register_hctx(hctx);
|
||||
|
|
@ -301,5 +302,8 @@ int blk_mq_sysfs_register_hctxs(struct request_queue *q)
|
|||
break;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&q->sysfs_dir_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4412,6 +4412,15 @@ struct gendisk *blk_mq_alloc_disk_for_queue(struct request_queue *q,
|
|||
}
|
||||
EXPORT_SYMBOL(blk_mq_alloc_disk_for_queue);
|
||||
|
||||
/*
|
||||
* Only hctx removed from cpuhp list can be reused
|
||||
*/
|
||||
static bool blk_mq_hctx_is_reusable(struct blk_mq_hw_ctx *hctx)
|
||||
{
|
||||
return hlist_unhashed(&hctx->cpuhp_online) &&
|
||||
hlist_unhashed(&hctx->cpuhp_dead);
|
||||
}
|
||||
|
||||
static struct blk_mq_hw_ctx *blk_mq_alloc_and_init_hctx(
|
||||
struct blk_mq_tag_set *set, struct request_queue *q,
|
||||
int hctx_idx, int node)
|
||||
|
|
@ -4421,7 +4430,7 @@ static struct blk_mq_hw_ctx *blk_mq_alloc_and_init_hctx(
|
|||
/* reuse dead hctx first */
|
||||
spin_lock(&q->unused_hctx_lock);
|
||||
list_for_each_entry(tmp, &q->unused_hctx_list, hctx_list) {
|
||||
if (tmp->numa_node == node) {
|
||||
if (tmp->numa_node == node && blk_mq_hctx_is_reusable(tmp)) {
|
||||
hctx = tmp;
|
||||
break;
|
||||
}
|
||||
|
|
@ -4453,8 +4462,7 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
|
|||
unsigned long i, j;
|
||||
|
||||
/* protect against switching io scheduler */
|
||||
lockdep_assert_held(&q->sysfs_lock);
|
||||
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
for (i = 0; i < set->nr_hw_queues; i++) {
|
||||
int old_node;
|
||||
int node = blk_mq_get_hctx_node(set, i);
|
||||
|
|
@ -4487,6 +4495,7 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
|
|||
|
||||
xa_for_each_start(&q->hctx_table, j, hctx, j)
|
||||
blk_mq_exit_hctx(q, set, hctx, j);
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
|
||||
/* unregister cpuhp callbacks for exited hctxs */
|
||||
blk_mq_remove_hw_queues_cpuhp(q);
|
||||
|
|
@ -4518,14 +4527,10 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
|
|||
|
||||
xa_init(&q->hctx_table);
|
||||
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
|
||||
blk_mq_realloc_hw_ctxs(set, q);
|
||||
if (!q->nr_hw_queues)
|
||||
goto err_hctxs;
|
||||
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
|
||||
INIT_WORK(&q->timeout_work, blk_mq_timeout_work);
|
||||
blk_queue_rq_timeout(q, set->timeout ? set->timeout : 30 * HZ);
|
||||
|
||||
|
|
@ -4544,7 +4549,6 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
|
|||
return 0;
|
||||
|
||||
err_hctxs:
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
blk_mq_release(q);
|
||||
err_exit:
|
||||
q->mq_ops = NULL;
|
||||
|
|
@ -4925,12 +4929,12 @@ static bool blk_mq_elv_switch_none(struct list_head *head,
|
|||
return false;
|
||||
|
||||
/* q->elevator needs protection from ->sysfs_lock */
|
||||
lockdep_assert_held(&q->sysfs_lock);
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
|
||||
/* the check has to be done with holding sysfs_lock */
|
||||
if (!q->elevator) {
|
||||
kfree(qe);
|
||||
goto out;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&qe->node);
|
||||
|
|
@ -4940,7 +4944,9 @@ static bool blk_mq_elv_switch_none(struct list_head *head,
|
|||
__elevator_get(qe->type);
|
||||
list_add(&qe->node, head);
|
||||
elevator_disable(q);
|
||||
out:
|
||||
unlock:
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -4969,9 +4975,11 @@ static void blk_mq_elv_switch_back(struct list_head *head,
|
|||
list_del(&qe->node);
|
||||
kfree(qe);
|
||||
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
elevator_switch(q, t);
|
||||
/* drop the reference acquired in blk_mq_elv_switch_none */
|
||||
elevator_put(t);
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
}
|
||||
|
||||
static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
|
||||
|
|
@ -4991,11 +4999,8 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
|
|||
if (set->nr_maps == 1 && nr_hw_queues == set->nr_hw_queues)
|
||||
return;
|
||||
|
||||
list_for_each_entry(q, &set->tag_list, tag_set_list) {
|
||||
mutex_lock(&q->sysfs_dir_lock);
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
list_for_each_entry(q, &set->tag_list, tag_set_list)
|
||||
blk_mq_freeze_queue(q);
|
||||
}
|
||||
/*
|
||||
* Switch IO scheduler to 'none', cleaning up the data associated
|
||||
* with the previous scheduler. We will switch back once we are done
|
||||
|
|
@ -5051,11 +5056,8 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
|
|||
list_for_each_entry(q, &set->tag_list, tag_set_list)
|
||||
blk_mq_elv_switch_back(&head, q);
|
||||
|
||||
list_for_each_entry(q, &set->tag_list, tag_set_list) {
|
||||
list_for_each_entry(q, &set->tag_list, tag_set_list)
|
||||
blk_mq_unfreeze_queue(q);
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
mutex_unlock(&q->sysfs_dir_lock);
|
||||
}
|
||||
|
||||
/* Free the excess tags when nr_hw_queues shrink. */
|
||||
for (i = set->nr_hw_queues; i < prev_nr_hw_queues; i++)
|
||||
|
|
|
|||
|
|
@ -706,11 +706,11 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr,
|
|||
if (entry->load_module)
|
||||
entry->load_module(disk, page, length);
|
||||
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
blk_mq_freeze_queue(q);
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
res = entry->store(disk, page, length);
|
||||
blk_mq_unfreeze_queue(q);
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
blk_mq_unfreeze_queue(q);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ static void ivpu_bo_print_info(struct ivpu_bo *bo, struct drm_printer *p)
|
|||
mutex_lock(&bo->lock);
|
||||
|
||||
drm_printf(p, "%-9p %-3u 0x%-12llx %-10lu 0x%-8x %-4u",
|
||||
bo, bo->ctx->id, bo->vpu_addr, bo->base.base.size,
|
||||
bo, bo->ctx ? bo->ctx->id : 0, bo->vpu_addr, bo->base.base.size,
|
||||
bo->flags, kref_read(&bo->base.base.refcount));
|
||||
|
||||
if (bo->base.pages)
|
||||
|
|
|
|||
|
|
@ -612,18 +612,22 @@ int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev)
|
|||
if (!ivpu_mmu_ensure_pgd(vdev, &vdev->rctx.pgtable)) {
|
||||
ivpu_err(vdev, "Failed to allocate root page table for reserved context\n");
|
||||
ret = -ENOMEM;
|
||||
goto unlock;
|
||||
goto err_ctx_fini;
|
||||
}
|
||||
|
||||
ret = ivpu_mmu_cd_set(vdev, vdev->rctx.id, &vdev->rctx.pgtable);
|
||||
if (ret) {
|
||||
ivpu_err(vdev, "Failed to set context descriptor for reserved context\n");
|
||||
goto unlock;
|
||||
goto err_ctx_fini;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&vdev->rctx.lock);
|
||||
return ret;
|
||||
|
||||
err_ctx_fini:
|
||||
mutex_unlock(&vdev->rctx.lock);
|
||||
ivpu_mmu_context_fini(vdev, &vdev->rctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev)
|
||||
|
|
|
|||
|
|
@ -378,6 +378,7 @@ void ivpu_pm_init(struct ivpu_device *vdev)
|
|||
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, delay);
|
||||
pm_runtime_set_active(dev);
|
||||
|
||||
ivpu_dbg(vdev, PM, "Autosuspend delay = %d\n", delay);
|
||||
}
|
||||
|
|
@ -392,7 +393,6 @@ void ivpu_pm_enable(struct ivpu_device *vdev)
|
|||
{
|
||||
struct device *dev = vdev->drm.dev;
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_allow(dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
|
|
|||
|
|
@ -135,10 +135,10 @@ config ACPI_REV_OVERRIDE_POSSIBLE
|
|||
config ACPI_EC
|
||||
bool "Embedded Controller"
|
||||
depends on HAS_IOPORT
|
||||
default X86
|
||||
default X86 || LOONGARCH
|
||||
help
|
||||
This driver handles communication with the microcontroller
|
||||
on many x86 laptops and other machines.
|
||||
on many x86/LoongArch laptops and other machines.
|
||||
|
||||
config ACPI_EC_DEBUGFS
|
||||
tristate "EC read/write access through /sys/kernel/debug/ec"
|
||||
|
|
|
|||
|
|
@ -489,7 +489,7 @@ config IMG_ASCII_LCD
|
|||
|
||||
config HT16K33
|
||||
tristate "Holtek Ht16K33 LED controller with keyscan"
|
||||
depends on FB && I2C && INPUT
|
||||
depends on FB && I2C && INPUT && BACKLIGHT_CLASS_DEVICE
|
||||
select FB_SYSMEM_HELPERS
|
||||
select INPUT_MATRIXKMAP
|
||||
select FB_BACKLIGHT
|
||||
|
|
|
|||
|
|
@ -614,6 +614,12 @@ static ssize_t backing_dev_store(struct device *dev,
|
|||
}
|
||||
|
||||
nr_pages = i_size_read(inode) >> PAGE_SHIFT;
|
||||
/* Refuse to use zero sized device (also prevents self reference) */
|
||||
if (!nr_pages) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bitmap_sz = BITS_TO_LONGS(nr_pages) * sizeof(long);
|
||||
bitmap = kvzalloc(bitmap_sz, GFP_KERNEL);
|
||||
if (!bitmap) {
|
||||
|
|
@ -1438,12 +1444,16 @@ static void zram_meta_free(struct zram *zram, u64 disksize)
|
|||
size_t num_pages = disksize >> PAGE_SHIFT;
|
||||
size_t index;
|
||||
|
||||
if (!zram->table)
|
||||
return;
|
||||
|
||||
/* Free all pages that are still in this zram device */
|
||||
for (index = 0; index < num_pages; index++)
|
||||
zram_free_page(zram, index);
|
||||
|
||||
zs_destroy_pool(zram->mem_pool);
|
||||
vfree(zram->table);
|
||||
zram->table = NULL;
|
||||
}
|
||||
|
||||
static bool zram_meta_alloc(struct zram *zram, u64 disksize)
|
||||
|
|
@ -2320,11 +2330,6 @@ static void zram_reset_device(struct zram *zram)
|
|||
|
||||
zram->limit_pages = 0;
|
||||
|
||||
if (!init_done(zram)) {
|
||||
up_write(&zram->init_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
set_capacity_and_notify(zram->disk, 0);
|
||||
part_stat_set_all(zram->disk->part0, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@
|
|||
#include <asm/mshyperv.h>
|
||||
|
||||
static struct clock_event_device __percpu *hv_clock_event;
|
||||
static u64 hv_sched_clock_offset __ro_after_init;
|
||||
/* Note: offset can hold negative values after hibernation. */
|
||||
static u64 hv_sched_clock_offset __read_mostly;
|
||||
|
||||
/*
|
||||
* If false, we're using the old mechanism for stimer0 interrupts
|
||||
|
|
@ -470,6 +471,17 @@ static void resume_hv_clock_tsc(struct clocksource *arg)
|
|||
hv_set_msr(HV_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called during resume from hibernation, from overridden
|
||||
* x86_platform.restore_sched_clock_state routine. This is to adjust offsets
|
||||
* used to calculate time for hv tsc page based sched_clock, to account for
|
||||
* time spent before hibernation.
|
||||
*/
|
||||
void hv_adj_sched_clock_offset(u64 offset)
|
||||
{
|
||||
hv_sched_clock_offset -= offset;
|
||||
}
|
||||
|
||||
#ifdef HAVE_VDSO_CLOCKMODE_HVCLOCK
|
||||
static int hv_cs_enable(struct clocksource *cs)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -374,15 +374,19 @@ static inline int amd_pstate_cppc_enable(bool enable)
|
|||
|
||||
static int msr_init_perf(struct amd_cpudata *cpudata)
|
||||
{
|
||||
u64 cap1;
|
||||
u64 cap1, numerator;
|
||||
|
||||
int ret = rdmsrl_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
|
||||
&cap1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
WRITE_ONCE(cpudata->highest_perf, AMD_CPPC_HIGHEST_PERF(cap1));
|
||||
WRITE_ONCE(cpudata->max_limit_perf, AMD_CPPC_HIGHEST_PERF(cap1));
|
||||
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
WRITE_ONCE(cpudata->highest_perf, numerator);
|
||||
WRITE_ONCE(cpudata->max_limit_perf, numerator);
|
||||
WRITE_ONCE(cpudata->nominal_perf, AMD_CPPC_NOMINAL_PERF(cap1));
|
||||
WRITE_ONCE(cpudata->lowest_nonlinear_perf, AMD_CPPC_LOWNONLIN_PERF(cap1));
|
||||
WRITE_ONCE(cpudata->lowest_perf, AMD_CPPC_LOWEST_PERF(cap1));
|
||||
|
|
@ -394,13 +398,18 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
|
|||
static int shmem_init_perf(struct amd_cpudata *cpudata)
|
||||
{
|
||||
struct cppc_perf_caps cppc_perf;
|
||||
u64 numerator;
|
||||
|
||||
int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
WRITE_ONCE(cpudata->highest_perf, cppc_perf.highest_perf);
|
||||
WRITE_ONCE(cpudata->max_limit_perf, cppc_perf.highest_perf);
|
||||
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
WRITE_ONCE(cpudata->highest_perf, numerator);
|
||||
WRITE_ONCE(cpudata->max_limit_perf, numerator);
|
||||
WRITE_ONCE(cpudata->nominal_perf, cppc_perf.nominal_perf);
|
||||
WRITE_ONCE(cpudata->lowest_nonlinear_perf,
|
||||
cppc_perf.lowest_nonlinear_perf);
|
||||
|
|
@ -561,16 +570,13 @@ static int amd_pstate_verify(struct cpufreq_policy_data *policy_data)
|
|||
|
||||
static int amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
|
||||
{
|
||||
u32 max_limit_perf, min_limit_perf, lowest_perf, max_perf;
|
||||
u32 max_limit_perf, min_limit_perf, lowest_perf, max_perf, max_freq;
|
||||
struct amd_cpudata *cpudata = policy->driver_data;
|
||||
|
||||
if (cpudata->boost_supported && !policy->boost_enabled)
|
||||
max_perf = READ_ONCE(cpudata->nominal_perf);
|
||||
else
|
||||
max_perf = READ_ONCE(cpudata->highest_perf);
|
||||
|
||||
max_limit_perf = div_u64(policy->max * max_perf, policy->cpuinfo.max_freq);
|
||||
min_limit_perf = div_u64(policy->min * max_perf, policy->cpuinfo.max_freq);
|
||||
max_perf = READ_ONCE(cpudata->highest_perf);
|
||||
max_freq = READ_ONCE(cpudata->max_freq);
|
||||
max_limit_perf = div_u64(policy->max * max_perf, max_freq);
|
||||
min_limit_perf = div_u64(policy->min * max_perf, max_freq);
|
||||
|
||||
lowest_perf = READ_ONCE(cpudata->lowest_perf);
|
||||
if (min_limit_perf < lowest_perf)
|
||||
|
|
@ -889,7 +895,6 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
|
|||
{
|
||||
int ret;
|
||||
u32 min_freq, max_freq;
|
||||
u64 numerator;
|
||||
u32 nominal_perf, nominal_freq;
|
||||
u32 lowest_nonlinear_perf, lowest_nonlinear_freq;
|
||||
u32 boost_ratio, lowest_nonlinear_ratio;
|
||||
|
|
@ -911,10 +916,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
|
|||
|
||||
nominal_perf = READ_ONCE(cpudata->nominal_perf);
|
||||
|
||||
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
|
||||
if (ret)
|
||||
return ret;
|
||||
boost_ratio = div_u64(numerator << SCHED_CAPACITY_SHIFT, nominal_perf);
|
||||
boost_ratio = div_u64(cpudata->highest_perf << SCHED_CAPACITY_SHIFT, nominal_perf);
|
||||
max_freq = (nominal_freq * boost_ratio >> SCHED_CAPACITY_SHIFT) * 1000;
|
||||
|
||||
lowest_nonlinear_perf = READ_ONCE(cpudata->lowest_nonlinear_perf);
|
||||
|
|
@ -1869,18 +1871,18 @@ static int __init amd_pstate_init(void)
|
|||
static_call_update(amd_pstate_update_perf, shmem_update_perf);
|
||||
}
|
||||
|
||||
ret = amd_pstate_register_driver(cppc_state);
|
||||
if (ret) {
|
||||
pr_err("failed to register with return %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (amd_pstate_prefcore) {
|
||||
ret = amd_detect_prefcore(&amd_pstate_prefcore);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = amd_pstate_register_driver(cppc_state);
|
||||
if (ret) {
|
||||
pr_err("failed to register with return %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_root = bus_get_dev_root(&cpu_subsys);
|
||||
if (dev_root) {
|
||||
ret = sysfs_create_group(&dev_root->kobj, &amd_pstate_global_attr_group);
|
||||
|
|
|
|||
|
|
@ -1295,6 +1295,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
|
|||
struct cxl_region_params *p = &cxlr->params;
|
||||
struct cxl_decoder *cxld = cxl_rr->decoder;
|
||||
struct cxl_switch_decoder *cxlsd;
|
||||
struct cxl_port *iter = port;
|
||||
u16 eig, peig;
|
||||
u8 eiw, peiw;
|
||||
|
||||
|
|
@ -1311,16 +1312,26 @@ static int cxl_port_setup_targets(struct cxl_port *port,
|
|||
|
||||
cxlsd = to_cxl_switch_decoder(&cxld->dev);
|
||||
if (cxl_rr->nr_targets_set) {
|
||||
int i, distance;
|
||||
int i, distance = 1;
|
||||
struct cxl_region_ref *cxl_rr_iter;
|
||||
|
||||
/*
|
||||
* Passthrough decoders impose no distance requirements between
|
||||
* peers
|
||||
* The "distance" between peer downstream ports represents which
|
||||
* endpoint positions in the region interleave a given port can
|
||||
* host.
|
||||
*
|
||||
* For example, at the root of a hierarchy the distance is
|
||||
* always 1 as every index targets a different host-bridge. At
|
||||
* each subsequent switch level those ports map every Nth region
|
||||
* position where N is the width of the switch == distance.
|
||||
*/
|
||||
if (cxl_rr->nr_targets == 1)
|
||||
distance = 0;
|
||||
else
|
||||
distance = p->nr_targets / cxl_rr->nr_targets;
|
||||
do {
|
||||
cxl_rr_iter = cxl_rr_load(iter, cxlr);
|
||||
distance *= cxl_rr_iter->nr_targets;
|
||||
iter = to_cxl_port(iter->dev.parent);
|
||||
} while (!is_cxl_root(iter));
|
||||
distance *= cxlrd->cxlsd.cxld.interleave_ways;
|
||||
|
||||
for (i = 0; i < cxl_rr->nr_targets_set; i++)
|
||||
if (ep->dport == cxlsd->target[i]) {
|
||||
rc = check_last_peer(cxled, ep, cxl_rr,
|
||||
|
|
|
|||
|
|
@ -836,6 +836,9 @@ static ssize_t rcd_pcie_cap_emit(struct device *dev, u16 offset, char *buf, size
|
|||
if (!root_dev)
|
||||
return -ENXIO;
|
||||
|
||||
if (!dport->regs.rcd_pcie_cap)
|
||||
return -ENXIO;
|
||||
|
||||
guard(device)(root_dev);
|
||||
if (!root_dev->driver)
|
||||
return -ENXIO;
|
||||
|
|
@ -1032,8 +1035,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_pci_ras_unmask(pdev);
|
||||
if (rc)
|
||||
if (cxl_pci_ras_unmask(pdev))
|
||||
dev_dbg(&pdev->dev, "No RAS reporting unmasked\n");
|
||||
|
||||
pci_save_state(pdev);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ static void __dma_buf_debugfs_list_add(struct dma_buf *dmabuf)
|
|||
{
|
||||
}
|
||||
|
||||
static void __dma_buf_debugfs_list_del(struct file *file)
|
||||
static void __dma_buf_debugfs_list_del(struct dma_buf *dmabuf)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ static const struct dma_buf_ops udmabuf_ops = {
|
|||
};
|
||||
|
||||
#define SEALS_WANTED (F_SEAL_SHRINK)
|
||||
#define SEALS_DENIED (F_SEAL_WRITE)
|
||||
#define SEALS_DENIED (F_SEAL_WRITE|F_SEAL_FUTURE_WRITE)
|
||||
|
||||
static int check_memfd_seals(struct file *memfd)
|
||||
{
|
||||
|
|
@ -317,12 +317,10 @@ static int check_memfd_seals(struct file *memfd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int export_udmabuf(struct udmabuf *ubuf,
|
||||
struct miscdevice *device,
|
||||
u32 flags)
|
||||
static struct dma_buf *export_udmabuf(struct udmabuf *ubuf,
|
||||
struct miscdevice *device)
|
||||
{
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||
struct dma_buf *buf;
|
||||
|
||||
ubuf->device = device;
|
||||
exp_info.ops = &udmabuf_ops;
|
||||
|
|
@ -330,11 +328,7 @@ static int export_udmabuf(struct udmabuf *ubuf,
|
|||
exp_info.priv = ubuf;
|
||||
exp_info.flags = O_RDWR;
|
||||
|
||||
buf = dma_buf_export(&exp_info);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
return dma_buf_fd(buf, flags);
|
||||
return dma_buf_export(&exp_info);
|
||||
}
|
||||
|
||||
static long udmabuf_pin_folios(struct udmabuf *ubuf, struct file *memfd,
|
||||
|
|
@ -391,6 +385,7 @@ static long udmabuf_create(struct miscdevice *device,
|
|||
struct folio **folios = NULL;
|
||||
pgoff_t pgcnt = 0, pglimit;
|
||||
struct udmabuf *ubuf;
|
||||
struct dma_buf *dmabuf;
|
||||
long ret = -EINVAL;
|
||||
u32 i, flags;
|
||||
|
||||
|
|
@ -436,23 +431,39 @@ static long udmabuf_create(struct miscdevice *device,
|
|||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take the inode lock to protect against concurrent
|
||||
* memfd_add_seals(), which takes this lock in write mode.
|
||||
*/
|
||||
inode_lock_shared(file_inode(memfd));
|
||||
ret = check_memfd_seals(memfd);
|
||||
if (ret < 0) {
|
||||
fput(memfd);
|
||||
goto err;
|
||||
}
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
ret = udmabuf_pin_folios(ubuf, memfd, list[i].offset,
|
||||
list[i].size, folios);
|
||||
out_unlock:
|
||||
inode_unlock_shared(file_inode(memfd));
|
||||
fput(memfd);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
flags = head->flags & UDMABUF_FLAGS_CLOEXEC ? O_CLOEXEC : 0;
|
||||
ret = export_udmabuf(ubuf, device, flags);
|
||||
if (ret < 0)
|
||||
dmabuf = export_udmabuf(ubuf, device);
|
||||
if (IS_ERR(dmabuf)) {
|
||||
ret = PTR_ERR(dmabuf);
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
* Ownership of ubuf is held by the dmabuf from here.
|
||||
* If the following dma_buf_fd() fails, dma_buf_put() cleans up both the
|
||||
* dmabuf and the ubuf (through udmabuf_ops.release).
|
||||
*/
|
||||
|
||||
ret = dma_buf_fd(dmabuf, flags);
|
||||
if (ret < 0)
|
||||
dma_buf_put(dmabuf);
|
||||
|
||||
kvfree(folios);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -187,13 +187,18 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev)
|
|||
return valid;
|
||||
}
|
||||
|
||||
struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id,
|
||||
const struct ffa_ops *ops)
|
||||
struct ffa_device *
|
||||
ffa_device_register(const struct ffa_partition_info *part_info,
|
||||
const struct ffa_ops *ops)
|
||||
{
|
||||
int id, ret;
|
||||
uuid_t uuid;
|
||||
struct device *dev;
|
||||
struct ffa_device *ffa_dev;
|
||||
|
||||
if (!part_info)
|
||||
return NULL;
|
||||
|
||||
id = ida_alloc_min(&ffa_bus_id, 1, GFP_KERNEL);
|
||||
if (id < 0)
|
||||
return NULL;
|
||||
|
|
@ -210,9 +215,11 @@ struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id,
|
|||
dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id);
|
||||
|
||||
ffa_dev->id = id;
|
||||
ffa_dev->vm_id = vm_id;
|
||||
ffa_dev->vm_id = part_info->id;
|
||||
ffa_dev->properties = part_info->properties;
|
||||
ffa_dev->ops = ops;
|
||||
uuid_copy(&ffa_dev->uuid, uuid);
|
||||
import_uuid(&uuid, (u8 *)part_info->uuid);
|
||||
uuid_copy(&ffa_dev->uuid, &uuid);
|
||||
|
||||
ret = device_register(&ffa_dev->dev);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -1387,7 +1387,6 @@ static struct notifier_block ffa_bus_nb = {
|
|||
static int ffa_setup_partitions(void)
|
||||
{
|
||||
int count, idx, ret;
|
||||
uuid_t uuid;
|
||||
struct ffa_device *ffa_dev;
|
||||
struct ffa_dev_part_info *info;
|
||||
struct ffa_partition_info *pbuf, *tpbuf;
|
||||
|
|
@ -1406,23 +1405,19 @@ static int ffa_setup_partitions(void)
|
|||
|
||||
xa_init(&drv_info->partition_info);
|
||||
for (idx = 0, tpbuf = pbuf; idx < count; idx++, tpbuf++) {
|
||||
import_uuid(&uuid, (u8 *)tpbuf->uuid);
|
||||
|
||||
/* Note that if the UUID will be uuid_null, that will require
|
||||
* ffa_bus_notifier() to find the UUID of this partition id
|
||||
* with help of ffa_device_match_uuid(). FF-A v1.1 and above
|
||||
* provides UUID here for each partition as part of the
|
||||
* discovery API and the same is passed.
|
||||
*/
|
||||
ffa_dev = ffa_device_register(&uuid, tpbuf->id, &ffa_drv_ops);
|
||||
ffa_dev = ffa_device_register(tpbuf, &ffa_drv_ops);
|
||||
if (!ffa_dev) {
|
||||
pr_err("%s: failed to register partition ID 0x%x\n",
|
||||
__func__, tpbuf->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
ffa_dev->properties = tpbuf->properties;
|
||||
|
||||
if (drv_info->version > FFA_VERSION_1_0 &&
|
||||
!(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
|
||||
ffa_mode_32bit_set(ffa_dev);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ config IMX_SCMI_BBM_EXT
|
|||
config IMX_SCMI_MISC_EXT
|
||||
tristate "i.MX SCMI MISC EXTENSION"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
depends on IMX_SCMI_MISC_DRV
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
This enables i.MX System MISC control logic such as gpio expander
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ config IMX_SCU
|
|||
|
||||
config IMX_SCMI_MISC_DRV
|
||||
tristate "IMX SCMI MISC Protocol driver"
|
||||
depends on IMX_SCMI_MISC_EXT || COMPILE_TEST
|
||||
default y if ARCH_MXC
|
||||
help
|
||||
The System Controller Management Interface firmware (SCMI FW) is
|
||||
|
|
|
|||
|
|
@ -402,10 +402,10 @@ static int mpfs_auto_update_available(struct mpfs_auto_update_priv *priv)
|
|||
return -EIO;
|
||||
|
||||
/*
|
||||
* Bit 5 of byte 1 is "UL_Auto Update" & if it is set, Auto Update is
|
||||
* Bit 5 of byte 1 is "UL_IAP" & if it is set, Auto Update is
|
||||
* not possible.
|
||||
*/
|
||||
if (response_msg[1] & AUTO_UPDATE_FEATURE_ENABLED)
|
||||
if ((((u8 *)response_msg)[1] & AUTO_UPDATE_FEATURE_ENABLED))
|
||||
return -EPERM;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ config DRM_KUNIT_TEST
|
|||
config DRM_KMS_HELPER
|
||||
tristate
|
||||
depends on DRM
|
||||
select FB_CORE if DRM_FBDEV_EMULATION
|
||||
help
|
||||
CRTC helpers for KMS drivers.
|
||||
|
||||
|
|
@ -358,6 +359,7 @@ config DRM_TTM_HELPER
|
|||
tristate
|
||||
depends on DRM
|
||||
select DRM_TTM
|
||||
select FB_CORE if DRM_FBDEV_EMULATION
|
||||
select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
|
||||
help
|
||||
Helpers for ttm-based gem objects
|
||||
|
|
@ -365,6 +367,7 @@ config DRM_TTM_HELPER
|
|||
config DRM_GEM_DMA_HELPER
|
||||
tristate
|
||||
depends on DRM
|
||||
select FB_CORE if DRM_FBDEV_EMULATION
|
||||
select FB_DMAMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
|
||||
help
|
||||
Choose this if you need the GEM DMA helper functions
|
||||
|
|
@ -372,6 +375,7 @@ config DRM_GEM_DMA_HELPER
|
|||
config DRM_GEM_SHMEM_HELPER
|
||||
tristate
|
||||
depends on DRM && MMU
|
||||
select FB_CORE if DRM_FBDEV_EMULATION
|
||||
select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
|
||||
help
|
||||
Choose this if you need the GEM shmem helper functions
|
||||
|
|
|
|||
|
|
@ -343,11 +343,10 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
|
|||
coredump->skip_vram_check = skip_vram_check;
|
||||
coredump->reset_vram_lost = vram_lost;
|
||||
|
||||
if (job && job->vm) {
|
||||
struct amdgpu_vm *vm = job->vm;
|
||||
if (job && job->pasid) {
|
||||
struct amdgpu_task_info *ti;
|
||||
|
||||
ti = amdgpu_vm_get_task_info_vm(vm);
|
||||
ti = amdgpu_vm_get_task_info_pasid(adev, job->pasid);
|
||||
if (ti) {
|
||||
coredump->reset_task_info = *ti;
|
||||
amdgpu_vm_put_task_info(ti);
|
||||
|
|
|
|||
|
|
@ -417,6 +417,9 @@ bool amdgpu_device_supports_boco(struct drm_device *dev)
|
|||
{
|
||||
struct amdgpu_device *adev = drm_to_adev(dev);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
|
||||
return false;
|
||||
|
||||
if (adev->has_pr3 ||
|
||||
((adev->flags & AMD_IS_PX) && amdgpu_is_atpx_hybrid()))
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -255,7 +255,6 @@ void amdgpu_job_set_resources(struct amdgpu_job *job, struct amdgpu_bo *gds,
|
|||
|
||||
void amdgpu_job_free_resources(struct amdgpu_job *job)
|
||||
{
|
||||
struct amdgpu_ring *ring = to_amdgpu_ring(job->base.sched);
|
||||
struct dma_fence *f;
|
||||
unsigned i;
|
||||
|
||||
|
|
@ -268,7 +267,7 @@ void amdgpu_job_free_resources(struct amdgpu_job *job)
|
|||
f = NULL;
|
||||
|
||||
for (i = 0; i < job->num_ibs; ++i)
|
||||
amdgpu_ib_free(ring->adev, &job->ibs[i], f);
|
||||
amdgpu_ib_free(NULL, &job->ibs[i], f);
|
||||
}
|
||||
|
||||
static void amdgpu_job_free_cb(struct drm_sched_job *s_job)
|
||||
|
|
|
|||
|
|
@ -1266,10 +1266,9 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va,
|
|||
* next command submission.
|
||||
*/
|
||||
if (amdgpu_vm_is_bo_always_valid(vm, bo)) {
|
||||
uint32_t mem_type = bo->tbo.resource->mem_type;
|
||||
|
||||
if (!(bo->preferred_domains &
|
||||
amdgpu_mem_type_to_domain(mem_type)))
|
||||
if (bo->tbo.resource &&
|
||||
!(bo->preferred_domains &
|
||||
amdgpu_mem_type_to_domain(bo->tbo.resource->mem_type)))
|
||||
amdgpu_vm_bo_evicted(&bo_va->base);
|
||||
else
|
||||
amdgpu_vm_bo_idle(&bo_va->base);
|
||||
|
|
|
|||
|
|
@ -4123,7 +4123,7 @@ static int gfx_v12_0_set_clockgating_state(void *handle,
|
|||
if (amdgpu_sriov_vf(adev))
|
||||
return 0;
|
||||
|
||||
switch (adev->ip_versions[GC_HWIP][0]) {
|
||||
switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
|
||||
case IP_VERSION(12, 0, 0):
|
||||
case IP_VERSION(12, 0, 1):
|
||||
gfx_v12_0_update_gfx_clock_gating(adev,
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ mmhub_v4_1_0_print_l2_protection_fault_status(struct amdgpu_device *adev,
|
|||
dev_err(adev->dev,
|
||||
"MMVM_L2_PROTECTION_FAULT_STATUS_LO32:0x%08X\n",
|
||||
status);
|
||||
switch (adev->ip_versions[MMHUB_HWIP][0]) {
|
||||
switch (amdgpu_ip_version(adev, MMHUB_HWIP, 0)) {
|
||||
case IP_VERSION(4, 1, 0):
|
||||
mmhub_cid = mmhub_client_ids_v4_1_0[cid][rw];
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -271,8 +271,19 @@ const struct nbio_hdp_flush_reg nbio_v7_0_hdp_flush_reg = {
|
|||
.ref_and_mask_sdma1 = GPU_HDP_FLUSH_DONE__SDMA1_MASK,
|
||||
};
|
||||
|
||||
#define regRCC_DEV0_EPF6_STRAP4 0xd304
|
||||
#define regRCC_DEV0_EPF6_STRAP4_BASE_IDX 5
|
||||
|
||||
static void nbio_v7_0_init_registers(struct amdgpu_device *adev)
|
||||
{
|
||||
uint32_t data;
|
||||
|
||||
switch (amdgpu_ip_version(adev, NBIO_HWIP, 0)) {
|
||||
case IP_VERSION(2, 5, 0):
|
||||
data = RREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF6_STRAP4) & ~BIT(23);
|
||||
WREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF6_STRAP4, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define MMIO_REG_HOLE_OFFSET (0x80000 - PAGE_SIZE)
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ static void nbio_v7_11_init_registers(struct amdgpu_device *adev)
|
|||
if (def != data)
|
||||
WREG32_SOC15(NBIO, 0, regBIF_BIF256_CI256_RC3X4_USB4_PCIE_MST_CTRL_3, data);
|
||||
|
||||
switch (adev->ip_versions[NBIO_HWIP][0]) {
|
||||
switch (amdgpu_ip_version(adev, NBIO_HWIP, 0)) {
|
||||
case IP_VERSION(7, 11, 0):
|
||||
case IP_VERSION(7, 11, 1):
|
||||
case IP_VERSION(7, 11, 2):
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ static void nbio_v7_7_init_registers(struct amdgpu_device *adev)
|
|||
if (def != data)
|
||||
WREG32_SOC15(NBIO, 0, regBIF0_PCIE_MST_CTRL_3, data);
|
||||
|
||||
switch (adev->ip_versions[NBIO_HWIP][0]) {
|
||||
switch (amdgpu_ip_version(adev, NBIO_HWIP, 0)) {
|
||||
case IP_VERSION(7, 7, 0):
|
||||
data = RREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF5_STRAP4) & ~BIT(23);
|
||||
WREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF5_STRAP4, data);
|
||||
|
|
|
|||
|
|
@ -2096,7 +2096,7 @@ static int smu_v14_0_2_enable_gfx_features(struct smu_context *smu)
|
|||
{
|
||||
struct amdgpu_device *adev = smu->adev;
|
||||
|
||||
if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(14, 0, 2))
|
||||
if (amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(14, 0, 2))
|
||||
return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_EnableAllSmuFeatures,
|
||||
FEATURE_PWR_GFX, NULL);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1896,8 +1896,8 @@ static void destroy_mgr(struct drm_dp_tunnel_mgr *mgr)
|
|||
*
|
||||
* Creates a DP tunnel manager for @dev.
|
||||
*
|
||||
* Returns a pointer to the tunnel manager if created successfully or NULL in
|
||||
* case of an error.
|
||||
* Returns a pointer to the tunnel manager if created successfully or error
|
||||
* pointer in case of failure.
|
||||
*/
|
||||
struct drm_dp_tunnel_mgr *
|
||||
drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count)
|
||||
|
|
@ -1907,7 +1907,7 @@ drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count)
|
|||
|
||||
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
|
||||
if (!mgr)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mgr->dev = dev;
|
||||
init_waitqueue_head(&mgr->bw_req_queue);
|
||||
|
|
@ -1916,7 +1916,7 @@ drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count)
|
|||
if (!mgr->groups) {
|
||||
kfree(mgr);
|
||||
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_DISPLAY_DP_TUNNEL_STATE_DEBUG
|
||||
|
|
@ -1927,7 +1927,7 @@ drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count)
|
|||
if (!init_group(mgr, &mgr->groups[i])) {
|
||||
destroy_mgr(mgr);
|
||||
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
mgr->group_count++;
|
||||
|
|
|
|||
|
|
@ -1287,14 +1287,11 @@ EXPORT_SYMBOL(drm_mode_set_name);
|
|||
*/
|
||||
int drm_mode_vrefresh(const struct drm_display_mode *mode)
|
||||
{
|
||||
unsigned int num, den;
|
||||
unsigned int num = 1, den = 1;
|
||||
|
||||
if (mode->htotal == 0 || mode->vtotal == 0)
|
||||
return 0;
|
||||
|
||||
num = mode->clock;
|
||||
den = mode->htotal * mode->vtotal;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
num *= 2;
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
|
|
@ -1302,6 +1299,12 @@ int drm_mode_vrefresh(const struct drm_display_mode *mode)
|
|||
if (mode->vscan > 1)
|
||||
den *= mode->vscan;
|
||||
|
||||
if (check_mul_overflow(mode->clock, num, &num))
|
||||
return 0;
|
||||
|
||||
if (check_mul_overflow(mode->htotal * mode->vtotal, den, &den))
|
||||
return 0;
|
||||
|
||||
return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(num, 1000), den);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_vrefresh);
|
||||
|
|
|
|||
|
|
@ -343,6 +343,11 @@ struct intel_engine_guc_stats {
|
|||
* @start_gt_clk: GT clock time of last idle to active transition.
|
||||
*/
|
||||
u64 start_gt_clk;
|
||||
|
||||
/**
|
||||
* @total: The last value of total returned
|
||||
*/
|
||||
u64 total;
|
||||
};
|
||||
|
||||
union intel_engine_tlb_inv_reg {
|
||||
|
|
|
|||
|
|
@ -1243,6 +1243,21 @@ static void __get_engine_usage_record(struct intel_engine_cs *engine,
|
|||
} while (++i < 6);
|
||||
}
|
||||
|
||||
static void __set_engine_usage_record(struct intel_engine_cs *engine,
|
||||
u32 last_in, u32 id, u32 total)
|
||||
{
|
||||
struct iosys_map rec_map = intel_guc_engine_usage_record_map(engine);
|
||||
|
||||
#define record_write(map_, field_, val_) \
|
||||
iosys_map_wr_field(map_, 0, struct guc_engine_usage_record, field_, val_)
|
||||
|
||||
record_write(&rec_map, last_switch_in_stamp, last_in);
|
||||
record_write(&rec_map, current_context_index, id);
|
||||
record_write(&rec_map, total_runtime, total);
|
||||
|
||||
#undef record_write
|
||||
}
|
||||
|
||||
static void guc_update_engine_gt_clks(struct intel_engine_cs *engine)
|
||||
{
|
||||
struct intel_engine_guc_stats *stats = &engine->stats.guc;
|
||||
|
|
@ -1363,9 +1378,12 @@ static ktime_t guc_engine_busyness(struct intel_engine_cs *engine, ktime_t *now)
|
|||
total += intel_gt_clock_interval_to_ns(gt, clk);
|
||||
}
|
||||
|
||||
if (total > stats->total)
|
||||
stats->total = total;
|
||||
|
||||
spin_unlock_irqrestore(&guc->timestamp.lock, flags);
|
||||
|
||||
return ns_to_ktime(total);
|
||||
return ns_to_ktime(stats->total);
|
||||
}
|
||||
|
||||
static void guc_enable_busyness_worker(struct intel_guc *guc)
|
||||
|
|
@ -1431,8 +1449,21 @@ static void __reset_guc_busyness_stats(struct intel_guc *guc)
|
|||
|
||||
guc_update_pm_timestamp(guc, &unused);
|
||||
for_each_engine(engine, gt, id) {
|
||||
struct intel_engine_guc_stats *stats = &engine->stats.guc;
|
||||
|
||||
guc_update_engine_gt_clks(engine);
|
||||
engine->stats.guc.prev_total = 0;
|
||||
|
||||
/*
|
||||
* If resetting a running context, accumulate the active
|
||||
* time as well since there will be no context switch.
|
||||
*/
|
||||
if (stats->running) {
|
||||
u64 clk = guc->timestamp.gt_stamp - stats->start_gt_clk;
|
||||
|
||||
stats->total_gt_clks += clk;
|
||||
}
|
||||
stats->prev_total = 0;
|
||||
stats->running = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&guc->timestamp.lock, flags);
|
||||
|
|
@ -1543,6 +1574,9 @@ static void guc_timestamp_ping(struct work_struct *wrk)
|
|||
|
||||
static int guc_action_enable_usage_stats(struct intel_guc *guc)
|
||||
{
|
||||
struct intel_gt *gt = guc_to_gt(guc);
|
||||
struct intel_engine_cs *engine;
|
||||
enum intel_engine_id id;
|
||||
u32 offset = intel_guc_engine_usage_offset(guc);
|
||||
u32 action[] = {
|
||||
INTEL_GUC_ACTION_SET_ENG_UTIL_BUFF,
|
||||
|
|
@ -1550,6 +1584,9 @@ static int guc_action_enable_usage_stats(struct intel_guc *guc)
|
|||
0,
|
||||
};
|
||||
|
||||
for_each_engine(engine, gt, id)
|
||||
__set_engine_usage_record(engine, 0, 0xffffffff, 0);
|
||||
|
||||
return intel_guc_send(guc, action, ARRAY_SIZE(action));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -565,6 +565,8 @@ static int hx83102_get_modes(struct drm_panel *panel,
|
|||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, m);
|
||||
if (!mode)
|
||||
return -ENOMEM;
|
||||
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_set_name(mode);
|
||||
|
|
|
|||
|
|
@ -481,9 +481,9 @@ static int nt35950_probe(struct mipi_dsi_device *dsi)
|
|||
return dev_err_probe(dev, -EPROBE_DEFER, "Cannot get secondary DSI host\n");
|
||||
|
||||
nt->dsi[1] = mipi_dsi_device_register_full(dsi_r_host, info);
|
||||
if (!nt->dsi[1]) {
|
||||
if (IS_ERR(nt->dsi[1])) {
|
||||
dev_err(dev, "Cannot get secondary DSI node\n");
|
||||
return -ENODEV;
|
||||
return PTR_ERR(nt->dsi[1]);
|
||||
}
|
||||
num_dsis++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1177,6 +1177,7 @@ static int st7701_probe(struct device *dev, int connector_type)
|
|||
return dev_err_probe(dev, ret, "Failed to get orientation\n");
|
||||
|
||||
drm_panel_init(&st7701->panel, dev, &st7701_funcs, connector_type);
|
||||
st7701->panel.prepare_prev_first = true;
|
||||
|
||||
/**
|
||||
* Once sleep out has been issued, ST7701 IC required to wait 120ms
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ static void r63353_panel_shutdown(struct mipi_dsi_device *dsi)
|
|||
{
|
||||
struct r63353_panel *rpanel = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
r63353_panel_unprepare(&rpanel->base);
|
||||
drm_panel_unprepare(&rpanel->base);
|
||||
}
|
||||
|
||||
static const struct r63353_desc sharp_ls068b3sx02_data = {
|
||||
|
|
|
|||
|
|
@ -1355,7 +1355,8 @@ EXPORT_SYMBOL(drm_sched_init);
|
|||
* drm_sched_backend_ops.run_job(). Consequently, drm_sched_backend_ops.free_job()
|
||||
* will not be called for all jobs still in drm_gpu_scheduler.pending_list.
|
||||
* There is no solution for this currently. Thus, it is up to the driver to make
|
||||
* sure that
|
||||
* sure that:
|
||||
*
|
||||
* a) drm_sched_fini() is only called after for all submitted jobs
|
||||
* drm_sched_backend_ops.free_job() has been called or that
|
||||
* b) the jobs for which drm_sched_backend_ops.free_job() has not been called
|
||||
|
|
|
|||
|
|
@ -756,7 +756,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
|
|||
* adding succeeded, it is ok to proceed even if the memory was
|
||||
* not onlined in time.
|
||||
*/
|
||||
wait_for_completion_timeout(&dm_device.ol_waitevent, 5 * HZ);
|
||||
wait_for_completion_timeout(&dm_device.ol_waitevent, secs_to_jiffies(5));
|
||||
post_status(&dm_device);
|
||||
}
|
||||
}
|
||||
|
|
@ -1373,7 +1373,8 @@ static int dm_thread_func(void *dm_dev)
|
|||
struct hv_dynmem_device *dm = dm_dev;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
wait_for_completion_interruptible_timeout(&dm_device.config_event, 1 * HZ);
|
||||
wait_for_completion_interruptible_timeout(&dm_device.config_event,
|
||||
secs_to_jiffies(1));
|
||||
/*
|
||||
* The host expects us to post information on the memory
|
||||
* pressure every second.
|
||||
|
|
@ -1748,7 +1749,7 @@ static int balloon_connect_vsp(struct hv_device *dev)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
t = wait_for_completion_timeout(&dm_device.host_event, 5 * HZ);
|
||||
t = wait_for_completion_timeout(&dm_device.host_event, secs_to_jiffies(5));
|
||||
if (t == 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
|
|
@ -1806,7 +1807,7 @@ static int balloon_connect_vsp(struct hv_device *dev)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
t = wait_for_completion_timeout(&dm_device.host_event, 5 * HZ);
|
||||
t = wait_for_completion_timeout(&dm_device.host_event, secs_to_jiffies(5));
|
||||
if (t == 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -655,7 +655,7 @@ void hv_kvp_onchannelcallback(void *context)
|
|||
if (host_negotiatied == NEGO_NOT_STARTED) {
|
||||
host_negotiatied = NEGO_IN_PROGRESS;
|
||||
schedule_delayed_work(&kvp_host_handshake_work,
|
||||
HV_UTIL_NEGO_TIMEOUT * HZ);
|
||||
secs_to_jiffies(HV_UTIL_NEGO_TIMEOUT));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -724,7 +724,7 @@ void hv_kvp_onchannelcallback(void *context)
|
|||
*/
|
||||
schedule_work(&kvp_sendkey_work);
|
||||
schedule_delayed_work(&kvp_timeout_work,
|
||||
HV_UTIL_TIMEOUT * HZ);
|
||||
secs_to_jiffies(HV_UTIL_TIMEOUT));
|
||||
|
||||
return;
|
||||
|
||||
|
|
@ -767,6 +767,12 @@ hv_kvp_init(struct hv_util_service *srv)
|
|||
*/
|
||||
kvp_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hv_kvp_init_transport(void)
|
||||
{
|
||||
hvt = hvutil_transport_init(kvp_devname, CN_KVP_IDX, CN_KVP_VAL,
|
||||
kvp_on_msg, kvp_on_reset);
|
||||
if (!hvt)
|
||||
|
|
|
|||
|
|
@ -193,7 +193,8 @@ static void vss_send_op(void)
|
|||
vss_transaction.state = HVUTIL_USERSPACE_REQ;
|
||||
|
||||
schedule_delayed_work(&vss_timeout_work, op == VSS_OP_FREEZE ?
|
||||
VSS_FREEZE_TIMEOUT * HZ : HV_UTIL_TIMEOUT * HZ);
|
||||
secs_to_jiffies(VSS_FREEZE_TIMEOUT) :
|
||||
secs_to_jiffies(HV_UTIL_TIMEOUT));
|
||||
|
||||
rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
|
||||
if (rc) {
|
||||
|
|
@ -388,6 +389,12 @@ hv_vss_init(struct hv_util_service *srv)
|
|||
*/
|
||||
vss_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hv_vss_init_transport(void)
|
||||
{
|
||||
hvt = hvutil_transport_init(vss_devname, CN_VSS_IDX, CN_VSS_VAL,
|
||||
vss_on_msg, vss_on_reset);
|
||||
if (!hvt) {
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ static struct hv_util_service util_heartbeat = {
|
|||
static struct hv_util_service util_kvp = {
|
||||
.util_cb = hv_kvp_onchannelcallback,
|
||||
.util_init = hv_kvp_init,
|
||||
.util_init_transport = hv_kvp_init_transport,
|
||||
.util_pre_suspend = hv_kvp_pre_suspend,
|
||||
.util_pre_resume = hv_kvp_pre_resume,
|
||||
.util_deinit = hv_kvp_deinit,
|
||||
|
|
@ -149,6 +150,7 @@ static struct hv_util_service util_kvp = {
|
|||
static struct hv_util_service util_vss = {
|
||||
.util_cb = hv_vss_onchannelcallback,
|
||||
.util_init = hv_vss_init,
|
||||
.util_init_transport = hv_vss_init_transport,
|
||||
.util_pre_suspend = hv_vss_pre_suspend,
|
||||
.util_pre_resume = hv_vss_pre_resume,
|
||||
.util_deinit = hv_vss_deinit,
|
||||
|
|
@ -590,10 +592,8 @@ static int util_probe(struct hv_device *dev,
|
|||
srv->channel = dev->channel;
|
||||
if (srv->util_init) {
|
||||
ret = srv->util_init(srv);
|
||||
if (ret) {
|
||||
ret = -ENODEV;
|
||||
if (ret)
|
||||
goto error1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -613,6 +613,13 @@ static int util_probe(struct hv_device *dev,
|
|||
if (ret)
|
||||
goto error;
|
||||
|
||||
if (srv->util_init_transport) {
|
||||
ret = srv->util_init_transport();
|
||||
if (ret) {
|
||||
vmbus_close(dev->channel);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
|
|
|||
|
|
@ -370,12 +370,14 @@ void vmbus_on_event(unsigned long data);
|
|||
void vmbus_on_msg_dpc(unsigned long data);
|
||||
|
||||
int hv_kvp_init(struct hv_util_service *srv);
|
||||
int hv_kvp_init_transport(void);
|
||||
void hv_kvp_deinit(void);
|
||||
int hv_kvp_pre_suspend(void);
|
||||
int hv_kvp_pre_resume(void);
|
||||
void hv_kvp_onchannelcallback(void *context);
|
||||
|
||||
int hv_vss_init(struct hv_util_service *srv);
|
||||
int hv_vss_init_transport(void);
|
||||
void hv_vss_deinit(void);
|
||||
int hv_vss_pre_suspend(void);
|
||||
int hv_vss_pre_resume(void);
|
||||
|
|
|
|||
|
|
@ -2507,7 +2507,7 @@ static int vmbus_bus_resume(struct device *dev)
|
|||
vmbus_request_offers();
|
||||
|
||||
if (wait_for_completion_timeout(
|
||||
&vmbus_connection.ready_for_resume_event, 10 * HZ) == 0)
|
||||
&vmbus_connection.ready_for_resume_event, secs_to_jiffies(10)) == 0)
|
||||
pr_err("Some vmbus device is missing after suspending?\n");
|
||||
|
||||
/* Reset the event for the next suspend. */
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ struct tmp51x_data {
|
|||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
// Set the shift based on the gain 8=4, 4=3, 2=2, 1=1
|
||||
// Set the shift based on the gain: 8 -> 1, 4 -> 2, 2 -> 3, 1 -> 4
|
||||
static inline u8 tmp51x_get_pga_shift(struct tmp51x_data *data)
|
||||
{
|
||||
return 5 - ffs(data->pga_gain);
|
||||
|
|
@ -204,7 +204,9 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
|
|||
* 2's complement number shifted by one to four depending
|
||||
* on the pga gain setting. 1lsb = 10uV
|
||||
*/
|
||||
*val = sign_extend32(regval, 17 - tmp51x_get_pga_shift(data));
|
||||
*val = sign_extend32(regval,
|
||||
reg == TMP51X_SHUNT_CURRENT_RESULT ?
|
||||
16 - tmp51x_get_pga_shift(data) : 15);
|
||||
*val = DIV_ROUND_CLOSEST(*val * 10 * MILLI, data->shunt_uohms);
|
||||
break;
|
||||
case TMP51X_BUS_VOLTAGE_RESULT:
|
||||
|
|
@ -220,7 +222,7 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
|
|||
break;
|
||||
case TMP51X_BUS_CURRENT_RESULT:
|
||||
// Current = (ShuntVoltage * CalibrationRegister) / 4096
|
||||
*val = sign_extend32(regval, 16) * data->curr_lsb_ua;
|
||||
*val = sign_extend32(regval, 15) * (long)data->curr_lsb_ua;
|
||||
*val = DIV_ROUND_CLOSEST(*val, MILLI);
|
||||
break;
|
||||
case TMP51X_LOCAL_TEMP_RESULT:
|
||||
|
|
@ -232,7 +234,7 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
|
|||
case TMP51X_REMOTE_TEMP_LIMIT_2:
|
||||
case TMP513_REMOTE_TEMP_LIMIT_3:
|
||||
// 1lsb = 0.0625 degrees centigrade
|
||||
*val = sign_extend32(regval, 16) >> TMP51X_TEMP_SHIFT;
|
||||
*val = sign_extend32(regval, 15) >> TMP51X_TEMP_SHIFT;
|
||||
*val = DIV_ROUND_CLOSEST(*val * 625, 10);
|
||||
break;
|
||||
case TMP51X_N_FACTOR_AND_HYST_1:
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ config PMAC_MEDIABAY
|
|||
config PMAC_BACKLIGHT
|
||||
bool "Backlight control for LCD screens"
|
||||
depends on PPC_PMAC && ADB_PMU && FB = y && (BROKEN || !PPC64)
|
||||
depends on BACKLIGHT_CLASS_DEVICE=y
|
||||
select FB_BACKLIGHT
|
||||
help
|
||||
Say Y here to enable Macintosh specific extensions of the generic
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-a
|
|||
static int dib3000_read_reg(struct dib3000_state *state, u16 reg)
|
||||
{
|
||||
u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
|
||||
u8 rb[2];
|
||||
u8 rb[2] = {};
|
||||
struct i2c_msg msg[] = {
|
||||
{ .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 },
|
||||
{ .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
|
||||
|
|
|
|||
|
|
@ -1188,7 +1188,8 @@ static int vdec_vp9_slice_setup_lat(struct vdec_vp9_slice_instance *instance,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
/* clang stack usage explodes if this is inlined */
|
||||
static noinline_for_stack
|
||||
void vdec_vp9_slice_map_counts_eob_coef(unsigned int i, unsigned int j, unsigned int k,
|
||||
struct vdec_vp9_slice_frame_counts *counts,
|
||||
struct v4l2_vp9_frame_symbol_counts *counts_helper)
|
||||
|
|
|
|||
|
|
@ -3070,6 +3070,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
|||
msdc_gate_clock(host);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
release_mem:
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
if (host->dma.gpd)
|
||||
dma_free_coherent(&pdev->dev,
|
||||
2 * sizeof(struct mt_gpdma_desc),
|
||||
|
|
@ -3103,6 +3104,7 @@ static void msdc_drv_remove(struct platform_device *pdev)
|
|||
host->dma.gpd, host->dma.gpd_addr);
|
||||
dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc),
|
||||
host->dma.bd, host->dma.bd_addr);
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
}
|
||||
|
||||
static void msdc_save_reg(struct msdc_host *host)
|
||||
|
|
|
|||
|
|
@ -1525,7 +1525,6 @@ static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
|
|||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT |
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER,
|
||||
|
|
|
|||
|
|
@ -1220,20 +1220,32 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir)
|
|||
static int m_can_interrupt_handler(struct m_can_classdev *cdev)
|
||||
{
|
||||
struct net_device *dev = cdev->net;
|
||||
u32 ir;
|
||||
u32 ir = 0, ir_read;
|
||||
int ret;
|
||||
|
||||
if (pm_runtime_suspended(cdev->dev))
|
||||
return IRQ_NONE;
|
||||
|
||||
ir = m_can_read(cdev, M_CAN_IR);
|
||||
/* The m_can controller signals its interrupt status as a level, but
|
||||
* depending in the integration the CPU may interpret the signal as
|
||||
* edge-triggered (for example with m_can_pci). For these
|
||||
* edge-triggered integrations, we must observe that IR is 0 at least
|
||||
* once to be sure that the next interrupt will generate an edge.
|
||||
*/
|
||||
while ((ir_read = m_can_read(cdev, M_CAN_IR)) != 0) {
|
||||
ir |= ir_read;
|
||||
|
||||
/* ACK all irqs */
|
||||
m_can_write(cdev, M_CAN_IR, ir);
|
||||
|
||||
if (!cdev->irq_edge_triggered)
|
||||
break;
|
||||
}
|
||||
|
||||
m_can_coalescing_update(cdev, ir);
|
||||
if (!ir)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* ACK all irqs */
|
||||
m_can_write(cdev, M_CAN_IR, ir);
|
||||
|
||||
if (cdev->ops->clear_interrupts)
|
||||
cdev->ops->clear_interrupts(cdev);
|
||||
|
||||
|
|
@ -1695,6 +1707,14 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Write the INIT bit, in case no hardware reset has happened before
|
||||
* the probe (for example, it was observed that the Intel Elkhart Lake
|
||||
* SoCs do not properly reset the CAN controllers on reboot)
|
||||
*/
|
||||
err = m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!cdev->is_peripheral)
|
||||
netif_napi_add(dev, &cdev->napi, m_can_poll);
|
||||
|
||||
|
|
@ -1746,11 +1766,7 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Forcing standby mode should be redundant, as the chip should be in
|
||||
* standby after a reset. Write the INIT bit anyways, should the chip
|
||||
* be configured by previous stage.
|
||||
*/
|
||||
return m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void m_can_stop(struct net_device *dev)
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ struct m_can_classdev {
|
|||
int pm_clock_support;
|
||||
int pm_wake_source;
|
||||
int is_peripheral;
|
||||
bool irq_edge_triggered;
|
||||
|
||||
// Cached M_CAN_IE register content
|
||||
u32 active_interrupts;
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
|
|||
mcan_class->pm_clock_support = 1;
|
||||
mcan_class->pm_wake_source = 0;
|
||||
mcan_class->can.clock.freq = id->driver_data;
|
||||
mcan_class->irq_edge_triggered = true;
|
||||
mcan_class->ops = &m_can_pci_ops;
|
||||
|
||||
pci_set_drvdata(pci, mcan_class);
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ static int platform_phy_connect(struct bgmac *bgmac)
|
|||
static int bgmac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *phy_node;
|
||||
struct bgmac *bgmac;
|
||||
struct resource *regs;
|
||||
int ret;
|
||||
|
|
@ -236,7 +237,9 @@ static int bgmac_probe(struct platform_device *pdev)
|
|||
bgmac->cco_ctl_maskset = platform_bgmac_cco_ctl_maskset;
|
||||
bgmac->get_bus_clock = platform_bgmac_get_bus_clock;
|
||||
bgmac->cmn_maskset32 = platform_bgmac_cmn_maskset32;
|
||||
if (of_parse_phandle(np, "phy-handle", 0)) {
|
||||
phy_node = of_parse_phandle(np, "phy-handle", 0);
|
||||
if (phy_node) {
|
||||
of_node_put(phy_node);
|
||||
bgmac->phy_connect = platform_phy_connect;
|
||||
} else {
|
||||
bgmac->phy_connect = bgmac_phy_connect_direct;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user