Merge ba3e43a9e6 ("Merge tag 'soc-fixes-7.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc") into android-mainline

Steps on the way to v7.1-rc7

Change-Id: I77cc4bdaeb9f352f8b14d4ed9caf6a5800fb4e30
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas committed 2026-06-02 20:06:44 +00:00
commit 8a1ef95dff
37 files changed
+502 -271

No files matched your search

@@ -30,6 +30,16 @@ properties:
maxItems: 1
clocks:
minItems: 1
maxItems: 2
clock-names:
minItems: 1
items:
- const: core
- const: iface
power-domains:
maxItems: 1
operating-points-v2: true
@@ -44,6 +54,25 @@ required:
additionalProperties: false
allOf:
- if:
properties:
compatible:
contains:
enum:
- qcom,eliza-inline-crypto-engine
- qcom,milos-inline-crypto-engine
then:
required:
- power-domains
- clock-names
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
examples:
- |
#include <dt-bindings/clock/qcom,sm8550-gcc.h>
@@ -52,7 +81,11 @@ examples:
compatible = "qcom,sm8550-inline-crypto-engine",
"qcom,inline-crypto-engine";
reg = <0x01d88000 0x8000>;
clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>,
<&gcc GCC_UFS_PHY_AHB_CLK>;
clock-names = "core",
"iface";
power-domains = <&gcc UFS_PHY_GDSC>;
operating-points-v2 = <&ice_opp_table>;
+1 -1
View File
@@ -146,7 +146,7 @@
partitions {
compatible = "redboot-fis";
/* Eraseblock at 0xfe0000 */
fis-index-block = <0x1fc>;
fis-index-block = <0x7f>;
};
};
+1 -1
View File
@@ -134,7 +134,7 @@
partitions {
compatible = "redboot-fis";
/* Eraseblock at 0xfe0000 */
fis-index-block = <0x1fc>;
fis-index-block = <0x7f>;
};
};
+3 -3
View File
@@ -990,9 +990,9 @@
<62 IRQ_TYPE_LEVEL_HIGH 3>, /* Queue 3 */
<63 IRQ_TYPE_LEVEL_HIGH 3>, /* Queue 4 */
<64 IRQ_TYPE_LEVEL_HIGH 3>; /* Queue 5 */
clocks = <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_GCK 24>, <&pmc PMC_TYPE_GCK 67>;
clock-names = "hclk", "pclk", "tx_clk", "tsu_clk";
assigned-clocks = <&pmc PMC_TYPE_GCK 67>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_GCK 24>;
clock-names = "hclk", "pclk", "tsu_clk";
assigned-clocks = <&pmc PMC_TYPE_GCK 24>;
assigned-clock-rates = <266666666>;
status = "disabled";
};
+1
View File
@@ -78,6 +78,7 @@ static void __init socfpga_smp_prepare_cpus(unsigned int max_cpus)
}
socfpga_scu_base_addr = of_iomap(np, 0);
of_node_put(np);
if (!socfpga_scu_base_addr)
return;
scu_enable(socfpga_scu_base_addr);
+5 -1
View File
@@ -843,7 +843,11 @@
"qcom,inline-crypto-engine";
reg = <0x0 0x01d88000 0x0 0x18000>;
clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>,
<&gcc GCC_UFS_PHY_AHB_CLK>;
clock-names = "core",
"iface";
power-domains = <&gcc GCC_UFS_PHY_GDSC>;
};
tcsr_mutex: hwlock@1f40000 {
+6 -14
View File
@@ -2314,11 +2314,9 @@
clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>,
<&tcsr TCSR_USB3_0_CLKREF_EN>,
<&rpmhcc RPMH_CXO_CLK>,
<&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>,
<&gcc GCC_USB3_MP_PHY_PIPE_0_CLK>;
clock-names = "aux",
"clkref",
"ref",
"com_aux",
"pipe";
@@ -2343,11 +2341,9 @@
clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>,
<&tcsr TCSR_USB3_1_CLKREF_EN>,
<&rpmhcc RPMH_CXO_CLK>,
<&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>,
<&gcc GCC_USB3_MP_PHY_PIPE_1_CLK>;
clock-names = "aux",
"clkref",
"ref",
"com_aux",
"pipe";
@@ -2482,15 +2478,13 @@
reg = <0x0 0x00fde000 0x0 0x8000>;
clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>,
<&rpmhcc RPMH_CXO_CLK>,
<&tcsr TCSR_USB4_1_CLKREF_EN>,
<&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>,
<&gcc GCC_USB3_SEC_PHY_PIPE_CLK>,
<&tcsr TCSR_USB4_1_CLKREF_EN>;
<&gcc GCC_USB3_SEC_PHY_PIPE_CLK>;
clock-names = "aux",
"ref",
"com_aux",
"usb3_pipe",
"clkref";
"usb3_pipe";
power-domains = <&gcc GCC_USB_1_PHY_GDSC>;
@@ -3750,15 +3744,13 @@
reg = <0x0 0x088e1000 0x0 0x8000>;
clocks = <&gcc GCC_USB3_TERT_PHY_AUX_CLK>,
<&rpmhcc RPMH_CXO_CLK>,
<&tcsr TCSR_USB4_2_CLKREF_EN>,
<&gcc GCC_USB3_TERT_PHY_COM_AUX_CLK>,
<&gcc GCC_USB3_TERT_PHY_PIPE_CLK>,
<&tcsr TCSR_USB4_2_CLKREF_EN>;
<&gcc GCC_USB3_TERT_PHY_PIPE_CLK>;
clock-names = "aux",
"ref",
"com_aux",
"usb3_pipe",
"clkref";
"usb3_pipe";
power-domains = <&gcc GCC_USB_2_PHY_GDSC>;
+5 -1
View File
@@ -1275,7 +1275,11 @@
"qcom,inline-crypto-engine";
reg = <0x0 0x01d88000 0x0 0x18000>;
clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>,
<&gcc GCC_UFS_PHY_AHB_CLK>;
clock-names = "core",
"iface";
power-domains = <&gcc UFS_PHY_GDSC>;
};
tcsr_mutex: hwlock@1f40000 {
+1 -6
View File
@@ -982,12 +982,6 @@
status = "okay";
};
&i2c20 {
clock-frequency = <400000>;
status = "okay";
};
&lpass_tlmm {
spkr_01_sd_n_active: spkr-01-sd-n-active-state {
pins = "gpio12";
@@ -1308,6 +1302,7 @@
&tlmm {
gpio-reserved-ranges = <44 4>, /* SPI11 (TPM) */
<76 4>, /* SPI19 (TZ Protected) */
<80 2>, /* I2C20 (Battery SMBus) */
<238 1>; /* UFS Reset */
cam_rgb_default: cam-rgb-default-state {
+1
View File
@@ -260,6 +260,7 @@ CONFIG_PCI_ENDPOINT=y
CONFIG_PCI_ENDPOINT_CONFIGFS=y
CONFIG_PCI_EPF_TEST=m
CONFIG_PCI_PWRCTRL_GENERIC=m
CONFIG_POWER_SEQUENCING_PCIE_M2=m
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_FW_LOADER_USER_HELPER=y
+1 -1
View File
@@ -327,7 +327,7 @@ config PANEL_CHANGE_MESSAGE
say 'N' and keep the default message with the version.
config PANEL_BOOT_MESSAGE
depends on PANEL_CHANGE_MESSAGE="y"
depends on PANEL_CHANGE_MESSAGE
string "New initialization message"
default ""
help
+1 -1
View File
@@ -173,7 +173,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
count = strlen(msg);
/* if the string ends with a newline, trim it */
if (msg[count - 1] == '\n')
if (count && msg[count - 1] == '\n')
count--;
if (!count) {
+1 -4
View File
@@ -86,10 +86,7 @@ static const struct linedisp_ops max6959_linedisp_ops = {
static int max6959_enable(struct max6959_priv *priv, bool enable)
{
u8 mask = REG_CONFIGURATION_S_BIT;
u8 value = enable ? mask : 0;
return regmap_update_bits(priv->regmap, REG_CONFIGURATION, mask, value);
return regmap_assign_bits(priv->regmap, REG_CONFIGURATION, REG_CONFIGURATION_S_BIT, enable);
}
static void max6959_power_off(void *priv)
@@ -31,6 +31,9 @@ static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
if (response) {
xfer->rxcnt = cmdlen;
xfer->rxd = cmd;
} else {
xfer->rxcnt = 0;
xfer->rxd = NULL;
}
}
+97 -44
View File
@@ -7,11 +7,12 @@
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/bits.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/find.h>
#include <linux/firmware/samsung/exynos-acpm-protocol.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -104,12 +105,15 @@ struct acpm_queue {
*
* @cmd: pointer to where the data shall be saved.
* @n_cmd: number of 32-bit commands.
* @response: true if the client expects the RX data.
* @rxcnt: expected length of the response in 32-bit words.
* @completed: flag indicating if the firmware response has been fully
* processed.
*/
struct acpm_rx_data {
u32 *cmd;
size_t n_cmd;
bool response;
size_t rxcnt;
bool completed;
};
#define ACPM_SEQNUM_MAX 64
@@ -199,31 +203,33 @@ static void acpm_get_saved_rx(struct acpm_chan *achan,
const struct acpm_rx_data *rx_data = &achan->rx_data[tx_seqnum - 1];
u32 rx_seqnum;
if (!rx_data->response)
if (!rx_data->rxcnt)
return;
rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]);
if (rx_seqnum == tx_seqnum) {
if (rx_seqnum == tx_seqnum)
memcpy(xfer->rxd, rx_data->cmd, xfer->rxcnt * sizeof(*xfer->rxd));
clear_bit(rx_seqnum - 1, achan->bitmap_seqnum);
}
}
/**
* acpm_get_rx() - get response from RX queue.
* @achan: ACPM channel info.
* @xfer: reference to the transfer to get response for.
* @native_match: pointer to a boolean set to true if the thread natively
* processed its own sequence number during this call.
*
* Return: 0 on success, -errno otherwise.
*/
static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer,
bool *native_match)
{
u32 rx_front, rx_seqnum, tx_seqnum, seqnum;
const void __iomem *base, *addr;
struct acpm_rx_data *rx_data;
u32 i, val, mlen;
bool rx_set = false;
*native_match = false;
guard(mutex)(&achan->rx_lock);
@@ -232,10 +238,8 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
if (i == rx_front) {
acpm_get_saved_rx(achan, xfer, tx_seqnum);
if (i == rx_front)
return 0;
}
base = achan->rx.base;
mlen = achan->mlen;
@@ -256,11 +260,16 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
seqnum = rx_seqnum - 1;
rx_data = &achan->rx_data[seqnum];
if (rx_data->response) {
if (rx_data->rxcnt) {
if (rx_seqnum == tx_seqnum) {
__ioread32_copy(xfer->rxd, addr, xfer->rxcnt);
rx_set = true;
clear_bit(seqnum, achan->bitmap_seqnum);
/*
* Signal completion to the polling thread.
* Pairs with smp_load_acquire() in polling
* loop.
*/
smp_store_release(&rx_data->completed, true);
*native_match = true;
} else {
/*
* The RX data corresponds to another request.
@@ -268,10 +277,23 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
* clear yet the bitmap. It will be cleared
* after the response is copied to the request.
*/
__ioread32_copy(rx_data->cmd, addr, xfer->rxcnt);
__ioread32_copy(rx_data->cmd, addr,
rx_data->rxcnt);
/*
* Signal completion to the polling thread.
* Pairs with smp_load_acquire() in polling
* loop.
*/
smp_store_release(&rx_data->completed, true);
}
} else {
clear_bit(seqnum, achan->bitmap_seqnum);
/*
* Signal completion to the polling thread.
* Pairs with smp_load_acquire() in polling loop.
*/
smp_store_release(&rx_data->completed, true);
if (rx_seqnum == tx_seqnum)
*native_match = true;
}
i = (i + 1) % achan->qlen;
@@ -280,13 +302,6 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
/* We saved all responses, mark RX empty. */
writel(rx_front, achan->rx.rear);
/*
* If the response was not in this iteration of the queue, check if the
* RX data was previously saved.
*/
if (!rx_set)
acpm_get_saved_rx(achan, xfer, tx_seqnum);
return 0;
}
@@ -301,6 +316,7 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan,
const struct acpm_xfer *xfer)
{
struct device *dev = achan->acpm->dev;
bool native_match;
ktime_t timeout;
u32 seqnum;
int ret;
@@ -309,12 +325,25 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan,
timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US);
do {
ret = acpm_get_rx(achan, xfer);
ret = acpm_get_rx(achan, xfer, &native_match);
if (ret)
return ret;
if (!test_bit(seqnum - 1, achan->bitmap_seqnum))
/*
* Safely check if our specific transaction has been processed.
* smp_load_acquire prevents the CPU from speculatively
* executing subsequent instructions before the transaction is
* synchronized.
*/
if (smp_load_acquire(&achan->rx_data[seqnum - 1].completed)) {
/* Retrieve payload if another thread cached it for us */
if (!native_match)
acpm_get_saved_rx(achan, xfer, seqnum);
/* Relinquish ownership of the sequence slot */
clear_bit_unlock(seqnum - 1, achan->bitmap_seqnum);
return 0;
}
/* Determined experimentally. */
udelay(20);
@@ -362,29 +391,48 @@ static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front)
* TX queue.
* @achan: ACPM channel info.
* @xfer: reference to the transfer being prepared.
*
* Return: 0 on success, -errno otherwise.
*/
static void acpm_prepare_xfer(struct acpm_chan *achan,
const struct acpm_xfer *xfer)
static int acpm_prepare_xfer(struct acpm_chan *achan,
const struct acpm_xfer *xfer)
{
struct acpm_rx_data *rx_data;
u32 *txd = (u32 *)xfer->txd;
unsigned long size = ACPM_SEQNUM_MAX - 1;
unsigned long bit = achan->seqnum;
/* Prevent chan->seqnum from being re-used */
do {
if (++achan->seqnum == ACPM_SEQNUM_MAX)
achan->seqnum = 1;
} while (test_bit(achan->seqnum - 1, achan->bitmap_seqnum));
bit = find_next_zero_bit(achan->bitmap_seqnum, size, bit);
if (bit >= size) {
bit = find_first_zero_bit(achan->bitmap_seqnum, size);
if (bit >= size) {
dev_err_ratelimited(achan->acpm->dev,
"ACPM sequence number pool exhausted\n");
return -EBUSY;
}
}
/*
* Execute the atomic set to formally claim the bit and establish
* LKMM Acquire semantics against the RX thread's clear_bit_unlock().
* A loop is unnecessary because allocations are strictly serialized
* by tx_lock.
*/
if (WARN_ON_ONCE(test_and_set_bit_lock(bit, achan->bitmap_seqnum)))
return -EIO;
/* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */
achan->seqnum = bit + 1;
txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum);
/* Clear data for upcoming responses */
rx_data = &achan->rx_data[achan->seqnum - 1];
rx_data = &achan->rx_data[bit];
rx_data->completed = false;
memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd);
if (xfer->rxd)
rx_data->response = true;
/* zero means no response expected */
rx_data->rxcnt = xfer->rxcnt;
/* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */
set_bit(achan->seqnum - 1, achan->bitmap_seqnum);
return 0;
}
/**
@@ -444,7 +492,9 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer)
if (ret)
return ret;
acpm_prepare_xfer(achan, xfer);
ret = acpm_prepare_xfer(achan, xfer);
if (ret)
return ret;
/* Write TX command. */
__iowrite32_copy(achan->tx.base + achan->mlen * tx_front,
@@ -526,10 +576,11 @@ static int acpm_achan_alloc_cmds(struct acpm_chan *achan)
/**
* acpm_free_mbox_chans() - free mailbox channels.
* @acpm: pointer to driver data.
* @data: pointer to driver data.
*/
static void acpm_free_mbox_chans(struct acpm_info *acpm)
static void acpm_free_mbox_chans(void *data)
{
struct acpm_info *acpm = data;
int i;
for (i = 0; i < acpm->num_chans; i++)
@@ -557,6 +608,10 @@ static int acpm_channels_init(struct acpm_info *acpm)
if (!acpm->chans)
return -ENOMEM;
ret = devm_add_action_or_reset(dev, acpm_free_mbox_chans, acpm);
if (ret)
return dev_err_probe(dev, ret, "Failed to add mbox free action.\n");
chans_shmem = acpm->sram_base + readl(&shmem->chans);
for (i = 0; i < acpm->num_chans; i++) {
@@ -578,10 +633,8 @@ static int acpm_channels_init(struct acpm_info *acpm)
cl->dev = dev;
achan->chan = mbox_request_channel(cl, 0);
if (IS_ERR(achan->chan)) {
acpm_free_mbox_chans(acpm);
if (IS_ERR(achan->chan))
return PTR_ERR(achan->chan);
}
}
return 0;
+8 -4
View File
@@ -1590,18 +1590,22 @@ static int smq_invalidate_mapping(struct dm_cache_policy *p, dm_cblock_t cblock)
struct smq_policy *mq = to_smq_policy(p);
struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock));
unsigned long flags;
if (!e->allocated)
return -ENODATA;
int r = 0;
spin_lock_irqsave(&mq->lock, flags);
if (!e->allocated) {
r = -ENODATA;
goto out;
}
// FIXME: what if this block has pending background work?
del_queue(mq, e);
h_remove(&mq->table, e);
free_entry(&mq->cache_alloc, e);
out:
spin_unlock_irqrestore(&mq->lock, flags);
return 0;
return r;
}
static uint32_t smq_get_hint(struct dm_cache_policy *p, dm_cblock_t cblock)
+2 -1
View File
@@ -628,10 +628,11 @@ static __maybe_unused int atmel_ebi_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(atmel_ebi_pm_ops, NULL, atmel_ebi_resume);
static struct platform_driver atmel_ebi_driver = {
.probe = atmel_ebi_probe,
.driver = {
.name = "atmel-ebi",
.of_match_table = atmel_ebi_id_table,
.pm = &atmel_ebi_pm_ops,
},
};
builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe);
builtin_platform_driver(atmel_ebi_driver);
+6 -6
View File
@@ -1918,13 +1918,13 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
return 0;
ice = devm_of_qcom_ice_get(dev);
if (ice == ERR_PTR(-EOPNOTSUPP)) {
dev_warn(dev, "Disabling inline encryption support\n");
ice = NULL;
}
if (IS_ERR(ice)) {
if (ice != ERR_PTR(-EOPNOTSUPP))
return PTR_ERR(ice);
if (IS_ERR_OR_NULL(ice))
return PTR_ERR_OR_ZERO(ice);
dev_warn(dev, "Disabling inline encryption support\n");
return 0;
}
msm_host->ice = ice;
+1 -1
View File
@@ -247,7 +247,7 @@ static int imx8m_soc_probe(struct platform_device *pdev)
if (ret)
return ret;
data = device_get_match_data(dev);
data = of_machine_get_match_data(imx8_soc_match);
if (data) {
soc_dev_attr->soc_id = data->name;
ret = imx8m_soc_prepare(pdev, data->ocotp_compatible);
+51 -15
View File
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/xarray.h>
#include <linux/firmware/qcom/qcom_scm.h>
@@ -108,11 +109,15 @@ struct qcom_ice {
void __iomem *base;
struct clk *core_clk;
struct clk *iface_clk;
bool use_hwkm;
bool hwkm_init_complete;
u8 hwkm_version;
};
static DEFINE_XARRAY(ice_handles);
static DEFINE_MUTEX(ice_mutex);
static bool qcom_ice_check_supported(struct qcom_ice *ice)
{
u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION);
@@ -312,8 +317,13 @@ int qcom_ice_resume(struct qcom_ice *ice)
err = clk_prepare_enable(ice->core_clk);
if (err) {
dev_err(dev, "failed to enable core clock (%d)\n",
err);
dev_err(dev, "Failed to enable core clock: %d\n", err);
return err;
}
err = clk_prepare_enable(ice->iface_clk);
if (err) {
dev_err(dev, "Failed to enable iface clock: %d\n", err);
return err;
}
qcom_ice_hwkm_init(ice);
@@ -323,6 +333,7 @@ EXPORT_SYMBOL_GPL(qcom_ice_resume);
int qcom_ice_suspend(struct qcom_ice *ice)
{
clk_disable_unprepare(ice->iface_clk);
clk_disable_unprepare(ice->core_clk);
ice->hwkm_init_complete = false;
@@ -559,7 +570,7 @@ static struct qcom_ice *qcom_ice_create(struct device *dev,
if (!qcom_scm_ice_available()) {
dev_warn(dev, "ICE SCM interface not found\n");
return NULL;
return ERR_PTR(-EOPNOTSUPP);
}
engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL);
@@ -579,11 +590,17 @@ static struct qcom_ice *qcom_ice_create(struct device *dev,
engine->core_clk = devm_clk_get_optional_enabled(dev, "ice_core_clk");
if (!engine->core_clk)
engine->core_clk = devm_clk_get_optional_enabled(dev, "ice");
if (!engine->core_clk)
engine->core_clk = devm_clk_get_optional_enabled(dev, "core");
if (!engine->core_clk)
engine->core_clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(engine->core_clk))
return ERR_CAST(engine->core_clk);
engine->iface_clk = devm_clk_get_optional_enabled(dev, "iface");
if (IS_ERR(engine->iface_clk))
return ERR_CAST(engine->iface_clk);
if (!qcom_ice_check_supported(engine))
return ERR_PTR(-EOPNOTSUPP);
@@ -631,6 +648,8 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev)
return qcom_ice_create(&pdev->dev, base);
}
guard(mutex)(&ice_mutex);
/*
* If the consumer node does not provider an 'ice' reg range
* (legacy DT binding), then it must at least provide a phandle
@@ -639,20 +658,21 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev)
struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node,
"qcom,ice", 0);
if (!node)
return NULL;
return ERR_PTR(-EOPNOTSUPP);
pdev = of_find_device_by_node(node);
if (!pdev) {
dev_err(dev, "Cannot find device node %s\n", node->name);
return ERR_PTR(-EPROBE_DEFER);
return ERR_PTR(-ENODEV);
}
ice = platform_get_drvdata(pdev);
if (!ice) {
dev_err(dev, "Cannot get ice instance from %s\n",
dev_name(&pdev->dev));
ice = xa_load(&ice_handles, pdev->dev.of_node->phandle);
if (IS_ERR_OR_NULL(ice)) {
platform_device_put(pdev);
return ERR_PTR(-EPROBE_DEFER);
if (!ice)
return ERR_PTR(-EPROBE_DEFER);
else
return ice;
}
link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER);
@@ -691,8 +711,7 @@ static void devm_of_qcom_ice_put(struct device *dev, void *res)
* phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already
* be created and so this function will return that instead.
*
* Return: ICE pointer on success, NULL if there is no ICE data provided by the
* consumer or ERR_PTR() on error.
* Return: ICE pointer on success, ERR_PTR() on error.
*/
struct qcom_ice *devm_of_qcom_ice_get(struct device *dev)
{
@@ -703,7 +722,7 @@ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev)
return ERR_PTR(-ENOMEM);
ice = of_qcom_ice_get(dev);
if (!IS_ERR_OR_NULL(ice)) {
if (!IS_ERR(ice)) {
*dr = ice;
devres_add(dev, dr);
} else {
@@ -716,24 +735,40 @@ EXPORT_SYMBOL_GPL(devm_of_qcom_ice_get);
static int qcom_ice_probe(struct platform_device *pdev)
{
unsigned long phandle = pdev->dev.of_node->phandle;
struct qcom_ice *engine;
void __iomem *base;
guard(mutex)(&ice_mutex);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
dev_warn(&pdev->dev, "ICE registers not found\n");
/* Store the error pointer for devm_of_qcom_ice_get() */
xa_store(&ice_handles, phandle, (__force void *)base, GFP_KERNEL);
return PTR_ERR(base);
}
engine = qcom_ice_create(&pdev->dev, base);
if (IS_ERR(engine))
if (IS_ERR(engine)) {
/* Store the error pointer for devm_of_qcom_ice_get() */
xa_store(&ice_handles, phandle, engine, GFP_KERNEL);
return PTR_ERR(engine);
}
platform_set_drvdata(pdev, engine);
xa_store(&ice_handles, phandle, engine, GFP_KERNEL);
return 0;
}
static void qcom_ice_remove(struct platform_device *pdev)
{
unsigned long phandle = pdev->dev.of_node->phandle;
guard(mutex)(&ice_mutex);
xa_store(&ice_handles, phandle, NULL, GFP_KERNEL);
}
static const struct of_device_id qcom_ice_of_match_table[] = {
{ .compatible = "qcom,inline-crypto-engine" },
{ },
@@ -742,6 +777,7 @@ MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table);
static struct platform_driver qcom_ice_driver = {
.probe = qcom_ice_probe,
.remove = qcom_ice_remove,
.driver = {
.name = "qcom-ice",
.of_match_table = qcom_ice_of_match_table,
+74 -33
View File
@@ -10,7 +10,11 @@
struct optee_supp_req {
struct list_head link;
int id;
bool in_queue;
bool processed;
u32 func;
u32 ret;
size_t num_params;
@@ -19,6 +23,9 @@ struct optee_supp_req {
struct completion c;
};
/* It is temporary request used for revoked pending request in supp->idr. */
#define INVALID_REQ_PTR ((struct optee_supp_req *)ERR_PTR(-EBADF))
void optee_supp_init(struct optee_supp *supp)
{
memset(supp, 0, sizeof(*supp));
@@ -39,21 +46,23 @@ void optee_supp_release(struct optee_supp *supp)
{
int id;
struct optee_supp_req *req;
struct optee_supp_req *req_tmp;
mutex_lock(&supp->mutex);
/* Abort all request retrieved by supplicant */
/* Abort all request */
idr_for_each_entry(&supp->idr, req, id) {
idr_remove(&supp->idr, id);
req->ret = TEEC_ERROR_COMMUNICATION;
complete(&req->c);
}
/* Skip if request was already marked invalid */
if (IS_ERR(req))
continue;
/* Abort all queued requests */
list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) {
list_del(&req->link);
req->in_queue = false;
/* For queued requests where supplicant has not seen it */
if (req->in_queue) {
list_del(&req->link);
req->in_queue = false;
}
req->processed = true;
req->ret = TEEC_ERROR_COMMUNICATION;
complete(&req->c);
}
@@ -100,8 +109,16 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
/* Insert the request in the request list */
mutex_lock(&supp->mutex);
req->id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL);
if (req->id < 0) {
mutex_unlock(&supp->mutex);
kfree(req);
return TEEC_ERROR_OUT_OF_MEMORY;
}
list_add_tail(&req->link, &supp->reqs);
req->in_queue = true;
req->processed = false;
mutex_unlock(&supp->mutex);
/* Tell an eventual waiter there's a new request */
@@ -117,21 +134,43 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
if (wait_for_completion_killable(&req->c)) {
mutex_lock(&supp->mutex);
if (req->in_queue) {
/* Supplicant has not seen this request yet. */
idr_remove(&supp->idr, req->id);
list_del(&req->link);
req->in_queue = false;
ret = TEEC_ERROR_COMMUNICATION;
} else if (req->processed) {
/*
* Supplicant has processed this request. Ignore the
* kill signal for now and submit the result. req is not
* in supp->reqs (removed by supp_pop_entry()) nor in
* supp->idr (removed by supp_pop_req()).
*/
ret = req->ret;
} else {
/*
* Supplicant is in the middle of processing this
* request. Replace req with INVALID_REQ_PTR so that
* the ID remains busy, causing optee_supp_send() to
* fail on the next call to supp_pop_req() with this ID.
*/
idr_replace(&supp->idr, INVALID_REQ_PTR, req->id);
ret = TEEC_ERROR_COMMUNICATION;
}
mutex_unlock(&supp->mutex);
req->ret = TEEC_ERROR_COMMUNICATION;
} else {
ret = req->ret;
}
ret = req->ret;
kfree(req);
return ret;
}
static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp,
int num_params, int *id)
int num_params)
{
struct optee_supp_req *req;
@@ -153,10 +192,6 @@ static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp,
return ERR_PTR(-EINVAL);
}
*id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL);
if (*id < 0)
return ERR_PTR(-ENOMEM);
list_del(&req->link);
req->in_queue = false;
@@ -214,7 +249,6 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
struct optee *optee = tee_get_drvdata(teedev);
struct optee_supp *supp = &optee->supp;
struct optee_supp_req *req = NULL;
int id;
size_t num_meta;
int rc;
@@ -224,15 +258,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
while (true) {
mutex_lock(&supp->mutex);
req = supp_pop_entry(supp, *num_params - num_meta, &id);
req = supp_pop_entry(supp, *num_params - num_meta);
if (req)
break; /* Keep mutex held. */
mutex_unlock(&supp->mutex);
if (req) {
if (IS_ERR(req))
return PTR_ERR(req);
break;
}
/*
* If we didn't get a request we'll block in
* wait_for_completion() to avoid needless spinning.
@@ -245,6 +275,13 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
return -ERESTARTSYS;
}
/* supp->mutex held and req != NULL. */
if (IS_ERR(req)) {
mutex_unlock(&supp->mutex);
return PTR_ERR(req);
}
if (num_meta) {
/*
* tee-supplicant support meta parameters -> requsts can be
@@ -252,13 +289,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
*/
param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
TEE_IOCTL_PARAM_ATTR_META;
param->u.value.a = id;
param->u.value.a = req->id;
param->u.value.b = 0;
param->u.value.c = 0;
} else {
mutex_lock(&supp->mutex);
supp->req_id = id;
mutex_unlock(&supp->mutex);
supp->req_id = req->id;
}
*func = req->func;
@@ -266,6 +301,7 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
memcpy(param + num_meta, req->param,
sizeof(struct tee_param) * req->num_params);
mutex_unlock(&supp->mutex);
return 0;
}
@@ -297,12 +333,17 @@ static struct optee_supp_req *supp_pop_req(struct optee_supp *supp,
if (!req)
return ERR_PTR(-ENOENT);
/* optee_supp_thrd_req() already returned to optee. */
if (IS_ERR(req))
goto failed_req;
if ((num_params - nm) != req->num_params)
return ERR_PTR(-EINVAL);
*num_meta = nm;
failed_req:
idr_remove(&supp->idr, id);
supp->req_id = -1;
*num_meta = nm;
return req;
}
@@ -328,10 +369,9 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
mutex_lock(&supp->mutex);
req = supp_pop_req(supp, num_params, param, &num_meta);
mutex_unlock(&supp->mutex);
if (IS_ERR(req)) {
/* Something is wrong, let supplicant restart. */
mutex_unlock(&supp->mutex);
/* Something is wrong, let supplicant handel it. */
return PTR_ERR(req);
}
@@ -355,9 +395,10 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
}
}
req->ret = ret;
req->processed = true;
/* Let the requesting thread continue */
complete(&req->c);
mutex_unlock(&supp->mutex);
return 0;
}
+4 -2
View File
@@ -306,8 +306,10 @@ int qcomtee_object_user_init(struct qcomtee_object *object,
break;
case QCOMTEE_OBJECT_TYPE_CB:
object->ops = ops;
if (!object->ops->dispatch)
return -EINVAL;
if (!object->ops->dispatch) {
ret = -EINVAL;
break;
}
/* If failed, "no-name". */
object->name = kvasprintf_const(GFP_KERNEL, fmt, ap);
+25 -31
View File
@@ -530,11 +530,24 @@ static int params_to_user(struct tee_ioctl_param __user *uparams,
return 0;
}
static void free_params(struct tee_param *params, size_t num_params)
{
size_t n;
if (!params)
return;
for (n = 0; n < num_params; n++)
if (tee_param_is_memref(params + n) && params[n].u.memref.shm)
tee_shm_put(params[n].u.memref.shm);
kfree(params);
}
static int tee_ioctl_open_session(struct tee_context *ctx,
struct tee_ioctl_buf_data __user *ubuf)
{
int rc;
size_t n;
struct tee_ioctl_buf_data buf;
struct tee_ioctl_open_session_arg __user *uarg;
struct tee_ioctl_open_session_arg arg;
@@ -595,16 +608,7 @@ out:
*/
if (rc && have_session && ctx->teedev->desc->ops->close_session)
ctx->teedev->desc->ops->close_session(ctx, arg.session);
if (params) {
/* Decrease ref count for all valid shared memory pointers */
for (n = 0; n < arg.num_params; n++)
if (tee_param_is_memref(params + n) &&
params[n].u.memref.shm)
tee_shm_put(params[n].u.memref.shm);
kfree(params);
}
free_params(params, arg.num_params);
return rc;
}
@@ -612,7 +616,6 @@ static int tee_ioctl_invoke(struct tee_context *ctx,
struct tee_ioctl_buf_data __user *ubuf)
{
int rc;
size_t n;
struct tee_ioctl_buf_data buf;
struct tee_ioctl_invoke_arg __user *uarg;
struct tee_ioctl_invoke_arg arg;
@@ -657,14 +660,7 @@ static int tee_ioctl_invoke(struct tee_context *ctx,
}
rc = params_to_user(uparams, arg.num_params, params);
out:
if (params) {
/* Decrease ref count for all valid shared memory pointers */
for (n = 0; n < arg.num_params; n++)
if (tee_param_is_memref(params + n) &&
params[n].u.memref.shm)
tee_shm_put(params[n].u.memref.shm);
kfree(params);
}
free_params(params, arg.num_params);
return rc;
}
@@ -672,7 +668,6 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx,
struct tee_ioctl_buf_data __user *ubuf)
{
int rc;
size_t n;
struct tee_ioctl_buf_data buf;
struct tee_ioctl_object_invoke_arg __user *uarg;
struct tee_ioctl_object_invoke_arg arg;
@@ -716,14 +711,7 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx,
}
rc = params_to_user(uparams, arg.num_params, params);
out:
if (params) {
/* Decrease ref count for all valid shared memory pointers */
for (n = 0; n < arg.num_params; n++)
if (tee_param_is_memref(params + n) &&
params[n].u.memref.shm)
tee_shm_put(params[n].u.memref.shm);
kfree(params);
}
free_params(params, arg.num_params);
return rc;
}
@@ -846,9 +834,15 @@ static int tee_ioctl_supp_recv(struct tee_context *ctx,
return -ENOMEM;
rc = params_from_user(ctx, params, num_params, uarg->params);
if (rc)
goto out;
if (rc) {
free_params(params, num_params);
return rc;
}
/*
* supp_recv() may consume and replace the supplied parameters, so the
* final cleanup cannot use free_params() like the other ioctl paths.
*/
rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params);
if (rc)
goto out;
+1 -1
View File
@@ -435,7 +435,7 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags,
num_pages = iov_iter_npages(iter, INT_MAX);
if (!num_pages) {
ret = ERR_PTR(-ENOMEM);
goto err_ctx_put;
goto err_free_shm;
}
shm->pages = kzalloc_objs(*shm->pages, num_pages);
+6 -6
View File
@@ -177,13 +177,13 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
int i;
ice = devm_of_qcom_ice_get(dev);
if (ice == ERR_PTR(-EOPNOTSUPP)) {
dev_warn(dev, "Disabling inline encryption support\n");
ice = NULL;
}
if (IS_ERR(ice)) {
if (ice != ERR_PTR(-EOPNOTSUPP))
return PTR_ERR(ice);
if (IS_ERR_OR_NULL(ice))
return PTR_ERR_OR_ZERO(ice);
dev_warn(dev, "Disabling inline encryption support\n");
return 0;
}
host->ice = ice;
-8
View File
@@ -153,8 +153,6 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
long freed);
bool folio_isolate_hugetlb(struct folio *folio, struct list_head *list);
int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison);
int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
bool *migratable_cleared);
void folio_putback_hugetlb(struct folio *folio);
void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason);
void hugetlb_fix_reserve_counts(struct inode *inode);
@@ -421,12 +419,6 @@ static inline int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb,
return 0;
}
static inline int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
bool *migratable_cleared)
{
return 0;
}
static inline void folio_putback_hugetlb(struct folio *folio)
{
}
-8
View File
@@ -4975,8 +4975,6 @@ extern int soft_offline_page(unsigned long pfn, int flags);
*/
extern const struct attribute_group memory_failure_attr_group;
extern void memory_failure_queue(unsigned long pfn, int flags);
extern int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
bool *migratable_cleared);
void num_poisoned_pages_inc(unsigned long pfn);
void num_poisoned_pages_sub(unsigned long pfn, long i);
#else
@@ -4984,12 +4982,6 @@ static inline void memory_failure_queue(unsigned long pfn, int flags)
{
}
static inline int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
bool *migratable_cleared)
{
return 0;
}
static inline void num_poisoned_pages_inc(unsigned long pfn)
{
}
+1
View File
@@ -470,6 +470,7 @@ struct tee_ioctl_object_invoke_arg {
__u32 op;
__u32 ret;
__u32 num_params;
__u32 :32;
/* num_params tells the actual number of element in params */
struct tee_ioctl_param params[];
};
+5 -2
View File
@@ -191,10 +191,13 @@ cleanup:
/* Expose all pages to the buddy, they are useless for CMA. */
if (!test_bit(CMA_RESERVE_PAGES_ON_ERROR, &cma->flags)) {
for (r = 0; r < allocrange; r++) {
for (r = 0; r < cma->nranges; r++) {
unsigned long start_pfn;
cmr = &cma->ranges[r];
start_pfn = r <= allocrange ? early_pfn[r] : cmr->early_pfn;
end_pfn = cmr->base_pfn + cmr->count;
for (pfn = early_pfn[r]; pfn < end_pfn; pfn++)
for (pfn = start_pfn; pfn < end_pfn; pfn++)
free_reserved_page(pfn_to_page(pfn));
}
}
+2 -1
View File
@@ -205,7 +205,8 @@ static int __init cma_debugfs_init(void)
cma_debugfs_root = debugfs_create_dir("cma", NULL);
for (i = 0; i < cma_area_count; i++)
cma_debugfs_add_one(&cma_areas[i], cma_debugfs_root);
if (test_bit(CMA_ACTIVATED, &cma_areas[i].flags))
cma_debugfs_add_one(&cma_areas[i], cma_debugfs_root);
return 0;
}
+2 -2
View File
@@ -32,9 +32,9 @@ struct folio *damon_get_folio(unsigned long pfn)
return NULL;
folio = page_folio(page);
if (!folio_test_lru(folio) || !folio_try_get(folio))
if (!folio_try_get(folio))
return NULL;
if (unlikely(page_folio(page) != folio || !folio_test_lru(folio))) {
if (unlikely(page_folio(page) != folio) || !folio_test_lru(folio)) {
folio_put(folio);
folio = NULL;
}
+3 -1
View File
@@ -3015,9 +3015,9 @@ static void __split_huge_pud_locked(struct vm_area_struct *vma, pud_t *pud,
if (!folio_test_referenced(folio) && pud_young(old_pud))
folio_set_referenced(folio);
folio_remove_rmap_pud(folio, page, vma);
folio_put(folio);
add_mm_counter(vma->vm_mm, mm_counter_file(folio),
-HPAGE_PUD_NR);
folio_put(folio);
}
void __split_huge_pud(struct vm_area_struct *vma, pud_t *pud,
@@ -3133,7 +3133,9 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
if (!folio_test_referenced(folio) && pmd_young(old_pmd))
folio_set_referenced(folio);
folio_remove_rmap_pmd(folio, page, vma);
add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR);
folio_put(folio);
return;
}
add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR);
return;
+39 -30
View File
@@ -118,6 +118,9 @@ static int hugetlb_acct_memory(struct hstate *h, long delta);
static void hugetlb_vma_lock_free(struct vm_area_struct *vma);
static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma);
static void __hugetlb_vma_unlock_write_free(struct vm_area_struct *vma);
static int __huge_pmd_unshare(struct mmu_gather *tlb,
struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
bool check_locks);
static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
unsigned long start, unsigned long end, bool take_locks);
static struct resv_map *vma_resv_map(struct vm_area_struct *vma);
@@ -4974,6 +4977,7 @@ again:
addr, dst_vma);
folio_put(pte_folio);
if (ret) {
restore_reserve_on_error(h, dst_vma, addr, new_folio);
folio_put(new_folio);
break;
}
@@ -6270,6 +6274,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte,
folio_put(*foliop);
*foliop = NULL;
if (ret) {
restore_reserve_on_error(h, dst_vma, dst_addr, folio);
folio_put(folio);
goto out;
}
@@ -6891,6 +6896,31 @@ out:
return pte;
}
static int __huge_pmd_unshare(struct mmu_gather *tlb,
struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
bool check_locks)
{
unsigned long sz = huge_page_size(hstate_vma(vma));
struct mm_struct *mm = vma->vm_mm;
pgd_t *pgd = pgd_offset(mm, addr);
p4d_t *p4d = p4d_offset(pgd, addr);
pud_t *pud = pud_offset(p4d, addr);
if (sz != PMD_SIZE)
return 0;
if (!ptdesc_pmd_is_shared(virt_to_ptdesc(ptep)))
return 0;
i_mmap_assert_write_locked(vma->vm_file->f_mapping);
if (check_locks)
hugetlb_vma_assert_locked(vma);
pud_clear(pud);
tlb_unshare_pmd_ptdesc(tlb, virt_to_ptdesc(ptep), addr);
mm_dec_nr_pmds(mm);
return 1;
}
/**
* huge_pmd_unshare - Unmap a pmd table if it is shared by multiple users
* @tlb: the current mmu_gather.
@@ -6910,24 +6940,7 @@ out:
int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
unsigned long sz = huge_page_size(hstate_vma(vma));
struct mm_struct *mm = vma->vm_mm;
pgd_t *pgd = pgd_offset(mm, addr);
p4d_t *p4d = p4d_offset(pgd, addr);
pud_t *pud = pud_offset(p4d, addr);
if (sz != PMD_SIZE)
return 0;
if (!ptdesc_pmd_is_shared(virt_to_ptdesc(ptep)))
return 0;
i_mmap_assert_write_locked(vma->vm_file->f_mapping);
hugetlb_vma_assert_locked(vma);
pud_clear(pud);
tlb_unshare_pmd_ptdesc(tlb, virt_to_ptdesc(ptep), addr);
mm_dec_nr_pmds(mm);
return 1;
return __huge_pmd_unshare(tlb, vma, addr, ptep, /*check_locks=*/true);
}
/*
@@ -6961,6 +6974,13 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,
return NULL;
}
static int __huge_pmd_unshare(struct mmu_gather *tlb,
struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
bool check_locks)
{
return 0;
}
int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
@@ -7141,17 +7161,6 @@ int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison
return ret;
}
int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
bool *migratable_cleared)
{
int ret;
spin_lock_irq(&hugetlb_lock);
ret = __get_huge_page_for_hwpoison(pfn, flags, migratable_cleared);
spin_unlock_irq(&hugetlb_lock);
return ret;
}
/**
* folio_putback_hugetlb - unisolate a hugetlb folio
* @folio: the isolated hugetlb folio
@@ -7269,7 +7278,7 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
if (!ptep)
continue;
ptl = huge_pte_lock(h, mm, ptep);
huge_pmd_unshare(&tlb, vma, address, ptep);
__huge_pmd_unshare(&tlb, vma, address, ptep, take_locks);
spin_unlock(ptl);
}
huge_pmd_unshare_flush(&tlb, vma);
+18 -18
View File
@@ -207,6 +207,8 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
/* Remapping the head page requires r/w */
if (unlikely(walk->nr_walked == 0 && walk->vmemmap_head)) {
VM_WARN_ON_ONCE(!PageHead((const struct page *)addr));
list_del(&walk->vmemmap_head->lru);
/*
@@ -218,6 +220,8 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
entry = mk_pte(walk->vmemmap_head, PAGE_KERNEL);
} else {
VM_WARN_ON_ONCE(!PageTail((const struct page *)addr));
/*
* Remap the tail pages as read-only to catch illegal write
* operation to the tail pages.
@@ -232,33 +236,28 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
static void vmemmap_restore_pte(pte_t *pte, unsigned long addr,
struct vmemmap_remap_walk *walk)
{
struct page *page;
struct page *from, *to;
page = list_first_entry(walk->vmemmap_pages, struct page, lru);
list_del(&page->lru);
struct page *src = pte_page(ptep_get(pte)), *dst;
/*
* Initialize tail pages in the newly allocated vmemmap page.
*
* There is folio-scope metadata that is encoded in the first few
* tail pages.
*
* Use the value last tail page in the page with the head page
* to initialize the rest of tail pages.
* When rolling back vmemmap_remap_free(), keep the copied head page
* mapping and restore only PTEs currently pointing at the shared tail
* page.
*/
from = compound_head((struct page *)addr) +
PAGE_SIZE / sizeof(struct page) - 1;
to = page_to_virt(page);
for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++, to++)
*to = *from;
if (walk->vmemmap_tail && walk->vmemmap_tail != src)
return;
VM_WARN_ON_ONCE(PageHead((const struct page *)addr));
dst = list_first_entry(walk->vmemmap_pages, struct page, lru);
list_del(&dst->lru);
copy_page(page_to_virt(dst), page_to_virt(src));
/*
* Makes sure that preceding stores to the page contents become visible
* before the set_pte_at() write.
*/
smp_wmb();
set_pte_at(&init_mm, addr, pte, mk_pte(page, PAGE_KERNEL));
set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
}
/**
@@ -324,6 +323,7 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
*/
walk = (struct vmemmap_remap_walk) {
.remap_pte = vmemmap_restore_pte,
.vmemmap_tail = vmemmap_tail,
.vmemmap_pages = vmemmap_pages,
.flags = 0,
};
+4 -1
View File
@@ -2011,6 +2011,7 @@ struct memcg_stock_pcp {
struct work_struct work;
unsigned long flags;
uint8_t drain_idx;
};
static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {
@@ -2194,7 +2195,9 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
if (!success) {
i = empty_slot;
if (i == -1) {
i = get_random_u32_below(NR_MEMCG_STOCK);
i = stock->drain_idx++;
if (stock->drain_idx == NR_MEMCG_STOCK)
stock->drain_idx = 0;
drain_stock(stock, i);
}
css_get(&memcg->css);
+10 -9
View File
@@ -1966,20 +1966,19 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)
folio_free_raw_hwp(folio, true);
}
/*
* Called from hugetlb code with hugetlb_lock held.
*/
int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
static int get_huge_page_for_hwpoison(unsigned long pfn, int flags,
bool *migratable_cleared)
{
struct page *page = pfn_to_page(pfn);
struct folio *folio = page_folio(page);
struct folio *folio;
bool count_increased = false;
int ret, rc;
spin_lock_irq(&hugetlb_lock);
folio = page_folio(page);
if (!folio_test_hugetlb(folio)) {
ret = MF_HUGETLB_NON_HUGEPAGE;
goto out;
goto out_unlock;
} else if (flags & MF_COUNT_INCREASED) {
ret = MF_HUGETLB_IN_USED;
count_increased = true;
@@ -1995,13 +1994,13 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
} else {
ret = MF_HUGETLB_RETRY;
if (!(flags & MF_NO_RETRY))
goto out;
goto out_unlock;
}
rc = hugetlb_update_hwpoison(folio, page);
if (rc >= MF_HUGETLB_FOLIO_PRE_POISONED) {
ret = rc;
goto out;
goto out_unlock;
}
/*
@@ -2013,8 +2012,10 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags,
*migratable_cleared = true;
}
spin_unlock_irq(&hugetlb_lock);
return ret;
out:
out_unlock:
spin_unlock_irq(&hugetlb_lock);
if (count_increased)
folio_put(folio);
return ret;
+79 -13
View File
@@ -14,6 +14,8 @@
#include <linux/userfaultfd_k.h>
#include <linux/mmu_notifier.h>
#include <linux/hugetlb.h>
#include <linux/file.h>
#include <linux/cleanup.h>
#include <asm/tlbflush.h>
#include <asm/tlb.h>
#include "internal.h"
@@ -66,7 +68,7 @@ static const struct vm_uffd_ops *vma_uffd_ops(struct vm_area_struct *vma)
{
if (vma_is_anonymous(vma))
return &anon_uffd_ops;
return vma->vm_ops ? vma->vm_ops->uffd_ops : NULL;
return vma->vm_ops->uffd_ops;
}
static __always_inline
@@ -443,16 +445,80 @@ static int mfill_copy_folio_locked(struct folio *folio, unsigned long src_addr)
return ret;
}
static int mfill_copy_folio_retry(struct mfill_state *state,
#define MFILL_RETRY_STATE_VMA_FLAGS \
append_vma_flags(__VMA_UFFD_FLAGS, VMA_SHARED_BIT)
/*
* VMA state saved before dropping the locks in mfill_copy_folio_retry().
* Used to detect VMA replacement or incompatible changes after reacquiring the
* locks.
*/
struct mfill_retry_state {
const struct vm_uffd_ops *ops;
struct file *file;
vma_flags_t flags;
pgoff_t pgoff;
};
static void mfill_retry_state_save(struct mfill_retry_state *s,
struct vm_area_struct *vma)
{
s->flags = vma_flags_and_mask(&vma->flags, MFILL_RETRY_STATE_VMA_FLAGS);
s->ops = vma_uffd_ops(vma);
s->pgoff = vma->vm_pgoff;
if (vma->vm_file)
s->file = get_file(vma->vm_file);
}
static bool mfill_retry_state_changed(struct mfill_retry_state *state,
struct vm_area_struct *vma)
{
vma_flags_t flags = vma_flags_and_mask(&vma->flags,
MFILL_RETRY_STATE_VMA_FLAGS);
/* Have any UFFD flags (missing, WP, minor) changed? */
if (!vma_flags_same_pair(&state->flags, &flags))
return true;
/* VMA type or effective uffd_ops changed while the lock was dropped */
if (state->ops != vma_uffd_ops(vma))
return true;
/* VMA was anonymous before; changed only if it no longer is */
if (!state->file)
return !vma_is_anonymous(vma);
/* VMA was file backed, but file, inode or offset has changed */
if (!vma->vm_file || vma->vm_file->f_inode != state->file->f_inode ||
state->file != vma->vm_file || vma->vm_pgoff != state->pgoff)
return true;
return false;
}
static void mfill_retry_state_put(struct mfill_retry_state *s)
{
if (s->file)
fput(s->file);
}
DEFINE_FREE(retry_put, struct mfill_retry_state *,
if (_T) mfill_retry_state_put(_T));
static int mfill_copy_folio_retry(struct mfill_state *mfill_state,
struct folio *folio)
{
const struct vm_uffd_ops *orig_ops = vma_uffd_ops(state->vma);
unsigned long src_addr = state->src_addr;
struct mfill_retry_state retry_state = { 0 };
struct mfill_retry_state *for_free __free(retry_put) = &retry_state;
unsigned long src_addr = mfill_state->src_addr;
void *kaddr;
int err;
mfill_retry_state_save(&retry_state, mfill_state->vma);
/* retry copying with mm_lock dropped */
mfill_put_vma(state);
mfill_put_vma(mfill_state);
kaddr = kmap_local_folio(folio, 0);
err = copy_from_user(kaddr, (const void __user *) src_addr, PAGE_SIZE);
@@ -463,19 +529,14 @@ static int mfill_copy_folio_retry(struct mfill_state *state,
flush_dcache_folio(folio);
/* reget VMA and PMD, they could change underneath us */
err = mfill_get_vma(state);
err = mfill_get_vma(mfill_state);
if (err)
return err;
/*
* The VMA type may have changed while the lock was dropped
* (e.g. replaced with a hugetlb mapping), making the caller's
* ops pointer stale.
*/
if (vma_uffd_ops(state->vma) != orig_ops)
if (mfill_retry_state_changed(&retry_state, mfill_state->vma))
return -EAGAIN;
err = mfill_establish_pmd(state);
err = mfill_establish_pmd(mfill_state);
if (err)
return err;
@@ -491,6 +552,11 @@ static int __mfill_atomic_pte(struct mfill_state *state,
struct folio *folio;
int ret;
if (!ops) {
VM_WARN_ONCE(1, "UFFDIO_COPY for unsupported VMA");
return -EOPNOTSUPP;
}
folio = ops->alloc_folio(state->vma, state->dst_addr);
if (!folio)
return -ENOMEM;