diff options
author | Russell King <rmk@arm.linux.org.uk> | 2015-05-31 09:03:49 +0100 |
---|---|---|
committer | Russell King <rmk@arm.linux.org.uk> | 2015-06-29 12:58:33 +0100 |
commit | 36e8cf446758f22f827e29ea3ab9d7a0525a850d (patch) | |
tree | 3a7cc70a7d9e0a1cc08a8937c73ced87d9d2e5e0 | |
parent | 7fed6f6b1d3ff97e6feecdf93c65b357f10bae4a (diff) |
etnaviv: etnadrm: add support for GEM_WAIT ioctl
Add support for the new GEM_WAIT ioctl, which waits for the kernel to
finish with the GEM object (all render complete, and has been retired).
This is different from the WAIT_FENCE ioctl, which just waits for
rendering to complete.
This is an important distinction, as when a gem object is retired and
freed, the kernel will invalidate the cache for this object, which
effectively changes the data userspace sees. If userspace merely
waits for rendering to complete, there is a race condition where a
userptr BO can be free()d and re-used, and the kernel's invalidation
then corrupts the malloc() free lists.
This commit ensures that etna_bo_del() on a userptr BO will not
return until the kernel has done with the BO.
On kernels which do not support the GEM_WAIT ioctl, we presently
ignore the error.
Signed-off-by: Russell King <rmk@arm.linux.org.uk>
-rw-r--r-- | etnaviv/etnadrm.c | 51 | ||||
-rw-r--r-- | etnaviv/etnaviv_drm.h | 10 |
2 files changed, 46 insertions, 15 deletions
diff --git a/etnaviv/etnadrm.c b/etnaviv/etnadrm.c index 38fbe06..a2cc27b 100644 --- a/etnaviv/etnadrm.c +++ b/etnaviv/etnadrm.c @@ -256,28 +256,32 @@ int viv_close(struct viv_conn *conn) return 0; } +static void etnadrm_convert_timeout(struct drm_etnaviv_timespec *ts, + uint32_t timeout) +{ + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC, &now); + + ts->tv_sec = now.tv_sec + timeout / 1000; + ts->tv_nsec = now.tv_nsec + (timeout % 1000) * 10000000; + if (ts->tv_nsec > 1000000000) { + ts->tv_nsec -= 1000000000; + ts->tv_sec += 1; + } +} + int viv_fence_finish(struct viv_conn *conn, uint32_t fence, uint32_t timeout) { - struct timespec ts; struct drm_etnaviv_wait_fence req = { .pipe = conn->etnadrm_pipe, .fence = fence, }; - int ret; - - clock_gettime(CLOCK_MONOTONIC, &ts); - - req.timeout.tv_sec = ts.tv_sec + timeout / 1000; - req.timeout.tv_nsec = ts.tv_nsec + (timeout % 1000) * 10000000; - if (req.timeout.tv_nsec > 1000000000) { - req.timeout.tv_nsec -= 1000000000; - req.timeout.tv_sec += 1; - } - ret = drmCommandWrite(conn->fd, DRM_ETNAVIV_WAIT_FENCE, - &req, sizeof(req)); + etnadrm_convert_timeout(&req.timeout, timeout); - return ret; + return drmCommandWrite(conn->fd, DRM_ETNAVIV_WAIT_FENCE, + &req, sizeof(req)); } struct etna_bo { @@ -289,8 +293,23 @@ struct etna_bo { int bo_idx; struct xorg_list node; struct bo_entry cache; + uint8_t is_usermem; }; +static int etna_bo_gem_wait(struct etna_bo *bo, uint32_t timeout) +{ + struct viv_conn *conn = bo->conn; + struct drm_etnaviv_gem_wait req = { + .pipe = conn->etnadrm_pipe, + .handle = bo->handle, + }; + + etnadrm_convert_timeout(&req.timeout, timeout); + + return drmCommandWrite(conn->fd, DRM_ETNAVIV_GEM_WAIT, + &req, sizeof(req)); +} + static void etna_bo_free(struct etna_bo *bo) { struct viv_conn *conn = bo->conn; @@ -301,6 +320,9 @@ static void etna_bo_free(struct etna_bo *bo) if (bo->logical) munmap(bo->logical, bo->size); + if (bo->is_usermem) + etna_bo_gem_wait(bo, VIV_WAIT_INDEFINITE); + drmIoctl(conn->fd, DRM_IOCTL_GEM_CLOSE, &req); free(bo); } @@ -469,6 +491,7 @@ struct etna_bo *etna_bo_from_usermem_prot(struct viv_conn *conn, void *memory, s mem = NULL; } else { mem->handle = req.handle; + mem->is_usermem = TRUE; } return mem; diff --git a/etnaviv/etnaviv_drm.h b/etnaviv/etnaviv_drm.h index 7bae78b..1818d7a 100644 --- a/etnaviv/etnaviv_drm.h +++ b/etnaviv/etnaviv_drm.h @@ -235,6 +235,12 @@ struct drm_etnaviv_gem_userptr { uint32_t handle; /* out, non-zero handle */ }; +struct drm_etnaviv_gem_wait { + __u32 pipe; /* in */ + __u32 handle; /* in, bo to be waited for */ + struct drm_etnaviv_timespec timeout; /* in */ +}; + #define DRM_ETNAVIV_GET_PARAM 0x00 #define DRM_ETNAVIV_SET_PARAM 0x01 #define DRM_ETNAVIV_GEM_NEW 0x02 @@ -244,7 +250,8 @@ struct drm_etnaviv_gem_userptr { #define DRM_ETNAVIV_GEM_SUBMIT 0x06 #define DRM_ETNAVIV_WAIT_FENCE 0x07 #define DRM_ETNAVIV_GEM_USERPTR 0x08 -#define DRM_ETNAVIV_NUM_IOCTLS 0x09 +#define DRM_ETNAVIV_GEM_WAIT 0x09 +#define DRM_ETNAVIV_NUM_IOCTLS 0x0a #define DRM_IOCTL_ETNAVIV_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param) #define DRM_IOCTL_ETNAVIV_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new) @@ -253,6 +260,7 @@ struct drm_etnaviv_gem_userptr { #define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini) #define DRM_IOCTL_ETNAVIV_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit) #define DRM_IOCTL_ETNAVIV_WAIT_FENCE DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence) +#define DRM_IOCTL_ETNAVIV_GEM_WAIT DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait) #define __STR(x) #x #define _STR(x) __STR(x) |