forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drm/ttm: Add a minimal prime implementation for ttm base objects
Signed-off-by: Thomas Hellstrom <[email protected]> Reviewed-by: Jakob Bornecrantz <[email protected]>
- Loading branch information
1 parent
ac49251
commit 65981f7
Showing
2 changed files
with
307 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/************************************************************************** | ||
* | ||
* Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA | ||
* Copyright (c) 2009-2013 VMware, Inc., Palo Alto, CA., USA | ||
* All Rights Reserved. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a | ||
|
@@ -26,6 +26,12 @@ | |
**************************************************************************/ | ||
/* | ||
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> | ||
* | ||
* While no substantial code is shared, the prime code is inspired by | ||
* drm_prime.c, with | ||
* Authors: | ||
* Dave Airlie <[email protected]> | ||
* Rob Clark <[email protected]> | ||
*/ | ||
/** @file ttm_ref_object.c | ||
* | ||
|
@@ -34,6 +40,7 @@ | |
* and release on file close. | ||
*/ | ||
|
||
|
||
/** | ||
* struct ttm_object_file | ||
* | ||
|
@@ -84,6 +91,9 @@ struct ttm_object_device { | |
struct drm_open_hash object_hash; | ||
atomic_t object_count; | ||
struct ttm_mem_global *mem_glob; | ||
struct dma_buf_ops ops; | ||
void (*dmabuf_release)(struct dma_buf *dma_buf); | ||
size_t dma_buf_size; | ||
}; | ||
|
||
/** | ||
|
@@ -116,6 +126,8 @@ struct ttm_ref_object { | |
struct ttm_object_file *tfile; | ||
}; | ||
|
||
static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf); | ||
|
||
static inline struct ttm_object_file * | ||
ttm_object_file_ref(struct ttm_object_file *tfile) | ||
{ | ||
|
@@ -416,9 +428,10 @@ struct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev, | |
} | ||
EXPORT_SYMBOL(ttm_object_file_init); | ||
|
||
struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global | ||
*mem_glob, | ||
unsigned int hash_order) | ||
struct ttm_object_device * | ||
ttm_object_device_init(struct ttm_mem_global *mem_glob, | ||
unsigned int hash_order, | ||
const struct dma_buf_ops *ops) | ||
{ | ||
struct ttm_object_device *tdev = kmalloc(sizeof(*tdev), GFP_KERNEL); | ||
int ret; | ||
|
@@ -430,10 +443,17 @@ struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global | |
spin_lock_init(&tdev->object_lock); | ||
atomic_set(&tdev->object_count, 0); | ||
ret = drm_ht_create(&tdev->object_hash, hash_order); | ||
if (ret != 0) | ||
goto out_no_object_hash; | ||
|
||
if (likely(ret == 0)) | ||
return tdev; | ||
tdev->ops = *ops; | ||
tdev->dmabuf_release = tdev->ops.release; | ||
tdev->ops.release = ttm_prime_dmabuf_release; | ||
tdev->dma_buf_size = ttm_round_pot(sizeof(struct dma_buf)) + | ||
ttm_round_pot(sizeof(struct file)); | ||
return tdev; | ||
|
||
out_no_object_hash: | ||
kfree(tdev); | ||
return NULL; | ||
} | ||
|
@@ -452,3 +472,225 @@ void ttm_object_device_release(struct ttm_object_device **p_tdev) | |
kfree(tdev); | ||
} | ||
EXPORT_SYMBOL(ttm_object_device_release); | ||
|
||
/** | ||
* get_dma_buf_unless_doomed - get a dma_buf reference if possible. | ||
* | ||
* @dma_buf: Non-refcounted pointer to a struct dma-buf. | ||
* | ||
* Obtain a file reference from a lookup structure that doesn't refcount | ||
* the file, but synchronizes with its release method to make sure it has | ||
* not been freed yet. See for example kref_get_unless_zero documentation. | ||
* Returns true if refcounting succeeds, false otherwise. | ||
* | ||
* Nobody really wants this as a public API yet, so let it mature here | ||
* for some time... | ||
*/ | ||
static bool __must_check get_dma_buf_unless_doomed(struct dma_buf *dmabuf) | ||
{ | ||
return atomic_long_inc_not_zero(&dmabuf->file->f_count) != 0L; | ||
} | ||
|
||
/** | ||
* ttm_prime_refcount_release - refcount release method for a prime object. | ||
* | ||
* @p_base: Pointer to ttm_base_object pointer. | ||
* | ||
* This is a wrapper that calls the refcount_release founction of the | ||
* underlying object. At the same time it cleans up the prime object. | ||
* This function is called when all references to the base object we | ||
* derive from are gone. | ||
*/ | ||
static void ttm_prime_refcount_release(struct ttm_base_object **p_base) | ||
{ | ||
struct ttm_base_object *base = *p_base; | ||
struct ttm_prime_object *prime; | ||
|
||
*p_base = NULL; | ||
prime = container_of(base, struct ttm_prime_object, base); | ||
BUG_ON(prime->dma_buf != NULL); | ||
mutex_destroy(&prime->mutex); | ||
if (prime->refcount_release) | ||
prime->refcount_release(&base); | ||
} | ||
|
||
/** | ||
* ttm_prime_dmabuf_release - Release method for the dma-bufs we export | ||
* | ||
* @dma_buf: | ||
* | ||
* This function first calls the dma_buf release method the driver | ||
* provides. Then it cleans up our dma_buf pointer used for lookup, | ||
* and finally releases the reference the dma_buf has on our base | ||
* object. | ||
*/ | ||
static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf) | ||
{ | ||
struct ttm_prime_object *prime = | ||
(struct ttm_prime_object *) dma_buf->priv; | ||
struct ttm_base_object *base = &prime->base; | ||
struct ttm_object_device *tdev = base->tfile->tdev; | ||
|
||
if (tdev->dmabuf_release) | ||
tdev->dmabuf_release(dma_buf); | ||
mutex_lock(&prime->mutex); | ||
if (prime->dma_buf == dma_buf) | ||
prime->dma_buf = NULL; | ||
mutex_unlock(&prime->mutex); | ||
ttm_mem_global_free(tdev->mem_glob, tdev->dma_buf_size); | ||
ttm_base_object_unref(&base); | ||
} | ||
|
||
/** | ||
* ttm_prime_fd_to_handle - Get a base object handle from a prime fd | ||
* | ||
* @tfile: A struct ttm_object_file identifying the caller. | ||
* @fd: The prime / dmabuf fd. | ||
* @handle: The returned handle. | ||
* | ||
* This function returns a handle to an object that previously exported | ||
* a dma-buf. Note that we don't handle imports yet, because we simply | ||
* have no consumers of that implementation. | ||
*/ | ||
int ttm_prime_fd_to_handle(struct ttm_object_file *tfile, | ||
int fd, u32 *handle) | ||
{ | ||
struct ttm_object_device *tdev = tfile->tdev; | ||
struct dma_buf *dma_buf; | ||
struct ttm_prime_object *prime; | ||
struct ttm_base_object *base; | ||
int ret; | ||
|
||
dma_buf = dma_buf_get(fd); | ||
if (IS_ERR(dma_buf)) | ||
return PTR_ERR(dma_buf); | ||
|
||
if (dma_buf->ops != &tdev->ops) | ||
return -ENOSYS; | ||
|
||
prime = (struct ttm_prime_object *) dma_buf->priv; | ||
base = &prime->base; | ||
*handle = base->hash.key; | ||
ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL); | ||
|
||
dma_buf_put(dma_buf); | ||
|
||
return ret; | ||
} | ||
EXPORT_SYMBOL_GPL(ttm_prime_fd_to_handle); | ||
|
||
/** | ||
* ttm_prime_handle_to_fd - Return a dma_buf fd from a ttm prime object | ||
* | ||
* @tfile: Struct ttm_object_file identifying the caller. | ||
* @handle: Handle to the object we're exporting from. | ||
* @flags: flags for dma-buf creation. We just pass them on. | ||
* @prime_fd: The returned file descriptor. | ||
* | ||
*/ | ||
int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, | ||
uint32_t handle, uint32_t flags, | ||
int *prime_fd) | ||
{ | ||
struct ttm_object_device *tdev = tfile->tdev; | ||
struct ttm_base_object *base; | ||
struct dma_buf *dma_buf; | ||
struct ttm_prime_object *prime; | ||
int ret; | ||
|
||
base = ttm_base_object_lookup(tfile, handle); | ||
if (unlikely(base == NULL || | ||
base->object_type != ttm_prime_type)) { | ||
ret = -ENOENT; | ||
goto out_unref; | ||
} | ||
|
||
prime = container_of(base, struct ttm_prime_object, base); | ||
if (unlikely(!base->shareable)) { | ||
ret = -EPERM; | ||
goto out_unref; | ||
} | ||
|
||
ret = mutex_lock_interruptible(&prime->mutex); | ||
if (unlikely(ret != 0)) { | ||
ret = -ERESTARTSYS; | ||
goto out_unref; | ||
} | ||
|
||
dma_buf = prime->dma_buf; | ||
if (!dma_buf || !get_dma_buf_unless_doomed(dma_buf)) { | ||
|
||
/* | ||
* Need to create a new dma_buf, with memory accounting. | ||
*/ | ||
ret = ttm_mem_global_alloc(tdev->mem_glob, tdev->dma_buf_size, | ||
false, true); | ||
if (unlikely(ret != 0)) { | ||
mutex_unlock(&prime->mutex); | ||
goto out_unref; | ||
} | ||
|
||
dma_buf = dma_buf_export(prime, &tdev->ops, | ||
prime->size, flags); | ||
if (IS_ERR(dma_buf)) { | ||
ret = PTR_ERR(dma_buf); | ||
ttm_mem_global_free(tdev->mem_glob, | ||
tdev->dma_buf_size); | ||
mutex_unlock(&prime->mutex); | ||
goto out_unref; | ||
} | ||
|
||
/* | ||
* dma_buf has taken the base object reference | ||
*/ | ||
base = NULL; | ||
prime->dma_buf = dma_buf; | ||
} | ||
mutex_unlock(&prime->mutex); | ||
|
||
ret = dma_buf_fd(dma_buf, flags); | ||
if (ret >= 0) { | ||
*prime_fd = ret; | ||
ret = 0; | ||
} else | ||
dma_buf_put(dma_buf); | ||
|
||
out_unref: | ||
if (base) | ||
ttm_base_object_unref(&base); | ||
return ret; | ||
} | ||
EXPORT_SYMBOL_GPL(ttm_prime_handle_to_fd); | ||
|
||
/** | ||
* ttm_prime_object_init - Initialize a ttm_prime_object | ||
* | ||
* @tfile: struct ttm_object_file identifying the caller | ||
* @size: The size of the dma_bufs we export. | ||
* @prime: The object to be initialized. | ||
* @shareable: See ttm_base_object_init | ||
* @type: See ttm_base_object_init | ||
* @refcount_release: See ttm_base_object_init | ||
* @ref_obj_release: See ttm_base_object_init | ||
* | ||
* Initializes an object which is compatible with the drm_prime model | ||
* for data sharing between processes and devices. | ||
*/ | ||
int ttm_prime_object_init(struct ttm_object_file *tfile, size_t size, | ||
struct ttm_prime_object *prime, bool shareable, | ||
enum ttm_object_type type, | ||
void (*refcount_release) (struct ttm_base_object **), | ||
void (*ref_obj_release) (struct ttm_base_object *, | ||
enum ttm_ref_type ref_type)) | ||
{ | ||
mutex_init(&prime->mutex); | ||
prime->size = PAGE_ALIGN(size); | ||
prime->real_type = type; | ||
prime->dma_buf = NULL; | ||
prime->refcount_release = refcount_release; | ||
return ttm_base_object_init(tfile, &prime->base, shareable, | ||
ttm_prime_type, | ||
ttm_prime_refcount_release, | ||
ref_obj_release); | ||
} | ||
EXPORT_SYMBOL(ttm_prime_object_init); |
Oops, something went wrong.