* [PATCH v7 2/5] dmaengine: Add support for repeating transactions
From: Laurent Pinchart @ 2020-07-17 1:33 UTC (permalink / raw)
To: dmaengine
Cc: Michal Simek, Hyun Kwon, Tejas Upadhyay, Satish Kumar Nagireddy,
Vinod Koul, Peter Ujfalusi
In-Reply-To: <20200717013337.24122-1-laurent.pinchart@ideasonboard.com>
DMA engines used with displays perform 2D interleaved transfers to read
framebuffers from memory and feed the data to the display engine. As the
same framebuffer can be displayed for multiple frames, the DMA
transactions need to be repeated until a new framebuffer replaces the
current one. This feature is implemented natively by some DMA engines
that have the ability to repeat transactions and switch to a new
transaction at the end of a transfer without any race condition or frame
loss.
This patch implements support for this feature in the DMA engine API. A
new DMA_PREP_REPEAT transaction flag allows DMA clients to instruct the
DMA channel to repeat the transaction automatically until one or more
new transactions are issued on the channel (or until all active DMA
transfers are explicitly terminated with the dmaengine_terminate_*()
functions). A new DMA_REPEAT transaction type is also added for DMA
engine drivers to report their support of the DMA_PREP_REPEAT flag.
A new DMA_PREP_LOAD_EOT transaction flag is also introduced (with a
corresponding DMA_LOAD_EOT capability bit), as requested during the
review of v4. The flag instructs the DMA channel that the transaction
being queued should replace the active repeated transaction when the
latter terminates (at End Of Transaction). Not setting the flag will
result in the active repeated transaction to continue being repeated,
and the new transaction being silently ignored.
The DMA_PREP_REPEAT flag is currently supported for interleaved
transactions only. Its usage can easily be extended to cover more
transaction types simply by adding an appropriate check in the
corresponding dmaengine_prep_*() function.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
Changes since v5:
- Document the API in Documentation/driver-api/dmaengine/
- Small improvements to documentation in header file
Changes since v4:
- Add DMA_LOAD_EOT and DMA_PREP_LOAD_EOT flags
---
Documentation/driver-api/dmaengine/client.rst | 4 +-
.../driver-api/dmaengine/provider.rst | 49 +++++++++++++++++++
include/linux/dmaengine.h | 17 +++++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst
index 2104830a99ae..41938aa2bdeb 100644
--- a/Documentation/driver-api/dmaengine/client.rst
+++ b/Documentation/driver-api/dmaengine/client.rst
@@ -86,7 +86,9 @@ The details of these operations are:
- interleaved_dma: This is common to Slave as well as M2M clients. For slave
address of devices' fifo could be already known to the driver.
Various types of operations could be expressed by setting
- appropriate values to the 'dma_interleaved_template' members.
+ appropriate values to the 'dma_interleaved_template' members. Cyclic
+ interleaved DMA transfers are also possible if supported by the channel by
+ setting the DMA_PREP_REPEAT transfer flag.
A non-NULL return of this transfer API represents a "descriptor" for
the given transaction.
diff --git a/Documentation/driver-api/dmaengine/provider.rst b/Documentation/driver-api/dmaengine/provider.rst
index 56e5833e8a07..f896acccdfee 100644
--- a/Documentation/driver-api/dmaengine/provider.rst
+++ b/Documentation/driver-api/dmaengine/provider.rst
@@ -239,6 +239,27 @@ Currently, the types available are:
want to transfer a portion of uncompressed data directly to the
display to print it
+- DMA_REPEAT
+
+ - The device supports repeated transfers. A repeated transfer, indicated by
+ the DMA_PREP_REPEAT transfer flag, is similar to a cyclic transfer in that
+ it gets automatically repeated when it ends, but can additionally be
+ replaced by the client.
+
+ - This feature is limited to interleaved transfers, this flag should thus not
+ be set if the DMA_INTERLEAVE flag isn't set. This limitation is based on
+ the current needs of DMA clients, support for additional transfer types
+ should be added in the future if and when the need arises.
+
+- DMA_LOAD_EOT
+
+ - The device supports replacing repeated transfers at end of transfer (EOT)
+ by queuing a new transfer with the DMA_PREP_LOAD_EOT flag set.
+
+ - Support for replacing a currently running transfer at another point (such
+ as end of burst instead of end of transfer) will be added in the future
+ based on DMA clients needs, if and when the need arises.
+
These various types will also affect how the source and destination
addresses change over time.
@@ -531,6 +552,34 @@ DMA_CTRL_REUSE
writes for which the descriptor should be in different format from
normal data descriptors.
+- DMA_PREP_REPEAT
+
+ - If set, the transfer will be automatically repeated when it ends until a
+ new transfer is queued on the same channel with the DMA_PREP_LOAD_EOT flag.
+ If the next transfer to be queued on the channel does not have the
+ DMA_PREP_LOAD_EOT flag set, the current transfer will be repeated until the
+ client terminates all transfers.
+
+ - This flag is only supported if the channel reports the DMA_REPEAT
+ capability.
+
+- DMA_PREP_LOAD_EOT
+
+ - If set, the transfer will replace the transfer currently being executed at
+ the end of the transfer.
+
+ - This is the default behaviour for non-repeated transfers, specifying
+ DMA_PREP_LOAD_EOT for non-repeated transfers will thus make no difference.
+
+ - When using repeated transfers, DMA clients will usually need to set the
+ DMA_PREP_LOAD_EOT flag on all transfers, otherwise the channel will keep
+ repeating the last repeated transfer and ignore the new transfers being
+ queued. Failure to set DMA_PREP_LOAD_EOT will appear as if the channel was
+ stuck on the previous transfer.
+
+ - This flag is only supported if the channel reports the DMA_LOAD_EOT
+ capability.
+
General Design Notes
====================
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index e1c03339918f..328e3aca7f51 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -61,6 +61,8 @@ enum dma_transaction_type {
DMA_SLAVE,
DMA_CYCLIC,
DMA_INTERLEAVE,
+ DMA_REPEAT,
+ DMA_LOAD_EOT,
/* last transaction type for creation of the capabilities mask */
DMA_TX_TYPE_END,
};
@@ -176,6 +178,16 @@ struct dma_interleaved_template {
* @DMA_PREP_CMD: tell the driver that the data passed to DMA API is command
* data and the descriptor should be in different format from normal
* data descriptors.
+ * @DMA_PREP_REPEAT: tell the driver that the transaction shall be automatically
+ * repeated when it ends until a transaction is issued on the same channel
+ * with the DMA_PREP_LOAD_EOT flag set. This flag is only applicable to
+ * interleaved transactions and is ignored for all other transaction types.
+ * @DMA_PREP_LOAD_EOT: tell the driver that the transaction shall replace any
+ * active repeated (as indicated by DMA_PREP_REPEAT) transaction when the
+ * repeated transaction ends. Not setting this flag when the previously queued
+ * transaction is marked with DMA_PREP_REPEAT will cause the new transaction
+ * to never be processed and stay in the issued queue forever. The flag is
+ * ignored if the previous transaction is not a repeated transaction.
*/
enum dma_ctrl_flags {
DMA_PREP_INTERRUPT = (1 << 0),
@@ -186,6 +198,8 @@ enum dma_ctrl_flags {
DMA_PREP_FENCE = (1 << 5),
DMA_CTRL_REUSE = (1 << 6),
DMA_PREP_CMD = (1 << 7),
+ DMA_PREP_REPEAT = (1 << 8),
+ DMA_PREP_LOAD_EOT = (1 << 9),
};
/**
@@ -980,6 +994,9 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(
{
if (!chan || !chan->device || !chan->device->device_prep_interleaved_dma)
return NULL;
+ if (flags & DMA_PREP_REPEAT &&
+ !test_bit(DMA_REPEAT, chan->device->cap_mask.bits))
+ return NULL;
return chan->device->device_prep_interleaved_dma(chan, xt, flags);
}
--
Regards,
Laurent Pinchart
^ permalink raw reply related
* [PATCH v7 3/5] dmaengine: xilinx: dpdma: Add the Xilinx DisplayPort DMA engine driver
From: Laurent Pinchart @ 2020-07-17 1:33 UTC (permalink / raw)
To: dmaengine
Cc: Michal Simek, Hyun Kwon, Tejas Upadhyay, Satish Kumar Nagireddy,
Vinod Koul, Peter Ujfalusi
In-Reply-To: <20200717013337.24122-1-laurent.pinchart@ideasonboard.com>
From: Hyun Kwon <hyun.kwon@xilinx.com>
The ZynqMP DisplayPort subsystem includes a DMA engine called DPDMA with
6 DMa channels (4 for display and 2 for audio). This driver exposes the
DPDMA through the dmaengine API, to be used by audio (ALSA) and display
(DRM) drivers for the DisplayPort subsystem.
Signed-off-by: Hyun Kwon <hyun.kwon@xilinx.com>
Signed-off-by: Tejas Upadhyay <tejasu@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes since v6:
- Reject transactions without DMA_PREP_LOAD_EOT
Changes since v5:
- Update copyright year
- Use GFP_NOWAIT to allocate descriptor
- Drop check of deprecated direction parameter in channel configuration
- Don't reject channel configuration if the channel is busy
Changes since v4:
- Support DMA_PREP_LOAD_EOT, ignoring any transaction that doesn't have
the flag set, as requested in the review of v4
Changes since v3:
- Fix uninitialized variable in xilinx_dpdma_config()
- Free pending and active descriptors upon termination
- Switch to DMA_PREP_REPEAT
Changes since v2:
- Switch to virt-dma
- Support interleaved cyclic transfers and nothing else
- Fix terminate_all behaviour (don't wait)
- Fix bug in extended address handling for hw desc
- Clean up video group handling
- Update driver name
- Use macros for bitfields
- Remove unneeded header
- Coding style and typo fixes
Changes since v1:
- Remove unneeded #include
- Drop enum xilinx_dpdma_chan_id
- Update compatible string
- Drop DT subnodes
- Replace XILINX_DPDMA_NUM_CHAN with ARRAY_SIZE(xdev->chan)
- Disable IRQ at remove() time
- Use devm_platform_ioremap_resource()
- Don't inline functions manually
- Add section headers
- Merge DMA engine implementation in their wrappers
- Rename xilinx_dpdma_sw_desc::phys to dma_addr
- Use GENMASK()
- Use FIELD_PREP/FIELD_GET
- Fix MSB handling in xilinx_dpdma_sw_desc_addr_64()
- Fix logic in xilinx_dpdma_chan_prep_slave_sg()
- Document why xilinx_dpdma_config() doesn't need to check most
parameters
- Remove debugfs support
- Rechedule errored descriptor
- Align the line size with 128bit
- SPDX header formatting
---
MAINTAINERS | 1 +
drivers/dma/Kconfig | 10 +
drivers/dma/xilinx/Makefile | 1 +
drivers/dma/xilinx/xilinx_dpdma.c | 1533 +++++++++++++++++++++++++++++
4 files changed, 1545 insertions(+)
create mode 100644 drivers/dma/xilinx/xilinx_dpdma.c
diff --git a/MAINTAINERS b/MAINTAINERS
index fa52d4f9f8c8..6c20a6d338f0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18858,6 +18858,7 @@ M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
L: dmaengine@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml
+F: drivers/dma/xilinx/xilinx_dpdma.c
F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
XILLYBUS DRIVER
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index de41d7928bff..668e9636c547 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -707,6 +707,16 @@ config XILINX_ZYNQMP_DMA
help
Enable support for Xilinx ZynqMP DMA controller.
+config XILINX_ZYNQMP_DPDMA
+ tristate "Xilinx DPDMA Engine"
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Enable support for Xilinx ZynqMP DisplayPort DMA. Choose this option
+ if you have a Xilinx ZynqMP SoC with a DisplayPort subsystem. The
+ driver provides the dmaengine required by the DisplayPort subsystem
+ display driver.
+
config ZX_DMA
tristate "ZTE ZX DMA support"
depends on ARCH_ZX || COMPILE_TEST
diff --git a/drivers/dma/xilinx/Makefile b/drivers/dma/xilinx/Makefile
index e921de575b55..767bb45f641f 100644
--- a/drivers/dma/xilinx/Makefile
+++ b/drivers/dma/xilinx/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_XILINX_DMA) += xilinx_dma.o
obj-$(CONFIG_XILINX_ZYNQMP_DMA) += zynqmp_dma.o
+obj-$(CONFIG_XILINX_ZYNQMP_DPDMA) += xilinx_dpdma.o
diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c
new file mode 100644
index 000000000000..af88a6762ef4
--- /dev/null
+++ b/drivers/dma/xilinx/xilinx_dpdma.c
@@ -0,0 +1,1533 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx ZynqMP DPDMA Engine driver
+ *
+ * Copyright (C) 2015 - 2020 Xilinx, Inc.
+ *
+ * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <dt-bindings/dma/xlnx-zynqmp-dpdma.h>
+
+#include "../dmaengine.h"
+#include "../virt-dma.h"
+
+/* DPDMA registers */
+#define XILINX_DPDMA_ERR_CTRL 0x000
+#define XILINX_DPDMA_ISR 0x004
+#define XILINX_DPDMA_IMR 0x008
+#define XILINX_DPDMA_IEN 0x00c
+#define XILINX_DPDMA_IDS 0x010
+#define XILINX_DPDMA_INTR_DESC_DONE(n) BIT((n) + 0)
+#define XILINX_DPDMA_INTR_DESC_DONE_MASK GENMASK(5, 0)
+#define XILINX_DPDMA_INTR_NO_OSTAND(n) BIT((n) + 6)
+#define XILINX_DPDMA_INTR_NO_OSTAND_MASK GENMASK(11, 6)
+#define XILINX_DPDMA_INTR_AXI_ERR(n) BIT((n) + 12)
+#define XILINX_DPDMA_INTR_AXI_ERR_MASK GENMASK(17, 12)
+#define XILINX_DPDMA_INTR_DESC_ERR(n) BIT((n) + 16)
+#define XILINX_DPDMA_INTR_DESC_ERR_MASK GENMASK(23, 18)
+#define XILINX_DPDMA_INTR_WR_CMD_FIFO_FULL BIT(24)
+#define XILINX_DPDMA_INTR_WR_DATA_FIFO_FULL BIT(25)
+#define XILINX_DPDMA_INTR_AXI_4K_CROSS BIT(26)
+#define XILINX_DPDMA_INTR_VSYNC BIT(27)
+#define XILINX_DPDMA_INTR_CHAN_ERR_MASK 0x00041000
+#define XILINX_DPDMA_INTR_CHAN_ERR 0x00fff000
+#define XILINX_DPDMA_INTR_GLOBAL_ERR 0x07000000
+#define XILINX_DPDMA_INTR_ERR_ALL 0x07fff000
+#define XILINX_DPDMA_INTR_CHAN_MASK 0x00041041
+#define XILINX_DPDMA_INTR_GLOBAL_MASK 0x0f000000
+#define XILINX_DPDMA_INTR_ALL 0x0fffffff
+#define XILINX_DPDMA_EISR 0x014
+#define XILINX_DPDMA_EIMR 0x018
+#define XILINX_DPDMA_EIEN 0x01c
+#define XILINX_DPDMA_EIDS 0x020
+#define XILINX_DPDMA_EINTR_INV_APB BIT(0)
+#define XILINX_DPDMA_EINTR_RD_AXI_ERR(n) BIT((n) + 1)
+#define XILINX_DPDMA_EINTR_RD_AXI_ERR_MASK GENMASK(6, 1)
+#define XILINX_DPDMA_EINTR_PRE_ERR(n) BIT((n) + 7)
+#define XILINX_DPDMA_EINTR_PRE_ERR_MASK GENMASK(12, 7)
+#define XILINX_DPDMA_EINTR_CRC_ERR(n) BIT((n) + 13)
+#define XILINX_DPDMA_EINTR_CRC_ERR_MASK GENMASK(18, 13)
+#define XILINX_DPDMA_EINTR_WR_AXI_ERR(n) BIT((n) + 19)
+#define XILINX_DPDMA_EINTR_WR_AXI_ERR_MASK GENMASK(24, 19)
+#define XILINX_DPDMA_EINTR_DESC_DONE_ERR(n) BIT((n) + 25)
+#define XILINX_DPDMA_EINTR_DESC_DONE_ERR_MASK GENMASK(30, 25)
+#define XILINX_DPDMA_EINTR_RD_CMD_FIFO_FULL BIT(32)
+#define XILINX_DPDMA_EINTR_CHAN_ERR_MASK 0x02082082
+#define XILINX_DPDMA_EINTR_CHAN_ERR 0x7ffffffe
+#define XILINX_DPDMA_EINTR_GLOBAL_ERR 0x80000001
+#define XILINX_DPDMA_EINTR_ALL 0xffffffff
+#define XILINX_DPDMA_CNTL 0x100
+#define XILINX_DPDMA_GBL 0x104
+#define XILINX_DPDMA_GBL_TRIG_MASK(n) ((n) << 0)
+#define XILINX_DPDMA_GBL_RETRIG_MASK(n) ((n) << 6)
+#define XILINX_DPDMA_ALC0_CNTL 0x108
+#define XILINX_DPDMA_ALC0_STATUS 0x10c
+#define XILINX_DPDMA_ALC0_MAX 0x110
+#define XILINX_DPDMA_ALC0_MIN 0x114
+#define XILINX_DPDMA_ALC0_ACC 0x118
+#define XILINX_DPDMA_ALC0_ACC_TRAN 0x11c
+#define XILINX_DPDMA_ALC1_CNTL 0x120
+#define XILINX_DPDMA_ALC1_STATUS 0x124
+#define XILINX_DPDMA_ALC1_MAX 0x128
+#define XILINX_DPDMA_ALC1_MIN 0x12c
+#define XILINX_DPDMA_ALC1_ACC 0x130
+#define XILINX_DPDMA_ALC1_ACC_TRAN 0x134
+
+/* Channel register */
+#define XILINX_DPDMA_CH_BASE 0x200
+#define XILINX_DPDMA_CH_OFFSET 0x100
+#define XILINX_DPDMA_CH_DESC_START_ADDRE 0x000
+#define XILINX_DPDMA_CH_DESC_START_ADDRE_MASK GENMASK(15, 0)
+#define XILINX_DPDMA_CH_DESC_START_ADDR 0x004
+#define XILINX_DPDMA_CH_DESC_NEXT_ADDRE 0x008
+#define XILINX_DPDMA_CH_DESC_NEXT_ADDR 0x00c
+#define XILINX_DPDMA_CH_PYLD_CUR_ADDRE 0x010
+#define XILINX_DPDMA_CH_PYLD_CUR_ADDR 0x014
+#define XILINX_DPDMA_CH_CNTL 0x018
+#define XILINX_DPDMA_CH_CNTL_ENABLE BIT(0)
+#define XILINX_DPDMA_CH_CNTL_PAUSE BIT(1)
+#define XILINX_DPDMA_CH_CNTL_QOS_DSCR_WR_MASK GENMASK(5, 2)
+#define XILINX_DPDMA_CH_CNTL_QOS_DSCR_RD_MASK GENMASK(9, 6)
+#define XILINX_DPDMA_CH_CNTL_QOS_DATA_RD_MASK GENMASK(13, 10)
+#define XILINX_DPDMA_CH_CNTL_QOS_VID_CLASS 11
+#define XILINX_DPDMA_CH_STATUS 0x01c
+#define XILINX_DPDMA_CH_STATUS_OTRAN_CNT_MASK GENMASK(24, 21)
+#define XILINX_DPDMA_CH_VDO 0x020
+#define XILINX_DPDMA_CH_PYLD_SZ 0x024
+#define XILINX_DPDMA_CH_DESC_ID 0x028
+
+/* DPDMA descriptor fields */
+#define XILINX_DPDMA_DESC_CONTROL_PREEMBLE 0xa5
+#define XILINX_DPDMA_DESC_CONTROL_COMPLETE_INTR BIT(8)
+#define XILINX_DPDMA_DESC_CONTROL_DESC_UPDATE BIT(9)
+#define XILINX_DPDMA_DESC_CONTROL_IGNORE_DONE BIT(10)
+#define XILINX_DPDMA_DESC_CONTROL_FRAG_MODE BIT(18)
+#define XILINX_DPDMA_DESC_CONTROL_LAST BIT(19)
+#define XILINX_DPDMA_DESC_CONTROL_ENABLE_CRC BIT(20)
+#define XILINX_DPDMA_DESC_CONTROL_LAST_OF_FRAME BIT(21)
+#define XILINX_DPDMA_DESC_ID_MASK GENMASK(15, 0)
+#define XILINX_DPDMA_DESC_HSIZE_STRIDE_HSIZE_MASK GENMASK(17, 0)
+#define XILINX_DPDMA_DESC_HSIZE_STRIDE_STRIDE_MASK GENMASK(31, 18)
+#define XILINX_DPDMA_DESC_ADDR_EXT_NEXT_ADDR_MASK GENMASK(15, 0)
+#define XILINX_DPDMA_DESC_ADDR_EXT_SRC_ADDR_MASK GENMASK(31, 16)
+
+#define XILINX_DPDMA_ALIGN_BYTES 256
+#define XILINX_DPDMA_LINESIZE_ALIGN_BITS 128
+
+#define XILINX_DPDMA_NUM_CHAN 6
+
+struct xilinx_dpdma_chan;
+
+/**
+ * struct xilinx_dpdma_hw_desc - DPDMA hardware descriptor
+ * @control: control configuration field
+ * @desc_id: descriptor ID
+ * @xfer_size: transfer size
+ * @hsize_stride: horizontal size and stride
+ * @timestamp_lsb: LSB of time stamp
+ * @timestamp_msb: MSB of time stamp
+ * @addr_ext: upper 16 bit of 48 bit address (next_desc and src_addr)
+ * @next_desc: next descriptor 32 bit address
+ * @src_addr: payload source address (1st page, 32 LSB)
+ * @addr_ext_23: payload source address (3nd and 3rd pages, 16 LSBs)
+ * @addr_ext_45: payload source address (4th and 5th pages, 16 LSBs)
+ * @src_addr2: payload source address (2nd page, 32 LSB)
+ * @src_addr3: payload source address (3rd page, 32 LSB)
+ * @src_addr4: payload source address (4th page, 32 LSB)
+ * @src_addr5: payload source address (5th page, 32 LSB)
+ * @crc: descriptor CRC
+ */
+struct xilinx_dpdma_hw_desc {
+ u32 control;
+ u32 desc_id;
+ u32 xfer_size;
+ u32 hsize_stride;
+ u32 timestamp_lsb;
+ u32 timestamp_msb;
+ u32 addr_ext;
+ u32 next_desc;
+ u32 src_addr;
+ u32 addr_ext_23;
+ u32 addr_ext_45;
+ u32 src_addr2;
+ u32 src_addr3;
+ u32 src_addr4;
+ u32 src_addr5;
+ u32 crc;
+} __aligned(XILINX_DPDMA_ALIGN_BYTES);
+
+/**
+ * struct xilinx_dpdma_sw_desc - DPDMA software descriptor
+ * @hw: DPDMA hardware descriptor
+ * @node: list node for software descriptors
+ * @dma_addr: DMA address of the software descriptor
+ */
+struct xilinx_dpdma_sw_desc {
+ struct xilinx_dpdma_hw_desc hw;
+ struct list_head node;
+ dma_addr_t dma_addr;
+};
+
+/**
+ * struct xilinx_dpdma_tx_desc - DPDMA transaction descriptor
+ * @vdesc: virtual DMA descriptor
+ * @chan: DMA channel
+ * @descriptors: list of software descriptors
+ * @error: an error has been detected with this descriptor
+ */
+struct xilinx_dpdma_tx_desc {
+ struct virt_dma_desc vdesc;
+ struct xilinx_dpdma_chan *chan;
+ struct list_head descriptors;
+ bool error;
+};
+
+#define to_dpdma_tx_desc(_desc) \
+ container_of(_desc, struct xilinx_dpdma_tx_desc, vdesc)
+
+/**
+ * struct xilinx_dpdma_chan - DPDMA channel
+ * @vchan: virtual DMA channel
+ * @reg: register base address
+ * @id: channel ID
+ * @wait_to_stop: queue to wait for outstanding transacitons before stopping
+ * @running: true if the channel is running
+ * @first_frame: flag for the first frame of stream
+ * @video_group: flag if multi-channel operation is needed for video channels
+ * @lock: lock to access struct xilinx_dpdma_chan
+ * @desc_pool: descriptor allocation pool
+ * @err_task: error IRQ bottom half handler
+ * @desc.pending: Descriptor schedule to the hardware, pending execution
+ * @desc.active: Descriptor being executed by the hardware
+ * @xdev: DPDMA device
+ */
+struct xilinx_dpdma_chan {
+ struct virt_dma_chan vchan;
+ void __iomem *reg;
+ unsigned int id;
+
+ wait_queue_head_t wait_to_stop;
+ bool running;
+ bool first_frame;
+ bool video_group;
+
+ spinlock_t lock; /* lock to access struct xilinx_dpdma_chan */
+ struct dma_pool *desc_pool;
+ struct tasklet_struct err_task;
+
+ struct {
+ struct xilinx_dpdma_tx_desc *pending;
+ struct xilinx_dpdma_tx_desc *active;
+ } desc;
+
+ struct xilinx_dpdma_device *xdev;
+};
+
+#define to_xilinx_chan(_chan) \
+ container_of(_chan, struct xilinx_dpdma_chan, vchan.chan)
+
+/**
+ * struct xilinx_dpdma_device - DPDMA device
+ * @common: generic dma device structure
+ * @reg: register base address
+ * @dev: generic device structure
+ * @irq: the interrupt number
+ * @axi_clk: axi clock
+ * @chan: DPDMA channels
+ * @ext_addr: flag for 64 bit system (48 bit addressing)
+ */
+struct xilinx_dpdma_device {
+ struct dma_device common;
+ void __iomem *reg;
+ struct device *dev;
+ int irq;
+
+ struct clk *axi_clk;
+ struct xilinx_dpdma_chan *chan[XILINX_DPDMA_NUM_CHAN];
+
+ bool ext_addr;
+};
+
+/* -----------------------------------------------------------------------------
+ * I/O Accessors
+ */
+
+static inline u32 dpdma_read(void __iomem *base, u32 offset)
+{
+ return ioread32(base + offset);
+}
+
+static inline void dpdma_write(void __iomem *base, u32 offset, u32 val)
+{
+ iowrite32(val, base + offset);
+}
+
+static inline void dpdma_clr(void __iomem *base, u32 offset, u32 clr)
+{
+ dpdma_write(base, offset, dpdma_read(base, offset) & ~clr);
+}
+
+static inline void dpdma_set(void __iomem *base, u32 offset, u32 set)
+{
+ dpdma_write(base, offset, dpdma_read(base, offset) | set);
+}
+
+/* -----------------------------------------------------------------------------
+ * Descriptor Operations
+ */
+
+/**
+ * xilinx_dpdma_sw_desc_set_dma_addrs - Set DMA addresses in the descriptor
+ * @sw_desc: The software descriptor in which to set DMA addresses
+ * @prev: The previous descriptor
+ * @dma_addr: array of dma addresses
+ * @num_src_addr: number of addresses in @dma_addr
+ *
+ * Set all the DMA addresses in the hardware descriptor corresponding to @dev
+ * from @dma_addr. If a previous descriptor is specified in @prev, its next
+ * descriptor DMA address is set to the DMA address of @sw_desc. @prev may be
+ * identical to @sw_desc for cyclic transfers.
+ */
+static void xilinx_dpdma_sw_desc_set_dma_addrs(struct xilinx_dpdma_device *xdev,
+ struct xilinx_dpdma_sw_desc *sw_desc,
+ struct xilinx_dpdma_sw_desc *prev,
+ dma_addr_t dma_addr[],
+ unsigned int num_src_addr)
+{
+ struct xilinx_dpdma_hw_desc *hw_desc = &sw_desc->hw;
+ unsigned int i;
+
+ hw_desc->src_addr = lower_32_bits(dma_addr[0]);
+ if (xdev->ext_addr)
+ hw_desc->addr_ext |=
+ FIELD_PREP(XILINX_DPDMA_DESC_ADDR_EXT_SRC_ADDR_MASK,
+ upper_32_bits(dma_addr[0]));
+
+ for (i = 1; i < num_src_addr; i++) {
+ u32 *addr = &hw_desc->src_addr2;
+
+ addr[i-1] = lower_32_bits(dma_addr[i]);
+
+ if (xdev->ext_addr) {
+ u32 *addr_ext = &hw_desc->addr_ext_23;
+ u32 addr_msb;
+
+ addr_msb = upper_32_bits(dma_addr[i]) & GENMASK(15, 0);
+ addr_msb <<= 16 * ((i - 1) % 2);
+ addr_ext[(i - 1) / 2] |= addr_msb;
+ }
+ }
+
+ if (!prev)
+ return;
+
+ prev->hw.next_desc = lower_32_bits(sw_desc->dma_addr);
+ if (xdev->ext_addr)
+ prev->hw.addr_ext |=
+ FIELD_PREP(XILINX_DPDMA_DESC_ADDR_EXT_NEXT_ADDR_MASK,
+ upper_32_bits(sw_desc->dma_addr));
+}
+
+/**
+ * xilinx_dpdma_chan_alloc_sw_desc - Allocate a software descriptor
+ * @chan: DPDMA channel
+ *
+ * Allocate a software descriptor from the channel's descriptor pool.
+ *
+ * Return: a software descriptor or NULL.
+ */
+static struct xilinx_dpdma_sw_desc *
+xilinx_dpdma_chan_alloc_sw_desc(struct xilinx_dpdma_chan *chan)
+{
+ struct xilinx_dpdma_sw_desc *sw_desc;
+ dma_addr_t dma_addr;
+
+ sw_desc = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &dma_addr);
+ if (!sw_desc)
+ return NULL;
+
+ sw_desc->dma_addr = dma_addr;
+
+ return sw_desc;
+}
+
+/**
+ * xilinx_dpdma_chan_free_sw_desc - Free a software descriptor
+ * @chan: DPDMA channel
+ * @sw_desc: software descriptor to free
+ *
+ * Free a software descriptor from the channel's descriptor pool.
+ */
+static void
+xilinx_dpdma_chan_free_sw_desc(struct xilinx_dpdma_chan *chan,
+ struct xilinx_dpdma_sw_desc *sw_desc)
+{
+ dma_pool_free(chan->desc_pool, sw_desc, sw_desc->dma_addr);
+}
+
+/**
+ * xilinx_dpdma_chan_dump_tx_desc - Dump a tx descriptor
+ * @chan: DPDMA channel
+ * @tx_desc: tx descriptor to dump
+ *
+ * Dump contents of a tx descriptor
+ */
+static void xilinx_dpdma_chan_dump_tx_desc(struct xilinx_dpdma_chan *chan,
+ struct xilinx_dpdma_tx_desc *tx_desc)
+{
+ struct xilinx_dpdma_sw_desc *sw_desc;
+ struct device *dev = chan->xdev->dev;
+ unsigned int i = 0;
+
+ dev_dbg(dev, "------- TX descriptor dump start -------\n");
+ dev_dbg(dev, "------- channel ID = %d -------\n", chan->id);
+
+ list_for_each_entry(sw_desc, &tx_desc->descriptors, node) {
+ struct xilinx_dpdma_hw_desc *hw_desc = &sw_desc->hw;
+
+ dev_dbg(dev, "------- HW descriptor %d -------\n", i++);
+ dev_dbg(dev, "descriptor DMA addr: %pad\n", &sw_desc->dma_addr);
+ dev_dbg(dev, "control: 0x%08x\n", hw_desc->control);
+ dev_dbg(dev, "desc_id: 0x%08x\n", hw_desc->desc_id);
+ dev_dbg(dev, "xfer_size: 0x%08x\n", hw_desc->xfer_size);
+ dev_dbg(dev, "hsize_stride: 0x%08x\n", hw_desc->hsize_stride);
+ dev_dbg(dev, "timestamp_lsb: 0x%08x\n", hw_desc->timestamp_lsb);
+ dev_dbg(dev, "timestamp_msb: 0x%08x\n", hw_desc->timestamp_msb);
+ dev_dbg(dev, "addr_ext: 0x%08x\n", hw_desc->addr_ext);
+ dev_dbg(dev, "next_desc: 0x%08x\n", hw_desc->next_desc);
+ dev_dbg(dev, "src_addr: 0x%08x\n", hw_desc->src_addr);
+ dev_dbg(dev, "addr_ext_23: 0x%08x\n", hw_desc->addr_ext_23);
+ dev_dbg(dev, "addr_ext_45: 0x%08x\n", hw_desc->addr_ext_45);
+ dev_dbg(dev, "src_addr2: 0x%08x\n", hw_desc->src_addr2);
+ dev_dbg(dev, "src_addr3: 0x%08x\n", hw_desc->src_addr3);
+ dev_dbg(dev, "src_addr4: 0x%08x\n", hw_desc->src_addr4);
+ dev_dbg(dev, "src_addr5: 0x%08x\n", hw_desc->src_addr5);
+ dev_dbg(dev, "crc: 0x%08x\n", hw_desc->crc);
+ }
+
+ dev_dbg(dev, "------- TX descriptor dump end -------\n");
+}
+
+/**
+ * xilinx_dpdma_chan_alloc_tx_desc - Allocate a transaction descriptor
+ * @chan: DPDMA channel
+ *
+ * Allocate a tx descriptor.
+ *
+ * Return: a tx descriptor or NULL.
+ */
+static struct xilinx_dpdma_tx_desc *
+xilinx_dpdma_chan_alloc_tx_desc(struct xilinx_dpdma_chan *chan)
+{
+ struct xilinx_dpdma_tx_desc *tx_desc;
+
+ tx_desc = kzalloc(sizeof(*tx_desc), GFP_NOWAIT);
+ if (!tx_desc)
+ return NULL;
+
+ INIT_LIST_HEAD(&tx_desc->descriptors);
+ tx_desc->chan = chan;
+ tx_desc->error = false;
+
+ return tx_desc;
+}
+
+/**
+ * xilinx_dpdma_chan_free_tx_desc - Free a virtual DMA descriptor
+ * @vdesc: virtual DMA descriptor
+ *
+ * Free the virtual DMA descriptor @vdesc including its software descriptors.
+ */
+static void xilinx_dpdma_chan_free_tx_desc(struct virt_dma_desc *vdesc)
+{
+ struct xilinx_dpdma_sw_desc *sw_desc, *next;
+ struct xilinx_dpdma_tx_desc *desc;
+
+ if (!vdesc)
+ return;
+
+ desc = to_dpdma_tx_desc(vdesc);
+
+ list_for_each_entry_safe(sw_desc, next, &desc->descriptors, node) {
+ list_del(&sw_desc->node);
+ xilinx_dpdma_chan_free_sw_desc(desc->chan, sw_desc);
+ }
+
+ kfree(desc);
+}
+
+/**
+ * xilinx_dpdma_chan_prep_interleaved_dma - Prepare an interleaved dma
+ * descriptor
+ * @chan: DPDMA channel
+ * @xt: dma interleaved template
+ *
+ * Prepare a tx descriptor including internal software/hardware descriptors
+ * based on @xt.
+ *
+ * Return: A DPDMA TX descriptor on success, or NULL.
+ */
+static struct xilinx_dpdma_tx_desc *
+xilinx_dpdma_chan_prep_interleaved_dma(struct xilinx_dpdma_chan *chan,
+ struct dma_interleaved_template *xt)
+{
+ struct xilinx_dpdma_tx_desc *tx_desc;
+ struct xilinx_dpdma_sw_desc *sw_desc;
+ struct xilinx_dpdma_hw_desc *hw_desc;
+ size_t hsize = xt->sgl[0].size;
+ size_t stride = hsize + xt->sgl[0].icg;
+
+ if (!IS_ALIGNED(xt->src_start, XILINX_DPDMA_ALIGN_BYTES)) {
+ dev_err(chan->xdev->dev, "buffer should be aligned at %d B\n",
+ XILINX_DPDMA_ALIGN_BYTES);
+ return NULL;
+ }
+
+ tx_desc = xilinx_dpdma_chan_alloc_tx_desc(chan);
+ if (!tx_desc)
+ return NULL;
+
+ sw_desc = xilinx_dpdma_chan_alloc_sw_desc(chan);
+ if (!sw_desc) {
+ xilinx_dpdma_chan_free_tx_desc(&tx_desc->vdesc);
+ return NULL;
+ }
+
+ xilinx_dpdma_sw_desc_set_dma_addrs(chan->xdev, sw_desc, sw_desc,
+ &xt->src_start, 1);
+
+ hw_desc = &sw_desc->hw;
+ hsize = ALIGN(hsize, XILINX_DPDMA_LINESIZE_ALIGN_BITS / 8);
+ hw_desc->xfer_size = hsize * xt->numf;
+ hw_desc->hsize_stride =
+ FIELD_PREP(XILINX_DPDMA_DESC_HSIZE_STRIDE_HSIZE_MASK, hsize) |
+ FIELD_PREP(XILINX_DPDMA_DESC_HSIZE_STRIDE_STRIDE_MASK,
+ stride / 16);
+ hw_desc->control |= XILINX_DPDMA_DESC_CONTROL_PREEMBLE;
+ hw_desc->control |= XILINX_DPDMA_DESC_CONTROL_COMPLETE_INTR;
+ hw_desc->control |= XILINX_DPDMA_DESC_CONTROL_IGNORE_DONE;
+ hw_desc->control |= XILINX_DPDMA_DESC_CONTROL_LAST_OF_FRAME;
+
+ list_add_tail(&sw_desc->node, &tx_desc->descriptors);
+
+ return tx_desc;
+}
+
+/* -----------------------------------------------------------------------------
+ * DPDMA Channel Operations
+ */
+
+/**
+ * xilinx_dpdma_chan_enable - Enable the channel
+ * @chan: DPDMA channel
+ *
+ * Enable the channel and its interrupts. Set the QoS values for video class.
+ */
+static void xilinx_dpdma_chan_enable(struct xilinx_dpdma_chan *chan)
+{
+ u32 reg;
+
+ reg = (XILINX_DPDMA_INTR_CHAN_MASK << chan->id)
+ | XILINX_DPDMA_INTR_GLOBAL_MASK;
+ dpdma_write(chan->xdev->reg, XILINX_DPDMA_IEN, reg);
+ reg = (XILINX_DPDMA_EINTR_CHAN_ERR_MASK << chan->id)
+ | XILINX_DPDMA_INTR_GLOBAL_ERR;
+ dpdma_write(chan->xdev->reg, XILINX_DPDMA_EIEN, reg);
+
+ reg = XILINX_DPDMA_CH_CNTL_ENABLE
+ | FIELD_PREP(XILINX_DPDMA_CH_CNTL_QOS_DSCR_WR_MASK,
+ XILINX_DPDMA_CH_CNTL_QOS_VID_CLASS)
+ | FIELD_PREP(XILINX_DPDMA_CH_CNTL_QOS_DSCR_RD_MASK,
+ XILINX_DPDMA_CH_CNTL_QOS_VID_CLASS)
+ | FIELD_PREP(XILINX_DPDMA_CH_CNTL_QOS_DATA_RD_MASK,
+ XILINX_DPDMA_CH_CNTL_QOS_VID_CLASS);
+ dpdma_set(chan->reg, XILINX_DPDMA_CH_CNTL, reg);
+}
+
+/**
+ * xilinx_dpdma_chan_disable - Disable the channel
+ * @chan: DPDMA channel
+ *
+ * Disable the channel and its interrupts.
+ */
+static void xilinx_dpdma_chan_disable(struct xilinx_dpdma_chan *chan)
+{
+ u32 reg;
+
+ reg = XILINX_DPDMA_INTR_CHAN_MASK << chan->id;
+ dpdma_write(chan->xdev->reg, XILINX_DPDMA_IEN, reg);
+ reg = XILINX_DPDMA_EINTR_CHAN_ERR_MASK << chan->id;
+ dpdma_write(chan->xdev->reg, XILINX_DPDMA_EIEN, reg);
+
+ dpdma_clr(chan->reg, XILINX_DPDMA_CH_CNTL, XILINX_DPDMA_CH_CNTL_ENABLE);
+}
+
+/**
+ * xilinx_dpdma_chan_pause - Pause the channel
+ * @chan: DPDMA channel
+ *
+ * Pause the channel.
+ */
+static void xilinx_dpdma_chan_pause(struct xilinx_dpdma_chan *chan)
+{
+ dpdma_set(chan->reg, XILINX_DPDMA_CH_CNTL, XILINX_DPDMA_CH_CNTL_PAUSE);
+}
+
+/**
+ * xilinx_dpdma_chan_unpause - Unpause the channel
+ * @chan: DPDMA channel
+ *
+ * Unpause the channel.
+ */
+static void xilinx_dpdma_chan_unpause(struct xilinx_dpdma_chan *chan)
+{
+ dpdma_clr(chan->reg, XILINX_DPDMA_CH_CNTL, XILINX_DPDMA_CH_CNTL_PAUSE);
+}
+
+static u32 xilinx_dpdma_chan_video_group_ready(struct xilinx_dpdma_chan *chan)
+{
+ struct xilinx_dpdma_device *xdev = chan->xdev;
+ u32 channels = 0;
+ unsigned int i;
+
+ for (i = ZYNQMP_DPDMA_VIDEO0; i <= ZYNQMP_DPDMA_VIDEO2; i++) {
+ if (xdev->chan[i]->video_group && !xdev->chan[i]->running)
+ return 0;
+
+ if (xdev->chan[i]->video_group)
+ channels |= BIT(i);
+ }
+
+ return channels;
+}
+
+/**
+ * xilinx_dpdma_chan_queue_transfer - Queue the next transfer
+ * @chan: DPDMA channel
+ *
+ * Queue the next descriptor, if any, to the hardware. If the channel is
+ * stopped, start it first. Otherwise retrigger it with the next descriptor.
+ */
+static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
+{
+ struct xilinx_dpdma_device *xdev = chan->xdev;
+ struct xilinx_dpdma_sw_desc *sw_desc;
+ struct xilinx_dpdma_tx_desc *desc;
+ struct virt_dma_desc *vdesc;
+ u32 reg, channels;
+
+ lockdep_assert_held(&chan->lock);
+
+ if (chan->desc.pending)
+ return;
+
+ if (!chan->running) {
+ xilinx_dpdma_chan_unpause(chan);
+ xilinx_dpdma_chan_enable(chan);
+ chan->first_frame = true;
+ chan->running = true;
+ }
+
+ if (chan->video_group)
+ channels = xilinx_dpdma_chan_video_group_ready(chan);
+ else
+ channels = BIT(chan->id);
+
+ if (!channels)
+ return;
+
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc)
+ return;
+
+ desc = to_dpdma_tx_desc(vdesc);
+ chan->desc.pending = desc;
+ list_del(&desc->vdesc.node);
+
+ /*
+ * Assign the cookie to descriptors in this transaction. Only 16 bit
+ * will be used, but it should be enough.
+ */
+ list_for_each_entry(sw_desc, &desc->descriptors, node)
+ sw_desc->hw.desc_id = desc->vdesc.tx.cookie;
+
+ sw_desc = list_first_entry(&desc->descriptors,
+ struct xilinx_dpdma_sw_desc, node);
+ dpdma_write(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDR,
+ lower_32_bits(sw_desc->dma_addr));
+ if (xdev->ext_addr)
+ dpdma_write(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDRE,
+ FIELD_PREP(XILINX_DPDMA_CH_DESC_START_ADDRE_MASK,
+ upper_32_bits(sw_desc->dma_addr)));
+
+ if (chan->first_frame)
+ reg = XILINX_DPDMA_GBL_TRIG_MASK(channels);
+ else
+ reg = XILINX_DPDMA_GBL_RETRIG_MASK(channels);
+
+ chan->first_frame = false;
+
+ dpdma_write(xdev->reg, XILINX_DPDMA_GBL, reg);
+}
+
+/**
+ * xilinx_dpdma_chan_ostand - Number of outstanding transactions
+ * @chan: DPDMA channel
+ *
+ * Read and return the number of outstanding transactions from register.
+ *
+ * Return: Number of outstanding transactions from the status register.
+ */
+static u32 xilinx_dpdma_chan_ostand(struct xilinx_dpdma_chan *chan)
+{
+ return FIELD_GET(XILINX_DPDMA_CH_STATUS_OTRAN_CNT_MASK,
+ dpdma_read(chan->reg, XILINX_DPDMA_CH_STATUS));
+}
+
+/**
+ * xilinx_dpdma_chan_no_ostand - Notify no outstanding transaction event
+ * @chan: DPDMA channel
+ *
+ * Notify waiters for no outstanding event, so waiters can stop the channel
+ * safely. This function is supposed to be called when 'no outstanding'
+ * interrupt is generated. The 'no outstanding' interrupt is disabled and
+ * should be re-enabled when this event is handled. If the channel status
+ * register still shows some number of outstanding transactions, the interrupt
+ * remains enabled.
+ *
+ * Return: 0 on success. On failure, -EWOULDBLOCK if there's still outstanding
+ * transaction(s).
+ */
+static int xilinx_dpdma_chan_notify_no_ostand(struct xilinx_dpdma_chan *chan)
+{
+ u32 cnt;
+
+ cnt = xilinx_dpdma_chan_ostand(chan);
+ if (cnt) {
+ dev_dbg(chan->xdev->dev, "%d outstanding transactions\n", cnt);
+ return -EWOULDBLOCK;
+ }
+
+ /* Disable 'no outstanding' interrupt */
+ dpdma_write(chan->xdev->reg, XILINX_DPDMA_IDS,
+ XILINX_DPDMA_INTR_NO_OSTAND(chan->id));
+ wake_up(&chan->wait_to_stop);
+
+ return 0;
+}
+
+/**
+ * xilinx_dpdma_chan_wait_no_ostand - Wait for the no outstanding irq
+ * @chan: DPDMA channel
+ *
+ * Wait for the no outstanding transaction interrupt. This functions can sleep
+ * for 50ms.
+ *
+ * Return: 0 on success. On failure, -ETIMEOUT for time out, or the error code
+ * from wait_event_interruptible_timeout().
+ */
+static int xilinx_dpdma_chan_wait_no_ostand(struct xilinx_dpdma_chan *chan)
+{
+ int ret;
+
+ /* Wait for a no outstanding transaction interrupt upto 50msec */
+ ret = wait_event_interruptible_timeout(chan->wait_to_stop,
+ !xilinx_dpdma_chan_ostand(chan),
+ msecs_to_jiffies(50));
+ if (ret > 0) {
+ dpdma_write(chan->xdev->reg, XILINX_DPDMA_IEN,
+ XILINX_DPDMA_INTR_NO_OSTAND(chan->id));
+ return 0;
+ }
+
+ dev_err(chan->xdev->dev, "not ready to stop: %d trans\n",
+ xilinx_dpdma_chan_ostand(chan));
+
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return ret;
+}
+
+/**
+ * xilinx_dpdma_chan_poll_no_ostand - Poll the outstanding transaction status
+ * @chan: DPDMA channel
+ *
+ * Poll the outstanding transaction status, and return when there's no
+ * outstanding transaction. This functions can be used in the interrupt context
+ * or where the atomicity is required. Calling thread may wait more than 50ms.
+ *
+ * Return: 0 on success, or -ETIMEDOUT.
+ */
+static int xilinx_dpdma_chan_poll_no_ostand(struct xilinx_dpdma_chan *chan)
+{
+ u32 cnt, loop = 50000;
+
+ /* Poll at least for 50ms (20 fps). */
+ do {
+ cnt = xilinx_dpdma_chan_ostand(chan);
+ udelay(1);
+ } while (loop-- > 0 && cnt);
+
+ if (loop) {
+ dpdma_write(chan->xdev->reg, XILINX_DPDMA_IEN,
+ XILINX_DPDMA_INTR_NO_OSTAND(chan->id));
+ return 0;
+ }
+
+ dev_err(chan->xdev->dev, "not ready to stop: %d trans\n",
+ xilinx_dpdma_chan_ostand(chan));
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * xilinx_dpdma_chan_stop - Stop the channel
+ * @chan: DPDMA channel
+ *
+ * Stop a previously paused channel by first waiting for completion of all
+ * outstanding transaction and then disabling the channel.
+ *
+ * Return: 0 on success, or -ETIMEDOUT if the channel failed to stop.
+ */
+static int xilinx_dpdma_chan_stop(struct xilinx_dpdma_chan *chan)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = xilinx_dpdma_chan_wait_no_ostand(chan);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ xilinx_dpdma_chan_disable(chan);
+ chan->running = false;
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ return 0;
+}
+
+/**
+ * xilinx_dpdma_chan_done_irq - Handle hardware descriptor completion
+ * @chan: DPDMA channel
+ *
+ * Handle completion of the currently active descriptor (@chan->desc.active). As
+ * we currently support cyclic transfers only, this just invokes the cyclic
+ * callback. The descriptor will be completed at the VSYNC interrupt when a new
+ * descriptor replaces it.
+ */
+static void xilinx_dpdma_chan_done_irq(struct xilinx_dpdma_chan *chan)
+{
+ struct xilinx_dpdma_tx_desc *active = chan->desc.active;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ if (active)
+ vchan_cyclic_callback(&active->vdesc);
+ else
+ dev_warn(chan->xdev->dev,
+ "DONE IRQ with no active descriptor!\n");
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+/**
+ * xilinx_dpdma_chan_vsync_irq - Handle hardware descriptor scheduling
+ * @chan: DPDMA channel
+ *
+ * At VSYNC the active descriptor may have been replaced by the pending
+ * descriptor. Detect this through the DESC_ID and perform appropriate
+ * bookkeeping.
+ */
+static void xilinx_dpdma_chan_vsync_irq(struct xilinx_dpdma_chan *chan)
+{
+ struct xilinx_dpdma_tx_desc *pending;
+ struct xilinx_dpdma_sw_desc *sw_desc;
+ unsigned long flags;
+ u32 desc_id;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ pending = chan->desc.pending;
+ if (!chan->running || !pending)
+ goto out;
+
+ desc_id = dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_ID);
+
+ /* If the retrigger raced with vsync, retry at the next frame. */
+ sw_desc = list_first_entry(&pending->descriptors,
+ struct xilinx_dpdma_sw_desc, node);
+ if (sw_desc->hw.desc_id != desc_id)
+ goto out;
+
+ /*
+ * Complete the active descriptor, if any, promote the pending
+ * descriptor to active, and queue the next transfer, if any.
+ */
+ if (chan->desc.active)
+ vchan_cookie_complete(&chan->desc.active->vdesc);
+ chan->desc.active = pending;
+ chan->desc.pending = NULL;
+
+ xilinx_dpdma_chan_queue_transfer(chan);
+
+out:
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+/**
+ * xilinx_dpdma_chan_err - Detect any channel error
+ * @chan: DPDMA channel
+ * @isr: masked Interrupt Status Register
+ * @eisr: Error Interrupt Status Register
+ *
+ * Return: true if any channel error occurs, or false otherwise.
+ */
+static bool
+xilinx_dpdma_chan_err(struct xilinx_dpdma_chan *chan, u32 isr, u32 eisr)
+{
+ if (!chan)
+ return false;
+
+ if (chan->running &&
+ ((isr & (XILINX_DPDMA_INTR_CHAN_ERR_MASK << chan->id)) ||
+ (eisr & (XILINX_DPDMA_EINTR_CHAN_ERR_MASK << chan->id))))
+ return true;
+
+ return false;
+}
+
+/**
+ * xilinx_dpdma_chan_handle_err - DPDMA channel error handling
+ * @chan: DPDMA channel
+ *
+ * This function is called when any channel error or any global error occurs.
+ * The function disables the paused channel by errors and determines
+ * if the current active descriptor can be rescheduled depending on
+ * the descriptor status.
+ */
+static void xilinx_dpdma_chan_handle_err(struct xilinx_dpdma_chan *chan)
+{
+ struct xilinx_dpdma_device *xdev = chan->xdev;
+ struct xilinx_dpdma_tx_desc *active;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ dev_dbg(xdev->dev, "cur desc addr = 0x%04x%08x\n",
+ dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDRE),
+ dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDR));
+ dev_dbg(xdev->dev, "cur payload addr = 0x%04x%08x\n",
+ dpdma_read(chan->reg, XILINX_DPDMA_CH_PYLD_CUR_ADDRE),
+ dpdma_read(chan->reg, XILINX_DPDMA_CH_PYLD_CUR_ADDR));
+
+ xilinx_dpdma_chan_disable(chan);
+ chan->running = false;
+
+ if (!chan->desc.active)
+ goto out_unlock;
+
+ active = chan->desc.active;
+ chan->desc.active = NULL;
+
+ xilinx_dpdma_chan_dump_tx_desc(chan, active);
+
+ if (active->error)
+ dev_dbg(xdev->dev, "repeated error on desc\n");
+
+ /* Reschedule if there's no new descriptor */
+ if (!chan->desc.pending &&
+ list_empty(&chan->vchan.desc_issued)) {
+ active->error = true;
+ list_add_tail(&active->vdesc.node,
+ &chan->vchan.desc_issued);
+ } else {
+ xilinx_dpdma_chan_free_tx_desc(&active->vdesc);
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * DMA Engine Operations
+ */
+
+static struct dma_async_tx_descriptor *
+xilinx_dpdma_prep_interleaved_dma(struct dma_chan *dchan,
+ struct dma_interleaved_template *xt,
+ unsigned long flags)
+{
+ struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan);
+ struct xilinx_dpdma_tx_desc *desc;
+
+ if (xt->dir != DMA_MEM_TO_DEV)
+ return NULL;
+
+ if (!xt->numf || !xt->sgl[0].size)
+ return NULL;
+
+ if (!(flags & DMA_PREP_REPEAT) || !(flags & DMA_PREP_LOAD_EOT))
+ return NULL;
+
+ desc = xilinx_dpdma_chan_prep_interleaved_dma(chan, xt);
+ if (!desc)
+ return NULL;
+
+ vchan_tx_prep(&chan->vchan, &desc->vdesc, flags | DMA_CTRL_ACK);
+
+ return &desc->vdesc.tx;
+}
+
+/**
+ * xilinx_dpdma_alloc_chan_resources - Allocate resources for the channel
+ * @dchan: DMA channel
+ *
+ * Allocate a descriptor pool for the channel.
+ *
+ * Return: 0 on success, or -ENOMEM if failed to allocate a pool.
+ */
+static int xilinx_dpdma_alloc_chan_resources(struct dma_chan *dchan)
+{
+ struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan);
+ size_t align = __alignof__(struct xilinx_dpdma_sw_desc);
+
+ chan->desc_pool = dma_pool_create(dev_name(chan->xdev->dev),
+ chan->xdev->dev,
+ sizeof(struct xilinx_dpdma_sw_desc),
+ align, 0);
+ if (!chan->desc_pool) {
+ dev_err(chan->xdev->dev,
+ "failed to allocate a descriptor pool\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * xilinx_dpdma_free_chan_resources - Free all resources for the channel
+ * @dchan: DMA channel
+ *
+ * Free resources associated with the virtual DMA channel, and destroy the
+ * descriptor pool.
+ */
+static void xilinx_dpdma_free_chan_resources(struct dma_chan *dchan)
+{
+ struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan);
+
+ vchan_free_chan_resources(&chan->vchan);
+
+ dma_pool_destroy(chan->desc_pool);
+ chan->desc_pool = NULL;
+}
+
+static void xilinx_dpdma_issue_pending(struct dma_chan *dchan)
+{
+ struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (vchan_issue_pending(&chan->vchan))
+ xilinx_dpdma_chan_queue_transfer(chan);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+}
+
+static int xilinx_dpdma_config(struct dma_chan *dchan,
+ struct dma_slave_config *config)
+{
+ struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan);
+ unsigned long flags;
+
+ /*
+ * The destination address doesn't need to be specified as the DPDMA is
+ * hardwired to the destination (the DP controller). The transfer
+ * width, burst size and port window size are thus meaningless, they're
+ * fixed both on the DPDMA side and on the DP controller side.
+ */
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ /*
+ * Abuse the slave_id to indicate that the channel is part of a video
+ * group.
+ */
+ if (chan->id >= ZYNQMP_DPDMA_VIDEO0 && chan->id <= ZYNQMP_DPDMA_VIDEO2)
+ chan->video_group = config->slave_id != 0;
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ return 0;
+}
+
+static int xilinx_dpdma_pause(struct dma_chan *dchan)
+{
+ xilinx_dpdma_chan_pause(to_xilinx_chan(dchan));
+
+ return 0;
+}
+
+static int xilinx_dpdma_resume(struct dma_chan *dchan)
+{
+ xilinx_dpdma_chan_unpause(to_xilinx_chan(dchan));
+
+ return 0;
+}
+
+/**
+ * xilinx_dpdma_terminate_all - Terminate the channel and descriptors
+ * @dchan: DMA channel
+ *
+ * Pause the channel without waiting for ongoing transfers to complete. Waiting
+ * for completion is performed by xilinx_dpdma_synchronize() that will disable
+ * the channel to complete the stop.
+ *
+ * All the descriptors associated with the channel that are guaranteed not to
+ * be touched by the hardware. The pending and active descriptor are not
+ * touched, and will be freed either upon completion, or by
+ * xilinx_dpdma_synchronize().
+ *
+ * Return: 0 on success, or -ETIMEDOUT if the channel failed to stop.
+ */
+static int xilinx_dpdma_terminate_all(struct dma_chan *dchan)
+{
+ struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan);
+ struct xilinx_dpdma_device *xdev = chan->xdev;
+ LIST_HEAD(descriptors);
+ unsigned long flags;
+ unsigned int i;
+
+ /* Pause the channel (including the whole video group if applicable). */
+ if (chan->video_group) {
+ for (i = ZYNQMP_DPDMA_VIDEO0; i <= ZYNQMP_DPDMA_VIDEO2; i++) {
+ if (xdev->chan[i]->video_group &&
+ xdev->chan[i]->running) {
+ xilinx_dpdma_chan_pause(xdev->chan[i]);
+ xdev->chan[i]->video_group = false;
+ }
+ }
+ } else {
+ xilinx_dpdma_chan_pause(chan);
+ }
+
+ /* Gather all the descriptors we can free and free them. */
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ vchan_get_all_descriptors(&chan->vchan, &descriptors);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ vchan_dma_desc_free_list(&chan->vchan, &descriptors);
+
+ return 0;
+}
+
+/**
+ * xilinx_dpdma_synchronize - Synchronize callback execution
+ * @dchan: DMA channel
+ *
+ * Synchronizing callback execution ensures that all previously issued
+ * transfers have completed and all associated callbacks have been called and
+ * have returned.
+ *
+ * This function waits for the DMA channel to stop. It assumes it has been
+ * paused by a previous call to dmaengine_terminate_async(), and that no new
+ * pending descriptors have been issued with dma_async_issue_pending(). The
+ * behaviour is undefined otherwise.
+ */
+static void xilinx_dpdma_synchronize(struct dma_chan *dchan)
+{
+ struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan);
+ unsigned long flags;
+
+ xilinx_dpdma_chan_stop(chan);
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (chan->desc.pending) {
+ vchan_terminate_vdesc(&chan->desc.pending->vdesc);
+ chan->desc.pending = NULL;
+ }
+ if (chan->desc.active) {
+ vchan_terminate_vdesc(&chan->desc.active->vdesc);
+ chan->desc.active = NULL;
+ }
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ vchan_synchronize(&chan->vchan);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt and Tasklet Handling
+ */
+
+/**
+ * xilinx_dpdma_err - Detect any global error
+ * @isr: Interrupt Status Register
+ * @eisr: Error Interrupt Status Register
+ *
+ * Return: True if any global error occurs, or false otherwise.
+ */
+static bool xilinx_dpdma_err(u32 isr, u32 eisr)
+{
+ if (isr & XILINX_DPDMA_INTR_GLOBAL_ERR ||
+ eisr & XILINX_DPDMA_EINTR_GLOBAL_ERR)
+ return true;
+
+ return false;
+}
+
+/**
+ * xilinx_dpdma_handle_err_irq - Handle DPDMA error interrupt
+ * @xdev: DPDMA device
+ * @isr: masked Interrupt Status Register
+ * @eisr: Error Interrupt Status Register
+ *
+ * Handle if any error occurs based on @isr and @eisr. This function disables
+ * corresponding error interrupts, and those should be re-enabled once handling
+ * is done.
+ */
+static void xilinx_dpdma_handle_err_irq(struct xilinx_dpdma_device *xdev,
+ u32 isr, u32 eisr)
+{
+ bool err = xilinx_dpdma_err(isr, eisr);
+ unsigned int i;
+
+ dev_dbg_ratelimited(xdev->dev,
+ "error irq: isr = 0x%08x, eisr = 0x%08x\n",
+ isr, eisr);
+
+ /* Disable channel error interrupts until errors are handled. */
+ dpdma_write(xdev->reg, XILINX_DPDMA_IDS,
+ isr & ~XILINX_DPDMA_INTR_GLOBAL_ERR);
+ dpdma_write(xdev->reg, XILINX_DPDMA_EIDS,
+ eisr & ~XILINX_DPDMA_EINTR_GLOBAL_ERR);
+
+ for (i = 0; i < ARRAY_SIZE(xdev->chan); i++)
+ if (err || xilinx_dpdma_chan_err(xdev->chan[i], isr, eisr))
+ tasklet_schedule(&xdev->chan[i]->err_task);
+}
+
+/**
+ * xilinx_dpdma_enable_irq - Enable interrupts
+ * @xdev: DPDMA device
+ *
+ * Enable interrupts.
+ */
+static void xilinx_dpdma_enable_irq(struct xilinx_dpdma_device *xdev)
+{
+ dpdma_write(xdev->reg, XILINX_DPDMA_IEN, XILINX_DPDMA_INTR_ALL);
+ dpdma_write(xdev->reg, XILINX_DPDMA_EIEN, XILINX_DPDMA_EINTR_ALL);
+}
+
+/**
+ * xilinx_dpdma_disable_irq - Disable interrupts
+ * @xdev: DPDMA device
+ *
+ * Disable interrupts.
+ */
+static void xilinx_dpdma_disable_irq(struct xilinx_dpdma_device *xdev)
+{
+ dpdma_write(xdev->reg, XILINX_DPDMA_IDS, XILINX_DPDMA_INTR_ERR_ALL);
+ dpdma_write(xdev->reg, XILINX_DPDMA_EIDS, XILINX_DPDMA_EINTR_ALL);
+}
+
+/**
+ * xilinx_dpdma_chan_err_task - Per channel tasklet for error handling
+ * @data: tasklet data to be casted to DPDMA channel structure
+ *
+ * Per channel error handling tasklet. This function waits for the outstanding
+ * transaction to complete and triggers error handling. After error handling,
+ * re-enable channel error interrupts, and restart the channel if needed.
+ */
+static void xilinx_dpdma_chan_err_task(unsigned long data)
+{
+ struct xilinx_dpdma_chan *chan = (struct xilinx_dpdma_chan *)data;
+ struct xilinx_dpdma_device *xdev = chan->xdev;
+ unsigned long flags;
+
+ /* Proceed error handling even when polling fails. */
+ xilinx_dpdma_chan_poll_no_ostand(chan);
+
+ xilinx_dpdma_chan_handle_err(chan);
+
+ dpdma_write(xdev->reg, XILINX_DPDMA_IEN,
+ XILINX_DPDMA_INTR_CHAN_ERR_MASK << chan->id);
+ dpdma_write(xdev->reg, XILINX_DPDMA_EIEN,
+ XILINX_DPDMA_EINTR_CHAN_ERR_MASK << chan->id);
+
+ spin_lock_irqsave(&chan->lock, flags);
+ xilinx_dpdma_chan_queue_transfer(chan);
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+static irqreturn_t xilinx_dpdma_irq_handler(int irq, void *data)
+{
+ struct xilinx_dpdma_device *xdev = data;
+ unsigned long mask;
+ unsigned int i;
+ u32 status;
+ u32 error;
+
+ status = dpdma_read(xdev->reg, XILINX_DPDMA_ISR);
+ error = dpdma_read(xdev->reg, XILINX_DPDMA_EISR);
+ if (!status && !error)
+ return IRQ_NONE;
+
+ dpdma_write(xdev->reg, XILINX_DPDMA_ISR, status);
+ dpdma_write(xdev->reg, XILINX_DPDMA_EISR, error);
+
+ if (status & XILINX_DPDMA_INTR_VSYNC) {
+ /*
+ * There's a single VSYNC interrupt that needs to be processed
+ * by each running channel to update the active descriptor.
+ */
+ for (i = 0; i < ARRAY_SIZE(xdev->chan); i++) {
+ struct xilinx_dpdma_chan *chan = xdev->chan[i];
+
+ if (chan)
+ xilinx_dpdma_chan_vsync_irq(chan);
+ }
+ }
+
+ mask = FIELD_GET(XILINX_DPDMA_INTR_DESC_DONE_MASK, status);
+ if (mask) {
+ for_each_set_bit(i, &mask, ARRAY_SIZE(xdev->chan))
+ xilinx_dpdma_chan_done_irq(xdev->chan[i]);
+ }
+
+ mask = FIELD_GET(XILINX_DPDMA_INTR_NO_OSTAND_MASK, status);
+ if (mask) {
+ for_each_set_bit(i, &mask, ARRAY_SIZE(xdev->chan))
+ xilinx_dpdma_chan_notify_no_ostand(xdev->chan[i]);
+ }
+
+ mask = status & XILINX_DPDMA_INTR_ERR_ALL;
+ if (mask || error)
+ xilinx_dpdma_handle_err_irq(xdev, mask, error);
+
+ return IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * Initialization & Cleanup
+ */
+
+static int xilinx_dpdma_chan_init(struct xilinx_dpdma_device *xdev,
+ unsigned int chan_id)
+{
+ struct xilinx_dpdma_chan *chan;
+
+ chan = devm_kzalloc(xdev->dev, sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+
+ chan->id = chan_id;
+ chan->reg = xdev->reg + XILINX_DPDMA_CH_BASE
+ + XILINX_DPDMA_CH_OFFSET * chan->id;
+ chan->running = false;
+ chan->xdev = xdev;
+
+ spin_lock_init(&chan->lock);
+ init_waitqueue_head(&chan->wait_to_stop);
+
+ tasklet_init(&chan->err_task, xilinx_dpdma_chan_err_task,
+ (unsigned long)chan);
+
+ chan->vchan.desc_free = xilinx_dpdma_chan_free_tx_desc;
+ vchan_init(&chan->vchan, &xdev->common);
+
+ xdev->chan[chan->id] = chan;
+
+ return 0;
+}
+
+static void xilinx_dpdma_chan_remove(struct xilinx_dpdma_chan *chan)
+{
+ if (!chan)
+ return;
+
+ tasklet_kill(&chan->err_task);
+ list_del(&chan->vchan.chan.device_node);
+}
+
+static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct xilinx_dpdma_device *xdev = ofdma->of_dma_data;
+ uint32_t chan_id = dma_spec->args[0];
+
+ if (chan_id >= ARRAY_SIZE(xdev->chan))
+ return NULL;
+
+ if (!xdev->chan[chan_id])
+ return NULL;
+
+ return dma_get_slave_channel(&xdev->chan[chan_id]->vchan.chan);
+}
+
+static int xilinx_dpdma_probe(struct platform_device *pdev)
+{
+ struct xilinx_dpdma_device *xdev;
+ struct dma_device *ddev;
+ unsigned int i;
+ int ret;
+
+ xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
+ if (!xdev)
+ return -ENOMEM;
+
+ xdev->dev = &pdev->dev;
+ xdev->ext_addr = sizeof(dma_addr_t) > 4;
+
+ INIT_LIST_HEAD(&xdev->common.channels);
+
+ platform_set_drvdata(pdev, xdev);
+
+ xdev->axi_clk = devm_clk_get(xdev->dev, "axi_clk");
+ if (IS_ERR(xdev->axi_clk))
+ return PTR_ERR(xdev->axi_clk);
+
+ xdev->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(xdev->reg))
+ return PTR_ERR(xdev->reg);
+
+ xdev->irq = platform_get_irq(pdev, 0);
+ if (xdev->irq < 0) {
+ dev_err(xdev->dev, "failed to get platform irq\n");
+ return xdev->irq;
+ }
+
+ ret = request_irq(xdev->irq, xilinx_dpdma_irq_handler, IRQF_SHARED,
+ dev_name(xdev->dev), xdev);
+ if (ret) {
+ dev_err(xdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+
+ ddev = &xdev->common;
+ ddev->dev = &pdev->dev;
+
+ dma_cap_set(DMA_SLAVE, ddev->cap_mask);
+ dma_cap_set(DMA_PRIVATE, ddev->cap_mask);
+ dma_cap_set(DMA_INTERLEAVE, ddev->cap_mask);
+ dma_cap_set(DMA_REPEAT, ddev->cap_mask);
+ dma_cap_set(DMA_LOAD_EOT, ddev->cap_mask);
+ ddev->copy_align = fls(XILINX_DPDMA_ALIGN_BYTES - 1);
+
+ ddev->device_alloc_chan_resources = xilinx_dpdma_alloc_chan_resources;
+ ddev->device_free_chan_resources = xilinx_dpdma_free_chan_resources;
+ ddev->device_prep_interleaved_dma = xilinx_dpdma_prep_interleaved_dma;
+ /* TODO: Can we achieve better granularity ? */
+ ddev->device_tx_status = dma_cookie_status;
+ ddev->device_issue_pending = xilinx_dpdma_issue_pending;
+ ddev->device_config = xilinx_dpdma_config;
+ ddev->device_pause = xilinx_dpdma_pause;
+ ddev->device_resume = xilinx_dpdma_resume;
+ ddev->device_terminate_all = xilinx_dpdma_terminate_all;
+ ddev->device_synchronize = xilinx_dpdma_synchronize;
+ ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED);
+ ddev->directions = BIT(DMA_MEM_TO_DEV);
+ ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+
+ for (i = 0; i < ARRAY_SIZE(xdev->chan); ++i) {
+ ret = xilinx_dpdma_chan_init(xdev, i);
+ if (ret < 0) {
+ dev_err(xdev->dev, "failed to initialize channel %u\n",
+ i);
+ goto error;
+ }
+ }
+
+ ret = clk_prepare_enable(xdev->axi_clk);
+ if (ret) {
+ dev_err(xdev->dev, "failed to enable the axi clock\n");
+ goto error;
+ }
+
+ ret = dma_async_device_register(ddev);
+ if (ret) {
+ dev_err(xdev->dev, "failed to register the dma device\n");
+ goto error_dma_async;
+ }
+
+ ret = of_dma_controller_register(xdev->dev->of_node,
+ of_dma_xilinx_xlate, ddev);
+ if (ret) {
+ dev_err(xdev->dev, "failed to register DMA to DT DMA helper\n");
+ goto error_of_dma;
+ }
+
+ xilinx_dpdma_enable_irq(xdev);
+
+ dev_info(&pdev->dev, "Xilinx DPDMA engine is probed\n");
+
+ return 0;
+
+error_of_dma:
+ dma_async_device_unregister(ddev);
+error_dma_async:
+ clk_disable_unprepare(xdev->axi_clk);
+error:
+ for (i = 0; i < ARRAY_SIZE(xdev->chan); i++)
+ xilinx_dpdma_chan_remove(xdev->chan[i]);
+
+ free_irq(xdev->irq, xdev);
+
+ return ret;
+}
+
+static int xilinx_dpdma_remove(struct platform_device *pdev)
+{
+ struct xilinx_dpdma_device *xdev = platform_get_drvdata(pdev);
+ unsigned int i;
+
+ /* Start by disabling the IRQ to avoid races during cleanup. */
+ free_irq(xdev->irq, xdev);
+
+ xilinx_dpdma_disable_irq(xdev);
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&xdev->common);
+ clk_disable_unprepare(xdev->axi_clk);
+
+ for (i = 0; i < ARRAY_SIZE(xdev->chan); i++)
+ xilinx_dpdma_chan_remove(xdev->chan[i]);
+
+ return 0;
+}
+
+static const struct of_device_id xilinx_dpdma_of_match[] = {
+ { .compatible = "xlnx,zynqmp-dpdma",},
+ { /* end of table */ },
+};
+MODULE_DEVICE_TABLE(of, xilinx_dpdma_of_match);
+
+static struct platform_driver xilinx_dpdma_driver = {
+ .probe = xilinx_dpdma_probe,
+ .remove = xilinx_dpdma_remove,
+ .driver = {
+ .name = "xilinx-zynqmp-dpdma",
+ .of_match_table = xilinx_dpdma_of_match,
+ },
+};
+
+module_platform_driver(xilinx_dpdma_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx ZynqMP DPDMA driver");
+MODULE_LICENSE("GPL v2");
--
Regards,
Laurent Pinchart
^ permalink raw reply related
* [PATCH v7 1/5] dt: bindings: dma: xilinx: dpdma: DT bindings for Xilinx DPDMA
From: Laurent Pinchart @ 2020-07-17 1:33 UTC (permalink / raw)
To: dmaengine
Cc: Michal Simek, Hyun Kwon, Tejas Upadhyay, Satish Kumar Nagireddy,
Vinod Koul, Peter Ujfalusi
In-Reply-To: <20200717013337.24122-1-laurent.pinchart@ideasonboard.com>
The ZynqMP includes the DisplayPort subsystem with its own DMA engine
called DPDMA. The DPDMA IP comes with 6 individual channels
(4 for display, 2 for audio). This documentation describes DT bindings
of DPDMA.
Signed-off-by: Hyun Kwon <hyun.kwon@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changes since v2:
- Fix id URL
- Fix path to dma-controller.yaml
- Update license to GPL-2.0-only OR BSD-2-Clause
Changes since v1:
- Convert the DT bindings to YAML
- Drop the DT child nodes
---
.../dma/xilinx/xlnx,zynqmp-dpdma.yaml | 68 +++++++++++++++++++
MAINTAINERS | 8 +++
include/dt-bindings/dma/xlnx-zynqmp-dpdma.h | 16 +++++
3 files changed, 92 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml
create mode 100644 include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
diff --git a/Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml b/Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml
new file mode 100644
index 000000000000..5de510f8c88c
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/xilinx/xlnx,zynqmp-dpdma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Xilinx ZynqMP DisplayPort DMA Controller Device Tree Bindings
+
+description: |
+ These bindings describe the DMA engine included in the Xilinx ZynqMP
+ DisplayPort Subsystem. The DMA engine supports up to 6 DMA channels (3
+ channels for a video stream, 1 channel for a graphics stream, and 2 channels
+ for an audio stream).
+
+maintainers:
+ - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+
+allOf:
+ - $ref: "../dma-controller.yaml#"
+
+properties:
+ "#dma-cells":
+ const: 1
+ description: |
+ The cell is the DMA channel ID (see dt-bindings/dma/xlnx-zynqmp-dpdma.h
+ for a list of channel IDs).
+
+ compatible:
+ const: xlnx,zynqmp-dpdma
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ description: The AXI clock
+ maxItems: 1
+
+ clock-names:
+ const: axi_clk
+
+required:
+ - "#dma-cells"
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ dma: dma-controller@fd4c0000 {
+ compatible = "xlnx,zynqmp-dpdma";
+ reg = <0x0 0xfd4c0000 0x0 0x1000>;
+ interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&gic>;
+ clocks = <&dpdma_clk>;
+ clock-names = "axi_clk";
+ #dma-cells = <1>;
+ };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 68f21d46614c..fa52d4f9f8c8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18852,6 +18852,14 @@ F: Documentation/devicetree/bindings/media/xilinx/
F: drivers/media/platform/xilinx/
F: include/uapi/linux/xilinx-v4l2-controls.h
+XILINX ZYNQMP DPDMA DRIVER
+M: Hyun Kwon <hyun.kwon@xilinx.com>
+M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L: dmaengine@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml
+F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
+
XILLYBUS DRIVER
M: Eli Billauer <eli.billauer@gmail.com>
L: linux-kernel@vger.kernel.org
diff --git a/include/dt-bindings/dma/xlnx-zynqmp-dpdma.h b/include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
new file mode 100644
index 000000000000..3719cda5679d
--- /dev/null
+++ b/include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#ifndef __DT_BINDINGS_DMA_XLNX_ZYNQMP_DPDMA_H__
+#define __DT_BINDINGS_DMA_XLNX_ZYNQMP_DPDMA_H__
+
+#define ZYNQMP_DPDMA_VIDEO0 0
+#define ZYNQMP_DPDMA_VIDEO1 1
+#define ZYNQMP_DPDMA_VIDEO2 2
+#define ZYNQMP_DPDMA_GRAPHICS 3
+#define ZYNQMP_DPDMA_AUDIO0 4
+#define ZYNQMP_DPDMA_AUDIO1 5
+
+#endif /* __DT_BINDINGS_DMA_XLNX_ZYNQMP_DPDMA_H__ */
--
Regards,
Laurent Pinchart
^ permalink raw reply related
* [PATCH v7 0/5] dma: Add Xilinx ZynqMP DPDMA driver
From: Laurent Pinchart @ 2020-07-17 1:33 UTC (permalink / raw)
To: dmaengine
Cc: Michal Simek, Hyun Kwon, Tejas Upadhyay, Satish Kumar Nagireddy,
Vinod Koul, Peter Ujfalusi
Hello,
This patch series adds a new driver for the DPDMA engine found in the
Xilinx ZynqMP.
The previous version can be found at [1]. All review comments have been
taken into account. The main changes are in the DPDMA driver, with
cleanups in the debugfs support, and handling of the !LOAD_EOT case when
preparing transactions.
The driver has been successfully tested with the ZynqMP DisplayPort
subsystem DRM driver.
As I would like to merge both this series and the DRM driver that
depends on it for v5.9 (if still possible), I have based those patches
on top of v5.8-rc1. There's unfortunately a conflict with the DMA engine
next branch, which is easy to resolve.
Vinod, if you're fine with the series, I can propose two ways forward:
- You can apply the patches on top of v5.8-rc1, push that to a base
branch, merge it into the dmaengine -next branch, and push the base
branch to a public git tree to let me base the DRM driver on it.
- I can perform that work if you give me a Reviewed-by tag for the
series (at for patches 2/5 to 4/5), providing you with two signed
tags, one for the base branch and one for the merged branch. You could
then just pull the merged branch (please make sure not to rebase it or
otherwise modify it in that case).
What's your preference ?
[1] https://lore.kernel.org/dmaengine/20200708201906.4546-1-laurent.pinchart@ideasonboard.com/
Hyun Kwon (1):
dmaengine: xilinx: dpdma: Add the Xilinx DisplayPort DMA engine driver
Laurent Pinchart (4):
dt: bindings: dma: xilinx: dpdma: DT bindings for Xilinx DPDMA
dmaengine: Add support for repeating transactions
dmaengine: xilinx: dpdma: Add debugfs support
arm64: dts: zynqmp: Add DPDMA node
.../dma/xilinx/xlnx,zynqmp-dpdma.yaml | 68 +
Documentation/driver-api/dmaengine/client.rst | 4 +-
.../driver-api/dmaengine/provider.rst | 49 +
MAINTAINERS | 9 +
.../arm64/boot/dts/xilinx/zynqmp-clk-ccf.dtsi | 4 +
arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 10 +
drivers/dma/Kconfig | 10 +
drivers/dma/xilinx/Makefile | 1 +
drivers/dma/xilinx/xilinx_dpdma.c | 1771 +++++++++++++++++
include/dt-bindings/dma/xlnx-zynqmp-dpdma.h | 16 +
include/linux/dmaengine.h | 17 +
11 files changed, 1958 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml
create mode 100644 drivers/dma/xilinx/xilinx_dpdma.c
create mode 100644 include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
--
Regards,
Laurent Pinchart
^ permalink raw reply
* Re: [PATCH 4/4] MIPS: Loongson64: Reserve legacy MMIO space according to bridge type
From: Huacai Chen @ 2020-07-17 1:33 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: open list:MIPS, Fuxin Zhang, Zhangjin Wu, Jiaxun Yang
In-Reply-To: <20200716120400.GB11361@alpha.franken.de>
Hi, Thomas,
On Thu, Jul 16, 2020 at 8:05 PM Thomas Bogendoerfer
<tsbogend@alpha.franken.de> wrote:
>
> On Thu, Jul 16, 2020 at 07:10:09PM +0800, Huacai Chen wrote:
> > Hi, Thomas,
> >
> > On Thu, Jul 16, 2020 at 6:01 PM Thomas Bogendoerfer
> > <tsbogend@alpha.franken.de> wrote:
> > >
> > > On Thu, Jul 09, 2020 at 07:33:44PM +0800, Huacai Chen wrote:
> > > > Define MMIO_LOWER_RESERVED as a constant is incorrect, because different
> > > > PCHs (bridge types) have different legacy MMIO space size. According to
> > > > the datasheets, the legacy MMIO space size of LS7A is 0x20000, and which
> > > > of other PCHs is 0x4000. So it is necessary to reserve legacy MMIO space
> > > > according to the bridge type.
> > > >
> > > > Currently IO_SPACE_LIMIT is defined as 0xffff which is too small for the
> > > > LS7A bridge, so increase it to 0xfffff for LOONGSON64.
> > > >
> > > > Signed-off-by: Huacai Chen <chenhc@lemote.com>
> > > > ---
> > > > arch/mips/include/asm/io.h | 4 ++++
> > > > arch/mips/include/asm/mach-loongson64/spaces.h | 3 ---
> > > > arch/mips/loongson64/init.c | 18 ++++++++++++++----
> > > > 3 files changed, 18 insertions(+), 7 deletions(-)
> > > >
> > > > diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
> > > > index 346fffd..7358372 100644
> > > > --- a/arch/mips/include/asm/io.h
> > > > +++ b/arch/mips/include/asm/io.h
> > > > @@ -51,7 +51,11 @@
> > > >
> > > > /* ioswab[bwlq], __mem_ioswab[bwlq] are defined in mangle-port.h */
> > > >
> > > > +#ifndef CONFIG_CPU_LOONGSON64
> > > > #define IO_SPACE_LIMIT 0xffff
> > > > +#else
> > > > +#define IO_SPACE_LIMIT 0xfffff
> > > > +#endif
> > >
> > > can you please move this #define to mach-generic/spaces.h and
> > > override it in mach-loongson64/spaces.h ?
> > Maybe that's not a good idea, because all other archs define
> > IO_SPACE_LIMIT in io.h, moving to another file may cause some build
> > errors.
>
> it's already included via asm/addrspace.h
Yes, you are right, I'll send V2.
>
> Thomas.
>
> --
> Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
> good idea. [ RFC1925, 2.3 ]
^ permalink raw reply
* Re: [PATCH -next] gpu: host1x: Convert to DEFINE_SHOW_ATTRIBUTE
From: miaoqinglang @ 2020-07-17 1:32 UTC (permalink / raw)
To: Thierry Reding; +Cc: Greg Kroah-Hartman, dri-devel, linux-tegra, linux-kernel
In-Reply-To: <20200716133450.GJ535268@ulmo>
在 2020/7/16 21:34, Thierry Reding 写道:
> On Thu, Jul 16, 2020 at 05:03:23PM +0800, Qinglang Miao wrote:
>> From: Yongqiang Liu <liuyongqiang13@huawei.com>
>>
>> Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code.
>>
>> Signed-off-by: Yongqiang Liu <liuyongqiang13@huawei.com>
>> ---
>> drivers/gpu/host1x/debug.c | 28 ++++------------------------
>> 1 file changed, 4 insertions(+), 24 deletions(-)
> This doesn't apply. Can you please resend, based on something like
> linux-next?
>
> Thanks,
> Thierry
Hi, Thierry
Sorry I didn't mention it in commit log, but this patch is based on
linux-next where commit <4d4901c6d7> has switched over direct seq_read
method calls to seq_read_iter, this is why there's conflict in your apply.
Do you think I should send a new patch based on 5.8rc?
Thanks.
^ permalink raw reply
* Re: [PATCH -next] gpu: host1x: Convert to DEFINE_SHOW_ATTRIBUTE
From: miaoqinglang @ 2020-07-17 1:32 UTC (permalink / raw)
To: Thierry Reding
Cc: Greg Kroah-Hartman, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-tegra-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20200716133450.GJ535268@ulmo>
在 2020/7/16 21:34, Thierry Reding 写道:
> On Thu, Jul 16, 2020 at 05:03:23PM +0800, Qinglang Miao wrote:
>> From: Yongqiang Liu <liuyongqiang13-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>>
>> Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code.
>>
>> Signed-off-by: Yongqiang Liu <liuyongqiang13-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> ---
>> drivers/gpu/host1x/debug.c | 28 ++++------------------------
>> 1 file changed, 4 insertions(+), 24 deletions(-)
> This doesn't apply. Can you please resend, based on something like
> linux-next?
>
> Thanks,
> Thierry
Hi, Thierry
Sorry I didn't mention it in commit log, but this patch is based on
linux-next where commit <4d4901c6d7> has switched over direct seq_read
method calls to seq_read_iter, this is why there's conflict in your apply.
Do you think I should send a new patch based on 5.8rc?
Thanks.
^ permalink raw reply
* Re: nfs4_show_superblock considered harmful :-)
From: NeilBrown @ 2020-07-17 1:31 UTC (permalink / raw)
To: J. Bruce Fields; +Cc: Jeff Layton, linux-nfs
In-Reply-To: <20200717010301.GC18568@fieldses.org>
[-- Attachment #1: Type: text/plain, Size: 1346 bytes --]
On Thu, Jul 16 2020, J. Bruce Fields wrote:
> On Fri, Jul 17, 2020 at 09:43:40AM +1000, NeilBrown wrote:
>> On Thu, Jul 16 2020, J. Bruce Fields wrote:
>> > --- a/fs/nfsd/nfs4state.c
>> > +++ b/fs/nfsd/nfs4state.c
>> > @@ -507,6 +507,16 @@ find_any_file(struct nfs4_file *f)
>> > return ret;
>> > }
>> >
>> > +static struct nfsd_file *find_deleg_file(struct nfs4_file *f)
>> > +{
>> > + struct nfsd_file *ret;
>> > +
>> > + spin_lock(&f->fi_lock);
>> > + ret = nfsd_file_get(f->fi_deleg_file);
>>
>> A test on f->fi_deleg_file being non-NULL would make this look safer.
>> It would also make the subsequent test on the return value appear sane.
>
> Yes, thanks!-b.
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index c2a2e56c896d..6e8811e7c134 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -509,10 +509,11 @@ find_any_file(struct nfs4_file *f)
>
> static struct nfsd_file *find_deleg_file(struct nfs4_file *f)
> {
> - struct nfsd_file *ret;
> + struct nfsd_file *ret = NULL;
>
> spin_lock(&f->fi_lock);
> - ret = nfsd_file_get(f->fi_deleg_file);
> + if (f->fi_deleg_file)
> + ret = nfsd_file_get(f->fi_deleg_file);
> spin_unlock(&f->fi_lock);
> return ret;
> }
Reviewed-by: NeilBrown <neilb@suse.de>
for the whole patch.
Thanks,
NeilBrown
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: [PATCH 1/2] irqchip: imx-intmux: add system PM support
From: kernel test robot @ 2020-07-17 1:27 UTC (permalink / raw)
To: Joakim Zhang, tglx, jason, maz, shawnguo, s.hauer
Cc: kbuild-all, clang-built-linux, kernel, festevam, linux-imx,
linux-kernel, linux-arm-kernel
In-Reply-To: <20200716193244.31090-2-qiangqing.zhang@nxp.com>
[-- Attachment #1: Type: text/plain, Size: 1951 bytes --]
Hi Joakim,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on tip/irq/core]
[also build test WARNING on linux/master linus/master v5.8-rc5 next-20200716]
[cannot apply to arm-jcooper/irqchip/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Joakim-Zhang/irqchip-imx-intmux-add-PM-support/20200716-193311
base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git 8fa88a88d573093868565a1afba43b5ae5b3a316
config: x86_64-allyesconfig (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project ed6b578040a85977026c93bf4188f996148f3218)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install x86_64 cross compiling tool for clang build
# apt-get install binutils-x86-64-linux-gnu
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
>> drivers/irqchip/irq-imx-intmux.c:351:32: warning: unused variable 'imx_intmux_pm_ops' [-Wunused-const-variable]
static const struct dev_pm_ops imx_intmux_pm_ops = {
^
1 warning generated.
vim +/imx_intmux_pm_ops +351 drivers/irqchip/irq-imx-intmux.c
350
> 351 static const struct dev_pm_ops imx_intmux_pm_ops = {
352 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_intmux_suspend, imx_intmux_resume)
353 };
354
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 75349 bytes --]
^ permalink raw reply
* [radeon-alex:amd-staging-drm-next 1066/1110] drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:1233:3: warning: variable 'direct_poll' set but not used
From: kernel test robot @ 2020-07-17 1:26 UTC (permalink / raw)
To: Jack Zhang; +Cc: Leo Liu, kbuild-all, dri-devel
[-- Attachment #1: Type: text/plain, Size: 13547 bytes --]
tree: git://people.freedesktop.org/~agd5f/linux.git amd-staging-drm-next
head: cefd5db37208da458fa10f83f866f2f37eef70e9
commit: 4a33206e976be79b832d5a826565b5cb430de877 [1066/1110] drm/amd/sriov porting sriov cap to vcn3.0
config: parisc-randconfig-r015-20200717 (attached as .config)
compiler: hppa-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
git checkout 4a33206e976be79b832d5a826565b5cb430de877
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=parisc
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
In file included from include/linux/mm.h:94,
from include/drm/drm_vma_manager.h:27,
from include/drm/drm_gem.h:40,
from include/drm/ttm/ttm_bo_api.h:34,
from drivers/gpu/drm/amd/amdgpu/amdgpu.h:53,
from drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:25:
include/asm-generic/pgtable.h: In function 'pte_clear_not_present_full':
arch/parisc/include/asm/pgtable.h:96:9: warning: variable 'old_pte' set but not used [-Wunused-but-set-variable]
96 | pte_t old_pte; \
| ^~~~~~~
arch/parisc/include/asm/pgtable.h:322:34: note: in expansion of macro 'set_pte_at'
322 | #define pte_clear(mm, addr, xp) set_pte_at(mm, addr, xp, __pte(0))
| ^~~~~~~~~~
include/asm-generic/pgtable.h:201:2: note: in expansion of macro 'pte_clear'
201 | pte_clear(mm, address, ptep);
| ^~~~~~~~~
include/asm-generic/pgtable.h: In function '__ptep_modify_prot_commit':
arch/parisc/include/asm/pgtable.h:96:9: warning: variable 'old_pte' set but not used [-Wunused-but-set-variable]
96 | pte_t old_pte; \
| ^~~~~~~
include/asm-generic/pgtable.h:640:2: note: in expansion of macro 'set_pte_at'
640 | set_pte_at(vma->vm_mm, addr, ptep, pte);
| ^~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c: In function 'vcn_v3_0_start_sriov':
>> drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:1233:3: warning: variable 'direct_poll' set but not used [-Wunused-but-set-variable]
1233 | direct_poll = { {0} };
| ^~~~~~~~~~~
In file included from drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:25:
At top level:
drivers/gpu/drm/amd/amdgpu/amdgpu.h:192:19: warning: 'debug_evictions' defined but not used [-Wunused-const-variable=]
192 | static const bool debug_evictions; /* = false */
| ^~~~~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/amdgpu.h:191:18: warning: 'sched_policy' defined but not used [-Wunused-const-variable=]
191 | static const int sched_policy = KFD_SCHED_POLICY_HWS;
| ^~~~~~~~~~~~
In file included from drivers/gpu/drm/amd/amdgpu/../display/dc/dc_types.h:33,
from drivers/gpu/drm/amd/amdgpu/../display/dc/dm_services_types.h:30,
from drivers/gpu/drm/amd/amdgpu/../include/dm_pp_interface.h:26,
from drivers/gpu/drm/amd/amdgpu/amdgpu.h:65,
from drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:25:
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:76:32: warning: 'dc_fixpt_ln2_div_2' defined but not used [-Wunused-const-variable=]
76 | static const struct fixed31_32 dc_fixpt_ln2_div_2 = { 1488522236LL };
| ^~~~~~~~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:75:32: warning: 'dc_fixpt_ln2' defined but not used [-Wunused-const-variable=]
75 | static const struct fixed31_32 dc_fixpt_ln2 = { 2977044471LL };
| ^~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:74:32: warning: 'dc_fixpt_e' defined but not used [-Wunused-const-variable=]
74 | static const struct fixed31_32 dc_fixpt_e = { 11674931555LL };
| ^~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:73:32: warning: 'dc_fixpt_two_pi' defined but not used [-Wunused-const-variable=]
73 | static const struct fixed31_32 dc_fixpt_two_pi = { 26986075409LL };
| ^~~~~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:72:32: warning: 'dc_fixpt_pi' defined but not used [-Wunused-const-variable=]
72 | static const struct fixed31_32 dc_fixpt_pi = { 13493037705LL };
| ^~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:67:32: warning: 'dc_fixpt_zero' defined but not used [-Wunused-const-variable=]
67 | static const struct fixed31_32 dc_fixpt_zero = { 0 };
| ^~~~~~~~~~~~~
vim +/direct_poll +1233 drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
1210
1211 static int vcn_v3_0_start_sriov(struct amdgpu_device *adev)
1212 {
1213 int i, j;
1214 struct amdgpu_ring *ring;
1215 uint64_t cache_addr;
1216 uint64_t rb_addr;
1217 uint64_t ctx_addr;
1218 uint32_t param, resp, expected;
1219 uint32_t offset, cache_size;
1220 uint32_t tmp, timeout;
1221 uint32_t id;
1222
1223 struct amdgpu_mm_table *table = &adev->virt.mm_table;
1224 uint32_t *table_loc;
1225 uint32_t table_size;
1226 uint32_t size, size_dw;
1227
1228 struct mmsch_v3_0_cmd_direct_write
1229 direct_wt = { {0} };
1230 struct mmsch_v3_0_cmd_direct_read_modify_write
1231 direct_rd_mod_wt = { {0} };
1232 struct mmsch_v3_0_cmd_direct_polling
> 1233 direct_poll = { {0} };
1234 struct mmsch_v3_0_cmd_end end = { {0} };
1235 struct mmsch_v3_0_init_header header;
1236
1237 direct_wt.cmd_header.command_type =
1238 MMSCH_COMMAND__DIRECT_REG_WRITE;
1239 direct_rd_mod_wt.cmd_header.command_type =
1240 MMSCH_COMMAND__DIRECT_REG_READ_MODIFY_WRITE;
1241 direct_poll.cmd_header.command_type =
1242 MMSCH_COMMAND__DIRECT_REG_POLLING;
1243 end.cmd_header.command_type =
1244 MMSCH_COMMAND__END;
1245
1246 header.version = MMSCH_VERSION;
1247 header.total_size = sizeof(struct mmsch_v3_0_init_header) >> 2;
1248 for (i = 0; i < AMDGPU_MAX_VCN_INSTANCES; i++) {
1249 header.inst[i].init_status = 0;
1250 header.inst[i].table_offset = 0;
1251 header.inst[i].table_size = 0;
1252 }
1253
1254 table_loc = (uint32_t *)table->cpu_addr;
1255 table_loc += header.total_size;
1256 for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
1257 if (adev->vcn.harvest_config & (1 << i))
1258 continue;
1259
1260 table_size = 0;
1261
1262 MMSCH_V3_0_INSERT_DIRECT_RD_MOD_WT(SOC15_REG_OFFSET(VCN, i,
1263 mmUVD_STATUS),
1264 ~UVD_STATUS__UVD_BUSY, UVD_STATUS__UVD_BUSY);
1265
1266 cache_size = AMDGPU_GPU_PAGE_ALIGN(adev->vcn.fw->size + 4);
1267
1268 if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
1269 id = amdgpu_ucode_id_vcns[i];
1270 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1271 mmUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
1272 adev->firmware.ucode[id].tmr_mc_addr_lo);
1273 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1274 mmUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
1275 adev->firmware.ucode[id].tmr_mc_addr_hi);
1276 offset = 0;
1277 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1278 mmUVD_VCPU_CACHE_OFFSET0),
1279 0);
1280 } else {
1281 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1282 mmUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
1283 lower_32_bits(adev->vcn.inst[i].gpu_addr));
1284 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1285 mmUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
1286 upper_32_bits(adev->vcn.inst[i].gpu_addr));
1287 offset = cache_size;
1288 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1289 mmUVD_VCPU_CACHE_OFFSET0),
1290 AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
1291 }
1292
1293 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1294 mmUVD_VCPU_CACHE_SIZE0),
1295 cache_size);
1296
1297 cache_addr = adev->vcn.inst[i].gpu_addr + offset;
1298 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1299 mmUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW),
1300 lower_32_bits(cache_addr));
1301 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1302 mmUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH),
1303 upper_32_bits(cache_addr));
1304 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1305 mmUVD_VCPU_CACHE_OFFSET1),
1306 0);
1307 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1308 mmUVD_VCPU_CACHE_SIZE1),
1309 AMDGPU_VCN_STACK_SIZE);
1310
1311 cache_addr = adev->vcn.inst[i].gpu_addr + offset +
1312 AMDGPU_VCN_STACK_SIZE;
1313 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1314 mmUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW),
1315 lower_32_bits(cache_addr));
1316 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1317 mmUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH),
1318 upper_32_bits(cache_addr));
1319 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1320 mmUVD_VCPU_CACHE_OFFSET2),
1321 0);
1322 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1323 mmUVD_VCPU_CACHE_SIZE2),
1324 AMDGPU_VCN_CONTEXT_SIZE);
1325
1326 for (j = 0; j < adev->vcn.num_enc_rings; ++j) {
1327 ring = &adev->vcn.inst[i].ring_enc[j];
1328 ring->wptr = 0;
1329 rb_addr = ring->gpu_addr;
1330 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1331 mmUVD_RB_BASE_LO),
1332 lower_32_bits(rb_addr));
1333 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1334 mmUVD_RB_BASE_HI),
1335 upper_32_bits(rb_addr));
1336 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1337 mmUVD_RB_SIZE),
1338 ring->ring_size / 4);
1339 }
1340
1341 ring = &adev->vcn.inst[i].ring_dec;
1342 ring->wptr = 0;
1343 rb_addr = ring->gpu_addr;
1344 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1345 mmUVD_LMI_RBC_RB_64BIT_BAR_LOW),
1346 lower_32_bits(rb_addr));
1347 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1348 mmUVD_LMI_RBC_RB_64BIT_BAR_HIGH),
1349 upper_32_bits(rb_addr));
1350 /* force RBC into idle state */
1351 tmp = order_base_2(ring->ring_size);
1352 tmp = REG_SET_FIELD(0, UVD_RBC_RB_CNTL, RB_BUFSZ, tmp);
1353 tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_BLKSZ, 1);
1354 tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_FETCH, 1);
1355 tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_UPDATE, 1);
1356 tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_RPTR_WR_EN, 1);
1357 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1358 mmUVD_RBC_RB_CNTL),
1359 tmp);
1360
1361 /* add end packet */
1362 MMSCH_V3_0_INSERT_END();
1363
1364 /* refine header */
1365 header.inst[i].init_status = 1;
1366 header.inst[i].table_offset = header.total_size;
1367 header.inst[i].table_size = table_size;
1368 header.total_size += table_size;
1369 }
1370
1371 /* Update init table header in memory */
1372 size = sizeof(struct mmsch_v3_0_init_header);
1373 table_loc = (uint32_t *)table->cpu_addr;
1374 memcpy((void *)table_loc, &header, size);
1375
1376 /* message MMSCH (in VCN[0]) to initialize this client
1377 * 1, write to mmsch_vf_ctx_addr_lo/hi register with GPU mc addr
1378 * of memory descriptor location
1379 */
1380 ctx_addr = table->gpu_addr;
1381 WREG32_SOC15(VCN, 0, mmMMSCH_VF_CTX_ADDR_LO, lower_32_bits(ctx_addr));
1382 WREG32_SOC15(VCN, 0, mmMMSCH_VF_CTX_ADDR_HI, upper_32_bits(ctx_addr));
1383
1384 /* 2, update vmid of descriptor */
1385 tmp = RREG32_SOC15(VCN, 0, mmMMSCH_VF_VMID);
1386 tmp &= ~MMSCH_VF_VMID__VF_CTX_VMID_MASK;
1387 /* use domain0 for MM scheduler */
1388 tmp |= (0 << MMSCH_VF_VMID__VF_CTX_VMID__SHIFT);
1389 WREG32_SOC15(VCN, 0, mmMMSCH_VF_VMID, tmp);
1390
1391 /* 3, notify mmsch about the size of this descriptor */
1392 size = header.total_size;
1393 WREG32_SOC15(VCN, 0, mmMMSCH_VF_CTX_SIZE, size);
1394
1395 /* 4, set resp to zero */
1396 WREG32_SOC15(VCN, 0, mmMMSCH_VF_MAILBOX_RESP, 0);
1397
1398 /* 5, kick off the initialization and wait until
1399 * MMSCH_VF_MAILBOX_RESP becomes non-zero
1400 */
1401 param = 0x10000001;
1402 WREG32_SOC15(VCN, 0, mmMMSCH_VF_MAILBOX_HOST, param);
1403 tmp = 0;
1404 timeout = 1000;
1405 resp = 0;
1406 expected = param + 1;
1407 while (resp != expected) {
1408 resp = RREG32_SOC15(VCN, 0, mmMMSCH_VF_MAILBOX_RESP);
1409 if (resp == expected)
1410 break;
1411
1412 udelay(10);
1413 tmp = tmp + 10;
1414 if (tmp >= timeout) {
1415 DRM_ERROR("failed to init MMSCH. TIME-OUT after %d usec"\
1416 " waiting for mmMMSCH_VF_MAILBOX_RESP "\
1417 "(expected=0x%08x, readback=0x%08x)\n",
1418 tmp, expected, resp);
1419 return -EBUSY;
1420 }
1421 }
1422
1423 return 0;
1424 }
1425
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 25012 bytes --]
[-- Attachment #3: Type: text/plain, Size: 160 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH 2/2] Input: elan_i2c - Modify the IAP related functio n for page sizes 128, 512 bytes.
From: Dmitry Torokhov @ 2020-07-17 1:27 UTC (permalink / raw)
To: jingle.wu; +Cc: linux-kernel, linux-input, phoenix, josh.chen, kai.heng.feng
In-Reply-To: <1594880123.69588.jingle.wu@emc.com.tw>
Hi Jingle,
On Thu, Jul 16, 2020 at 02:15:23PM +0800, jingle.wu wrote:
> HI Dmitry:
>
> Just to confirm, the older devices (I assume that pattern 0 means older)
> have version command that is numerically higher than the one for the
> newer (pattern >= 1) devices?
>
> >> Yes, Pattern 1, 2 are newer devices.
>
> > @@ -324,7 +342,14 @@ static int elan_i2c_get_sm_version(struct i2c_client *client,
> > return error;
> > }
> > *version = val[0];
> > - *ic_type = val[1];
> > +
> > + error = elan_i2c_read_cmd(client, ETP_I2C_IAP_VERSION_CMD, val);
> > + if (error) {
> > + dev_err(&client->dev, "failed to get ic type: %d\n",
> > + error);
> > + return error;
> > + }
>
> Could you please tell me why this chunk is needed?
> >> Modify the old pattern IC firmware read the correct ic_type.
>
> In the elan_i2c_core.c, move this code to elan_i2c_i2c.c.
> static int elan_query_device_info(struct elan_tp_data *data)
> {
> .....
> if (data->pattern == 0x01)
> ic_type = data->ic_type;
> else
> ic_type = data->iap_version;
> .....
> return 0;
> }
I am concerned that unconditionally substituting iap_version for ic_type
for "pattern 0" devices will break check in
elan_check_ASUS_special_fw() as it operates on the ic_type returned by
ETP_I2C_OSM_VERSION_CMD and not iap_version.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH 1/2] irqchip: imx-intmux: add system PM support
From: kernel test robot @ 2020-07-17 1:27 UTC (permalink / raw)
To: kbuild-all
In-Reply-To: <20200716193244.31090-2-qiangqing.zhang@nxp.com>
[-- Attachment #1: Type: text/plain, Size: 1997 bytes --]
Hi Joakim,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on tip/irq/core]
[also build test WARNING on linux/master linus/master v5.8-rc5 next-20200716]
[cannot apply to arm-jcooper/irqchip/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Joakim-Zhang/irqchip-imx-intmux-add-PM-support/20200716-193311
base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git 8fa88a88d573093868565a1afba43b5ae5b3a316
config: x86_64-allyesconfig (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project ed6b578040a85977026c93bf4188f996148f3218)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install x86_64 cross compiling tool for clang build
# apt-get install binutils-x86-64-linux-gnu
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
>> drivers/irqchip/irq-imx-intmux.c:351:32: warning: unused variable 'imx_intmux_pm_ops' [-Wunused-const-variable]
static const struct dev_pm_ops imx_intmux_pm_ops = {
^
1 warning generated.
vim +/imx_intmux_pm_ops +351 drivers/irqchip/irq-imx-intmux.c
350
> 351 static const struct dev_pm_ops imx_intmux_pm_ops = {
352 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_intmux_suspend, imx_intmux_resume)
353 };
354
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 75349 bytes --]
^ permalink raw reply
* [radeon-alex:amd-staging-drm-next 1066/1110] drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:1233:3: warning: variable 'direct_poll' set but not used
From: kernel test robot @ 2020-07-17 1:26 UTC (permalink / raw)
To: kbuild-all
[-- Attachment #1: Type: text/plain, Size: 13847 bytes --]
tree: git://people.freedesktop.org/~agd5f/linux.git amd-staging-drm-next
head: cefd5db37208da458fa10f83f866f2f37eef70e9
commit: 4a33206e976be79b832d5a826565b5cb430de877 [1066/1110] drm/amd/sriov porting sriov cap to vcn3.0
config: parisc-randconfig-r015-20200717 (attached as .config)
compiler: hppa-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
git checkout 4a33206e976be79b832d5a826565b5cb430de877
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=parisc
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
In file included from include/linux/mm.h:94,
from include/drm/drm_vma_manager.h:27,
from include/drm/drm_gem.h:40,
from include/drm/ttm/ttm_bo_api.h:34,
from drivers/gpu/drm/amd/amdgpu/amdgpu.h:53,
from drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:25:
include/asm-generic/pgtable.h: In function 'pte_clear_not_present_full':
arch/parisc/include/asm/pgtable.h:96:9: warning: variable 'old_pte' set but not used [-Wunused-but-set-variable]
96 | pte_t old_pte; \
| ^~~~~~~
arch/parisc/include/asm/pgtable.h:322:34: note: in expansion of macro 'set_pte_at'
322 | #define pte_clear(mm, addr, xp) set_pte_at(mm, addr, xp, __pte(0))
| ^~~~~~~~~~
include/asm-generic/pgtable.h:201:2: note: in expansion of macro 'pte_clear'
201 | pte_clear(mm, address, ptep);
| ^~~~~~~~~
include/asm-generic/pgtable.h: In function '__ptep_modify_prot_commit':
arch/parisc/include/asm/pgtable.h:96:9: warning: variable 'old_pte' set but not used [-Wunused-but-set-variable]
96 | pte_t old_pte; \
| ^~~~~~~
include/asm-generic/pgtable.h:640:2: note: in expansion of macro 'set_pte_at'
640 | set_pte_at(vma->vm_mm, addr, ptep, pte);
| ^~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c: In function 'vcn_v3_0_start_sriov':
>> drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:1233:3: warning: variable 'direct_poll' set but not used [-Wunused-but-set-variable]
1233 | direct_poll = { {0} };
| ^~~~~~~~~~~
In file included from drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:25:
At top level:
drivers/gpu/drm/amd/amdgpu/amdgpu.h:192:19: warning: 'debug_evictions' defined but not used [-Wunused-const-variable=]
192 | static const bool debug_evictions; /* = false */
| ^~~~~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/amdgpu.h:191:18: warning: 'sched_policy' defined but not used [-Wunused-const-variable=]
191 | static const int sched_policy = KFD_SCHED_POLICY_HWS;
| ^~~~~~~~~~~~
In file included from drivers/gpu/drm/amd/amdgpu/../display/dc/dc_types.h:33,
from drivers/gpu/drm/amd/amdgpu/../display/dc/dm_services_types.h:30,
from drivers/gpu/drm/amd/amdgpu/../include/dm_pp_interface.h:26,
from drivers/gpu/drm/amd/amdgpu/amdgpu.h:65,
from drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c:25:
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:76:32: warning: 'dc_fixpt_ln2_div_2' defined but not used [-Wunused-const-variable=]
76 | static const struct fixed31_32 dc_fixpt_ln2_div_2 = { 1488522236LL };
| ^~~~~~~~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:75:32: warning: 'dc_fixpt_ln2' defined but not used [-Wunused-const-variable=]
75 | static const struct fixed31_32 dc_fixpt_ln2 = { 2977044471LL };
| ^~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:74:32: warning: 'dc_fixpt_e' defined but not used [-Wunused-const-variable=]
74 | static const struct fixed31_32 dc_fixpt_e = { 11674931555LL };
| ^~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:73:32: warning: 'dc_fixpt_two_pi' defined but not used [-Wunused-const-variable=]
73 | static const struct fixed31_32 dc_fixpt_two_pi = { 26986075409LL };
| ^~~~~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:72:32: warning: 'dc_fixpt_pi' defined but not used [-Wunused-const-variable=]
72 | static const struct fixed31_32 dc_fixpt_pi = { 13493037705LL };
| ^~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/include/fixed31_32.h:67:32: warning: 'dc_fixpt_zero' defined but not used [-Wunused-const-variable=]
67 | static const struct fixed31_32 dc_fixpt_zero = { 0 };
| ^~~~~~~~~~~~~
vim +/direct_poll +1233 drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c
1210
1211 static int vcn_v3_0_start_sriov(struct amdgpu_device *adev)
1212 {
1213 int i, j;
1214 struct amdgpu_ring *ring;
1215 uint64_t cache_addr;
1216 uint64_t rb_addr;
1217 uint64_t ctx_addr;
1218 uint32_t param, resp, expected;
1219 uint32_t offset, cache_size;
1220 uint32_t tmp, timeout;
1221 uint32_t id;
1222
1223 struct amdgpu_mm_table *table = &adev->virt.mm_table;
1224 uint32_t *table_loc;
1225 uint32_t table_size;
1226 uint32_t size, size_dw;
1227
1228 struct mmsch_v3_0_cmd_direct_write
1229 direct_wt = { {0} };
1230 struct mmsch_v3_0_cmd_direct_read_modify_write
1231 direct_rd_mod_wt = { {0} };
1232 struct mmsch_v3_0_cmd_direct_polling
> 1233 direct_poll = { {0} };
1234 struct mmsch_v3_0_cmd_end end = { {0} };
1235 struct mmsch_v3_0_init_header header;
1236
1237 direct_wt.cmd_header.command_type =
1238 MMSCH_COMMAND__DIRECT_REG_WRITE;
1239 direct_rd_mod_wt.cmd_header.command_type =
1240 MMSCH_COMMAND__DIRECT_REG_READ_MODIFY_WRITE;
1241 direct_poll.cmd_header.command_type =
1242 MMSCH_COMMAND__DIRECT_REG_POLLING;
1243 end.cmd_header.command_type =
1244 MMSCH_COMMAND__END;
1245
1246 header.version = MMSCH_VERSION;
1247 header.total_size = sizeof(struct mmsch_v3_0_init_header) >> 2;
1248 for (i = 0; i < AMDGPU_MAX_VCN_INSTANCES; i++) {
1249 header.inst[i].init_status = 0;
1250 header.inst[i].table_offset = 0;
1251 header.inst[i].table_size = 0;
1252 }
1253
1254 table_loc = (uint32_t *)table->cpu_addr;
1255 table_loc += header.total_size;
1256 for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
1257 if (adev->vcn.harvest_config & (1 << i))
1258 continue;
1259
1260 table_size = 0;
1261
1262 MMSCH_V3_0_INSERT_DIRECT_RD_MOD_WT(SOC15_REG_OFFSET(VCN, i,
1263 mmUVD_STATUS),
1264 ~UVD_STATUS__UVD_BUSY, UVD_STATUS__UVD_BUSY);
1265
1266 cache_size = AMDGPU_GPU_PAGE_ALIGN(adev->vcn.fw->size + 4);
1267
1268 if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
1269 id = amdgpu_ucode_id_vcns[i];
1270 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1271 mmUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
1272 adev->firmware.ucode[id].tmr_mc_addr_lo);
1273 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1274 mmUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
1275 adev->firmware.ucode[id].tmr_mc_addr_hi);
1276 offset = 0;
1277 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1278 mmUVD_VCPU_CACHE_OFFSET0),
1279 0);
1280 } else {
1281 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1282 mmUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
1283 lower_32_bits(adev->vcn.inst[i].gpu_addr));
1284 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1285 mmUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
1286 upper_32_bits(adev->vcn.inst[i].gpu_addr));
1287 offset = cache_size;
1288 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1289 mmUVD_VCPU_CACHE_OFFSET0),
1290 AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
1291 }
1292
1293 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1294 mmUVD_VCPU_CACHE_SIZE0),
1295 cache_size);
1296
1297 cache_addr = adev->vcn.inst[i].gpu_addr + offset;
1298 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1299 mmUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW),
1300 lower_32_bits(cache_addr));
1301 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1302 mmUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH),
1303 upper_32_bits(cache_addr));
1304 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1305 mmUVD_VCPU_CACHE_OFFSET1),
1306 0);
1307 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1308 mmUVD_VCPU_CACHE_SIZE1),
1309 AMDGPU_VCN_STACK_SIZE);
1310
1311 cache_addr = adev->vcn.inst[i].gpu_addr + offset +
1312 AMDGPU_VCN_STACK_SIZE;
1313 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1314 mmUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW),
1315 lower_32_bits(cache_addr));
1316 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1317 mmUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH),
1318 upper_32_bits(cache_addr));
1319 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1320 mmUVD_VCPU_CACHE_OFFSET2),
1321 0);
1322 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1323 mmUVD_VCPU_CACHE_SIZE2),
1324 AMDGPU_VCN_CONTEXT_SIZE);
1325
1326 for (j = 0; j < adev->vcn.num_enc_rings; ++j) {
1327 ring = &adev->vcn.inst[i].ring_enc[j];
1328 ring->wptr = 0;
1329 rb_addr = ring->gpu_addr;
1330 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1331 mmUVD_RB_BASE_LO),
1332 lower_32_bits(rb_addr));
1333 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1334 mmUVD_RB_BASE_HI),
1335 upper_32_bits(rb_addr));
1336 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1337 mmUVD_RB_SIZE),
1338 ring->ring_size / 4);
1339 }
1340
1341 ring = &adev->vcn.inst[i].ring_dec;
1342 ring->wptr = 0;
1343 rb_addr = ring->gpu_addr;
1344 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1345 mmUVD_LMI_RBC_RB_64BIT_BAR_LOW),
1346 lower_32_bits(rb_addr));
1347 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1348 mmUVD_LMI_RBC_RB_64BIT_BAR_HIGH),
1349 upper_32_bits(rb_addr));
1350 /* force RBC into idle state */
1351 tmp = order_base_2(ring->ring_size);
1352 tmp = REG_SET_FIELD(0, UVD_RBC_RB_CNTL, RB_BUFSZ, tmp);
1353 tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_BLKSZ, 1);
1354 tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_FETCH, 1);
1355 tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_UPDATE, 1);
1356 tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_RPTR_WR_EN, 1);
1357 MMSCH_V3_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, i,
1358 mmUVD_RBC_RB_CNTL),
1359 tmp);
1360
1361 /* add end packet */
1362 MMSCH_V3_0_INSERT_END();
1363
1364 /* refine header */
1365 header.inst[i].init_status = 1;
1366 header.inst[i].table_offset = header.total_size;
1367 header.inst[i].table_size = table_size;
1368 header.total_size += table_size;
1369 }
1370
1371 /* Update init table header in memory */
1372 size = sizeof(struct mmsch_v3_0_init_header);
1373 table_loc = (uint32_t *)table->cpu_addr;
1374 memcpy((void *)table_loc, &header, size);
1375
1376 /* message MMSCH (in VCN[0]) to initialize this client
1377 * 1, write to mmsch_vf_ctx_addr_lo/hi register with GPU mc addr
1378 * of memory descriptor location
1379 */
1380 ctx_addr = table->gpu_addr;
1381 WREG32_SOC15(VCN, 0, mmMMSCH_VF_CTX_ADDR_LO, lower_32_bits(ctx_addr));
1382 WREG32_SOC15(VCN, 0, mmMMSCH_VF_CTX_ADDR_HI, upper_32_bits(ctx_addr));
1383
1384 /* 2, update vmid of descriptor */
1385 tmp = RREG32_SOC15(VCN, 0, mmMMSCH_VF_VMID);
1386 tmp &= ~MMSCH_VF_VMID__VF_CTX_VMID_MASK;
1387 /* use domain0 for MM scheduler */
1388 tmp |= (0 << MMSCH_VF_VMID__VF_CTX_VMID__SHIFT);
1389 WREG32_SOC15(VCN, 0, mmMMSCH_VF_VMID, tmp);
1390
1391 /* 3, notify mmsch about the size of this descriptor */
1392 size = header.total_size;
1393 WREG32_SOC15(VCN, 0, mmMMSCH_VF_CTX_SIZE, size);
1394
1395 /* 4, set resp to zero */
1396 WREG32_SOC15(VCN, 0, mmMMSCH_VF_MAILBOX_RESP, 0);
1397
1398 /* 5, kick off the initialization and wait until
1399 * MMSCH_VF_MAILBOX_RESP becomes non-zero
1400 */
1401 param = 0x10000001;
1402 WREG32_SOC15(VCN, 0, mmMMSCH_VF_MAILBOX_HOST, param);
1403 tmp = 0;
1404 timeout = 1000;
1405 resp = 0;
1406 expected = param + 1;
1407 while (resp != expected) {
1408 resp = RREG32_SOC15(VCN, 0, mmMMSCH_VF_MAILBOX_RESP);
1409 if (resp == expected)
1410 break;
1411
1412 udelay(10);
1413 tmp = tmp + 10;
1414 if (tmp >= timeout) {
1415 DRM_ERROR("failed to init MMSCH. TIME-OUT after %d usec"\
1416 " waiting for mmMMSCH_VF_MAILBOX_RESP "\
1417 "(expected=0x%08x, readback=0x%08x)\n",
1418 tmp, expected, resp);
1419 return -EBUSY;
1420 }
1421 }
1422
1423 return 0;
1424 }
1425
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 25012 bytes --]
^ permalink raw reply
* Re: [PATCH] net: check payload length limit for all frames
From: Alexander Bulekov @ 2020-07-17 1:21 UTC (permalink / raw)
To: Li Qiang
Cc: Prasad J Pandit, Jason Wang, Dmitry Fleytman, QEMU Developers,
P J P
In-Reply-To: <CAKXe6SKL3aNiOKKLEMof6GGNjYLcX9fvfSf-0PBSX48rh4--FQ@mail.gmail.com>
On 200717 0853, Li Qiang wrote:
> P J P <ppandit@redhat.com> 于2020年7月17日周五 上午3:26写道:
> >
> > From: Prasad J Pandit <pjp@fedoraproject.org>
> >
> > While sending packets, the check that packet 'payload_len'
> > is within 64kB limit, seems to happen only for GSO frames.
> > It may lead to use-after-free or out-of-bounds access like
> > issues when sending non-GSO frames. Check the 'payload_len'
> > limit for all packets, irrespective of the gso type.
> >
>
> Hello Prasad,
> Which issue are you trying to solve, any reference linking?
>
> I also send a patch related this part and also a UAF.
>
> Thanks,
> Li Qiang
Hi Li, Prasad,
I reported a UAF privately to QEMU-Security in May. I believe the one Li
is referring to is this one https://bugs.launchpad.net/qemu/+bug/1886362
When I saw Prasad's email, I was worried that I reported the same bug
twice, but I can still reproduce LP#1886362 with Prasad's patch.
On the other hand, I cannot reproduce either issue with Li's patch:
Message-Id: <20200716161453.61295-1-liq3ea@163.com>
Based on this, I think there were two distinct issues. Both of the
crashes rely on e1000e tx loopback into e1000e MMIO. Since Li's
patch adds a TX bh, it seems to mitigate such types of issues.
Sorry about any confusion.
-Alex
> > Reported-by: Alexander Bulekov <alxndr@bu.edu>
> > Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
> > ---
> > hw/net/net_tx_pkt.c | 10 ++++------
> > 1 file changed, 4 insertions(+), 6 deletions(-)
> >
> > diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c
> > index 162f802dd7..e66998a8f9 100644
> > --- a/hw/net/net_tx_pkt.c
> > +++ b/hw/net/net_tx_pkt.c
> > @@ -607,12 +607,10 @@ bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc)
> > * Since underlying infrastructure does not support IP datagrams longer
> > * than 64K we should drop such packets and don't even try to send
> > */
> > - if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) {
> > - if (pkt->payload_len >
> > - ETH_MAX_IP_DGRAM_LEN -
> > - pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) {
> > - return false;
> > - }
> > + if (pkt->payload_len >
> > + ETH_MAX_IP_DGRAM_LEN -
> > + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) {
> > + return false;
> > }
> >
> > if (pkt->has_virt_hdr ||
> > --
> > 2.26.2
> >
> >
^ permalink raw reply
* Re: Regression: squashfs issues since change "squashfs: migrate from ll_rw_block usage to BIO"
From: Phillip Lougher @ 2020-07-17 1:22 UTC (permalink / raw)
To: Andrew Morton; +Cc: Bernd Amend, Phillip Lougher, LKML, Stefan Rommel
In-Reply-To: <20200716160723.474af23e3a362e77bec3fcf7@linux-foundation.org>
On Fri, Jul 17, 2020 at 12:07 AM Andrew Morton
<akpm@linux-foundation.org> wrote:
>
> On Tue, 14 Jul 2020 21:41:07 +0200 Bernd Amend <bernd.amend@gmail.com> wrote:
>
> > Hi,
> >
> > With the Linux Kernel version 5.8-rc5/master I am unable to mount some
> > squashfs filesystems compressed with "-comp lz4".
> > If I try to mount them I get the following error:
> > [ 1.084246] SQUASHFS error: lz4 decompression failed, data probably corrupt
> > [ 1.084545] SQUASHFS error: Failed to read block 0x873e1001: -5
> > [ 1.084761] SQUASHFS error: Unable to read metadata cache entry [873e0fff]
> > [ 1.084983] SQUASHFS error: Unable to read directory block [873e0fff:1586]
> > [ 1.122564] SQUASHFS error: Unable to read metadata cache entry [873e0fff]
> > [ 1.122708] SQUASHFS error: Unable to read directory block [873e0fff:1586]
> > [ 1.122862] Starting init: /sbin/init exists but couldn't execute
> > it (error -5)
> > [ 1.123027] SQUASHFS error: Unable to read metadata cache entry [873e0fff]
> > [ 1.123152] SQUASHFS error: Unable to read directory block [873e0fff:1586]
> > [ 1.123279] Starting init: /etc/init exists but couldn't execute it
> > (error -5)
> > [ 1.123444] SQUASHFS error: Unable to read metadata cache entry [873e0fff]
> > [ 1.123573] SQUASHFS error: Unable to read directory block [873e0fff:1586]
> > [ 1.123713] Starting init: /bin/init exists but couldn't execute it
> > (error -5)
> > [ 1.123900] SQUASHFS error: Unable to read metadata cache entry [873e0fff]
> >
> > or
> >
> > [ 4960.910693] attempt to access beyond end of device
> > [ 4960.910695] loop0: rw=2048, want=46, limit=40
> > [ 4960.910696] SQUASHFS error: Failed to read block 0x4001: -5
> > [ 4960.910697] SQUASHFS error: Unable to read metadata cache entry [3fff]
> > [ 4960.910698] SQUASHFS error: Unable to read inode 0x20c5000c
> >
> > I bisected the issue to the commit "squashfs: migrate from ll_rw_block
> > usage to BIO"
> > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/fs/squashfs?id=93e72b3c612adcaca13d874fcc86c53e6c8da541
> >
> > The issue can be reproduced by downloading
> > https://theworldsend.eu/demo.squashfs (20K) and the following command
> > line.
> > # mount demo.squashfs mnt && ls mnt && umount mnt
> >
> > The same squashfs can be mounted using Linux <=5.7.8.
> > The kernel config is identical to the Arch Linux Kernel configuration,
> > build using gcc 9 and 10 on x86_64.
>
> Thanks. I queued a reversion patch. I'll go ahead with this if we are
> unable to get this fixed in the next week or so.
>
Yes, there is a bug in the patch. I have tracked it down today, and I
will send out a fix patch tomorrow.
The bug is here:
+ /* Extract the length of the metadata block */
+ data = page_address(bvec->bv_page) + bvec->bv_offset;
+ length = data[offset];
+ if (offset <= bvec->bv_len - 1) {
This check is wrong, it should be
+ if (offset < bvec->bv_len - 1) {
Phillip
> Are you able to check that the below fixes things up?
>
> Thanks.
>
>
> From: Andrew Morton <akpm@linux-foundation.org>
> Subject: revert "squashfs: migrate from ll_rw_block usage to BIO"
>
> Revert 93e72b3c612adc ("squashfs: migrate from ll_rw_block usage to BIO")
> due to a regression reported by Bernd Amend.
>
> Link: http://lkml.kernel.org/r/CAF31+H5ZB7zn73obrc5svLzgfsTnyYe5TKvr7-6atUOqrRY+2w@mail.gmail.com
> Reported-by: Bernd Amend <bernd.amend@gmail.com>
> Cc: Philippe Liard <pliard@google.com>
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Adrien Schildknecht <adrien+dev@schischi.me>
> Cc: Phillip Lougher <phillip@squashfs.org.uk>
> Cc: Guenter Roeck <groeck@chromium.org>
> Cc: Daniel Rosenberg <drosen@google.com>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> ---
>
> fs/squashfs/block.c | 273 ++++++++++------------
> fs/squashfs/decompressor.h | 5
> fs/squashfs/decompressor_multi.c | 9
> fs/squashfs/decompressor_multi_percpu.c | 6
> fs/squashfs/decompressor_single.c | 9
> fs/squashfs/lz4_wrapper.c | 17 -
> fs/squashfs/lzo_wrapper.c | 17 -
> fs/squashfs/squashfs.h | 4
> fs/squashfs/xz_wrapper.c | 51 +---
> fs/squashfs/zlib_wrapper.c | 63 ++---
> fs/squashfs/zstd_wrapper.c | 62 ++--
> 11 files changed, 237 insertions(+), 279 deletions(-)
>
> --- a/fs/squashfs/block.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/block.c
> @@ -13,7 +13,6 @@
> * datablocks and metadata blocks.
> */
>
> -#include <linux/blkdev.h>
> #include <linux/fs.h>
> #include <linux/vfs.h>
> #include <linux/slab.h>
> @@ -28,104 +27,45 @@
> #include "page_actor.h"
>
> /*
> - * Returns the amount of bytes copied to the page actor.
> + * Read the metadata block length, this is stored in the first two
> + * bytes of the metadata block.
> */
> -static int copy_bio_to_actor(struct bio *bio,
> - struct squashfs_page_actor *actor,
> - int offset, int req_length)
> -{
> - void *actor_addr = squashfs_first_page(actor);
> - struct bvec_iter_all iter_all = {};
> - struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
> - int copied_bytes = 0;
> - int actor_offset = 0;
> -
> - if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all)))
> - return 0;
> -
> - while (copied_bytes < req_length) {
> - int bytes_to_copy = min_t(int, bvec->bv_len - offset,
> - PAGE_SIZE - actor_offset);
> -
> - bytes_to_copy = min_t(int, bytes_to_copy,
> - req_length - copied_bytes);
> - memcpy(actor_addr + actor_offset,
> - page_address(bvec->bv_page) + bvec->bv_offset + offset,
> - bytes_to_copy);
> -
> - actor_offset += bytes_to_copy;
> - copied_bytes += bytes_to_copy;
> - offset += bytes_to_copy;
> -
> - if (actor_offset >= PAGE_SIZE) {
> - actor_addr = squashfs_next_page(actor);
> - if (!actor_addr)
> - break;
> - actor_offset = 0;
> - }
> - if (offset >= bvec->bv_len) {
> - if (!bio_next_segment(bio, &iter_all))
> - break;
> - offset = 0;
> - }
> - }
> - squashfs_finish_page(actor);
> - return copied_bytes;
> -}
> -
> -static int squashfs_bio_read(struct super_block *sb, u64 index, int length,
> - struct bio **biop, int *block_offset)
> +static struct buffer_head *get_block_length(struct super_block *sb,
> + u64 *cur_index, int *offset, int *length)
> {
> struct squashfs_sb_info *msblk = sb->s_fs_info;
> - const u64 read_start = round_down(index, msblk->devblksize);
> - const sector_t block = read_start >> msblk->devblksize_log2;
> - const u64 read_end = round_up(index + length, msblk->devblksize);
> - const sector_t block_end = read_end >> msblk->devblksize_log2;
> - int offset = read_start - round_down(index, PAGE_SIZE);
> - int total_len = (block_end - block) << msblk->devblksize_log2;
> - const int page_count = DIV_ROUND_UP(total_len + offset, PAGE_SIZE);
> - int error, i;
> - struct bio *bio;
> -
> - bio = bio_alloc(GFP_NOIO, page_count);
> - if (!bio)
> - return -ENOMEM;
> + struct buffer_head *bh;
>
> - bio_set_dev(bio, sb->s_bdev);
> - bio->bi_opf = READ;
> - bio->bi_iter.bi_sector = block * (msblk->devblksize >> SECTOR_SHIFT);
> -
> - for (i = 0; i < page_count; ++i) {
> - unsigned int len =
> - min_t(unsigned int, PAGE_SIZE - offset, total_len);
> - struct page *page = alloc_page(GFP_NOIO);
> -
> - if (!page) {
> - error = -ENOMEM;
> - goto out_free_bio;
> - }
> - if (!bio_add_page(bio, page, len, offset)) {
> - error = -EIO;
> - goto out_free_bio;
> + bh = sb_bread(sb, *cur_index);
> + if (bh == NULL)
> + return NULL;
> +
> + if (msblk->devblksize - *offset == 1) {
> + *length = (unsigned char) bh->b_data[*offset];
> + put_bh(bh);
> + bh = sb_bread(sb, ++(*cur_index));
> + if (bh == NULL)
> + return NULL;
> + *length |= (unsigned char) bh->b_data[0] << 8;
> + *offset = 1;
> + } else {
> + *length = (unsigned char) bh->b_data[*offset] |
> + (unsigned char) bh->b_data[*offset + 1] << 8;
> + *offset += 2;
> +
> + if (*offset == msblk->devblksize) {
> + put_bh(bh);
> + bh = sb_bread(sb, ++(*cur_index));
> + if (bh == NULL)
> + return NULL;
> + *offset = 0;
> }
> - offset = 0;
> - total_len -= len;
> }
>
> - error = submit_bio_wait(bio);
> - if (error)
> - goto out_free_bio;
> -
> - *biop = bio;
> - *block_offset = index & ((1 << msblk->devblksize_log2) - 1);
> - return 0;
> -
> -out_free_bio:
> - bio_free_pages(bio);
> - bio_put(bio);
> - return error;
> + return bh;
> }
>
> +
> /*
> * Read and decompress a metadata block or datablock. Length is non-zero
> * if a datablock is being read (the size is stored elsewhere in the
> @@ -136,88 +76,129 @@ out_free_bio:
> * algorithms).
> */
> int squashfs_read_data(struct super_block *sb, u64 index, int length,
> - u64 *next_index, struct squashfs_page_actor *output)
> + u64 *next_index, struct squashfs_page_actor *output)
> {
> struct squashfs_sb_info *msblk = sb->s_fs_info;
> - struct bio *bio = NULL;
> - int compressed;
> - int res;
> - int offset;
> + struct buffer_head **bh;
> + int offset = index & ((1 << msblk->devblksize_log2) - 1);
> + u64 cur_index = index >> msblk->devblksize_log2;
> + int bytes, compressed, b = 0, k = 0, avail, i;
> +
> + bh = kcalloc(((output->length + msblk->devblksize - 1)
> + >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
> + if (bh == NULL)
> + return -ENOMEM;
>
> if (length) {
> /*
> * Datablock.
> */
> + bytes = -offset;
> compressed = SQUASHFS_COMPRESSED_BLOCK(length);
> length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
> + if (next_index)
> + *next_index = index + length;
> +
> TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
> index, compressed ? "" : "un", length, output->length);
> +
> + if (length < 0 || length > output->length ||
> + (index + length) > msblk->bytes_used)
> + goto read_failure;
> +
> + for (b = 0; bytes < length; b++, cur_index++) {
> + bh[b] = sb_getblk(sb, cur_index);
> + if (bh[b] == NULL)
> + goto block_release;
> + bytes += msblk->devblksize;
> + }
> + ll_rw_block(REQ_OP_READ, 0, b, bh);
> } else {
> /*
> * Metadata block.
> */
> - const u8 *data;
> - struct bvec_iter_all iter_all = {};
> - struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
> -
> - if (index + 2 > msblk->bytes_used) {
> - res = -EIO;
> - goto out;
> - }
> - res = squashfs_bio_read(sb, index, 2, &bio, &offset);
> - if (res)
> - goto out;
> -
> - if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) {
> - res = -EIO;
> - goto out_free_bio;
> - }
> - /* Extract the length of the metadata block */
> - data = page_address(bvec->bv_page) + bvec->bv_offset;
> - length = data[offset];
> - if (offset <= bvec->bv_len - 1) {
> - length |= data[offset + 1] << 8;
> - } else {
> - if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) {
> - res = -EIO;
> - goto out_free_bio;
> - }
> - data = page_address(bvec->bv_page) + bvec->bv_offset;
> - length |= data[0] << 8;
> - }
> - bio_free_pages(bio);
> - bio_put(bio);
> + if ((index + 2) > msblk->bytes_used)
> + goto read_failure;
>
> + bh[0] = get_block_length(sb, &cur_index, &offset, &length);
> + if (bh[0] == NULL)
> + goto read_failure;
> + b = 1;
> +
> + bytes = msblk->devblksize - offset;
> compressed = SQUASHFS_COMPRESSED(length);
> length = SQUASHFS_COMPRESSED_SIZE(length);
> - index += 2;
> + if (next_index)
> + *next_index = index + length + 2;
>
> TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
> - compressed ? "" : "un", length);
> + compressed ? "" : "un", length);
> +
> + if (length < 0 || length > output->length ||
> + (index + length) > msblk->bytes_used)
> + goto block_release;
> +
> + for (; bytes < length; b++) {
> + bh[b] = sb_getblk(sb, ++cur_index);
> + if (bh[b] == NULL)
> + goto block_release;
> + bytes += msblk->devblksize;
> + }
> + ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1);
> }
> - if (next_index)
> - *next_index = index + length;
>
> - res = squashfs_bio_read(sb, index, length, &bio, &offset);
> - if (res)
> - goto out;
> + for (i = 0; i < b; i++) {
> + wait_on_buffer(bh[i]);
> + if (!buffer_uptodate(bh[i]))
> + goto block_release;
> + }
>
> if (compressed) {
> - if (!msblk->stream) {
> - res = -EIO;
> - goto out_free_bio;
> - }
> - res = squashfs_decompress(msblk, bio, offset, length, output);
> + if (!msblk->stream)
> + goto read_failure;
> + length = squashfs_decompress(msblk, bh, b, offset, length,
> + output);
> + if (length < 0)
> + goto read_failure;
> } else {
> - res = copy_bio_to_actor(bio, output, offset, length);
> + /*
> + * Block is uncompressed.
> + */
> + int in, pg_offset = 0;
> + void *data = squashfs_first_page(output);
> +
> + for (bytes = length; k < b; k++) {
> + in = min(bytes, msblk->devblksize - offset);
> + bytes -= in;
> + while (in) {
> + if (pg_offset == PAGE_SIZE) {
> + data = squashfs_next_page(output);
> + pg_offset = 0;
> + }
> + avail = min_t(int, in, PAGE_SIZE -
> + pg_offset);
> + memcpy(data + pg_offset, bh[k]->b_data + offset,
> + avail);
> + in -= avail;
> + pg_offset += avail;
> + offset += avail;
> + }
> + offset = 0;
> + put_bh(bh[k]);
> + }
> + squashfs_finish_page(output);
> }
>
> -out_free_bio:
> - bio_free_pages(bio);
> - bio_put(bio);
> -out:
> - if (res < 0)
> - ERROR("Failed to read block 0x%llx: %d\n", index, res);
> + kfree(bh);
> + return length;
>
> - return res;
> +block_release:
> + for (; k < b; k++)
> + put_bh(bh[k]);
> +
> +read_failure:
> + ERROR("squashfs_read_data failed to read block 0x%llx\n",
> + (unsigned long long) index);
> + kfree(bh);
> + return -EIO;
> }
> --- a/fs/squashfs/decompressor.h~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/decompressor.h
> @@ -10,14 +10,13 @@
> * decompressor.h
> */
>
> -#include <linux/bio.h>
> -
> struct squashfs_decompressor {
> void *(*init)(struct squashfs_sb_info *, void *);
> void *(*comp_opts)(struct squashfs_sb_info *, void *, int);
> void (*free)(void *);
> int (*decompress)(struct squashfs_sb_info *, void *,
> - struct bio *, int, int, struct squashfs_page_actor *);
> + struct buffer_head **, int, int, int,
> + struct squashfs_page_actor *);
> int id;
> char *name;
> int supported;
> --- a/fs/squashfs/decompressor_multi.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/decompressor_multi.c
> @@ -6,7 +6,7 @@
> #include <linux/types.h>
> #include <linux/mutex.h>
> #include <linux/slab.h>
> -#include <linux/bio.h>
> +#include <linux/buffer_head.h>
> #include <linux/sched.h>
> #include <linux/wait.h>
> #include <linux/cpumask.h>
> @@ -180,15 +180,14 @@ wait:
> }
>
>
> -int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
> - int offset, int length,
> - struct squashfs_page_actor *output)
> +int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
> + int b, int offset, int length, struct squashfs_page_actor *output)
> {
> int res;
> struct squashfs_stream *stream = msblk->stream;
> struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream);
> res = msblk->decompressor->decompress(msblk, decomp_stream->stream,
> - bio, offset, length, output);
> + bh, b, offset, length, output);
> put_decomp_stream(decomp_stream, stream);
> if (res < 0)
> ERROR("%s decompression failed, data probably corrupt\n",
> --- a/fs/squashfs/decompressor_multi_percpu.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/decompressor_multi_percpu.c
> @@ -75,8 +75,8 @@ void squashfs_decompressor_destroy(struc
> }
> }
>
> -int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
> - int offset, int length, struct squashfs_page_actor *output)
> +int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
> + int b, int offset, int length, struct squashfs_page_actor *output)
> {
> struct squashfs_stream *stream;
> int res;
> @@ -84,7 +84,7 @@ int squashfs_decompress(struct squashfs_
> local_lock(&msblk->stream->lock);
> stream = this_cpu_ptr(msblk->stream);
>
> - res = msblk->decompressor->decompress(msblk, stream->stream, bio,
> + res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
> offset, length, output);
>
> local_unlock(&msblk->stream->lock);
> --- a/fs/squashfs/decompressor_single.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/decompressor_single.c
> @@ -7,7 +7,7 @@
> #include <linux/types.h>
> #include <linux/mutex.h>
> #include <linux/slab.h>
> -#include <linux/bio.h>
> +#include <linux/buffer_head.h>
>
> #include "squashfs_fs.h"
> #include "squashfs_fs_sb.h"
> @@ -59,15 +59,14 @@ void squashfs_decompressor_destroy(struc
> }
> }
>
> -int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
> - int offset, int length,
> - struct squashfs_page_actor *output)
> +int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
> + int b, int offset, int length, struct squashfs_page_actor *output)
> {
> int res;
> struct squashfs_stream *stream = msblk->stream;
>
> mutex_lock(&stream->mutex);
> - res = msblk->decompressor->decompress(msblk, stream->stream, bio,
> + res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
> offset, length, output);
> mutex_unlock(&stream->mutex);
>
> --- a/fs/squashfs/lz4_wrapper.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/lz4_wrapper.c
> @@ -4,7 +4,7 @@
> * Phillip Lougher <phillip@squashfs.org.uk>
> */
>
> -#include <linux/bio.h>
> +#include <linux/buffer_head.h>
> #include <linux/mutex.h>
> #include <linux/slab.h>
> #include <linux/vmalloc.h>
> @@ -89,23 +89,20 @@ static void lz4_free(void *strm)
>
>
> static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
> - struct bio *bio, int offset, int length,
> + struct buffer_head **bh, int b, int offset, int length,
> struct squashfs_page_actor *output)
> {
> - struct bvec_iter_all iter_all = {};
> - struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
> struct squashfs_lz4 *stream = strm;
> void *buff = stream->input, *data;
> - int bytes = length, res;
> + int avail, i, bytes = length, res;
>
> - while (bio_next_segment(bio, &iter_all)) {
> - int avail = min(bytes, ((int)bvec->bv_len) - offset);
> -
> - data = page_address(bvec->bv_page) + bvec->bv_offset;
> - memcpy(buff, data + offset, avail);
> + for (i = 0; i < b; i++) {
> + avail = min(bytes, msblk->devblksize - offset);
> + memcpy(buff, bh[i]->b_data + offset, avail);
> buff += avail;
> bytes -= avail;
> offset = 0;
> + put_bh(bh[i]);
> }
>
> res = LZ4_decompress_safe(stream->input, stream->output,
> --- a/fs/squashfs/lzo_wrapper.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/lzo_wrapper.c
> @@ -9,7 +9,7 @@
> */
>
> #include <linux/mutex.h>
> -#include <linux/bio.h>
> +#include <linux/buffer_head.h>
> #include <linux/slab.h>
> #include <linux/vmalloc.h>
> #include <linux/lzo.h>
> @@ -63,24 +63,21 @@ static void lzo_free(void *strm)
>
>
> static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
> - struct bio *bio, int offset, int length,
> + struct buffer_head **bh, int b, int offset, int length,
> struct squashfs_page_actor *output)
> {
> - struct bvec_iter_all iter_all = {};
> - struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
> struct squashfs_lzo *stream = strm;
> void *buff = stream->input, *data;
> - int bytes = length, res;
> + int avail, i, bytes = length, res;
> size_t out_len = output->length;
>
> - while (bio_next_segment(bio, &iter_all)) {
> - int avail = min(bytes, ((int)bvec->bv_len) - offset);
> -
> - data = page_address(bvec->bv_page) + bvec->bv_offset;
> - memcpy(buff, data + offset, avail);
> + for (i = 0; i < b; i++) {
> + avail = min(bytes, msblk->devblksize - offset);
> + memcpy(buff, bh[i]->b_data + offset, avail);
> buff += avail;
> bytes -= avail;
> offset = 0;
> + put_bh(bh[i]);
> }
>
> res = lzo1x_decompress_safe(stream->input, (size_t)length,
> --- a/fs/squashfs/squashfs.h~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/squashfs.h
> @@ -40,8 +40,8 @@ extern void *squashfs_decompressor_setup
> /* decompressor_xxx.c */
> extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
> extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
> -extern int squashfs_decompress(struct squashfs_sb_info *, struct bio *,
> - int, int, struct squashfs_page_actor *);
> +extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **,
> + int, int, int, struct squashfs_page_actor *);
> extern int squashfs_max_decompressors(void);
>
> /* export.c */
> --- a/fs/squashfs/xz_wrapper.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/xz_wrapper.c
> @@ -10,7 +10,7 @@
>
>
> #include <linux/mutex.h>
> -#include <linux/bio.h>
> +#include <linux/buffer_head.h>
> #include <linux/slab.h>
> #include <linux/xz.h>
> #include <linux/bitops.h>
> @@ -117,12 +117,11 @@ static void squashfs_xz_free(void *strm)
>
>
> static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
> - struct bio *bio, int offset, int length,
> + struct buffer_head **bh, int b, int offset, int length,
> struct squashfs_page_actor *output)
> {
> - struct bvec_iter_all iter_all = {};
> - struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
> - int total = 0, error = 0;
> + enum xz_ret xz_err;
> + int avail, total = 0, k = 0;
> struct squashfs_xz *stream = strm;
>
> xz_dec_reset(stream->state);
> @@ -132,23 +131,11 @@ static int squashfs_xz_uncompress(struct
> stream->buf.out_size = PAGE_SIZE;
> stream->buf.out = squashfs_first_page(output);
>
> - for (;;) {
> - enum xz_ret xz_err;
> -
> - if (stream->buf.in_pos == stream->buf.in_size) {
> - const void *data;
> - int avail;
> -
> - if (!bio_next_segment(bio, &iter_all)) {
> - /* XZ_STREAM_END must be reached. */
> - error = -EIO;
> - break;
> - }
> -
> - avail = min(length, ((int)bvec->bv_len) - offset);
> - data = page_address(bvec->bv_page) + bvec->bv_offset;
> + do {
> + if (stream->buf.in_pos == stream->buf.in_size && k < b) {
> + avail = min(length, msblk->devblksize - offset);
> length -= avail;
> - stream->buf.in = data + offset;
> + stream->buf.in = bh[k]->b_data + offset;
> stream->buf.in_size = avail;
> stream->buf.in_pos = 0;
> offset = 0;
> @@ -163,17 +150,23 @@ static int squashfs_xz_uncompress(struct
> }
>
> xz_err = xz_dec_run(stream->state, &stream->buf);
> - if (xz_err == XZ_STREAM_END)
> - break;
> - if (xz_err != XZ_OK) {
> - error = -EIO;
> - break;
> - }
> - }
> +
> + if (stream->buf.in_pos == stream->buf.in_size && k < b)
> + put_bh(bh[k++]);
> + } while (xz_err == XZ_OK);
>
> squashfs_finish_page(output);
>
> - return error ? error : total + stream->buf.out_pos;
> + if (xz_err != XZ_STREAM_END || k < b)
> + goto out;
> +
> + return total + stream->buf.out_pos;
> +
> +out:
> + for (; k < b; k++)
> + put_bh(bh[k]);
> +
> + return -EIO;
> }
>
> const struct squashfs_decompressor squashfs_xz_comp_ops = {
> --- a/fs/squashfs/zlib_wrapper.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/zlib_wrapper.c
> @@ -10,7 +10,7 @@
>
>
> #include <linux/mutex.h>
> -#include <linux/bio.h>
> +#include <linux/buffer_head.h>
> #include <linux/slab.h>
> #include <linux/zlib.h>
> #include <linux/vmalloc.h>
> @@ -50,35 +50,21 @@ static void zlib_free(void *strm)
>
>
> static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
> - struct bio *bio, int offset, int length,
> + struct buffer_head **bh, int b, int offset, int length,
> struct squashfs_page_actor *output)
> {
> - struct bvec_iter_all iter_all = {};
> - struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
> - int zlib_init = 0, error = 0;
> + int zlib_err, zlib_init = 0, k = 0;
> z_stream *stream = strm;
>
> stream->avail_out = PAGE_SIZE;
> stream->next_out = squashfs_first_page(output);
> stream->avail_in = 0;
>
> - for (;;) {
> - int zlib_err;
> -
> - if (stream->avail_in == 0) {
> - const void *data;
> - int avail;
> -
> - if (!bio_next_segment(bio, &iter_all)) {
> - /* Z_STREAM_END must be reached. */
> - error = -EIO;
> - break;
> - }
> -
> - avail = min(length, ((int)bvec->bv_len) - offset);
> - data = page_address(bvec->bv_page) + bvec->bv_offset;
> + do {
> + if (stream->avail_in == 0 && k < b) {
> + int avail = min(length, msblk->devblksize - offset);
> length -= avail;
> - stream->next_in = data + offset;
> + stream->next_in = bh[k]->b_data + offset;
> stream->avail_in = avail;
> offset = 0;
> }
> @@ -92,28 +78,37 @@ static int zlib_uncompress(struct squash
> if (!zlib_init) {
> zlib_err = zlib_inflateInit(stream);
> if (zlib_err != Z_OK) {
> - error = -EIO;
> - break;
> + squashfs_finish_page(output);
> + goto out;
> }
> zlib_init = 1;
> }
>
> zlib_err = zlib_inflate(stream, Z_SYNC_FLUSH);
> - if (zlib_err == Z_STREAM_END)
> - break;
> - if (zlib_err != Z_OK) {
> - error = -EIO;
> - break;
> - }
> - }
> +
> + if (stream->avail_in == 0 && k < b)
> + put_bh(bh[k++]);
> + } while (zlib_err == Z_OK);
>
> squashfs_finish_page(output);
>
> - if (!error)
> - if (zlib_inflateEnd(stream) != Z_OK)
> - error = -EIO;
> + if (zlib_err != Z_STREAM_END)
> + goto out;
> +
> + zlib_err = zlib_inflateEnd(stream);
> + if (zlib_err != Z_OK)
> + goto out;
> +
> + if (k < b)
> + goto out;
> +
> + return stream->total_out;
> +
> +out:
> + for (; k < b; k++)
> + put_bh(bh[k]);
>
> - return error ? error : stream->total_out;
> + return -EIO;
> }
>
> const struct squashfs_decompressor squashfs_zlib_comp_ops = {
> --- a/fs/squashfs/zstd_wrapper.c~revert-squashfs-migrate-from-ll_rw_block-usage-to-bio
> +++ a/fs/squashfs/zstd_wrapper.c
> @@ -9,7 +9,7 @@
> */
>
> #include <linux/mutex.h>
> -#include <linux/bio.h>
> +#include <linux/buffer_head.h>
> #include <linux/slab.h>
> #include <linux/zstd.h>
> #include <linux/vmalloc.h>
> @@ -59,44 +59,33 @@ static void zstd_free(void *strm)
>
>
> static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
> - struct bio *bio, int offset, int length,
> + struct buffer_head **bh, int b, int offset, int length,
> struct squashfs_page_actor *output)
> {
> struct workspace *wksp = strm;
> ZSTD_DStream *stream;
> size_t total_out = 0;
> - int error = 0;
> + size_t zstd_err;
> + int k = 0;
> ZSTD_inBuffer in_buf = { NULL, 0, 0 };
> ZSTD_outBuffer out_buf = { NULL, 0, 0 };
> - struct bvec_iter_all iter_all = {};
> - struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
>
> stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size);
>
> if (!stream) {
> ERROR("Failed to initialize zstd decompressor\n");
> - return -EIO;
> + goto out;
> }
>
> out_buf.size = PAGE_SIZE;
> out_buf.dst = squashfs_first_page(output);
>
> - for (;;) {
> - size_t zstd_err;
> + do {
> + if (in_buf.pos == in_buf.size && k < b) {
> + int avail = min(length, msblk->devblksize - offset);
>
> - if (in_buf.pos == in_buf.size) {
> - const void *data;
> - int avail;
> -
> - if (!bio_next_segment(bio, &iter_all)) {
> - error = -EIO;
> - break;
> - }
> -
> - avail = min(length, ((int)bvec->bv_len) - offset);
> - data = page_address(bvec->bv_page) + bvec->bv_offset;
> length -= avail;
> - in_buf.src = data + offset;
> + in_buf.src = bh[k]->b_data + offset;
> in_buf.size = avail;
> in_buf.pos = 0;
> offset = 0;
> @@ -108,8 +97,8 @@ static int zstd_uncompress(struct squash
> /* Shouldn't run out of pages
> * before stream is done.
> */
> - error = -EIO;
> - break;
> + squashfs_finish_page(output);
> + goto out;
> }
> out_buf.pos = 0;
> out_buf.size = PAGE_SIZE;
> @@ -118,20 +107,29 @@ static int zstd_uncompress(struct squash
> total_out -= out_buf.pos;
> zstd_err = ZSTD_decompressStream(stream, &out_buf, &in_buf);
> total_out += out_buf.pos; /* add the additional data produced */
> - if (zstd_err == 0)
> - break;
>
> - if (ZSTD_isError(zstd_err)) {
> - ERROR("zstd decompression error: %d\n",
> - (int)ZSTD_getErrorCode(zstd_err));
> - error = -EIO;
> - break;
> - }
> - }
> + if (in_buf.pos == in_buf.size && k < b)
> + put_bh(bh[k++]);
> + } while (zstd_err != 0 && !ZSTD_isError(zstd_err));
>
> squashfs_finish_page(output);
>
> - return error ? error : total_out;
> + if (ZSTD_isError(zstd_err)) {
> + ERROR("zstd decompression error: %d\n",
> + (int)ZSTD_getErrorCode(zstd_err));
> + goto out;
> + }
> +
> + if (k < b)
> + goto out;
> +
> + return (int)total_out;
> +
> +out:
> + for (; k < b; k++)
> + put_bh(bh[k]);
> +
> + return -EIO;
> }
>
> const struct squashfs_decompressor squashfs_zstd_comp_ops = {
> _
>
^ permalink raw reply
* Re: [PATCH v5 5/5] iommu/vt-d: Check UAPI data processed by IOMMU core
From: Lu Baolu @ 2020-07-17 1:15 UTC (permalink / raw)
To: Jacob Pan, iommu, LKML, Joerg Roedel, Alex Williamson
Cc: Tian, Kevin, Raj Ashok, Jonathan Corbet, Jean-Philippe Brucker,
Christoph Hellwig, David Woodhouse
In-Reply-To: <1594925117-64892-6-git-send-email-jacob.jun.pan@linux.intel.com>
On 7/17/20 2:45 AM, Jacob Pan wrote:
> IOMMU generic layer already does sanity checks UAPI data for version
> match and argsz range under generic information.
> Remove the redundant version check from VT-d driver and check for vendor
> specific data size.
>
> Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Best regards,
baolu
> ---
> drivers/iommu/intel/iommu.c | 3 +--
> drivers/iommu/intel/svm.c | 7 +++++--
> 2 files changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> index f3a6ca88cf95..5e80484f0537 100644
> --- a/drivers/iommu/intel/iommu.c
> +++ b/drivers/iommu/intel/iommu.c
> @@ -5383,8 +5383,7 @@ intel_iommu_sva_invalidate(struct iommu_domain *domain, struct device *dev,
> int ret = 0;
> u64 size = 0;
>
> - if (!inv_info || !dmar_domain ||
> - inv_info->version != IOMMU_CACHE_INVALIDATE_INFO_VERSION_1)
> + if (!inv_info || !dmar_domain)
> return -EINVAL;
>
> if (!dev || !dev_is_pci(dev))
> diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c
> index 713b3a218483..55ea11e9c0f5 100644
> --- a/drivers/iommu/intel/svm.c
> +++ b/drivers/iommu/intel/svm.c
> @@ -240,8 +240,11 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
> if (WARN_ON(!iommu) || !data)
> return -EINVAL;
>
> - if (data->version != IOMMU_GPASID_BIND_VERSION_1 ||
> - data->format != IOMMU_PASID_FORMAT_INTEL_VTD)
> + if (data->format != IOMMU_PASID_FORMAT_INTEL_VTD)
> + return -EINVAL;
> +
> + /* IOMMU core ensures argsz is more than the start of the union */
> + if (data->argsz < offsetofend(struct iommu_gpasid_bind_data, vendor.vtd))
> return -EINVAL;
>
> if (!dev_is_pci(dev))
>
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu
^ permalink raw reply
* Re: [PATCH v5 5/5] iommu/vt-d: Check UAPI data processed by IOMMU core
From: Lu Baolu @ 2020-07-17 1:15 UTC (permalink / raw)
To: Jacob Pan, iommu, LKML, Joerg Roedel, Alex Williamson
Cc: baolu.lu, David Woodhouse, Yi Liu, Tian, Kevin, Raj Ashok,
Christoph Hellwig, Jean-Philippe Brucker, Eric Auger,
Jonathan Corbet
In-Reply-To: <1594925117-64892-6-git-send-email-jacob.jun.pan@linux.intel.com>
On 7/17/20 2:45 AM, Jacob Pan wrote:
> IOMMU generic layer already does sanity checks UAPI data for version
> match and argsz range under generic information.
> Remove the redundant version check from VT-d driver and check for vendor
> specific data size.
>
> Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Best regards,
baolu
> ---
> drivers/iommu/intel/iommu.c | 3 +--
> drivers/iommu/intel/svm.c | 7 +++++--
> 2 files changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> index f3a6ca88cf95..5e80484f0537 100644
> --- a/drivers/iommu/intel/iommu.c
> +++ b/drivers/iommu/intel/iommu.c
> @@ -5383,8 +5383,7 @@ intel_iommu_sva_invalidate(struct iommu_domain *domain, struct device *dev,
> int ret = 0;
> u64 size = 0;
>
> - if (!inv_info || !dmar_domain ||
> - inv_info->version != IOMMU_CACHE_INVALIDATE_INFO_VERSION_1)
> + if (!inv_info || !dmar_domain)
> return -EINVAL;
>
> if (!dev || !dev_is_pci(dev))
> diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c
> index 713b3a218483..55ea11e9c0f5 100644
> --- a/drivers/iommu/intel/svm.c
> +++ b/drivers/iommu/intel/svm.c
> @@ -240,8 +240,11 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
> if (WARN_ON(!iommu) || !data)
> return -EINVAL;
>
> - if (data->version != IOMMU_GPASID_BIND_VERSION_1 ||
> - data->format != IOMMU_PASID_FORMAT_INTEL_VTD)
> + if (data->format != IOMMU_PASID_FORMAT_INTEL_VTD)
> + return -EINVAL;
> +
> + /* IOMMU core ensures argsz is more than the start of the union */
> + if (data->argsz < offsetofend(struct iommu_gpasid_bind_data, vendor.vtd))
> return -EINVAL;
>
> if (!dev_is_pci(dev))
>
^ permalink raw reply
* Re: [PATCH -next] s390/mm: Convert to DEFINE_SHOW_ATTRIBUTE
From: miaoqinglang @ 2020-07-17 1:19 UTC (permalink / raw)
To: Heiko Carstens
Cc: Greg Kroah-Hartman, Vasily Gorbik, Christian Borntraeger,
linux-s390, linux-kernel, Sven Schnelle
In-Reply-To: <20200716112630.GB8484@osiris>
在 2020/7/16 19:26, Heiko Carstens 写道:
> On Thu, Jul 16, 2020 at 05:07:03PM +0800, Qinglang Miao wrote:
>> From: Chen Huang <chenhuang5@huawei.com>
>>
>> Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code.
>>
>> Signed-off-by: Chen Huang <chenhuang5@huawei.com>
>> ---
>> arch/s390/mm/dump_pagetables.c | 12 +-----------
>> 1 file changed, 1 insertion(+), 11 deletions(-)
> Thanks for the patch, however we are going to convert the s390 page
> table dumper to generic code, so I'm not going to apply any cleanup
> patches for this code anymore.
Thanks for your replay, I'm glad to know that.
^ permalink raw reply
* Re: [PATCH -next] s390/mm: Convert to DEFINE_SHOW_ATTRIBUTE
From: miaoqinglang @ 2020-07-17 1:19 UTC (permalink / raw)
To: Heiko Carstens
Cc: Greg Kroah-Hartman, Vasily Gorbik, Christian Borntraeger,
linux-s390, linux-kernel, Sven Schnelle
In-Reply-To: <20200716112630.GB8484@osiris>
锟斤拷 2020/7/16 19:26, Heiko Carstens 写锟斤拷:
> On Thu, Jul 16, 2020 at 05:07:03PM +0800, Qinglang Miao wrote:
>> From: Chen Huang <chenhuang5@huawei.com>
>>
>> Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code.
>>
>> Signed-off-by: Chen Huang <chenhuang5@huawei.com>
>> ---
>> arch/s390/mm/dump_pagetables.c | 12 +-----------
>> 1 file changed, 1 insertion(+), 11 deletions(-)
> Thanks for the patch, however we are going to convert the s390 page
> table dumper to generic code, so I'm not going to apply any cleanup
> patches for this code anymore.
Thanks for your replay, I'm glad to know that.
^ permalink raw reply
* Re: [PATCH net-next 10/13] qed: add support for new port modes
From: Jakub Kicinski @ 2020-07-17 1:18 UTC (permalink / raw)
To: Alexander Lobakin, linux-scsi, linux-kernel
Cc: David S. Miller, Igor Russkikh, Michal Kalderon, Ariel Elior,
Denis Bolotin, James E.J. Bottomley, Martin K. Petersen,
GR-everest-linux-l2, QLogic-Storage-Upstream, netdev
In-Reply-To: <20200716115446.994-11-alobakin@marvell.com>
On Thu, 16 Jul 2020 14:54:43 +0300 Alexander Lobakin wrote:
> These ports ship on new boards revisions and are supported by newer
> firmware versions.
>
> Signed-off-by: Alexander Lobakin <alobakin@marvell.com>
> Signed-off-by: Igor Russkikh <irusskikh@marvell.com>
What is the driver actually doing with them, tho?
Looks like you translate some firmware specific field to a driver
specific field, but I can't figure out what part of the code cares
about hw_info.port_mode
> diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
> index 6a1d12da7910..63fcbd5a295a 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed.h
> +++ b/drivers/net/ethernet/qlogic/qed/qed.h
> @@ -257,6 +257,11 @@ enum QED_PORT_MODE {
> QED_PORT_MODE_DE_1X25G,
> QED_PORT_MODE_DE_4X25G,
> QED_PORT_MODE_DE_2X10G,
> + QED_PORT_MODE_DE_2X50G_R1,
> + QED_PORT_MODE_DE_4X50G_R1,
> + QED_PORT_MODE_DE_1X100G_R2,
> + QED_PORT_MODE_DE_2X100G_R2,
> + QED_PORT_MODE_DE_1X100G_R4,
> };
>
> enum qed_dev_cap {
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
> index d929556247a5..4bad836d0f74 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
> +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
> @@ -4026,6 +4026,21 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
> case NVM_CFG1_GLOB_NETWORK_PORT_MODE_4X25G:
> p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_4X25G;
> break;
> + case NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_2X50G_R1:
> + p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_2X50G_R1;
> + break;
> + case NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_4X50G_R1:
> + p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_4X50G_R1;
> + break;
> + case NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_1X100G_R2:
> + p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_1X100G_R2;
> + break;
> + case NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_2X100G_R2:
> + p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_2X100G_R2;
> + break;
> + case NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_1X100G_R4:
> + p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_1X100G_R4;
> + break;
> default:
> DP_NOTICE(p_hwfn, "Unknown port mode in 0x%08x\n", core_cfg);
> break;
> diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
> index a4a845579fd2..debc55923251 100644
> --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
> +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
> @@ -13015,6 +13015,11 @@ struct nvm_cfg1_glob {
> #define NVM_CFG1_GLOB_NETWORK_PORT_MODE_1X25G 0xd
> #define NVM_CFG1_GLOB_NETWORK_PORT_MODE_4X25G 0xe
> #define NVM_CFG1_GLOB_NETWORK_PORT_MODE_2X10G 0xf
> +#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_2X50G_R1 0x11
> +#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_4X50G_R1 0x12
> +#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_1X100G_R2 0x13
> +#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_2X100G_R2 0x14
> +#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_AHP_1X100G_R4 0x15
>
> u32 e_lane_cfg1;
> u32 e_lane_cfg2;
^ permalink raw reply
* Re: [PATCH v2 10/11] xfs: improve ondisk dquot flags checking
From: Dave Chinner @ 2020-07-17 1:18 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
In-Reply-To: <20200717011255.GM3151642@magnolia>
On Thu, Jul 16, 2020 at 06:12:55PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create an XFS_DQTYPE_ANY mask for ondisk dquots flags, and use that to
> ensure that we never accept any garbage flags when we're loading dquots.
> While we're at it, restructure the quota type flag checking to use the
> proper masking.
>
> Note that I plan to add y2038 support soon, which will require a new
> xfs_dqtype_t flag for extended timestamp support, hence all the work to
> make the type masking work correctly.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v2: amend commit message
> ---
Looks good.
Reviewed-by: Dave Chinner <dchinner@redhat.com>
--
Dave Chinner
david@fromorbit.com
^ permalink raw reply
* Re: [PATCH 08/11] xfs: replace a few open-coded XFS_DQTYPE_REC_MASK uses
From: Dave Chinner @ 2020-07-17 1:18 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
In-Reply-To: <20200717010715.GL3151642@magnolia>
On Thu, Jul 16, 2020 at 06:07:15PM -0700, Darrick J. Wong wrote:
> On Fri, Jul 17, 2020 at 10:02:42AM +1000, Dave Chinner wrote:
> > On Wed, Jul 15, 2020 at 11:46:10PM -0700, Darrick J. Wong wrote:
> > > From: Darrick J. Wong <darrick.wong@oracle.com>
> > >
> > > Fix a few places where we open-coded this mask constant.
> > >
> > > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > ---
> > > fs/xfs/xfs_dquot_item_recover.c | 4 ++--
> > > 1 file changed, 2 insertions(+), 2 deletions(-)
> > >
> > >
> > > diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c
> > > index d7eb85c7d394..93178341569a 100644
> > > --- a/fs/xfs/xfs_dquot_item_recover.c
> > > +++ b/fs/xfs/xfs_dquot_item_recover.c
> > > @@ -39,7 +39,7 @@ xlog_recover_dquot_ra_pass2(
> > > if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot))
> > > return;
> > >
> > > - type = recddq->d_flags & (XFS_DQTYPE_USER | XFS_DQTYPE_PROJ | XFS_DQTYPE_GROUP);
> > > + type = recddq->d_flags & XFS_DQTYPE_REC_MASK;
> > > ASSERT(type);
> > > if (log->l_quotaoffs_flag & type)
> > > return;
> > > @@ -91,7 +91,7 @@ xlog_recover_dquot_commit_pass2(
> > > /*
> > > * This type of quotas was turned off, so ignore this record.
> > > */
> > > - type = recddq->d_flags & (XFS_DQTYPE_USER | XFS_DQTYPE_PROJ | XFS_DQTYPE_GROUP);
> > > + type = recddq->d_flags & XFS_DQTYPE_REC_MASK;
> >
> > Couldn't these both be converted to xfs_dquot_type(recddq)?
>
> xfs_dquot_type takes a pointer to a incore dquot, not a struct
> xfs_disk_dquot, so no.
Ah, right, I didn't notice the recddq type mismatch there. Ok, code
is fine.
Reviewed-by: Dave Chinner <dchinner@redhat.com>
--
Dave Chinner
david@fromorbit.com
^ permalink raw reply
* [ljones-mfd:for-mfd-next 56/58] drivers/mfd/mfd-core.c:147:17: error: implicit declaration of function 'of_read_number'
From: kernel test robot @ 2020-07-17 1:17 UTC (permalink / raw)
To: kbuild-all
[-- Attachment #1: Type: text/plain, Size: 3109 bytes --]
tree: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next
head: ae387961ae94021c2322c59e069e7476fb7702f7
commit: 70d48975c152997bea1c715de3382ef854c288ed [56/58] mfd: core: Make a best effort attempt to match devices with the correct of_nodes
config: arm-randconfig-r025-20200716 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project ed6b578040a85977026c93bf4188f996148f3218)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm cross compiling tool for clang build
# apt-get install binutils-arm-linux-gnueabi
git checkout 70d48975c152997bea1c715de3382ef854c288ed
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
>> drivers/mfd/mfd-core.c:147:17: error: implicit declaration of function 'of_read_number' [-Werror,-Wimplicit-function-declaration]
of_node_addr = of_read_number(reg, of_n_addr_cells(np));
^
1 error generated.
vim +/of_read_number +147 drivers/mfd/mfd-core.c
119
120 static int mfd_match_of_node_to_dev(struct platform_device *pdev,
121 struct device_node *np,
122 const struct mfd_cell *cell)
123 {
124 struct mfd_of_node_entry *of_entry;
125 const __be32 *reg;
126 u64 of_node_addr;
127
128 /* Skip devices 'disabled' by Device Tree */
129 if (!of_device_is_available(np))
130 return -ENODEV;
131
132 /* Skip if OF node has previously been allocated to a device */
133 list_for_each_entry(of_entry, &mfd_of_node_list, list)
134 if (of_entry->np == np)
135 return -EAGAIN;
136
137 if (!cell->use_of_reg)
138 /* No of_reg defined - allocate first free compatible match */
139 goto allocate_of_node;
140
141 /* We only care about each node's first defined address */
142 reg = of_get_address(np, 0, NULL, NULL);
143 if (!reg)
144 /* OF node does not contatin a 'reg' property to match to */
145 return -EAGAIN;
146
> 147 of_node_addr = of_read_number(reg, of_n_addr_cells(np));
148
149 if (cell->of_reg != of_node_addr)
150 /* No match */
151 return -EAGAIN;
152
153 allocate_of_node:
154 of_entry = kzalloc(sizeof(*of_entry), GFP_KERNEL);
155 if (!of_entry)
156 return -ENOMEM;
157
158 of_entry->dev = &pdev->dev;
159 of_entry->np = np;
160 list_add_tail(&of_entry->list, &mfd_of_node_list);
161
162 pdev->dev.of_node = np;
163 pdev->dev.fwnode = &np->fwnode;
164
165 return 0;
166 }
167
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 30776 bytes --]
^ permalink raw reply
* [Bug 208591] connecting to bluetooth Samsung earbuds freezes the system (kubuntu 20.04) with 5.8rc kernels. Hard reset needed.
From: bugzilla-daemon @ 2020-07-17 1:17 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <bug-208591-62941@https.bugzilla.kernel.org/>
https://bugzilla.kernel.org/show_bug.cgi?id=208591
--- Comment #5 from Marian Klein (mkleinsoft@gmail.com) ---
Please provide pre-compiled kernel binaries x86_64 leading up to 5.8rc1 and I
will try to bisect or narrow down the problem or to find a commit causing this
problem.
--
You are receiving this mail because:
You are the assignee for the bug.
^ permalink raw reply
* [PATCH v2 10/11] xfs: improve ondisk dquot flags checking
From: Darrick J. Wong @ 2020-07-17 1:12 UTC (permalink / raw)
To: linux-xfs, Dave Chinner
In-Reply-To: <159488198306.3813063.16348101518917273554.stgit@magnolia>
From: Darrick J. Wong <darrick.wong@oracle.com>
Create an XFS_DQTYPE_ANY mask for ondisk dquots flags, and use that to
ensure that we never accept any garbage flags when we're loading dquots.
While we're at it, restructure the quota type flag checking to use the
proper masking.
Note that I plan to add y2038 support soon, which will require a new
xfs_dqtype_t flag for extended timestamp support, hence all the work to
make the type masking work correctly.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: amend commit message
---
fs/xfs/libxfs/xfs_dquot_buf.c | 11 ++++++++---
fs/xfs/libxfs/xfs_format.h | 2 ++
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c
index 75c164ed141c..39d64fbc6b87 100644
--- a/fs/xfs/libxfs/xfs_dquot_buf.c
+++ b/fs/xfs/libxfs/xfs_dquot_buf.c
@@ -39,6 +39,8 @@ xfs_dquot_verify(
struct xfs_disk_dquot *ddq,
xfs_dqid_t id) /* used only during quotacheck */
{
+ __u8 ddq_type;
+
/*
* We can encounter an uninitialized dquot buffer for 2 reasons:
* 1. If we crash while deleting the quotainode(s), and those blks got
@@ -59,9 +61,12 @@ xfs_dquot_verify(
if (ddq->d_version != XFS_DQUOT_VERSION)
return __this_address;
- if (ddq->d_flags != XFS_DQTYPE_USER &&
- ddq->d_flags != XFS_DQTYPE_PROJ &&
- ddq->d_flags != XFS_DQTYPE_GROUP)
+ if (ddq->d_flags & ~XFS_DQTYPE_ANY)
+ return __this_address;
+ ddq_type = ddq->d_flags & XFS_DQTYPE_REC_MASK;
+ if (ddq_type != XFS_DQTYPE_USER &&
+ ddq_type != XFS_DQTYPE_PROJ &&
+ ddq_type != XFS_DQTYPE_GROUP)
return __this_address;
if (id != -1 && id != be32_to_cpu(ddq->d_id))
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 0fa969f6202c..29564bd32bef 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -1158,6 +1158,8 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
XFS_DQTYPE_PROJ | \
XFS_DQTYPE_GROUP)
+#define XFS_DQTYPE_ANY (XFS_DQTYPE_REC_MASK)
+
/*
* This is the main portion of the on-disk representation of quota information
* for a user. We pad this with some more expansion room to construct the on
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.