mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
Add memory_group_manager introduced by Bifrost DDK r20p0-01rel0
drivers/gpu/arm/bifrost on DDK r20 depends on this module. Change-Id: I175fbd2429aa1b8a658c431ce71503c67976db3d Signed-off-by: Zhen Chen <chenzhen@rock-chips.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
#
|
||||
# This program is free software and is provided to you under the terms of the
|
||||
# GNU General Public License version 2 as published by the Free Software
|
||||
# Foundation, and any use by you of this program is subject to the terms
|
||||
# of such GNU licence.
|
||||
#
|
||||
# This program is distributed in the hope that 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, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
* Arm memory group manager for Mali GPU device drivers
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be "arm,physical-memory-group-manager"
|
||||
|
||||
An example node:
|
||||
|
||||
gpu_physical_memory_group_manager: physical-memory-group-manager {
|
||||
compatible = "arm,physical-memory-group-manager";
|
||||
};
|
||||
|
||||
It must be referenced by the GPU as well, see physical-memory-group-manager:
|
||||
|
||||
gpu: gpu@0x6e000000 {
|
||||
compatible = "arm,mali-midgard";
|
||||
reg = <0x0 0x6e000000 0x0 0x200000>;
|
||||
interrupts = <0 168 4>, <0 168 4>, <0 168 4>;
|
||||
interrupt-names = "JOB", "MMU", "GPU";
|
||||
clocks = <&scpi_dvfs 2>;
|
||||
clock-names = "clk_mali";
|
||||
system-coherency = <31>;
|
||||
physical-memory-group-manager = <&gpu_physical_memory_group_manager>;
|
||||
operating-points = <
|
||||
/* KHz uV */
|
||||
50000 820000
|
||||
>;
|
||||
};
|
||||
@@ -171,6 +171,8 @@ config SOC_BUS
|
||||
|
||||
source "drivers/base/regmap/Kconfig"
|
||||
|
||||
source "drivers/base/memory_group_manager/Kconfig"
|
||||
|
||||
config DMA_SHARED_BUFFER
|
||||
bool
|
||||
default n
|
||||
|
||||
@@ -18,6 +18,7 @@ obj-$(CONFIG_MODULES) += module.o
|
||||
endif
|
||||
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
|
||||
obj-$(CONFIG_REGMAP) += regmap/
|
||||
obj-$(CONFIG_MALI_MEMORY_GROUP_MANAGER) += memory_group_manager/
|
||||
obj-$(CONFIG_SOC_BUS) += soc.o
|
||||
obj-$(CONFIG_PINCTRL) += pinctrl.o
|
||||
obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o
|
||||
|
||||
22
drivers/base/memory_group_manager/Kbuild
Normal file
22
drivers/base/memory_group_manager/Kbuild
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
#
|
||||
# This program is free software and is provided to you under the terms of the
|
||||
# GNU General Public License version 2 as published by the Free Software
|
||||
# Foundation, and any use by you of this program is subject to the terms
|
||||
# of such GNU licence.
|
||||
#
|
||||
# This program is distributed in the hope that 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, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MALI_MEMORY_GROUP_MANAGER) := memory_group_manager.o
|
||||
28
drivers/base/memory_group_manager/Kconfig
Normal file
28
drivers/base/memory_group_manager/Kconfig
Normal file
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
#
|
||||
# This program is free software and is provided to you under the terms of the
|
||||
# GNU General Public License version 2 as published by the Free Software
|
||||
# Foundation, and any use by you of this program is subject to the terms
|
||||
# of such GNU licence.
|
||||
#
|
||||
# This program is distributed in the hope that 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, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
config MALI_MEMORY_GROUP_MANAGER
|
||||
tristate "MALI_MEMORY_GROUP_MANAGER"
|
||||
help
|
||||
This option enables an example implementation of a memory group manager
|
||||
for allocation and release of pages for memory pools managed by Mali GPU
|
||||
device drivers.
|
||||
35
drivers/base/memory_group_manager/Makefile
Normal file
35
drivers/base/memory_group_manager/Makefile
Normal file
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
#
|
||||
# This program is free software and is provided to you under the terms of the
|
||||
# GNU General Public License version 2 as published by the Free Software
|
||||
# Foundation, and any use by you of this program is subject to the terms
|
||||
# of such GNU licence.
|
||||
#
|
||||
# This program is distributed in the hope that 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, you can access it online at
|
||||
# http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
#
|
||||
|
||||
# linux build system bootstrap for out-of-tree module
|
||||
|
||||
# default to building for the host
|
||||
ARCH ?= $(shell uname -m)
|
||||
|
||||
ifeq ($(KDIR),)
|
||||
$(error Must specify KDIR to point to the kernel to target))
|
||||
endif
|
||||
|
||||
all:
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../include" modules CONFIG_MALI_MEMORY_GROUP_MANAGER=m
|
||||
|
||||
clean:
|
||||
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean
|
||||
22
drivers/base/memory_group_manager/build.bp
Normal file
22
drivers/base/memory_group_manager/build.bp
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
*
|
||||
* This program is free software and is provided to you under the terms of the
|
||||
* GNU General Public License version 2 as published by the Free Software
|
||||
* Foundation, and any use by you of this program is subject to the terms
|
||||
* of such GNU licence.
|
||||
*
|
||||
* A copy of the licence is included with the program, and can also be obtained
|
||||
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
bob_kernel_module {
|
||||
name: "memory_group_manager",
|
||||
srcs: [
|
||||
"Kbuild",
|
||||
"memory_group_manager.c",
|
||||
],
|
||||
kbuild_options: ["CONFIG_MALI_MEMORY_GROUP_MANAGER=m"],
|
||||
defaults: ["kernel_defaults"],
|
||||
}
|
||||
499
drivers/base/memory_group_manager/memory_group_manager.c
Normal file
499
drivers/base/memory_group_manager/memory_group_manager.c
Normal file
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
*
|
||||
* This program is free software and is provided to you under the terms of the
|
||||
* GNU General Public License version 2 as published by the Free Software
|
||||
* Foundation, and any use by you of this program is subject to the terms
|
||||
* of such GNU licence.
|
||||
*
|
||||
* This program is distributed in the hope that 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, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#include <linux/debugfs.h>
|
||||
#endif
|
||||
#include <linux/mm.h>
|
||||
#include <linux/memory_group_manager.h>
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
|
||||
#undef DEFINE_SIMPLE_ATTRIBUTE
|
||||
#define DEFINE_SIMPLE_ATTRIBUTE DEFINE_DEBUGFS_ATTRIBUTE
|
||||
#define debugfs_create_file debugfs_create_file_unsafe
|
||||
#endif
|
||||
|
||||
#if (KERNEL_VERSION(4, 20, 0) > LINUX_VERSION_CODE)
|
||||
static inline vm_fault_t vmf_insert_pfn_prot(struct vm_area_struct *vma,
|
||||
unsigned long addr, unsigned long pfn, pgprot_t pgprot)
|
||||
{
|
||||
int err;
|
||||
|
||||
#if ((KERNEL_VERSION(4, 4, 147) >= LINUX_VERSION_CODE) || \
|
||||
((KERNEL_VERSION(4, 6, 0) > LINUX_VERSION_CODE) && \
|
||||
(KERNEL_VERSION(4, 5, 0) <= LINUX_VERSION_CODE)))
|
||||
if (pgprot_val(pgprot) != pgprot_val(vma->vm_page_prot))
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
err = vm_insert_pfn(vma, addr, pfn);
|
||||
#else
|
||||
err = vm_insert_pfn_prot(vma, addr, pfn, pgprot);
|
||||
#endif
|
||||
|
||||
if (unlikely(err == -ENOMEM))
|
||||
return VM_FAULT_OOM;
|
||||
if (unlikely(err < 0 && err != -EBUSY))
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
return VM_FAULT_NOPAGE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define IMPORTED_MEMORY_ID (MEMORY_GROUP_MANAGER_NR_GROUPS - 1)
|
||||
|
||||
/**
|
||||
* struct mgm_group - Structure to keep track of the number of allocated
|
||||
* pages per group
|
||||
*
|
||||
* @size: The number of allocated small(4KB) pages
|
||||
* @lp_size: The number of allocated large(2MB) pages
|
||||
* @insert_pfn: The number of calls to map pages for CPU access.
|
||||
* @update_gpu_pte: The number of calls to update GPU page table entries.
|
||||
*
|
||||
* This structure allows page allocation information to be displayed via
|
||||
* debugfs. Display is organized per group with small and large sized pages.
|
||||
*/
|
||||
struct mgm_group {
|
||||
size_t size;
|
||||
size_t lp_size;
|
||||
size_t insert_pfn;
|
||||
size_t update_gpu_pte;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mgm_groups - Structure for groups of memory group manager
|
||||
*
|
||||
* @groups: To keep track of the number of allocated pages of all groups
|
||||
* @dev: device attached
|
||||
* @mgm_debugfs_root: debugfs root directory of memory group manager
|
||||
*
|
||||
* This structure allows page allocation information to be displayed via
|
||||
* debugfs. Display is organized per group with small and large sized pages.
|
||||
*/
|
||||
struct mgm_groups {
|
||||
struct mgm_group groups[MEMORY_GROUP_MANAGER_NR_GROUPS];
|
||||
struct device *dev;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *mgm_debugfs_root;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static int mgm_size_get(void *data, u64 *val)
|
||||
{
|
||||
struct mgm_group *group = data;
|
||||
|
||||
*val = group->size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mgm_lp_size_get(void *data, u64 *val)
|
||||
{
|
||||
struct mgm_group *group = data;
|
||||
|
||||
*val = group->lp_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mgm_insert_pfn_get(void *data, u64 *val)
|
||||
{
|
||||
struct mgm_group *group = data;
|
||||
|
||||
*val = group->insert_pfn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mgm_update_gpu_pte_get(void *data, u64 *val)
|
||||
{
|
||||
struct mgm_group *group = data;
|
||||
|
||||
*val = group->update_gpu_pte;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_mgm_size, mgm_size_get, NULL, "%llu\n");
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_mgm_lp_size, mgm_lp_size_get, NULL, "%llu\n");
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_mgm_insert_pfn, mgm_insert_pfn_get, NULL,
|
||||
"%llu\n");
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_mgm_update_gpu_pte, mgm_update_gpu_pte_get, NULL,
|
||||
"%llu\n");
|
||||
|
||||
static void mgm_term_debugfs(struct mgm_groups *data)
|
||||
{
|
||||
debugfs_remove_recursive(data->mgm_debugfs_root);
|
||||
}
|
||||
|
||||
#define MGM_DEBUGFS_GROUP_NAME_MAX 10
|
||||
static int mgm_initialize_debugfs(struct mgm_groups *mgm_data)
|
||||
{
|
||||
int i;
|
||||
struct dentry *e, *g;
|
||||
char debugfs_group_name[MGM_DEBUGFS_GROUP_NAME_MAX];
|
||||
|
||||
/*
|
||||
* Create root directory of memory-group-manager
|
||||
*/
|
||||
mgm_data->mgm_debugfs_root =
|
||||
debugfs_create_dir("physical-memory-group-manager", NULL);
|
||||
if (IS_ERR(mgm_data->mgm_debugfs_root)) {
|
||||
dev_err(mgm_data->dev, "fail to create debugfs root directory\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create debugfs files per group
|
||||
*/
|
||||
for (i = 0; i < MEMORY_GROUP_MANAGER_NR_GROUPS; i++) {
|
||||
scnprintf(debugfs_group_name, MGM_DEBUGFS_GROUP_NAME_MAX,
|
||||
"group_%d", i);
|
||||
g = debugfs_create_dir(debugfs_group_name,
|
||||
mgm_data->mgm_debugfs_root);
|
||||
if (IS_ERR(g)) {
|
||||
dev_err(mgm_data->dev, "fail to create group[%d]\n", i);
|
||||
goto remove_debugfs;
|
||||
}
|
||||
|
||||
e = debugfs_create_file("size", 0444, g, &mgm_data->groups[i],
|
||||
&fops_mgm_size);
|
||||
if (IS_ERR(e)) {
|
||||
dev_err(mgm_data->dev, "fail to create size[%d]\n", i);
|
||||
goto remove_debugfs;
|
||||
}
|
||||
|
||||
e = debugfs_create_file("lp_size", 0444, g,
|
||||
&mgm_data->groups[i], &fops_mgm_lp_size);
|
||||
if (IS_ERR(e)) {
|
||||
dev_err(mgm_data->dev,
|
||||
"fail to create lp_size[%d]\n", i);
|
||||
goto remove_debugfs;
|
||||
}
|
||||
|
||||
e = debugfs_create_file("insert_pfn", 0444, g,
|
||||
&mgm_data->groups[i], &fops_mgm_insert_pfn);
|
||||
if (IS_ERR(e)) {
|
||||
dev_err(mgm_data->dev,
|
||||
"fail to create insert_pfn[%d]\n", i);
|
||||
goto remove_debugfs;
|
||||
}
|
||||
|
||||
e = debugfs_create_file("update_gpu_pte", 0444, g,
|
||||
&mgm_data->groups[i], &fops_mgm_update_gpu_pte);
|
||||
if (IS_ERR(e)) {
|
||||
dev_err(mgm_data->dev,
|
||||
"fail to create update_gpu_pte[%d]\n", i);
|
||||
goto remove_debugfs;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
remove_debugfs:
|
||||
mgm_term_debugfs(mgm_data);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void mgm_term_debugfs(struct mgm_groups *data)
|
||||
{
|
||||
}
|
||||
|
||||
static int mgm_initialize_debugfs(struct mgm_groups *mgm_data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
#define ORDER_SMALL_PAGE 0
|
||||
#define ORDER_LARGE_PAGE 9
|
||||
static void update_size(struct memory_group_manager_device *mgm_dev, int
|
||||
group_id, int order, bool alloc)
|
||||
{
|
||||
struct mgm_groups *data = mgm_dev->data;
|
||||
|
||||
switch (order) {
|
||||
case ORDER_SMALL_PAGE:
|
||||
if (alloc)
|
||||
data->groups[group_id].size++;
|
||||
else {
|
||||
WARN_ON(data->groups[group_id].size == 0);
|
||||
data->groups[group_id].size--;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_LARGE_PAGE:
|
||||
if (alloc)
|
||||
data->groups[group_id].lp_size++;
|
||||
else {
|
||||
WARN_ON(data->groups[group_id].lp_size == 0);
|
||||
data->groups[group_id].lp_size--;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(data->dev, "Unknown order(%d)\n", order);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct page *example_mgm_alloc_page(
|
||||
struct memory_group_manager_device *mgm_dev, int group_id,
|
||||
gfp_t gfp_mask, unsigned int order)
|
||||
{
|
||||
struct mgm_groups *const data = mgm_dev->data;
|
||||
struct page *p;
|
||||
|
||||
dev_dbg(data->dev, "%s(mgm_dev=%p, group_id=%d gfp_mask=0x%x order=%u\n",
|
||||
__func__, (void *)mgm_dev, group_id, gfp_mask, order);
|
||||
|
||||
if (WARN_ON(group_id < 0) ||
|
||||
WARN_ON(group_id >= MEMORY_GROUP_MANAGER_NR_GROUPS))
|
||||
return NULL;
|
||||
|
||||
p = alloc_pages(gfp_mask, order);
|
||||
|
||||
if (p) {
|
||||
update_size(mgm_dev, group_id, order, true);
|
||||
} else {
|
||||
struct mgm_groups *data = mgm_dev->data;
|
||||
|
||||
dev_err(data->dev, "alloc_pages failed\n");
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void example_mgm_free_page(
|
||||
struct memory_group_manager_device *mgm_dev, int group_id,
|
||||
struct page *page, unsigned int order)
|
||||
{
|
||||
struct mgm_groups *const data = mgm_dev->data;
|
||||
|
||||
dev_dbg(data->dev, "%s(mgm_dev=%p, group_id=%d page=%p order=%u\n",
|
||||
__func__, (void *)mgm_dev, group_id, (void *)page, order);
|
||||
|
||||
if (WARN_ON(group_id < 0) ||
|
||||
WARN_ON(group_id >= MEMORY_GROUP_MANAGER_NR_GROUPS))
|
||||
return;
|
||||
|
||||
__free_pages(page, order);
|
||||
|
||||
update_size(mgm_dev, group_id, order, false);
|
||||
}
|
||||
|
||||
static int example_mgm_get_import_memory_id(
|
||||
struct memory_group_manager_device *mgm_dev,
|
||||
struct memory_group_manager_import_data *import_data)
|
||||
{
|
||||
struct mgm_groups *const data = mgm_dev->data;
|
||||
|
||||
dev_dbg(data->dev, "%s(mgm_dev=%p, import_data=%p (type=%d)\n",
|
||||
__func__, (void *)mgm_dev, (void *)import_data,
|
||||
(int)import_data->type);
|
||||
|
||||
if (!WARN_ON(!import_data)) {
|
||||
WARN_ON(!import_data->u.dma_buf);
|
||||
|
||||
WARN_ON(import_data->type !=
|
||||
MEMORY_GROUP_MANAGER_IMPORT_TYPE_DMA_BUF);
|
||||
}
|
||||
|
||||
return IMPORTED_MEMORY_ID;
|
||||
}
|
||||
|
||||
static u64 example_mgm_update_gpu_pte(
|
||||
struct memory_group_manager_device *const mgm_dev, int const group_id,
|
||||
int const mmu_level, u64 pte)
|
||||
{
|
||||
struct mgm_groups *const data = mgm_dev->data;
|
||||
const u32 pbha_bit_pos = 59; /* bits 62:59 */
|
||||
const u32 pbha_bit_mask = 0xf; /* 4-bit */
|
||||
|
||||
dev_dbg(data->dev,
|
||||
"%s(mgm_dev=%p, group_id=%d, mmu_level=%d, pte=0x%llx)\n",
|
||||
__func__, (void *)mgm_dev, group_id, mmu_level, pte);
|
||||
|
||||
if (WARN_ON(group_id < 0) ||
|
||||
WARN_ON(group_id >= MEMORY_GROUP_MANAGER_NR_GROUPS))
|
||||
return pte;
|
||||
|
||||
pte |= ((u64)group_id & pbha_bit_mask) << pbha_bit_pos;
|
||||
|
||||
data->groups[group_id].update_gpu_pte++;
|
||||
|
||||
return pte;
|
||||
}
|
||||
|
||||
static vm_fault_t example_mgm_vmf_insert_pfn_prot(
|
||||
struct memory_group_manager_device *const mgm_dev, int const group_id,
|
||||
struct vm_area_struct *const vma, unsigned long const addr,
|
||||
unsigned long const pfn, pgprot_t const prot)
|
||||
{
|
||||
struct mgm_groups *const data = mgm_dev->data;
|
||||
vm_fault_t fault;
|
||||
|
||||
dev_dbg(data->dev,
|
||||
"%s(mgm_dev=%p, group_id=%d, vma=%p, addr=0x%lx, pfn=0x%lx, prot=0x%llx)\n",
|
||||
__func__, (void *)mgm_dev, group_id, (void *)vma, addr, pfn,
|
||||
(unsigned long long int) pgprot_val(prot));
|
||||
|
||||
if (WARN_ON(group_id < 0) ||
|
||||
WARN_ON(group_id >= MEMORY_GROUP_MANAGER_NR_GROUPS))
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
fault = vmf_insert_pfn_prot(vma, addr, pfn, prot);
|
||||
|
||||
if (fault == VM_FAULT_NOPAGE)
|
||||
data->groups[group_id].insert_pfn++;
|
||||
else
|
||||
dev_err(data->dev, "vmf_insert_pfn_prot failed\n");
|
||||
|
||||
return fault;
|
||||
}
|
||||
|
||||
static int mgm_initialize_data(struct mgm_groups *mgm_data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MEMORY_GROUP_MANAGER_NR_GROUPS; i++) {
|
||||
mgm_data->groups[i].size = 0;
|
||||
mgm_data->groups[i].lp_size = 0;
|
||||
mgm_data->groups[i].insert_pfn = 0;
|
||||
mgm_data->groups[i].update_gpu_pte = 0;
|
||||
}
|
||||
|
||||
return mgm_initialize_debugfs(mgm_data);
|
||||
}
|
||||
|
||||
static void mgm_term_data(struct mgm_groups *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MEMORY_GROUP_MANAGER_NR_GROUPS; i++) {
|
||||
if (data->groups[i].size != 0)
|
||||
dev_warn(data->dev,
|
||||
"%zu 0-order pages in group(%d) leaked\n",
|
||||
data->groups[i].size, i);
|
||||
if (data->groups[i].lp_size != 0)
|
||||
dev_warn(data->dev,
|
||||
"%zu 9 order pages in group(%d) leaked\n",
|
||||
data->groups[i].lp_size, i);
|
||||
}
|
||||
|
||||
mgm_term_debugfs(data);
|
||||
}
|
||||
|
||||
static int memory_group_manager_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct memory_group_manager_device *mgm_dev;
|
||||
struct mgm_groups *mgm_data;
|
||||
|
||||
mgm_dev = kzalloc(sizeof(*mgm_dev), GFP_KERNEL);
|
||||
if (!mgm_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mgm_dev->owner = THIS_MODULE;
|
||||
mgm_dev->ops.mgm_alloc_page = example_mgm_alloc_page;
|
||||
mgm_dev->ops.mgm_free_page = example_mgm_free_page;
|
||||
mgm_dev->ops.mgm_get_import_memory_id =
|
||||
example_mgm_get_import_memory_id;
|
||||
mgm_dev->ops.mgm_vmf_insert_pfn_prot = example_mgm_vmf_insert_pfn_prot;
|
||||
mgm_dev->ops.mgm_update_gpu_pte = example_mgm_update_gpu_pte;
|
||||
|
||||
mgm_data = kzalloc(sizeof(*mgm_data), GFP_KERNEL);
|
||||
if (!mgm_data) {
|
||||
kfree(mgm_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mgm_dev->data = mgm_data;
|
||||
mgm_data->dev = &pdev->dev;
|
||||
|
||||
if (mgm_initialize_data(mgm_data)) {
|
||||
kfree(mgm_data);
|
||||
kfree(mgm_dev);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, mgm_dev);
|
||||
dev_info(&pdev->dev, "Memory group manager probed successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int memory_group_manager_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct memory_group_manager_device *mgm_dev =
|
||||
platform_get_drvdata(pdev);
|
||||
struct mgm_groups *mgm_data = mgm_dev->data;
|
||||
|
||||
mgm_term_data(mgm_data);
|
||||
kfree(mgm_data);
|
||||
|
||||
kfree(mgm_dev);
|
||||
|
||||
dev_info(&pdev->dev, "Memory group manager removed successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id memory_group_manager_dt_ids[] = {
|
||||
{ .compatible = "arm,physical-memory-group-manager" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, memory_group_manager_dt_ids);
|
||||
|
||||
static struct platform_driver memory_group_manager_driver = {
|
||||
.probe = memory_group_manager_probe,
|
||||
.remove = memory_group_manager_remove,
|
||||
.driver = {
|
||||
.name = "physical-memory-group-manager",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(memory_group_manager_dt_ids),
|
||||
/*
|
||||
* Prevent the mgm_dev from being unbound and freed, as other's
|
||||
* may have pointers to it and would get confused, or crash, if
|
||||
* it suddenly disappear.
|
||||
*/
|
||||
.suppress_bind_attrs = true,
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(memory_group_manager_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("ARM Ltd.");
|
||||
MODULE_VERSION("1.0");
|
||||
198
include/linux/memory_group_manager.h
Normal file
198
include/linux/memory_group_manager.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
*
|
||||
* (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
|
||||
*
|
||||
* This program is free software and is provided to you under the terms of the
|
||||
* GNU General Public License version 2 as published by the Free Software
|
||||
* Foundation, and any use by you of this program is subject to the terms
|
||||
* of such GNU licence.
|
||||
*
|
||||
* This program is distributed in the hope that 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, you can access it online at
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MEMORY_GROUP_MANAGER_H_
|
||||
#define _MEMORY_GROUP_MANAGER_H_
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#if (KERNEL_VERSION(4, 17, 0) > LINUX_VERSION_CODE)
|
||||
typedef int vm_fault_t;
|
||||
#endif
|
||||
|
||||
#define MEMORY_GROUP_MANAGER_NR_GROUPS (16)
|
||||
|
||||
struct memory_group_manager_device;
|
||||
struct memory_group_manager_import_data;
|
||||
|
||||
/**
|
||||
* struct memory_group_manager_ops - Callbacks for memory group manager
|
||||
* operations
|
||||
*
|
||||
* @mgm_alloc_page: Callback to allocate physical memory in a group
|
||||
* @mgm_free_page: Callback to free physical memory in a group
|
||||
* @mgm_get_import_memory_id: Callback to get the group ID for imported memory
|
||||
* @mgm_update_gpu_pte: Callback to modify a GPU page table entry
|
||||
* @mgm_vmf_insert_pfn_prot: Callback to map a physical memory page for the CPU
|
||||
*/
|
||||
struct memory_group_manager_ops {
|
||||
/**
|
||||
* mgm_alloc_page - Allocate a physical memory page in a group
|
||||
*
|
||||
* @mgm_dev: The memory group manager through which the request is
|
||||
* being made.
|
||||
* @group_id: A physical memory group ID. The meaning of this is defined
|
||||
* by the systems integrator. Its valid range is
|
||||
* 0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
|
||||
* @gfp_mask: Bitmask of Get Free Page flags affecting allocator
|
||||
* behavior.
|
||||
* @order: Page order for physical page size (order=0 means 4 KiB,
|
||||
* order=9 means 2 MiB).
|
||||
*
|
||||
* Return: Pointer to allocated page, or NULL if allocation failed.
|
||||
*/
|
||||
struct page *(*mgm_alloc_page)(
|
||||
struct memory_group_manager_device *mgm_dev, int group_id,
|
||||
gfp_t gfp_mask, unsigned int order);
|
||||
|
||||
/**
|
||||
* mgm_free_page - Free a physical memory page in a group
|
||||
*
|
||||
* @mgm_dev: The memory group manager through which the request
|
||||
* is being made.
|
||||
* @group_id: A physical memory group ID. The meaning of this is
|
||||
* defined by the systems integrator. Its valid range is
|
||||
* 0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
|
||||
* @page: Address of the struct associated with a page of physical
|
||||
* memory that was allocated by calling the mgm_alloc_page
|
||||
* method of the same memory pool with the same values of
|
||||
* @group_id and @order.
|
||||
* @order: Page order for physical page size (order=0 means 4 KiB,
|
||||
* order=9 means 2 MiB).
|
||||
*/
|
||||
void (*mgm_free_page)(
|
||||
struct memory_group_manager_device *mgm_dev, int group_id,
|
||||
struct page *page, unsigned int order);
|
||||
|
||||
/**
|
||||
* mgm_get_import_memory_id - Get the physical memory group ID for the
|
||||
* imported memory
|
||||
*
|
||||
* @mgm_dev: The memory group manager through which the request
|
||||
* is being made.
|
||||
* @import_data: Pointer to the data which describes imported memory.
|
||||
*
|
||||
* Note that provision of this call back is optional, where it is not
|
||||
* provided this call back pointer must be set to NULL to indicate it
|
||||
* is not in use.
|
||||
*
|
||||
* Return: The memory group ID to use when mapping pages from this
|
||||
* imported memory.
|
||||
*/
|
||||
int (*mgm_get_import_memory_id)(
|
||||
struct memory_group_manager_device *mgm_dev,
|
||||
struct memory_group_manager_import_data *import_data);
|
||||
|
||||
/**
|
||||
* mgm_update_gpu_pte - Modify a GPU page table entry for a memory group
|
||||
*
|
||||
* @mgm_dev: The memory group manager through which the request
|
||||
* is being made.
|
||||
* @group_id: A physical memory group ID. The meaning of this is
|
||||
* defined by the systems integrator. Its valid range is
|
||||
* 0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
|
||||
* @mmu_level: The level of the page table entry in @ate.
|
||||
* @pte: The page table entry to modify, in LPAE or AArch64 format
|
||||
* (depending on the driver's configuration). This should be
|
||||
* decoded to determine the physical address and any other
|
||||
* properties of the mapping the manager requires.
|
||||
*
|
||||
* This function allows the memory group manager to modify a GPU page
|
||||
* table entry before it is stored by the kbase module (controller
|
||||
* driver). It may set certain bits in the page table entry attributes
|
||||
* or in the physical address, based on the physical memory group ID.
|
||||
*
|
||||
* Return: A modified GPU page table entry to be stored in a page table.
|
||||
*/
|
||||
u64 (*mgm_update_gpu_pte)(struct memory_group_manager_device *mgm_dev,
|
||||
int group_id, int mmu_level, u64 pte);
|
||||
|
||||
/**
|
||||
* mgm_vmf_insert_pfn_prot - Map a physical page in a group for the CPU
|
||||
*
|
||||
* @mgm_dev: The memory group manager through which the request
|
||||
* is being made.
|
||||
* @group_id: A physical memory group ID. The meaning of this is
|
||||
* defined by the systems integrator. Its valid range is
|
||||
* 0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
|
||||
* @vma: The virtual memory area to insert the page into.
|
||||
* @addr: A virtual address (in @vma) to assign to the page.
|
||||
* @pfn: The kernel Page Frame Number to insert at @addr in @vma.
|
||||
* @pgprot: Protection flags for the inserted page.
|
||||
*
|
||||
* Called from a CPU virtual memory page fault handler. This function
|
||||
* creates a page table entry from the given parameter values and stores
|
||||
* it at the appropriate location (unlike mgm_update_gpu_pte, which
|
||||
* returns a modified entry).
|
||||
*
|
||||
* Return: Type of fault that occurred or VM_FAULT_NOPAGE if the page
|
||||
* table entry was successfully installed.
|
||||
*/
|
||||
vm_fault_t (*mgm_vmf_insert_pfn_prot)(
|
||||
struct memory_group_manager_device *mgm_dev, int group_id,
|
||||
struct vm_area_struct *vma, unsigned long addr,
|
||||
unsigned long pfn, pgprot_t pgprot);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct memory_group_manager_device - Device structure for a memory group
|
||||
* manager
|
||||
*
|
||||
* @ops - Callbacks associated with this device
|
||||
* @data - Pointer to device private data
|
||||
*
|
||||
* In order for a systems integrator to provide custom behaviors for memory
|
||||
* operations performed by the kbase module (controller driver), they must
|
||||
* provide a platform-specific driver module which implements this interface.
|
||||
*
|
||||
* This structure should be registered with the platform device using
|
||||
* platform_set_drvdata().
|
||||
*/
|
||||
struct memory_group_manager_device {
|
||||
struct memory_group_manager_ops ops;
|
||||
void *data;
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
|
||||
enum memory_group_manager_import_type {
|
||||
MEMORY_GROUP_MANAGER_IMPORT_TYPE_DMA_BUF
|
||||
};
|
||||
|
||||
/**
|
||||
* struct memory_group_manager_import_data - Structure describing the imported
|
||||
* memory
|
||||
*
|
||||
* @type - type of imported memory
|
||||
* @u - Union describing the imported memory
|
||||
*
|
||||
*/
|
||||
struct memory_group_manager_import_data {
|
||||
enum memory_group_manager_import_type type;
|
||||
union {
|
||||
struct dma_buf *dma_buf;
|
||||
} u;
|
||||
};
|
||||
|
||||
#endif /* _MEMORY_GROUP_MANAGER_H_ */
|
||||
Reference in New Issue
Block a user