![Kees Cook](/assets/img/avatar_default.png)
This converts all remaining cases of the old setup_timer() API into using timer_setup(), where the callback argument is the structure already holding the struct timer_list. These should have no behavioral changes, since they just change which pointer is passed into the callback with the same available pointers after conversion. It handles the following examples, in addition to some other variations. Casting from unsigned long: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... setup_timer(&ptr->my_timer, my_callback, ptr); and forced object casts: void my_callback(struct something *ptr) { ... } ... setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr); become: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... timer_setup(&ptr->my_timer, my_callback, 0); Direct function assignments: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... ptr->my_timer.function = my_callback; have a temporary cast added, along with converting the args: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback; And finally, callbacks without a data assignment: void my_callback(unsigned long data) { ... } ... setup_timer(&ptr->my_timer, my_callback, 0); have their argument renamed to verify they're unused during conversion: void my_callback(struct timer_list *unused) { ... } ... timer_setup(&ptr->my_timer, my_callback, 0); The conversion is done with the following Coccinelle script: spatch --very-quiet --all-includes --include-headers \ -I ./arch/x86/include -I ./arch/x86/include/generated \ -I ./include -I ./arch/x86/include/uapi \ -I ./arch/x86/include/generated/uapi -I ./include/uapi \ -I ./include/generated/uapi --include ./include/linux/kconfig.h \ --dir . \ --cocci-file ~/src/data/timer_setup.cocci @fix_address_of@ expression e; @@ setup_timer( -&(e) +&e , ...) // Update any raw setup_timer() usages that have a NULL callback, but // would otherwise match change_timer_function_usage, since the latter // will update all function assignments done in the face of a NULL // function initialization in setup_timer(). @change_timer_function_usage_NULL@ expression _E; identifier _timer; type _cast_data; @@ ( -setup_timer(&_E->_timer, NULL, _E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E->_timer, NULL, (_cast_data)_E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E._timer, NULL, &_E); +timer_setup(&_E._timer, NULL, 0); | -setup_timer(&_E._timer, NULL, (_cast_data)&_E); +timer_setup(&_E._timer, NULL, 0); ) @change_timer_function_usage@ expression _E; identifier _timer; struct timer_list _stl; identifier _callback; type _cast_func, _cast_data; @@ ( -setup_timer(&_E->_timer, _callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | _E->_timer@_stl.function = _callback; | _E->_timer@_stl.function = &_callback; | _E->_timer@_stl.function = (_cast_func)_callback; | _E->_timer@_stl.function = (_cast_func)&_callback; | _E._timer@_stl.function = _callback; | _E._timer@_stl.function = &_callback; | _E._timer@_stl.function = (_cast_func)_callback; | _E._timer@_stl.function = (_cast_func)&_callback; ) // callback(unsigned long arg) @change_callback_handle_cast depends on change_timer_function_usage@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; identifier _handle; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { ( ... when != _origarg _handletype *_handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg ) } // callback(unsigned long arg) without existing variable @change_callback_handle_cast_no_arg depends on change_timer_function_usage && !change_callback_handle_cast@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { + _handletype *_origarg = from_timer(_origarg, t, _timer); + ... when != _origarg - (_handletype *)_origarg + _origarg ... when != _origarg } // Avoid already converted callbacks. @match_callback_converted depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier t; @@ void _callback(struct timer_list *t) { ... } // callback(struct something *handle) @change_callback_handle_arg depends on change_timer_function_usage && !match_callback_converted && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; @@ void _callback( -_handletype *_handle +struct timer_list *t ) { + _handletype *_handle = from_timer(_handle, t, _timer); ... } // If change_callback_handle_arg ran on an empty function, remove // the added handler. @unchange_callback_handle_arg depends on change_timer_function_usage && change_callback_handle_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; identifier t; @@ void _callback(struct timer_list *t) { - _handletype *_handle = from_timer(_handle, t, _timer); } // We only want to refactor the setup_timer() data argument if we've found // the matching callback. This undoes changes in change_timer_function_usage. @unchange_timer_function_usage depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg && !change_callback_handle_arg@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type change_timer_function_usage._cast_data; @@ ( -timer_setup(&_E->_timer, _callback, 0); +setup_timer(&_E->_timer, _callback, (_cast_data)_E); | -timer_setup(&_E._timer, _callback, 0); +setup_timer(&_E._timer, _callback, (_cast_data)&_E); ) // If we fixed a callback from a .function assignment, fix the // assignment cast now. @change_timer_function_assignment depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_func; typedef TIMER_FUNC_TYPE; @@ ( _E->_timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -&_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)_callback; +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -&_callback; +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; ) // Sometimes timer functions are called directly. Replace matched args. @change_timer_function_calls depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression _E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_data; @@ _callback( ( -(_cast_data)_E +&_E->_timer | -(_cast_data)&_E +&_E._timer | -_E +&_E->_timer ) ) // If a timer has been configured without a data argument, it can be // converted without regard to the callback argument, since it is unused. @match_timer_function_unused_data@ expression _E; identifier _timer; identifier _callback; @@ ( -setup_timer(&_E->_timer, _callback, 0); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0L); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0UL); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0L); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0UL); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_timer, _callback, 0); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0L); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0UL); +timer_setup(&_timer, _callback, 0); | -setup_timer(_timer, _callback, 0); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0L); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0UL); +timer_setup(_timer, _callback, 0); ) @change_callback_unused_data depends on match_timer_function_unused_data@ identifier match_timer_function_unused_data._callback; type _origtype; identifier _origarg; @@ void _callback( -_origtype _origarg +struct timer_list *unused ) { ... when != _origarg } Signed-off-by: Kees Cook <keescook@chromium.org>
284 lines
7.7 KiB
C
284 lines
7.7 KiB
C
/*
|
|
* Copyright 2016 Intel Corporation
|
|
*
|
|
* 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
|
|
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
|
* license, and/or sell copies of the Software, and to permit persons to whom
|
|
* them Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) 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 MERCHANTIBILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS 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.
|
|
*/
|
|
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/reservation.h>
|
|
|
|
#include "vgem_drv.h"
|
|
|
|
#define VGEM_FENCE_TIMEOUT (10*HZ)
|
|
|
|
struct vgem_fence {
|
|
struct dma_fence base;
|
|
struct spinlock lock;
|
|
struct timer_list timer;
|
|
};
|
|
|
|
static const char *vgem_fence_get_driver_name(struct dma_fence *fence)
|
|
{
|
|
return "vgem";
|
|
}
|
|
|
|
static const char *vgem_fence_get_timeline_name(struct dma_fence *fence)
|
|
{
|
|
return "unbound";
|
|
}
|
|
|
|
static bool vgem_fence_signaled(struct dma_fence *fence)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static bool vgem_fence_enable_signaling(struct dma_fence *fence)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void vgem_fence_release(struct dma_fence *base)
|
|
{
|
|
struct vgem_fence *fence = container_of(base, typeof(*fence), base);
|
|
|
|
del_timer_sync(&fence->timer);
|
|
dma_fence_free(&fence->base);
|
|
}
|
|
|
|
static void vgem_fence_value_str(struct dma_fence *fence, char *str, int size)
|
|
{
|
|
snprintf(str, size, "%u", fence->seqno);
|
|
}
|
|
|
|
static void vgem_fence_timeline_value_str(struct dma_fence *fence, char *str,
|
|
int size)
|
|
{
|
|
snprintf(str, size, "%u",
|
|
dma_fence_is_signaled(fence) ? fence->seqno : 0);
|
|
}
|
|
|
|
static const struct dma_fence_ops vgem_fence_ops = {
|
|
.get_driver_name = vgem_fence_get_driver_name,
|
|
.get_timeline_name = vgem_fence_get_timeline_name,
|
|
.enable_signaling = vgem_fence_enable_signaling,
|
|
.signaled = vgem_fence_signaled,
|
|
.wait = dma_fence_default_wait,
|
|
.release = vgem_fence_release,
|
|
|
|
.fence_value_str = vgem_fence_value_str,
|
|
.timeline_value_str = vgem_fence_timeline_value_str,
|
|
};
|
|
|
|
static void vgem_fence_timeout(struct timer_list *t)
|
|
{
|
|
struct vgem_fence *fence = from_timer(fence, t, timer);
|
|
|
|
dma_fence_signal(&fence->base);
|
|
}
|
|
|
|
static struct dma_fence *vgem_fence_create(struct vgem_file *vfile,
|
|
unsigned int flags)
|
|
{
|
|
struct vgem_fence *fence;
|
|
|
|
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
|
|
if (!fence)
|
|
return NULL;
|
|
|
|
spin_lock_init(&fence->lock);
|
|
dma_fence_init(&fence->base, &vgem_fence_ops, &fence->lock,
|
|
dma_fence_context_alloc(1), 1);
|
|
|
|
timer_setup(&fence->timer, vgem_fence_timeout, 0);
|
|
|
|
/* We force the fence to expire within 10s to prevent driver hangs */
|
|
mod_timer(&fence->timer, jiffies + VGEM_FENCE_TIMEOUT);
|
|
|
|
return &fence->base;
|
|
}
|
|
|
|
static int attach_dmabuf(struct drm_device *dev,
|
|
struct drm_gem_object *obj)
|
|
{
|
|
struct dma_buf *dmabuf;
|
|
|
|
if (obj->dma_buf)
|
|
return 0;
|
|
|
|
dmabuf = dev->driver->gem_prime_export(dev, obj, 0);
|
|
if (IS_ERR(dmabuf))
|
|
return PTR_ERR(dmabuf);
|
|
|
|
obj->dma_buf = dmabuf;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* vgem_fence_attach_ioctl (DRM_IOCTL_VGEM_FENCE_ATTACH):
|
|
*
|
|
* Create and attach a fence to the vGEM handle. This fence is then exposed
|
|
* via the dma-buf reservation object and visible to consumers of the exported
|
|
* dma-buf. If the flags contain VGEM_FENCE_WRITE, the fence indicates the
|
|
* vGEM buffer is being written to by the client and is exposed as an exclusive
|
|
* fence, otherwise the fence indicates the client is current reading from the
|
|
* buffer and all future writes should wait for the client to signal its
|
|
* completion. Note that if a conflicting fence is already on the dma-buf (i.e.
|
|
* an exclusive fence when adding a read, or any fence when adding a write),
|
|
* -EBUSY is reported. Serialisation between operations should be handled
|
|
* by waiting upon the dma-buf.
|
|
*
|
|
* This returns the handle for the new fence that must be signaled within 10
|
|
* seconds (or otherwise it will automatically expire). See
|
|
* vgem_fence_signal_ioctl (DRM_IOCTL_VGEM_FENCE_SIGNAL).
|
|
*
|
|
* If the vGEM handle does not exist, vgem_fence_attach_ioctl returns -ENOENT.
|
|
*/
|
|
int vgem_fence_attach_ioctl(struct drm_device *dev,
|
|
void *data,
|
|
struct drm_file *file)
|
|
{
|
|
struct drm_vgem_fence_attach *arg = data;
|
|
struct vgem_file *vfile = file->driver_priv;
|
|
struct reservation_object *resv;
|
|
struct drm_gem_object *obj;
|
|
struct dma_fence *fence;
|
|
int ret;
|
|
|
|
if (arg->flags & ~VGEM_FENCE_WRITE)
|
|
return -EINVAL;
|
|
|
|
if (arg->pad)
|
|
return -EINVAL;
|
|
|
|
obj = drm_gem_object_lookup(file, arg->handle);
|
|
if (!obj)
|
|
return -ENOENT;
|
|
|
|
ret = attach_dmabuf(dev, obj);
|
|
if (ret)
|
|
goto err;
|
|
|
|
fence = vgem_fence_create(vfile, arg->flags);
|
|
if (!fence) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
/* Check for a conflicting fence */
|
|
resv = obj->dma_buf->resv;
|
|
if (!reservation_object_test_signaled_rcu(resv,
|
|
arg->flags & VGEM_FENCE_WRITE)) {
|
|
ret = -EBUSY;
|
|
goto err_fence;
|
|
}
|
|
|
|
/* Expose the fence via the dma-buf */
|
|
ret = 0;
|
|
reservation_object_lock(resv, NULL);
|
|
if (arg->flags & VGEM_FENCE_WRITE)
|
|
reservation_object_add_excl_fence(resv, fence);
|
|
else if ((ret = reservation_object_reserve_shared(resv)) == 0)
|
|
reservation_object_add_shared_fence(resv, fence);
|
|
reservation_object_unlock(resv);
|
|
|
|
/* Record the fence in our idr for later signaling */
|
|
if (ret == 0) {
|
|
mutex_lock(&vfile->fence_mutex);
|
|
ret = idr_alloc(&vfile->fence_idr, fence, 1, 0, GFP_KERNEL);
|
|
mutex_unlock(&vfile->fence_mutex);
|
|
if (ret > 0) {
|
|
arg->out_fence = ret;
|
|
ret = 0;
|
|
}
|
|
}
|
|
err_fence:
|
|
if (ret) {
|
|
dma_fence_signal(fence);
|
|
dma_fence_put(fence);
|
|
}
|
|
err:
|
|
drm_gem_object_put_unlocked(obj);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* vgem_fence_signal_ioctl (DRM_IOCTL_VGEM_FENCE_SIGNAL):
|
|
*
|
|
* Signal and consume a fence ealier attached to a vGEM handle using
|
|
* vgem_fence_attach_ioctl (DRM_IOCTL_VGEM_FENCE_ATTACH).
|
|
*
|
|
* All fences must be signaled within 10s of attachment or otherwise they
|
|
* will automatically expire (and a vgem_fence_signal_ioctl returns -ETIMEDOUT).
|
|
*
|
|
* Signaling a fence indicates to all consumers of the dma-buf that the
|
|
* client has completed the operation associated with the fence, and that the
|
|
* buffer is then ready for consumption.
|
|
*
|
|
* If the fence does not exist (or has already been signaled by the client),
|
|
* vgem_fence_signal_ioctl returns -ENOENT.
|
|
*/
|
|
int vgem_fence_signal_ioctl(struct drm_device *dev,
|
|
void *data,
|
|
struct drm_file *file)
|
|
{
|
|
struct vgem_file *vfile = file->driver_priv;
|
|
struct drm_vgem_fence_signal *arg = data;
|
|
struct dma_fence *fence;
|
|
int ret = 0;
|
|
|
|
if (arg->flags)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&vfile->fence_mutex);
|
|
fence = idr_replace(&vfile->fence_idr, NULL, arg->fence);
|
|
mutex_unlock(&vfile->fence_mutex);
|
|
if (!fence)
|
|
return -ENOENT;
|
|
if (IS_ERR(fence))
|
|
return PTR_ERR(fence);
|
|
|
|
if (dma_fence_is_signaled(fence))
|
|
ret = -ETIMEDOUT;
|
|
|
|
dma_fence_signal(fence);
|
|
dma_fence_put(fence);
|
|
return ret;
|
|
}
|
|
|
|
int vgem_fence_open(struct vgem_file *vfile)
|
|
{
|
|
mutex_init(&vfile->fence_mutex);
|
|
idr_init(&vfile->fence_idr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __vgem_fence_idr_fini(int id, void *p, void *data)
|
|
{
|
|
dma_fence_signal(p);
|
|
dma_fence_put(p);
|
|
return 0;
|
|
}
|
|
|
|
void vgem_fence_close(struct vgem_file *vfile)
|
|
{
|
|
idr_for_each(&vfile->fence_idr, __vgem_fence_idr_fini, vfile);
|
|
idr_destroy(&vfile->fence_idr);
|
|
}
|