2022-03-01 17:23:49

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 0/9] Host1x context isolation support

From: Mikko Perttunen <[email protected]>

***
New in v4:

Addressed review comments. See individual patches.
***

***
New in v3:

Added device tree bindings for new property.
***

***
New in v2:

Added support for Tegra194
Use standard iommu-map property instead of custom mechanism
***

This series adds support for Host1x 'context isolation'. Since
when programming engines through Host1x, userspace can program in
any addresses it wants, we need some way to isolate the engines'
memory spaces. Traditionally this has either been done imperfectly
with a single shared IOMMU domain, or by copying and verifying the
programming command stream at submit time (Host1x firewall).

Since Tegra186 there is a privileged (only usable by kernel)
Host1x opcode that allows setting the stream ID sent by the engine
to the SMMU. So, by allocating a number of context banks and stream
IDs for this purpose, and using this opcode at the beginning of
each job, we can implement isolation. Due to the limited number of
context banks only each process gets its own context, and not
each channel.

This feature also allows sharing engines among multiple VMs when
used with Host1x's hardware virtualization support - up to 8 VMs
can be configured with a subset of allowed stream IDs, enforced
at hardware level.

To implement this, this series adds a new host1x context bus, which
will contain the 'struct device's corresponding to each context
bank / stream ID, changes to device tree and SMMU code to allow
registering the devices and using the bus, as well as the Host1x
stream ID programming code and support in TegraDRM.

-------------
Merging notes
-------------

The changes to DT bindings should be applied on top of Thierry's patch
'dt-bindings: display: tegra: Convert to json-schema'.

Thanks,
Mikko

Mikko Perttunen (9):
dt-bindings: host1x: Add iommu-map property
gpu: host1x: Add context bus
gpu: host1x: Add context device management code
gpu: host1x: Program context stream ID on submission
iommu/arm-smmu: Attach to host1x context device bus
arm64: tegra: Add Host1x context stream IDs on Tegra186+
drm/tegra: falcon: Set DMACTX field on DMA transactions
drm/tegra: Support context isolation
drm/tegra: vic: Implement get_streamid_offset

.../display/tegra/nvidia,tegra20-host1x.yaml | 5 +
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 11 ++
arch/arm64/boot/dts/nvidia/tegra194.dtsi | 11 ++
drivers/gpu/Makefile | 3 +-
drivers/gpu/drm/tegra/drm.h | 2 +
drivers/gpu/drm/tegra/falcon.c | 8 +
drivers/gpu/drm/tegra/falcon.h | 1 +
drivers/gpu/drm/tegra/submit.c | 21 ++-
drivers/gpu/drm/tegra/uapi.c | 45 ++++-
drivers/gpu/drm/tegra/vic.c | 68 +++++++-
drivers/gpu/host1x/Kconfig | 5 +
drivers/gpu/host1x/Makefile | 2 +
drivers/gpu/host1x/context.c | 160 ++++++++++++++++++
drivers/gpu/host1x/context.h | 27 +++
drivers/gpu/host1x/context_bus.c | 31 ++++
drivers/gpu/host1x/dev.c | 12 +-
drivers/gpu/host1x/dev.h | 2 +
drivers/gpu/host1x/hw/channel_hw.c | 52 +++++-
drivers/gpu/host1x/hw/host1x06_hardware.h | 10 ++
drivers/gpu/host1x/hw/host1x07_hardware.h | 10 ++
drivers/iommu/arm/arm-smmu/arm-smmu.c | 13 ++
include/linux/host1x.h | 22 +++
include/linux/host1x_context_bus.h | 15 ++
23 files changed, 518 insertions(+), 18 deletions(-)
create mode 100644 drivers/gpu/host1x/context.c
create mode 100644 drivers/gpu/host1x/context.h
create mode 100644 drivers/gpu/host1x/context_bus.c
create mode 100644 include/linux/host1x_context_bus.h

--
2.35.0


2022-03-01 17:37:26

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 8/9] drm/tegra: Support context isolation

From: Mikko Perttunen <[email protected]>

For engines that support context isolation, allocate a context when
opening a channel, and set up stream ID offset and context fields
when submitting a job.

Signed-off-by: Mikko Perttunen <[email protected]>
---
v4:
* Separate error and output values in get_streamid_offset API
* Improve error handling
* Rename job->context to job->memory_context for clarity
---
drivers/gpu/drm/tegra/drm.h | 2 ++
drivers/gpu/drm/tegra/submit.c | 21 +++++++++++++++-
drivers/gpu/drm/tegra/uapi.c | 45 ++++++++++++++++++++++++++++++++--
3 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index fc0a19554eac..608daba01587 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -80,6 +80,7 @@ struct tegra_drm_context {

/* Only used by new UAPI. */
struct xarray mappings;
+ struct host1x_context *memory_context;
};

struct tegra_drm_client_ops {
@@ -91,6 +92,7 @@ struct tegra_drm_client_ops {
int (*submit)(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file);
+ int (*get_streamid_offset)(struct tegra_drm_client *client, u32 *offset);
};

int tegra_drm_submit(struct tegra_drm_context *context,
diff --git a/drivers/gpu/drm/tegra/submit.c b/drivers/gpu/drm/tegra/submit.c
index 6d6dd8c35475..fd0ba09e552a 100644
--- a/drivers/gpu/drm/tegra/submit.c
+++ b/drivers/gpu/drm/tegra/submit.c
@@ -498,6 +498,9 @@ static void release_job(struct host1x_job *job)
struct tegra_drm_submit_data *job_data = job->user_data;
u32 i;

+ if (job->memory_context)
+ host1x_context_put(job->memory_context);
+
for (i = 0; i < job_data->num_used_mappings; i++)
tegra_drm_mapping_put(job_data->used_mappings[i].mapping);

@@ -588,11 +591,24 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
goto put_job;
}

+ if (context->memory_context && context->client->ops->get_streamid_offset) {
+ int offset;
+
+ err = context->client->ops->get_streamid_offset(context->client, &offset);
+ if (!err) {
+ job->memory_context = context->memory_context;
+ job->engine_streamid_offset = offset;
+ host1x_context_get(job->memory_context);
+ } else if (err != -EOPNOTSUPP) {
+ goto unpin_job;
+ }
+ }
+
/* Boot engine. */
err = pm_runtime_resume_and_get(context->client->base.dev);
if (err < 0) {
SUBMIT_ERR(context, "could not power up engine: %d", err);
- goto unpin_job;
+ goto put_memory_context;
}

job->user_data = job_data;
@@ -627,6 +643,9 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,

goto put_job;

+put_memory_context:
+ if (job->memory_context)
+ host1x_context_put(job->memory_context);
unpin_job:
host1x_job_unpin(job);
put_job:
diff --git a/drivers/gpu/drm/tegra/uapi.c b/drivers/gpu/drm/tegra/uapi.c
index 9ab9179d2026..475febb6d86b 100644
--- a/drivers/gpu/drm/tegra/uapi.c
+++ b/drivers/gpu/drm/tegra/uapi.c
@@ -33,6 +33,9 @@ static void tegra_drm_channel_context_close(struct tegra_drm_context *context)
struct tegra_drm_mapping *mapping;
unsigned long id;

+ if (context->memory_context)
+ host1x_context_put(context->memory_context);
+
xa_for_each(&context->mappings, id, mapping)
tegra_drm_mapping_put(mapping);

@@ -72,6 +75,7 @@ static struct tegra_drm_client *tegra_drm_find_client(struct tegra_drm *tegra, u

int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_file *file)
{
+ struct host1x *host = tegra_drm_to_host1x(drm->dev_private);
struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm *tegra = drm->dev_private;
struct drm_tegra_channel_open *args = data;
@@ -102,10 +106,38 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_
}
}

+ /* Only allocate context if the engine supports context isolation. */
+ if (client->ops->get_streamid_offset) {
+ u32 offset;
+
+ err = client->ops->get_streamid_offset(client, &offset);
+ if (!err) {
+ context->memory_context =
+ host1x_context_alloc(host, get_task_pid(current, PIDTYPE_TGID));
+ } else if (err == -EOPNOTSUPP) {
+ context->memory_context = NULL;
+ } else {
+ goto put_channel;
+ }
+
+ if (IS_ERR(context->memory_context)) {
+ if (PTR_ERR(context->memory_context) != -EOPNOTSUPP) {
+ err = PTR_ERR(context->memory_context);
+ goto put_channel;
+ } else {
+ /*
+ * OK, HW does not support contexts or contexts
+ * are disabled.
+ */
+ context->memory_context = NULL;
+ }
+ }
+ }
+
err = xa_alloc(&fpriv->contexts, &args->context, context, XA_LIMIT(1, U32_MAX),
GFP_KERNEL);
if (err < 0)
- goto put_channel;
+ goto put_memctx;

context->client = client;
xa_init_flags(&context->mappings, XA_FLAGS_ALLOC1);
@@ -118,6 +150,9 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_

return 0;

+put_memctx:
+ if (context->memory_context)
+ host1x_context_put(context->memory_context);
put_channel:
host1x_channel_put(context->channel);
free:
@@ -156,6 +191,7 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f
struct tegra_drm_mapping *mapping;
struct tegra_drm_context *context;
enum dma_data_direction direction;
+ struct device *mapping_dev;
int err = 0;

if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READ_WRITE)
@@ -177,6 +213,11 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f

kref_init(&mapping->ref);

+ if (context->memory_context)
+ mapping_dev = &context->memory_context->dev;
+ else
+ mapping_dev = context->client->base.dev;
+
mapping->bo = tegra_gem_lookup(file, args->handle);
if (!mapping->bo) {
err = -EINVAL;
@@ -201,7 +242,7 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_f
goto put_gem;
}

- mapping->map = host1x_bo_pin(context->client->base.dev, mapping->bo, direction, NULL);
+ mapping->map = host1x_bo_pin(mapping_dev, mapping->bo, direction, NULL);
if (IS_ERR(mapping->map)) {
err = PTR_ERR(mapping->map);
goto put_gem;
--
2.35.0

2022-03-01 17:40:11

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 6/9] arm64: tegra: Add Host1x context stream IDs on Tegra186+

From: Mikko Perttunen <[email protected]>

Add Host1x context stream IDs on systems that support Host1x context
isolation. Host1x and attached engines can use these stream IDs to
allow isolation between memory used by different processes.

The specified stream IDs must match those configured by the hypervisor,
if one is present.

Signed-off-by: Mikko Perttunen <[email protected]>
---
v2:
* Added context devices on T194.
* Use iommu-map instead of custom property.
v4:
* Remove memory-contexts subnode.
---
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 11 +++++++++++
arch/arm64/boot/dts/nvidia/tegra194.dtsi | 11 +++++++++++
2 files changed, 22 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
index c91afff1b757..1b71cba0df06 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -1406,6 +1406,17 @@ host1x@13e00000 {

iommus = <&smmu TEGRA186_SID_HOST1X>;

+ /* Context isolation domains */
+ iommu-map = <
+ 0 &smmu TEGRA186_SID_HOST1X_CTX0 1
+ 1 &smmu TEGRA186_SID_HOST1X_CTX1 1
+ 2 &smmu TEGRA186_SID_HOST1X_CTX2 1
+ 3 &smmu TEGRA186_SID_HOST1X_CTX3 1
+ 4 &smmu TEGRA186_SID_HOST1X_CTX4 1
+ 5 &smmu TEGRA186_SID_HOST1X_CTX5 1
+ 6 &smmu TEGRA186_SID_HOST1X_CTX6 1
+ 7 &smmu TEGRA186_SID_HOST1X_CTX7 1>;
+
dpaux1: dpaux@15040000 {
compatible = "nvidia,tegra186-dpaux";
reg = <0x15040000 0x10000>;
diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
index 2d48c3715fc6..eb0d2ba89cb1 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
@@ -1686,6 +1686,17 @@ host1x@13e00000 {
interconnect-names = "dma-mem";
iommus = <&smmu TEGRA194_SID_HOST1X>;

+ /* Context isolation domains */
+ iommu-map = <
+ 0 &smmu TEGRA194_SID_HOST1X_CTX0 1
+ 1 &smmu TEGRA194_SID_HOST1X_CTX1 1
+ 2 &smmu TEGRA194_SID_HOST1X_CTX2 1
+ 3 &smmu TEGRA194_SID_HOST1X_CTX3 1
+ 4 &smmu TEGRA194_SID_HOST1X_CTX4 1
+ 5 &smmu TEGRA194_SID_HOST1X_CTX5 1
+ 6 &smmu TEGRA194_SID_HOST1X_CTX6 1
+ 7 &smmu TEGRA194_SID_HOST1X_CTX7 1>;
+
nvdec@15140000 {
compatible = "nvidia,tegra194-nvdec";
reg = <0x15140000 0x00040000>;
--
2.35.0

2022-03-01 18:05:37

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 2/9] gpu: host1x: Add context bus

From: Mikko Perttunen <[email protected]>

The context bus is a "dummy" bus that contains struct devices that
correspond to IOMMU contexts assigned through Host1x to processes.

Even when host1x itself is built as a module, the bus is registered
in built-in code so that the built-in ARM SMMU driver is able to
reference it.

Signed-off-by: Mikko Perttunen <[email protected]>
---
v4:
* Export bus as GPL
---
drivers/gpu/Makefile | 3 +--
drivers/gpu/host1x/Kconfig | 5 +++++
drivers/gpu/host1x/Makefile | 1 +
drivers/gpu/host1x/context_bus.c | 31 ++++++++++++++++++++++++++++++
include/linux/host1x_context_bus.h | 15 +++++++++++++++
5 files changed, 53 insertions(+), 2 deletions(-)
create mode 100644 drivers/gpu/host1x/context_bus.c
create mode 100644 include/linux/host1x_context_bus.h

diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile
index 835c88318cec..8997f0096545 100644
--- a/drivers/gpu/Makefile
+++ b/drivers/gpu/Makefile
@@ -2,7 +2,6 @@
# drm/tegra depends on host1x, so if both drivers are built-in care must be
# taken to initialize them in the correct order. Link order is the only way
# to ensure this currently.
-obj-$(CONFIG_TEGRA_HOST1X) += host1x/
-obj-y += drm/ vga/
+obj-y += host1x/ drm/ vga/
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
diff --git a/drivers/gpu/host1x/Kconfig b/drivers/gpu/host1x/Kconfig
index 6815b4db17c1..1861a8180d3f 100644
--- a/drivers/gpu/host1x/Kconfig
+++ b/drivers/gpu/host1x/Kconfig
@@ -1,8 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+config TEGRA_HOST1X_CONTEXT_BUS
+ bool
+
config TEGRA_HOST1X
tristate "NVIDIA Tegra host1x driver"
depends on ARCH_TEGRA || (ARM && COMPILE_TEST)
select DMA_SHARED_BUFFER
+ select TEGRA_HOST1X_CONTEXT_BUS
select IOMMU_IOVA
help
Driver for the NVIDIA Tegra host1x hardware.
diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
index d2b6f7de0498..c891a3e33844 100644
--- a/drivers/gpu/host1x/Makefile
+++ b/drivers/gpu/host1x/Makefile
@@ -18,3 +18,4 @@ host1x-y = \
hw/host1x07.o

obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
+obj-$(CONFIG_TEGRA_HOST1X_CONTEXT_BUS) += context_bus.o
diff --git a/drivers/gpu/host1x/context_bus.c b/drivers/gpu/host1x/context_bus.c
new file mode 100644
index 000000000000..b0d35b2bbe89
--- /dev/null
+++ b/drivers/gpu/host1x/context_bus.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, NVIDIA Corporation.
+ */
+
+#include <linux/device.h>
+#include <linux/of.h>
+
+struct bus_type host1x_context_device_bus_type = {
+ .name = "host1x-context",
+};
+EXPORT_SYMBOL_GPL(host1x_context_device_bus_type);
+
+static int __init host1x_context_device_bus_init(void)
+{
+ int err;
+
+ if (!of_machine_is_compatible("nvidia,tegra186") &&
+ !of_machine_is_compatible("nvidia,tegra194") &&
+ !of_machine_is_compatible("nvidia,tegra234"))
+ return 0;
+
+ err = bus_register(&host1x_context_device_bus_type);
+ if (err < 0) {
+ pr_err("bus type registration failed: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+postcore_initcall(host1x_context_device_bus_init);
diff --git a/include/linux/host1x_context_bus.h b/include/linux/host1x_context_bus.h
new file mode 100644
index 000000000000..72462737a6db
--- /dev/null
+++ b/include/linux/host1x_context_bus.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2021, NVIDIA Corporation. All rights reserved.
+ */
+
+#ifndef __LINUX_HOST1X_CONTEXT_BUS_H
+#define __LINUX_HOST1X_CONTEXT_BUS_H
+
+#include <linux/device.h>
+
+#ifdef CONFIG_TEGRA_HOST1X_CONTEXT_BUS
+extern struct bus_type host1x_context_device_bus_type;
+#endif
+
+#endif
--
2.35.0

2022-03-01 19:15:04

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 3/9] gpu: host1x: Add context device management code

From: Mikko Perttunen <[email protected]>

Add code to register context devices from device tree, allocate them
out and manage their refcounts.

Signed-off-by: Mikko Perttunen <[email protected]>
---
v2:
* Directly set DMA mask instead of inheriting from Host1x.
* Use iommu-map instead of custom DT property.
v4:
* Use u64 instead of dma_addr_t for DMA mask
* Use unsigned ints for indexes and adjust error handling flow
* Parse iommu-map property at top level host1x DT node
* Use separate DMA mask per device
* Export symbols as GPL
---
drivers/gpu/host1x/Makefile | 1 +
drivers/gpu/host1x/context.c | 160 +++++++++++++++++++++++++++++++++++
drivers/gpu/host1x/context.h | 27 ++++++
drivers/gpu/host1x/dev.c | 12 ++-
drivers/gpu/host1x/dev.h | 2 +
include/linux/host1x.h | 18 ++++
6 files changed, 219 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/host1x/context.c
create mode 100644 drivers/gpu/host1x/context.h

diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
index c891a3e33844..8a65e13d113a 100644
--- a/drivers/gpu/host1x/Makefile
+++ b/drivers/gpu/host1x/Makefile
@@ -10,6 +10,7 @@ host1x-y = \
debug.o \
mipi.o \
fence.o \
+ context.o \
hw/host1x01.o \
hw/host1x02.o \
hw/host1x04.o \
diff --git a/drivers/gpu/host1x/context.c b/drivers/gpu/host1x/context.c
new file mode 100644
index 000000000000..c5889be64634
--- /dev/null
+++ b/drivers/gpu/host1x/context.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, NVIDIA Corporation.
+ */
+
+#include <linux/device.h>
+#include <linux/kref.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pid.h>
+#include <linux/slab.h>
+
+#include "context.h"
+#include "dev.h"
+
+int host1x_context_list_init(struct host1x *host1x)
+{
+ struct host1x_context_list *cdl = &host1x->context_list;
+ struct device_node *node = host1x->dev->of_node;
+ struct host1x_context *ctx;
+ unsigned int i;
+ int err;
+
+ cdl->devs = NULL;
+ cdl->len = 0;
+ mutex_init(&cdl->lock);
+
+ err = of_property_count_u32_elems(node, "iommu-map");
+ if (err < 0)
+ return 0;
+
+ cdl->devs = kcalloc(err, sizeof(*cdl->devs), GFP_KERNEL);
+ if (!cdl->devs)
+ return -ENOMEM;
+ cdl->len = err / 4;
+
+ for (i = 0; i < cdl->len; i++) {
+ struct iommu_fwspec *fwspec;
+
+ ctx = &cdl->devs[i];
+
+ ctx->host = host1x;
+
+ device_initialize(&ctx->dev);
+
+ /*
+ * Due to an issue with T194 NVENC, only 38 bits can be used.
+ * Anyway, 256GiB of IOVA ought to be enough for anyone.
+ */
+ ctx->dma_mask = DMA_BIT_MASK(38);
+ ctx->dev.dma_mask = &ctx->dma_mask;
+ ctx->dev.coherent_dma_mask = ctx->dma_mask;
+ dev_set_name(&ctx->dev, "host1x-ctx.%d", i);
+ ctx->dev.bus = &host1x_context_device_bus_type;
+ ctx->dev.parent = host1x->dev;
+
+ dma_set_max_seg_size(&ctx->dev, UINT_MAX);
+
+ err = device_add(&ctx->dev);
+ if (err) {
+ dev_err(host1x->dev, "could not add context device %d: %d\n", i, err);
+ goto del_devices;
+ }
+
+ err = of_dma_configure_id(&ctx->dev, node, true, &i);
+ if (err) {
+ dev_err(host1x->dev, "IOMMU configuration failed for context device %d: %d\n",
+ i, err);
+ device_del(&ctx->dev);
+ goto del_devices;
+ }
+
+ fwspec = dev_iommu_fwspec_get(&ctx->dev);
+ if (!fwspec) {
+ dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i);
+ device_del(&ctx->dev);
+ goto del_devices;
+ }
+
+ ctx->stream_id = fwspec->ids[0] & 0xffff;
+ }
+
+ return 0;
+
+del_devices:
+ while (i--)
+ device_del(&cdl->devs[i].dev);
+
+ kfree(cdl->devs);
+ cdl->len = 0;
+
+ return err;
+}
+
+void host1x_context_list_free(struct host1x_context_list *cdl)
+{
+ unsigned int i;
+
+ for (i = 0; i < cdl->len; i++)
+ device_del(&cdl->devs[i].dev);
+
+ kfree(cdl->devs);
+ cdl->len = 0;
+}
+
+struct host1x_context *host1x_context_alloc(struct host1x *host1x,
+ struct pid *pid)
+{
+ struct host1x_context_list *cdl = &host1x->context_list;
+ struct host1x_context *free = NULL;
+ int i;
+
+ if (!cdl->len)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ mutex_lock(&cdl->lock);
+
+ for (i = 0; i < cdl->len; i++) {
+ struct host1x_context *cd = &cdl->devs[i];
+
+ if (cd->owner == pid) {
+ refcount_inc(&cd->ref);
+ mutex_unlock(&cdl->lock);
+ return cd;
+ } else if (!cd->owner && !free) {
+ free = cd;
+ }
+ }
+
+ if (!free) {
+ mutex_unlock(&cdl->lock);
+ return ERR_PTR(-EBUSY);
+ }
+
+ refcount_set(&free->ref, 1);
+ free->owner = get_pid(pid);
+
+ mutex_unlock(&cdl->lock);
+
+ return free;
+}
+EXPORT_SYMBOL_GPL(host1x_context_alloc);
+
+void host1x_context_get(struct host1x_context *cd)
+{
+ refcount_inc(&cd->ref);
+}
+EXPORT_SYMBOL_GPL(host1x_context_get);
+
+void host1x_context_put(struct host1x_context *cd)
+{
+ struct host1x_context_list *cdl = &cd->host->context_list;
+
+ if (refcount_dec_and_mutex_lock(&cd->ref, &cdl->lock)) {
+ put_pid(cd->owner);
+ cd->owner = NULL;
+ mutex_unlock(&cdl->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(host1x_context_put);
diff --git a/drivers/gpu/host1x/context.h b/drivers/gpu/host1x/context.h
new file mode 100644
index 000000000000..268ecdf6b1bb
--- /dev/null
+++ b/drivers/gpu/host1x/context.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Host1x context devices
+ *
+ * Copyright (c) 2020, NVIDIA Corporation.
+ */
+
+#ifndef __HOST1X_CONTEXT_H
+#define __HOST1X_CONTEXT_H
+
+#include <linux/mutex.h>
+#include <linux/refcount.h>
+
+struct host1x;
+
+extern struct bus_type host1x_context_device_bus_type;
+
+struct host1x_context_list {
+ struct mutex lock;
+ struct host1x_context *devs;
+ unsigned int len;
+};
+
+int host1x_context_list_init(struct host1x *host1x);
+void host1x_context_list_free(struct host1x_context_list *cdl);
+
+#endif
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 6994f8c0e02e..40f64efd5865 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -28,6 +28,7 @@

#include "bus.h"
#include "channel.h"
+#include "context.h"
#include "debug.h"
#include "dev.h"
#include "intr.h"
@@ -502,10 +503,16 @@ static int host1x_probe(struct platform_device *pdev)
goto iommu_exit;
}

+ err = host1x_context_list_init(host);
+ if (err) {
+ dev_err(&pdev->dev, "failed to initialize context list\n");
+ goto free_channels;
+ }
+
err = host1x_syncpt_init(host);
if (err) {
dev_err(&pdev->dev, "failed to initialize syncpts\n");
- goto free_channels;
+ goto free_contexts;
}

err = host1x_intr_init(host, syncpt_irq);
@@ -549,6 +556,8 @@ static int host1x_probe(struct platform_device *pdev)
host1x_intr_deinit(host);
deinit_syncpt:
host1x_syncpt_deinit(host);
+free_contexts:
+ host1x_context_list_free(&host->context_list);
free_channels:
host1x_channel_list_free(&host->channel_list);
iommu_exit:
@@ -568,6 +577,7 @@ static int host1x_remove(struct platform_device *pdev)

host1x_intr_deinit(host);
host1x_syncpt_deinit(host);
+ host1x_context_list_free(&host->context_list);
host1x_iommu_exit(host);
host1x_bo_cache_destroy(&host->cache);

diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index ca4b082f0cd4..92f4804d8b70 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -14,6 +14,7 @@

#include "cdma.h"
#include "channel.h"
+#include "context.h"
#include "intr.h"
#include "job.h"
#include "syncpt.h"
@@ -141,6 +142,7 @@ struct host1x {
struct mutex syncpt_mutex;

struct host1x_channel_list channel_list;
+ struct host1x_context_list context_list;

struct dentry *debugfs;

diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index e8dc5bc41f79..9dbdc4b0bb82 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -440,4 +440,22 @@ int tegra_mipi_disable(struct tegra_mipi_device *device);
int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);

+/* host1x context devices */
+
+struct host1x_context {
+ struct host1x *host;
+
+ refcount_t ref;
+ struct pid *owner;
+
+ struct device dev;
+ u64 dma_mask;
+ u32 stream_id;
+};
+
+struct host1x_context *host1x_context_alloc(struct host1x *host1x,
+ struct pid *pid);
+void host1x_context_get(struct host1x_context *cd);
+void host1x_context_put(struct host1x_context *cd);
+
#endif
--
2.35.0

2022-03-01 19:56:58

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 7/9] drm/tegra: falcon: Set DMACTX field on DMA transactions

From: Mikko Perttunen <[email protected]>

The DMACTX field determines which context, as specified in the
TRANSCFG register, is used. While during boot it doesn't matter
which is used, later on it matters and this value is reused by
the firmware.

Signed-off-by: Mikko Perttunen <[email protected]>
---
drivers/gpu/drm/tegra/falcon.c | 8 ++++++++
drivers/gpu/drm/tegra/falcon.h | 1 +
2 files changed, 9 insertions(+)

diff --git a/drivers/gpu/drm/tegra/falcon.c b/drivers/gpu/drm/tegra/falcon.c
index 223ab2ceb7e6..8bdb72f08f58 100644
--- a/drivers/gpu/drm/tegra/falcon.c
+++ b/drivers/gpu/drm/tegra/falcon.c
@@ -48,6 +48,14 @@ static int falcon_copy_chunk(struct falcon *falcon,
if (target == FALCON_MEMORY_IMEM)
cmd |= FALCON_DMATRFCMD_IMEM;

+ /*
+ * Use second DMA context (i.e. the one for firmware). Strictly
+ * speaking, at this point both DMA contexts point to the firmware
+ * stream ID, but this register's value will be reused by the firmware
+ * for later DMA transactions, so we need to use the correct value.
+ */
+ cmd |= FALCON_DMATRFCMD_DMACTX(1);
+
falcon_writel(falcon, offset, FALCON_DMATRFMOFFS);
falcon_writel(falcon, base, FALCON_DMATRFFBOFFS);
falcon_writel(falcon, cmd, FALCON_DMATRFCMD);
diff --git a/drivers/gpu/drm/tegra/falcon.h b/drivers/gpu/drm/tegra/falcon.h
index c56ee32d92ee..1955cf11a8a6 100644
--- a/drivers/gpu/drm/tegra/falcon.h
+++ b/drivers/gpu/drm/tegra/falcon.h
@@ -50,6 +50,7 @@
#define FALCON_DMATRFCMD_IDLE (1 << 1)
#define FALCON_DMATRFCMD_IMEM (1 << 4)
#define FALCON_DMATRFCMD_SIZE_256B (6 << 8)
+#define FALCON_DMATRFCMD_DMACTX(v) (((v) & 0x7) << 12)

#define FALCON_DMATRFFBOFFS 0x0000111c

--
2.35.0

2022-03-01 20:44:17

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 4/9] gpu: host1x: Program context stream ID on submission

From: Mikko Perttunen <[email protected]>

Add code to do stream ID switching at the beginning of a job. The
stream ID is switched to the stream ID specified by the context
passed in the job structure.

Before switching the stream ID, an OP_DONE wait is done on the
channel's engine to ensure that there is no residual ongoing
work that might do DMA using the new stream ID.

Signed-off-by: Mikko Perttunen <[email protected]>
---
v4:
* Rename job->context to job->memory_context for clarity
---
drivers/gpu/host1x/hw/channel_hw.c | 52 +++++++++++++++++++++--
drivers/gpu/host1x/hw/host1x06_hardware.h | 10 +++++
drivers/gpu/host1x/hw/host1x07_hardware.h | 10 +++++
include/linux/host1x.h | 4 ++
4 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c
index 6b40e9af1e88..f84caf06621a 100644
--- a/drivers/gpu/host1x/hw/channel_hw.c
+++ b/drivers/gpu/host1x/hw/channel_hw.c
@@ -180,6 +180,45 @@ static void host1x_enable_gather_filter(struct host1x_channel *ch)
#endif
}

+static void host1x_channel_program_engine_streamid(struct host1x_job *job)
+{
+#if HOST1X_HW >= 6
+ u32 fence;
+
+ if (!job->memory_context)
+ return;
+
+ fence = host1x_syncpt_incr_max(job->syncpt, 1);
+
+ /* First, increment a syncpoint on OP_DONE condition.. */
+
+ host1x_cdma_push(&job->channel->cdma,
+ host1x_opcode_nonincr(HOST1X_UCLASS_INCR_SYNCPT, 1),
+ HOST1X_UCLASS_INCR_SYNCPT_INDX_F(job->syncpt->id) |
+ HOST1X_UCLASS_INCR_SYNCPT_COND_F(1));
+
+ /* Wait for syncpoint to increment */
+
+ host1x_cdma_push(&job->channel->cdma,
+ host1x_opcode_setclass(HOST1X_CLASS_HOST1X,
+ host1x_uclass_wait_syncpt_r(), 1),
+ host1x_class_host_wait_syncpt(job->syncpt->id, fence));
+
+ /*
+ * Now that we know the engine is idle, return to class and
+ * change stream ID.
+ */
+
+ host1x_cdma_push(&job->channel->cdma,
+ host1x_opcode_setclass(job->class, 0, 0),
+ HOST1X_OPCODE_NOP);
+
+ host1x_cdma_push(&job->channel->cdma,
+ host1x_opcode_setpayload(job->memory_context->stream_id),
+ host1x_opcode_setstreamid(job->engine_streamid_offset / 4));
+#endif
+}
+
static int channel_submit(struct host1x_job *job)
{
struct host1x_channel *ch = job->channel;
@@ -236,18 +275,23 @@ static int channel_submit(struct host1x_job *job)
if (sp->base)
synchronize_syncpt_base(job);

- syncval = host1x_syncpt_incr_max(sp, user_syncpt_incrs);
-
host1x_hw_syncpt_assign_to_channel(host, sp, ch);

- job->syncpt_end = syncval;
-
/* add a setclass for modules that require it */
if (job->class)
host1x_cdma_push(&ch->cdma,
host1x_opcode_setclass(job->class, 0, 0),
HOST1X_OPCODE_NOP);

+ /*
+ * Ensure engine DMA is idle and set new stream ID. May increment
+ * syncpt max.
+ */
+ host1x_channel_program_engine_streamid(job);
+
+ syncval = host1x_syncpt_incr_max(sp, user_syncpt_incrs);
+ job->syncpt_end = syncval;
+
submit_gathers(job, syncval - user_syncpt_incrs);

/* end CDMA submit & stash pinned hMems into sync queue */
diff --git a/drivers/gpu/host1x/hw/host1x06_hardware.h b/drivers/gpu/host1x/hw/host1x06_hardware.h
index 01a142a09800..5d515745eee7 100644
--- a/drivers/gpu/host1x/hw/host1x06_hardware.h
+++ b/drivers/gpu/host1x/hw/host1x06_hardware.h
@@ -127,6 +127,16 @@ static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
}

+static inline u32 host1x_opcode_setstreamid(unsigned streamid)
+{
+ return (7 << 28) | streamid;
+}
+
+static inline u32 host1x_opcode_setpayload(unsigned payload)
+{
+ return (9 << 28) | payload;
+}
+
static inline u32 host1x_opcode_gather_wide(unsigned count)
{
return (12 << 28) | count;
diff --git a/drivers/gpu/host1x/hw/host1x07_hardware.h b/drivers/gpu/host1x/hw/host1x07_hardware.h
index e6582172ebfd..82c0cc9bb0b5 100644
--- a/drivers/gpu/host1x/hw/host1x07_hardware.h
+++ b/drivers/gpu/host1x/hw/host1x07_hardware.h
@@ -127,6 +127,16 @@ static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
}

+static inline u32 host1x_opcode_setstreamid(unsigned streamid)
+{
+ return (7 << 28) | streamid;
+}
+
+static inline u32 host1x_opcode_setpayload(unsigned payload)
+{
+ return (9 << 28) | payload;
+}
+
static inline u32 host1x_opcode_gather_wide(unsigned count)
{
return (12 << 28) | count;
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 9dbdc4b0bb82..2793131e8c96 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -321,6 +321,10 @@ struct host1x_job {

/* Whether host1x-side firewall should be ran for this job or not */
bool enable_firewall;
+
+ /* Options for configuring engine data stream ID */
+ struct host1x_context *memory_context;
+ u32 engine_streamid_offset;
};

struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
--
2.35.0

2022-03-01 21:09:16

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 1/9] dt-bindings: host1x: Add iommu-map property

From: Mikko Perttunen <[email protected]>

Add schema information for specifying context stream IDs. This uses
the standard iommu-map property.

Signed-off-by: Mikko Perttunen <[email protected]>
---
v3:
* New patch
v4:
* Remove memory-contexts subnode.
---
.../bindings/display/tegra/nvidia,tegra20-host1x.yaml | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
index 4fd513efb0f7..0adeb03b9e3a 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
@@ -144,6 +144,11 @@ allOf:
reset-names:
maxItems: 1

+ iommu-map:
+ description: Specification of stream IDs available for memory context device
+ use. Should be a mapping of IDs 0..n to IOMMU entries corresponding to
+ usable stream IDs.
+
required:
- reg-names

--
2.35.0

2022-03-01 23:25:45

by Mikko Perttunen

[permalink] [raw]
Subject: [PATCH v4 5/9] iommu/arm-smmu: Attach to host1x context device bus

From: Mikko Perttunen <[email protected]>

Set itself as the IOMMU for the host1x context device bus, containing
"dummy" devices used for Host1x context isolation.

Signed-off-by: Mikko Perttunen <[email protected]>
---
drivers/iommu/arm/arm-smmu/arm-smmu.c | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index 4bc75c4ce402..23082675d542 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -39,6 +39,7 @@

#include <linux/amba/bus.h>
#include <linux/fsl/mc.h>
+#include <linux/host1x_context_bus.h>

#include "arm-smmu.h"

@@ -2051,8 +2052,20 @@ static int arm_smmu_bus_init(struct iommu_ops *ops)
goto err_reset_pci_ops;
}
#endif
+#ifdef CONFIG_TEGRA_HOST1X_CONTEXT_BUS
+ if (!iommu_present(&host1x_context_device_bus_type)) {
+ err = bus_set_iommu(&host1x_context_device_bus_type, ops);
+ if (err)
+ goto err_reset_fsl_mc_ops;
+ }
+#endif
+
return 0;

+err_reset_fsl_mc_ops: __maybe_unused;
+#ifdef CONFIG_FSL_MC_BUS
+ bus_set_iommu(&fsl_mc_bus_type, NULL);
+#endif
err_reset_pci_ops: __maybe_unused;
#ifdef CONFIG_PCI
bus_set_iommu(&pci_bus_type, NULL);
--
2.35.0

2022-03-02 00:05:01

by Robin Murphy

[permalink] [raw]
Subject: Re: [PATCH v4 1/9] dt-bindings: host1x: Add iommu-map property

On 2022-03-01 16:14, [email protected] wrote:
> From: Mikko Perttunen <[email protected]>
>
> Add schema information for specifying context stream IDs. This uses
> the standard iommu-map property.
>
> Signed-off-by: Mikko Perttunen <[email protected]>
> ---
> v3:
> * New patch
> v4:
> * Remove memory-contexts subnode.
> ---
> .../bindings/display/tegra/nvidia,tegra20-host1x.yaml | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
> index 4fd513efb0f7..0adeb03b9e3a 100644
> --- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
> +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
> @@ -144,6 +144,11 @@ allOf:
> reset-names:
> maxItems: 1
>
> + iommu-map:
> + description: Specification of stream IDs available for memory context device
> + use. Should be a mapping of IDs 0..n to IOMMU entries corresponding to

Nit: maybe "context IDs 0..n" for maximum possible clarity?

Either way, though, I'm happy that if the simplest and most
straightforward approach works, then it's the best choice.

Reviewed-by: Robin Murphy <[email protected]>

Cheers,
Robin.

> + usable stream IDs.
> +
> required:
> - reg-names
>

2022-03-02 13:51:53

by Mikko Perttunen

[permalink] [raw]
Subject: Re: [PATCH v4 1/9] dt-bindings: host1x: Add iommu-map property

On 3/1/22 20:12, Robin Murphy wrote:
> On 2022-03-01 16:14, [email protected] wrote:
>> From: Mikko Perttunen <[email protected]>
>>
>> Add schema information for specifying context stream IDs. This uses
>> the standard iommu-map property.
>>
>> Signed-off-by: Mikko Perttunen <[email protected]>
>> ---
>> v3:
>> * New patch
>> v4:
>> * Remove memory-contexts subnode.
>> ---
>>   .../bindings/display/tegra/nvidia,tegra20-host1x.yaml        | 5 +++++
>>   1 file changed, 5 insertions(+)
>>
>> diff --git
>> a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
>> b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
>>
>> index 4fd513efb0f7..0adeb03b9e3a 100644
>> ---
>> a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
>>
>> +++
>> b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
>>
>> @@ -144,6 +144,11 @@ allOf:
>>           reset-names:
>>             maxItems: 1
>> +        iommu-map:
>> +          description: Specification of stream IDs available for
>> memory context device
>> +            use. Should be a mapping of IDs 0..n to IOMMU entries
>> corresponding to
>
> Nit: maybe "context IDs 0..n" for maximum possible clarity?

I left it at "IDs" since there is no "context ID" or similar concept in
the hardware, so I thought to leave it just as a kind of an 'abstract ID
as used in iommu-map property'.

>
> Either way, though, I'm happy that if the simplest and most
> straightforward approach works, then it's the best choice.

I am happy as well, this is certainly much cleaner than the mess in the
downstream driver :)

>
> Reviewed-by: Robin Murphy <[email protected]>
>
> Cheers,
> Robin.

Thanks!
Mikko

>
>> +            usable stream IDs.
>> +
>>         required:
>>           - reg-names

2022-03-12 23:05:16

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v4 3/9] gpu: host1x: Add context device management code

01.03.2022 19:14, [email protected] пишет:
> +/* host1x context devices */
> +
> +struct host1x_context {
> + struct host1x *host;
> +
> + refcount_t ref;
> + struct pid *owner;
> +
> + struct device dev;
> + u64 dma_mask;
> + u32 stream_id;
> +};

I'm still not very happy about the "context" names. For example here
it's only about the "memory context", then why not to name struct as
host1x_memory_context or host1x_memctx?

It's not good to use generic names for a special things, it hurts
readability of the code. It's important to choose good names.