// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2018 Noralf Trønnes
*/
#include <linux/dma-buf.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_X86
#include <asm/set_memory.h>
#endif
#include <kunit/visibility.h>
#include <drm/drm.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_dumb_buffers.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_prime.h>
#include <drm/drm_print.h>
MODULE_IMPORT_NS("DMA_BUF");
/**
* DOC: overview
*
* This library provides helpers for GEM objects backed by shmem buffers
* allocated using anonymous pageable memory.
*
* Functions that operate on the GEM object receive struct &drm_gem_shmem_object.
* For GEM callback helpers in struct &drm_gem_object functions, see likewise
* named functions with an _object_ infix (e.g., drm_gem_shmem_object_vmap() wraps
* drm_gem_shmem_vmap()). These helpers perform the necessary type conversion.
*/
static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
.free = drm_gem_shmem_object_free,
.print_info = drm_gem_shmem_object_print_info,
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
.vmap = drm_gem_shmem_object_vmap,
.vunmap = drm_gem_shmem_object_vunmap,
.mmap = drm_gem_shmem_object_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
};
static int __drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem,
size_t size, bool private)
{
struct drm_gem_object *obj = &shmem->base;
int ret = 0;
if (!obj->funcs)
obj->funcs = &drm_gem_shmem_funcs;
if (private) {
drm_gem_private_object_init(dev, obj, size);
shmem->map_wc = false; /* dma-buf mappings use always writecombine */
} else {
ret = drm_gem_object_init(dev, obj, size);
}
if (ret) {
drm_gem_private_object_fini(obj);
return ret;
}
ret = drm_gem_create_mmap_offset(obj);
if (ret)
goto err_release;
INIT_LIST_HEAD(&shmem->madv_list);
if (!private) {
/*
* Our buffers are kept pinned, so allocating them
* from the MOVABLE zone is a really bad idea, and
* conflicts with CMA. See comments above new_inode()
* why this is required _and_ expected if you're
* going to pin these pages.
*/
mapping_set_gfp_mask(obj->filp->f_mapping, GFP_HIGHUSER |
__GFP_RETRY_MAYFAIL | __GFP_NOWARN);
}
return 0;
err_release:
drm_gem_object_release(obj);
return ret;
}
/**
* drm_gem_shmem_init - Initialize an allocated object.
* @dev: DRM device
* @shmem: shmem GEM object to initialize
* @size: Buffer size in bytes
*
* This function initializes an allocated shmem GEM object.
*
* Returns:
* 0 on success, or a negative error code on failure.
*/
int drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem, size_t size)
{
return __drm_gem_shmem_init(dev, shmem, size, false);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_init);
static struct drm_gem_shmem_object *
__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
{
struct drm_gem_shmem_object *shmem;
struct drm_gem_object *obj;
int ret = 0;
size = PAGE_ALIGN(size);
if (dev->driver->gem_create_object) {
obj = dev->driver->gem_create_object(dev, size);
if (IS_ERR(obj))
return ERR_CAST(obj);
shmem = to_drm_gem_shmem_obj(obj);
} else {
shmem = kzalloc_obj(*shmem);
if (!shmem)
return ERR_PTR(-ENOMEM);
obj = &shmem->base;
}
ret = __drm_gem_shmem_init(dev, shmem, size, private);
if (ret) {
kfree(obj);
return ERR_PTR(ret);
}
return shmem;
}
/**
* drm_gem_shmem_create - Allocate an object with the given size
* @dev: DRM device
* @size: Size of the object to allocate
*
* This function creates a shmem GEM object.
*
* Returns:
* A struct drm_gem_shmem_object * on success or an ERR_PTR()-encoded negative
* error code on failure.
*/
struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size)
{
return __drm_gem_shmem_create(dev, size, false);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_create);
/**
* drm_gem_shmem_release - Release resources associated with a shmem GEM object.
* @shmem: shmem GEM object
*
* This function cleans up the GEM object state, but does not free the memory used to store the
* object itself. This function is meant to be a dedicated helper for the Rust GEM bindings.
*/
void drm_gem_shmem_release(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;
if (drm_gem_is_imported(obj)) {
drm_prime_gem_destroy(obj, shmem->sgt);
} else {
dma_resv_lock(shmem->base.resv, NULL);
drm_WARN_ON(obj->dev, refcount_read(&shmem->