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:
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>;
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
partitions {
|
||||
compatible = "redboot-fis";
|
||||
/* Eraseblock at 0xfe0000 */
|
||||
fis-index-block = <0x1fc>;
|
||||
fis-index-block = <0x7f>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
partitions {
|
||||
compatible = "redboot-fis";
|
||||
/* Eraseblock at 0xfe0000 */
|
||||
fis-index-block = <0x1fc>;
|
||||
fis-index-block = <0x7f>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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[];
|
||||
};
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user