From 23d6b361018365b41312f6258f0e12ca9b7b9832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konsta=20H=C3=B6ltt=C3=A4?= Date: Tue, 21 Apr 2020 14:21:06 +0300 Subject: [PATCH] gpu: nvgpu: add dma_fence semaphore support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support exporting and importing semaphore-based synchronization with the stable dma-fence API. The "Android" sync fence API used until now is deprecated. The Android sync framework is still kept as the default. Jira NVGPU-5353 Change-Id: I9e57947adeb4d2ef5d59135ed7d008553c44f97c Signed-off-by: Konsta Hölttä Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2336119 Tested-by: mobile promotions Reviewed-by: mobile promotions --- arch/nvgpu-linux.yaml | 6 + drivers/gpu/nvgpu/Kconfig | 7 + drivers/gpu/nvgpu/Makefile | 7 + drivers/gpu/nvgpu/common/fifo/channel.c | 5 +- drivers/gpu/nvgpu/include/nvgpu/gk20a.h | 3 +- .../nvgpu/include/nvgpu/linux/os_fence_dma.h | 48 ++++ drivers/gpu/nvgpu/os/linux/channel.h | 9 +- drivers/gpu/nvgpu/os/linux/linux-channel.c | 37 ++- drivers/gpu/nvgpu/os/linux/os_fence_dma.c | 85 +++++++ .../gpu/nvgpu/os/linux/os_fence_dma_sema.c | 87 +++++++ .../gpu/nvgpu/os/linux/os_fence_dma_syncpt.c | 70 +++++ .../gpu/nvgpu/os/linux/sync_sema_android.h | 4 +- drivers/gpu/nvgpu/os/linux/sync_sema_dma.c | 240 ++++++++++++++++++ drivers/gpu/nvgpu/os/linux/sync_sema_dma.h | 41 +++ 14 files changed, 641 insertions(+), 8 deletions(-) create mode 100644 drivers/gpu/nvgpu/include/nvgpu/linux/os_fence_dma.h create mode 100644 drivers/gpu/nvgpu/os/linux/os_fence_dma.c create mode 100644 drivers/gpu/nvgpu/os/linux/os_fence_dma_sema.c create mode 100644 drivers/gpu/nvgpu/os/linux/os_fence_dma_syncpt.c create mode 100644 drivers/gpu/nvgpu/os/linux/sync_sema_dma.c create mode 100644 drivers/gpu/nvgpu/os/linux/sync_sema_dma.h diff --git a/arch/nvgpu-linux.yaml b/arch/nvgpu-linux.yaml index e3785e78c..f8201585b 100644 --- a/arch/nvgpu-linux.yaml +++ b/arch/nvgpu-linux.yaml @@ -144,8 +144,13 @@ fence: sources: [ os/linux/os_fence_android.c, os/linux/os_fence_android_sema.c, os/linux/os_fence_android_syncpt.c, + os/linux/os_fence_dma.c, + os/linux/os_fence_dma_sema.c, + os/linux/os_fence_dma_syncpt.c, os/linux/sync_sema_android.c, os/linux/sync_sema_android.h, + os/linux/sync_sema_dma.c, + os/linux/sync_sema_dma.h, include/nvgpu/os_fence.h, include/nvgpu/os_fence_semas.h, include/nvgpu/os_fence_syncpts.h ] @@ -228,6 +233,7 @@ headers: include/nvgpu/linux/bitops.h, include/nvgpu/linux/nvgpu_mem.h, include/nvgpu/linux/os_fence_android.h, + include/nvgpu/linux/os_fence_dma.h, include/nvgpu/linux/rwsem.h, include/nvgpu/linux/sim.h, include/nvgpu/linux/sim_pci.h, diff --git a/drivers/gpu/nvgpu/Kconfig b/drivers/gpu/nvgpu/Kconfig index 2ec404933..a5f9cc609 100644 --- a/drivers/gpu/nvgpu/Kconfig +++ b/drivers/gpu/nvgpu/Kconfig @@ -246,6 +246,13 @@ choice Select which kernel-provided API is used for sync fds. Matching support is required for the userspace drivers too. +config NVGPU_SYNCFD_STABLE + bool "Upstream stabilized SYNC_FILE" + depends on SYNC_FILE + help + Select CONFIG_SYNC_FILE, the cross-driver "Explicit Synchronization + Framework" stabilized upstream from the Android sync since v4.9. + config NVGPU_SYNCFD_ANDROID bool "Android SYNC" depends on SYNC diff --git a/drivers/gpu/nvgpu/Makefile b/drivers/gpu/nvgpu/Makefile index 8a0092ebd..254802d56 100644 --- a/drivers/gpu/nvgpu/Makefile +++ b/drivers/gpu/nvgpu/Makefile @@ -444,9 +444,16 @@ nvgpu-$(CONFIG_NVGPU_SYNCFD_ANDROID) += \ os/linux/os_fence_android.o \ os/linux/os_fence_android_sema.o +nvgpu-$(CONFIG_NVGPU_SYNCFD_STABLE) += \ + os/linux/sync_sema_dma.o \ + os/linux/os_fence_dma.o \ + os/linux/os_fence_dma_sema.o + ifeq ($(CONFIG_TEGRA_GK20A_NVHOST), y) nvgpu-$(CONFIG_NVGPU_SYNCFD_ANDROID) += \ os/linux/os_fence_android_syncpt.o +nvgpu-$(CONFIG_NVGPU_SYNCFD_STABLE) += \ + os/linux/os_fence_dma_syncpt.o nvgpu-y += common/sync/channel_sync_syncpt.o endif diff --git a/drivers/gpu/nvgpu/common/fifo/channel.c b/drivers/gpu/nvgpu/common/fifo/channel.c index b580b1911..18adf3457 100644 --- a/drivers/gpu/nvgpu/common/fifo/channel.c +++ b/drivers/gpu/nvgpu/common/fifo/channel.c @@ -980,8 +980,9 @@ void nvgpu_channel_clean_up_jobs(struct nvgpu_channel *c, if (c->sync != NULL) { if (c->has_os_fence_framework_support && - g->os_channel.os_fence_framework_inst_exists(c)) { - g->os_channel.signal_os_fence_framework(c); + g->os_channel.os_fence_framework_inst_exists(c)) { + g->os_channel.signal_os_fence_framework(c, + job->post_fence); } if (g->aggressive_sync_destroy_thresh != 0U) { diff --git a/drivers/gpu/nvgpu/include/nvgpu/gk20a.h b/drivers/gpu/nvgpu/include/nvgpu/gk20a.h index 786d15eb2..a4ae6d283 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/gk20a.h +++ b/drivers/gpu/nvgpu/include/nvgpu/gk20a.h @@ -904,7 +904,8 @@ struct gk20a { bool (*os_fence_framework_inst_exists)(struct nvgpu_channel *ch); int (*init_os_fence_framework)( struct nvgpu_channel *ch, const char *fmt, ...); - void (*signal_os_fence_framework)(struct nvgpu_channel *ch); + void (*signal_os_fence_framework)(struct nvgpu_channel *ch, + struct nvgpu_fence_type *fence); void (*destroy_os_fence_framework)(struct nvgpu_channel *ch); int (*copy_user_gpfifo)(struct nvgpu_gpfifo_entry *dest, struct nvgpu_gpfifo_userdata userdata, diff --git a/drivers/gpu/nvgpu/include/nvgpu/linux/os_fence_dma.h b/drivers/gpu/nvgpu/include/nvgpu/linux/os_fence_dma.h new file mode 100644 index 000000000..bafa0be7b --- /dev/null +++ b/drivers/gpu/nvgpu/include/nvgpu/linux/os_fence_dma.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef NVGPU_LINUX_OS_FENCE_DMA_H +#define NVGPU_LINUX_OS_FENCE_DMA_H + +struct gk20a; +struct nvgpu_os_fence; +struct dma_fence; +struct nvgpu_channel; + +struct dma_fence *nvgpu_get_dma_fence(struct nvgpu_os_fence *s); + +void nvgpu_os_fence_dma_drop_ref(struct nvgpu_os_fence *s); + +int nvgpu_os_fence_sema_fdget(struct nvgpu_os_fence *fence_out, + struct nvgpu_channel *c, int fd); + +void nvgpu_os_fence_init(struct nvgpu_os_fence *fence_out, + struct gk20a *g, const struct nvgpu_os_fence_ops *fops, + struct dma_fence *fence); + +void nvgpu_os_fence_dma_install_fd(struct nvgpu_os_fence *s, int fd); + +int nvgpu_os_fence_syncpt_fdget(struct nvgpu_os_fence *fence_out, + struct nvgpu_channel *c, int fd); + +#endif /* NVGPU_LINUX_OS_FENCE_DMA_H */ + diff --git a/drivers/gpu/nvgpu/os/linux/channel.h b/drivers/gpu/nvgpu/os/linux/channel.h index 07717ad0a..d7262c3fe 100644 --- a/drivers/gpu/nvgpu/os/linux/channel.h +++ b/drivers/gpu/nvgpu/os/linux/channel.h @@ -56,11 +56,16 @@ struct nvgpu_error_notifier { }; /* - * This struct contains fence_related data. - * e.g. sync_timeline for sync_fences. + * channel-global data for sync fences created from the hardware + * synchronization primitive in each particular channel. */ struct nvgpu_os_fence_framework { +#if defined(CONFIG_NVGPU_SYNCFD_ANDROID) struct sync_timeline *timeline; +#elif defined(CONFIG_NVGPU_SYNCFD_STABLE) + u64 context; + bool exists; +#endif }; struct nvgpu_usermode_bufs_linux { diff --git a/drivers/gpu/nvgpu/os/linux/linux-channel.c b/drivers/gpu/nvgpu/os/linux/linux-channel.c index ee66bee45..1d77681a1 100644 --- a/drivers/gpu/nvgpu/os/linux/linux-channel.c +++ b/drivers/gpu/nvgpu/os/linux/linux-channel.c @@ -21,6 +21,7 @@ #include #include #include +#include /* * This is required for nvgpu_vm_find_buf() which is used in the tracing @@ -43,6 +44,8 @@ #include #include "sync_sema_android.h" +#include "sync_sema_dma.h" +#include u32 nvgpu_submit_gpfifo_user_flags_to_common_flags(u32 user_flags) { @@ -330,21 +333,42 @@ static int nvgpu_channel_init_os_fence_framework(struct nvgpu_channel *ch, (void) vsnprintf(name, sizeof(name), fmt, args); va_end(args); +#if defined(CONFIG_NVGPU_SYNCFD_ANDROID) fence_framework->timeline = gk20a_sync_timeline_create(name); if (!fence_framework->timeline) return -EINVAL; +#elif defined(CONFIG_NVGPU_SYNCFD_STABLE) + fence_framework->context = nvgpu_sync_dma_context_create(); + fence_framework->exists = true; +#endif return 0; } -static void nvgpu_channel_signal_os_fence_framework(struct nvgpu_channel *ch) +static void nvgpu_channel_signal_os_fence_framework(struct nvgpu_channel *ch, + struct nvgpu_fence_type *fence) { struct nvgpu_channel_linux *priv = ch->os_priv; struct nvgpu_os_fence_framework *fence_framework; +#if defined(CONFIG_NVGPU_SYNCFD_STABLE) + struct dma_fence *f; +#endif fence_framework = &priv->fence_framework; +#if defined(CONFIG_NVGPU_SYNCFD_ANDROID) gk20a_sync_timeline_signal(fence_framework->timeline); +#elif defined(CONFIG_NVGPU_SYNCFD_STABLE) + f = nvgpu_get_dma_fence(&fence->os_fence); + /* + * Sometimes the post fence of a job isn't a file. It can be a raw + * semaphore for kernel-internal tracking, or a raw syncpoint for + * internal tracking or for exposing to user. + */ + if (f != NULL) { + nvgpu_sync_dma_signal(f); + } +#endif } static void nvgpu_channel_destroy_os_fence_framework(struct nvgpu_channel *ch) @@ -354,8 +378,13 @@ static void nvgpu_channel_destroy_os_fence_framework(struct nvgpu_channel *ch) fence_framework = &priv->fence_framework; +#if defined(CONFIG_NVGPU_SYNCFD_ANDROID) gk20a_sync_timeline_destroy(fence_framework->timeline); fence_framework->timeline = NULL; +#elif defined(CONFIG_NVGPU_SYNCFD_STABLE) + /* fence_framework->context cannot be freed, see linux/dma-fence.h */ + fence_framework->exists = false; +#endif } static bool nvgpu_channel_fence_framework_exists(struct nvgpu_channel *ch) @@ -365,7 +394,13 @@ static bool nvgpu_channel_fence_framework_exists(struct nvgpu_channel *ch) fence_framework = &priv->fence_framework; +#if defined(CONFIG_NVGPU_SYNCFD_ANDROID) return (fence_framework->timeline != NULL); +#elif defined(CONFIG_NVGPU_SYNCFD_STABLE) + return fence_framework->exists; +#else + return false; +#endif } static int nvgpu_channel_copy_user_gpfifo(struct nvgpu_gpfifo_entry *dest, diff --git a/drivers/gpu/nvgpu/os/linux/os_fence_dma.c b/drivers/gpu/nvgpu/os/linux/os_fence_dma.c new file mode 100644 index 000000000..b24b20b63 --- /dev/null +++ b/drivers/gpu/nvgpu/os/linux/os_fence_dma.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +inline struct dma_fence *nvgpu_get_dma_fence(struct nvgpu_os_fence *s) +{ + struct dma_fence *fence = (struct dma_fence *)s->priv; + return fence; +} + +static void nvgpu_os_fence_clear(struct nvgpu_os_fence *fence_out) +{ + fence_out->priv = NULL; + fence_out->g = NULL; + fence_out->ops = NULL; +} + +void nvgpu_os_fence_init(struct nvgpu_os_fence *fence_out, + struct gk20a *g, const struct nvgpu_os_fence_ops *fops, + struct dma_fence *fence) +{ + fence_out->g = g; + fence_out->ops = fops; + fence_out->priv = (void *)fence; +} + +void nvgpu_os_fence_dma_drop_ref(struct nvgpu_os_fence *s) +{ + struct dma_fence *fence = nvgpu_get_dma_fence(s); + + dma_fence_put(fence); + + nvgpu_os_fence_clear(s); +} + +void nvgpu_os_fence_dma_install_fd(struct nvgpu_os_fence *s, int fd) +{ + struct dma_fence *fence = nvgpu_get_dma_fence(s); + struct sync_file *file = sync_file_create(fence); + + fd_install(fd, file->file); +} + +int nvgpu_os_fence_fdget(struct nvgpu_os_fence *fence_out, + struct nvgpu_channel *c, int fd) +{ + int err = -ENOSYS; + +#ifdef CONFIG_TEGRA_GK20A_NVHOST + if (nvgpu_has_syncpoints(c->g)) { + err = nvgpu_os_fence_syncpt_fdget(fence_out, c, fd); + } +#endif + + if (err) { + err = nvgpu_os_fence_sema_fdget(fence_out, c, fd); + } + + if (err) { + nvgpu_err(c->g, "error obtaining fence from fd %d", fd); + } + + return err; +} diff --git a/drivers/gpu/nvgpu/os/linux/os_fence_dma_sema.c b/drivers/gpu/nvgpu/os/linux/os_fence_dma_sema.c new file mode 100644 index 000000000..12e84a6ae --- /dev/null +++ b/drivers/gpu/nvgpu/os/linux/os_fence_dma_sema.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sync_sema_dma.h" + +static const struct nvgpu_os_fence_ops sema_ops = { + .drop_ref = nvgpu_os_fence_dma_drop_ref, + .install_fence = nvgpu_os_fence_dma_install_fd, +}; + +int nvgpu_os_fence_get_semas(struct nvgpu_os_fence_sema *fence_sema_out, + struct nvgpu_os_fence *fence_in) +{ + if (fence_in->ops != &sema_ops) { + return -EINVAL; + } + + fence_sema_out->fence = fence_in; + + return 0; +} + +u32 nvgpu_os_fence_sema_get_num_semaphores(struct nvgpu_os_fence_sema *fence) +{ + struct dma_fence *f = nvgpu_get_dma_fence(fence->fence); + + return nvgpu_dma_fence_length(f); +} + +void nvgpu_os_fence_sema_extract_nth_semaphore( + struct nvgpu_os_fence_sema *fence, u32 n, + struct nvgpu_semaphore **semaphore_out) +{ + struct dma_fence *f = nvgpu_get_dma_fence(fence->fence); + + *semaphore_out = nvgpu_dma_fence_nth(f, n); +} + +int nvgpu_os_fence_sema_create(struct nvgpu_os_fence *fence_out, + struct nvgpu_channel *c, struct nvgpu_semaphore *sema) +{ + struct dma_fence *fence = nvgpu_sync_dma_create(c, sema); + + if (!fence) { + nvgpu_err(c->g, "error constructing new fence"); + return -ENOMEM; + } + + nvgpu_os_fence_init(fence_out, c->g, &sema_ops, fence); + + return 0; +} + +int nvgpu_os_fence_sema_fdget(struct nvgpu_os_fence *fence_out, + struct nvgpu_channel *c, int fd) +{ + struct dma_fence *fence = nvgpu_sync_dma_fence_fdget(fd); + + if (fence == NULL) + return -EINVAL; + + nvgpu_os_fence_init(fence_out, c->g, &sema_ops, fence); + + return 0; +} diff --git a/drivers/gpu/nvgpu/os/linux/os_fence_dma_syncpt.c b/drivers/gpu/nvgpu/os/linux/os_fence_dma_syncpt.c new file mode 100644 index 000000000..eea248d33 --- /dev/null +++ b/drivers/gpu/nvgpu/os/linux/os_fence_dma_syncpt.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct nvgpu_os_fence_ops syncpt_ops = { + .drop_ref = nvgpu_os_fence_dma_drop_ref, + .install_fence = nvgpu_os_fence_dma_install_fd, +}; + +int nvgpu_os_fence_syncpt_create(struct nvgpu_os_fence *fence_out, + struct nvgpu_channel *c, struct nvgpu_nvhost_dev *nvhost_dev, + u32 id, u32 thresh) +{ + return -ENOSYS; +} + +int nvgpu_os_fence_syncpt_fdget(struct nvgpu_os_fence *fence_out, + struct nvgpu_channel *c, int fd) +{ + return -ENOSYS; +} + +int nvgpu_os_fence_get_syncpts(struct nvgpu_os_fence_syncpt *fence_syncpt_out, + struct nvgpu_os_fence *fence_in) +{ + if (fence_in->ops != &syncpt_ops) { + return -EINVAL; + } + + fence_syncpt_out->fence = fence_in; + + return 0; +} + +u32 nvgpu_os_fence_syncpt_get_num_syncpoints( + struct nvgpu_os_fence_syncpt *fence) +{ + WARN(1, "can't get here until nvhost support exists"); + return -EINVAL; +} + +void nvgpu_os_fence_syncpt_extract_nth_syncpt( + struct nvgpu_os_fence_syncpt *fence, u32 n, + u32 *syncpt_id, u32 *syncpt_threshold) +{ + WARN(1, "can't get here until nvhost support exists"); + *syncpt_id = 0; + *syncpt_threshold = 0; +} diff --git a/drivers/gpu/nvgpu/os/linux/sync_sema_android.h b/drivers/gpu/nvgpu/os/linux/sync_sema_android.h index d5c85bb16..bcc65e466 100644 --- a/drivers/gpu/nvgpu/os/linux/sync_sema_android.h +++ b/drivers/gpu/nvgpu/os/linux/sync_sema_android.h @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -#ifndef _GK20A_SYNC_H_ -#define _GK20A_SYNC_H_ +#ifndef NVGPU_OS_LINUX_SYNC_SEMA_ANDROID_H +#define NVGPU_OS_LINUX_SYNC_SEMA_ANDROID_H struct sync_timeline; struct sync_fence; diff --git a/drivers/gpu/nvgpu/os/linux/sync_sema_dma.c b/drivers/gpu/nvgpu/os/linux/sync_sema_dma.c new file mode 100644 index 000000000..200947c1f --- /dev/null +++ b/drivers/gpu/nvgpu/os/linux/sync_sema_dma.c @@ -0,0 +1,240 @@ +/* + * Semaphore Sync Framework Integration + * + * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "channel.h" + +#include +#include +#include +#include + +struct nvgpu_dma_fence { + struct dma_fence base; + spinlock_t lock; + + /* + * The origin of this sema (a channel) can get closed before this fence + * gets freed. This sema would still hold a reference to the pool where + * it was allocated from. Another channel may safely get the same sema + * location from the pool; the sema will be and stay expired here. + */ + struct nvgpu_semaphore *sema; + struct gk20a *g; + char timeline_name[16]; /* "ch%d-user" */ +}; + +static inline struct nvgpu_dma_fence *to_nvgpu_dma_fence(struct dma_fence *fence); + +static const char *nvgpu_dma_fence_get_driver_name(struct dma_fence *fence) +{ + struct nvgpu_dma_fence *nvfence = to_nvgpu_dma_fence(fence); + + return nvfence->g->name; +} + +static const char *nvgpu_dma_fence_get_timeline_name(struct dma_fence *fence) +{ + struct nvgpu_dma_fence *nvfence = to_nvgpu_dma_fence(fence); + + /* + * This shouldn't end with a digit because the caller appends the + * context number which would then be confusing. + */ + return nvfence->timeline_name; +} + +static bool nvgpu_dma_fence_enable_signaling(struct dma_fence *fence) +{ + struct nvgpu_dma_fence *f = to_nvgpu_dma_fence(fence); + + if (nvgpu_semaphore_is_released(f->sema)) + return false; + + /* signaling of all semas is always enabled */ + return true; +} + +static bool nvgpu_dma_fence_signaled(struct dma_fence *fence) +{ + struct nvgpu_dma_fence *f = to_nvgpu_dma_fence(fence); + + return nvgpu_semaphore_is_released(f->sema); +} + +static void nvgpu_dma_fence_release(struct dma_fence *fence) +{ + struct nvgpu_dma_fence *f = to_nvgpu_dma_fence(fence); + struct gk20a *g = f->g; + + nvgpu_semaphore_put(f->sema); + + nvgpu_kfree(g, f); +} + +static const struct dma_fence_ops nvgpu_dma_fence_ops = { + .get_driver_name = nvgpu_dma_fence_get_driver_name, + .get_timeline_name = nvgpu_dma_fence_get_timeline_name, + .enable_signaling = nvgpu_dma_fence_enable_signaling, + .signaled = nvgpu_dma_fence_signaled, + .wait = dma_fence_default_wait, + .release = nvgpu_dma_fence_release, +}; + +static inline struct nvgpu_dma_fence *to_nvgpu_dma_fence(struct dma_fence *fence) +{ + if (fence->ops != &nvgpu_dma_fence_ops) + return NULL; + + return container_of(fence, struct nvgpu_dma_fence, base); +} + +/* Public API */ + +u64 nvgpu_sync_dma_context_create(void) +{ + /* syncs in each context can be compared against each other */ + return dma_fence_context_alloc(1); +} + +static bool is_nvgpu_dma_fence_array(struct dma_fence *fence) +{ + struct dma_fence_array *farray = to_dma_fence_array(fence); + unsigned i; + + if (farray == NULL) { + return false; + } + + for (i = 0; i < farray->num_fences; i++) { + if (to_nvgpu_dma_fence(farray->fences[i]) == NULL) { + return false; + } + } + + return true; +} + +u32 nvgpu_dma_fence_length(struct dma_fence *fence) +{ + if (to_nvgpu_dma_fence(fence) != NULL) { + return 1; + } + + if (is_nvgpu_dma_fence_array(fence)) { + struct dma_fence_array *farray = to_dma_fence_array(fence); + + return farray->num_fences; + } + + /* + * this shall be called only after a is_nvgpu_dma_fence_or_array check + */ + WARN_ON(1); + + return 0; +} + +static bool is_nvgpu_dma_fence_or_array(struct dma_fence *fence) +{ + return to_nvgpu_dma_fence(fence) != NULL || + is_nvgpu_dma_fence_array(fence); +} + +static struct nvgpu_semaphore *nvgpu_dma_fence_sema(struct dma_fence *fence) +{ + struct nvgpu_dma_fence *f = to_nvgpu_dma_fence(fence); + + if (f != NULL) { + nvgpu_semaphore_get(f->sema); + return f->sema; + } + + return NULL; +} + +struct nvgpu_semaphore *nvgpu_dma_fence_nth(struct dma_fence *fence, u32 i) +{ + struct nvgpu_semaphore *s = nvgpu_dma_fence_sema(fence); + struct dma_fence_array *farray; + + if (s != NULL && i == 0) { + return s; + } + + farray = to_dma_fence_array(fence); + nvgpu_assert(i < farray->num_fences); + return nvgpu_dma_fence_sema(farray->fences[i]); +} + +void nvgpu_sync_dma_signal(struct dma_fence *fence) +{ + if (WARN_ON(to_nvgpu_dma_fence(fence) == NULL)) { + return; + } + + dma_fence_signal(fence); +} + +struct dma_fence *nvgpu_sync_dma_fence_fdget(int fd) +{ + struct dma_fence *fence = sync_file_get_fence(fd); + + if (fence == NULL) + return NULL; + + if (is_nvgpu_dma_fence_or_array(fence)) { + return fence; + } else { + dma_fence_put(fence); + return NULL; + } +} + +struct dma_fence *nvgpu_sync_dma_create(struct nvgpu_channel *c, + struct nvgpu_semaphore *sema) +{ + struct nvgpu_channel_linux *os_channel_priv = c->os_priv; + struct nvgpu_os_fence_framework *fence_framework; + struct nvgpu_dma_fence *f; + struct gk20a *g = c->g; + u64 context; + + f = nvgpu_kzalloc(g, sizeof(*f)); + if (f == NULL) { + return NULL; + } + + f->g = g; + f->sema = sema; + snprintf(f->timeline_name, sizeof(f->timeline_name), + "ch%d-user", c->chid); + spin_lock_init(&f->lock); + + fence_framework = &os_channel_priv->fence_framework; + context = fence_framework->context; + + /* our sema values are u32. The dma fence seqnos are unsigned int. */ + dma_fence_init(&f->base, &nvgpu_dma_fence_ops, &f->lock, context, + (unsigned)nvgpu_semaphore_get_value(sema)); + nvgpu_semaphore_get(sema); + + return &f->base; +} diff --git a/drivers/gpu/nvgpu/os/linux/sync_sema_dma.h b/drivers/gpu/nvgpu/os/linux/sync_sema_dma.h new file mode 100644 index 000000000..23409e6ab --- /dev/null +++ b/drivers/gpu/nvgpu/os/linux/sync_sema_dma.h @@ -0,0 +1,41 @@ +/* + * Semaphore Sync Framework Integration + * + * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NVGPU_OS_LINUX_SYNC_SEMA_DMA_H +#define NVGPU_OS_LINUX_SYNC_SEMA_DMA_H + +#ifdef CONFIG_NVGPU_SYNCFD_STABLE +#include + +struct nvgpu_channel; +struct nvgpu_semaphore; +struct dma_fence; + +u64 nvgpu_sync_dma_context_create(void); + +struct dma_fence *nvgpu_sync_dma_create(struct nvgpu_channel *c, + struct nvgpu_semaphore *sema); + +void nvgpu_sync_dma_signal(struct dma_fence *fence); +u32 nvgpu_dma_fence_length(struct dma_fence *fence); +struct nvgpu_semaphore *nvgpu_dma_fence_nth(struct dma_fence *fence, u32 i); + +struct dma_fence *nvgpu_sync_dma_fence_fdget(int fd); +#endif /* CONFIG_NVGPU_SYNCFD_STABLE */ + +#endif