cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sw_sync.c (9692B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Sync File validation framework
      4 *
      5 * Copyright (C) 2012 Google, Inc.
      6 */
      7
      8#include <linux/file.h>
      9#include <linux/fs.h>
     10#include <linux/uaccess.h>
     11#include <linux/slab.h>
     12#include <linux/sync_file.h>
     13
     14#include "sync_debug.h"
     15
     16#define CREATE_TRACE_POINTS
     17#include "sync_trace.h"
     18
     19/*
     20 * SW SYNC validation framework
     21 *
     22 * A sync object driver that uses a 32bit counter to coordinate
     23 * synchronization.  Useful when there is no hardware primitive backing
     24 * the synchronization.
     25 *
     26 * To start the framework just open:
     27 *
     28 * <debugfs>/sync/sw_sync
     29 *
     30 * That will create a sync timeline, all fences created under this timeline
     31 * file descriptor will belong to the this timeline.
     32 *
     33 * The 'sw_sync' file can be opened many times as to create different
     34 * timelines.
     35 *
     36 * Fences can be created with SW_SYNC_IOC_CREATE_FENCE ioctl with struct
     37 * sw_sync_create_fence_data as parameter.
     38 *
     39 * To increment the timeline counter, SW_SYNC_IOC_INC ioctl should be used
     40 * with the increment as u32. This will update the last signaled value
     41 * from the timeline and signal any fence that has a seqno smaller or equal
     42 * to it.
     43 *
     44 * struct sw_sync_create_fence_data
     45 * @value:	the seqno to initialise the fence with
     46 * @name:	the name of the new sync point
     47 * @fence:	return the fd of the new sync_file with the created fence
     48 */
     49struct sw_sync_create_fence_data {
     50	__u32	value;
     51	char	name[32];
     52	__s32	fence; /* fd of new fence */
     53};
     54
     55#define SW_SYNC_IOC_MAGIC	'W'
     56
     57#define SW_SYNC_IOC_CREATE_FENCE	_IOWR(SW_SYNC_IOC_MAGIC, 0,\
     58		struct sw_sync_create_fence_data)
     59
     60#define SW_SYNC_IOC_INC			_IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
     61
     62static const struct dma_fence_ops timeline_fence_ops;
     63
     64static inline struct sync_pt *dma_fence_to_sync_pt(struct dma_fence *fence)
     65{
     66	if (fence->ops != &timeline_fence_ops)
     67		return NULL;
     68	return container_of(fence, struct sync_pt, base);
     69}
     70
     71/**
     72 * sync_timeline_create() - creates a sync object
     73 * @name:	sync_timeline name
     74 *
     75 * Creates a new sync_timeline. Returns the sync_timeline object or NULL in
     76 * case of error.
     77 */
     78static struct sync_timeline *sync_timeline_create(const char *name)
     79{
     80	struct sync_timeline *obj;
     81
     82	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
     83	if (!obj)
     84		return NULL;
     85
     86	kref_init(&obj->kref);
     87	obj->context = dma_fence_context_alloc(1);
     88	strlcpy(obj->name, name, sizeof(obj->name));
     89
     90	obj->pt_tree = RB_ROOT;
     91	INIT_LIST_HEAD(&obj->pt_list);
     92	spin_lock_init(&obj->lock);
     93
     94	sync_timeline_debug_add(obj);
     95
     96	return obj;
     97}
     98
     99static void sync_timeline_free(struct kref *kref)
    100{
    101	struct sync_timeline *obj =
    102		container_of(kref, struct sync_timeline, kref);
    103
    104	sync_timeline_debug_remove(obj);
    105
    106	kfree(obj);
    107}
    108
    109static void sync_timeline_get(struct sync_timeline *obj)
    110{
    111	kref_get(&obj->kref);
    112}
    113
    114static void sync_timeline_put(struct sync_timeline *obj)
    115{
    116	kref_put(&obj->kref, sync_timeline_free);
    117}
    118
    119static const char *timeline_fence_get_driver_name(struct dma_fence *fence)
    120{
    121	return "sw_sync";
    122}
    123
    124static const char *timeline_fence_get_timeline_name(struct dma_fence *fence)
    125{
    126	struct sync_timeline *parent = dma_fence_parent(fence);
    127
    128	return parent->name;
    129}
    130
    131static void timeline_fence_release(struct dma_fence *fence)
    132{
    133	struct sync_pt *pt = dma_fence_to_sync_pt(fence);
    134	struct sync_timeline *parent = dma_fence_parent(fence);
    135	unsigned long flags;
    136
    137	spin_lock_irqsave(fence->lock, flags);
    138	if (!list_empty(&pt->link)) {
    139		list_del(&pt->link);
    140		rb_erase(&pt->node, &parent->pt_tree);
    141	}
    142	spin_unlock_irqrestore(fence->lock, flags);
    143
    144	sync_timeline_put(parent);
    145	dma_fence_free(fence);
    146}
    147
    148static bool timeline_fence_signaled(struct dma_fence *fence)
    149{
    150	struct sync_timeline *parent = dma_fence_parent(fence);
    151
    152	return !__dma_fence_is_later(fence->seqno, parent->value, fence->ops);
    153}
    154
    155static bool timeline_fence_enable_signaling(struct dma_fence *fence)
    156{
    157	return true;
    158}
    159
    160static void timeline_fence_value_str(struct dma_fence *fence,
    161				    char *str, int size)
    162{
    163	snprintf(str, size, "%lld", fence->seqno);
    164}
    165
    166static void timeline_fence_timeline_value_str(struct dma_fence *fence,
    167					     char *str, int size)
    168{
    169	struct sync_timeline *parent = dma_fence_parent(fence);
    170
    171	snprintf(str, size, "%d", parent->value);
    172}
    173
    174static const struct dma_fence_ops timeline_fence_ops = {
    175	.get_driver_name = timeline_fence_get_driver_name,
    176	.get_timeline_name = timeline_fence_get_timeline_name,
    177	.enable_signaling = timeline_fence_enable_signaling,
    178	.signaled = timeline_fence_signaled,
    179	.release = timeline_fence_release,
    180	.fence_value_str = timeline_fence_value_str,
    181	.timeline_value_str = timeline_fence_timeline_value_str,
    182};
    183
    184/**
    185 * sync_timeline_signal() - signal a status change on a sync_timeline
    186 * @obj:	sync_timeline to signal
    187 * @inc:	num to increment on timeline->value
    188 *
    189 * A sync implementation should call this any time one of it's fences
    190 * has signaled or has an error condition.
    191 */
    192static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc)
    193{
    194	struct sync_pt *pt, *next;
    195
    196	trace_sync_timeline(obj);
    197
    198	spin_lock_irq(&obj->lock);
    199
    200	obj->value += inc;
    201
    202	list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
    203		if (!timeline_fence_signaled(&pt->base))
    204			break;
    205
    206		list_del_init(&pt->link);
    207		rb_erase(&pt->node, &obj->pt_tree);
    208
    209		/*
    210		 * A signal callback may release the last reference to this
    211		 * fence, causing it to be freed. That operation has to be
    212		 * last to avoid a use after free inside this loop, and must
    213		 * be after we remove the fence from the timeline in order to
    214		 * prevent deadlocking on timeline->lock inside
    215		 * timeline_fence_release().
    216		 */
    217		dma_fence_signal_locked(&pt->base);
    218	}
    219
    220	spin_unlock_irq(&obj->lock);
    221}
    222
    223/**
    224 * sync_pt_create() - creates a sync pt
    225 * @obj:	parent sync_timeline
    226 * @value:	value of the fence
    227 *
    228 * Creates a new sync_pt (fence) as a child of @parent.  @size bytes will be
    229 * allocated allowing for implementation specific data to be kept after
    230 * the generic sync_timeline struct. Returns the sync_pt object or
    231 * NULL in case of error.
    232 */
    233static struct sync_pt *sync_pt_create(struct sync_timeline *obj,
    234				      unsigned int value)
    235{
    236	struct sync_pt *pt;
    237
    238	pt = kzalloc(sizeof(*pt), GFP_KERNEL);
    239	if (!pt)
    240		return NULL;
    241
    242	sync_timeline_get(obj);
    243	dma_fence_init(&pt->base, &timeline_fence_ops, &obj->lock,
    244		       obj->context, value);
    245	INIT_LIST_HEAD(&pt->link);
    246
    247	spin_lock_irq(&obj->lock);
    248	if (!dma_fence_is_signaled_locked(&pt->base)) {
    249		struct rb_node **p = &obj->pt_tree.rb_node;
    250		struct rb_node *parent = NULL;
    251
    252		while (*p) {
    253			struct sync_pt *other;
    254			int cmp;
    255
    256			parent = *p;
    257			other = rb_entry(parent, typeof(*pt), node);
    258			cmp = value - other->base.seqno;
    259			if (cmp > 0) {
    260				p = &parent->rb_right;
    261			} else if (cmp < 0) {
    262				p = &parent->rb_left;
    263			} else {
    264				if (dma_fence_get_rcu(&other->base)) {
    265					sync_timeline_put(obj);
    266					kfree(pt);
    267					pt = other;
    268					goto unlock;
    269				}
    270				p = &parent->rb_left;
    271			}
    272		}
    273		rb_link_node(&pt->node, parent, p);
    274		rb_insert_color(&pt->node, &obj->pt_tree);
    275
    276		parent = rb_next(&pt->node);
    277		list_add_tail(&pt->link,
    278			      parent ? &rb_entry(parent, typeof(*pt), node)->link : &obj->pt_list);
    279	}
    280unlock:
    281	spin_unlock_irq(&obj->lock);
    282
    283	return pt;
    284}
    285
    286/*
    287 * *WARNING*
    288 *
    289 * improper use of this can result in deadlocking kernel drivers from userspace.
    290 */
    291
    292/* opening sw_sync create a new sync obj */
    293static int sw_sync_debugfs_open(struct inode *inode, struct file *file)
    294{
    295	struct sync_timeline *obj;
    296	char task_comm[TASK_COMM_LEN];
    297
    298	get_task_comm(task_comm, current);
    299
    300	obj = sync_timeline_create(task_comm);
    301	if (!obj)
    302		return -ENOMEM;
    303
    304	file->private_data = obj;
    305
    306	return 0;
    307}
    308
    309static int sw_sync_debugfs_release(struct inode *inode, struct file *file)
    310{
    311	struct sync_timeline *obj = file->private_data;
    312	struct sync_pt *pt, *next;
    313
    314	spin_lock_irq(&obj->lock);
    315
    316	list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
    317		dma_fence_set_error(&pt->base, -ENOENT);
    318		dma_fence_signal_locked(&pt->base);
    319	}
    320
    321	spin_unlock_irq(&obj->lock);
    322
    323	sync_timeline_put(obj);
    324	return 0;
    325}
    326
    327static long sw_sync_ioctl_create_fence(struct sync_timeline *obj,
    328				       unsigned long arg)
    329{
    330	int fd = get_unused_fd_flags(O_CLOEXEC);
    331	int err;
    332	struct sync_pt *pt;
    333	struct sync_file *sync_file;
    334	struct sw_sync_create_fence_data data;
    335
    336	if (fd < 0)
    337		return fd;
    338
    339	if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
    340		err = -EFAULT;
    341		goto err;
    342	}
    343
    344	pt = sync_pt_create(obj, data.value);
    345	if (!pt) {
    346		err = -ENOMEM;
    347		goto err;
    348	}
    349
    350	sync_file = sync_file_create(&pt->base);
    351	dma_fence_put(&pt->base);
    352	if (!sync_file) {
    353		err = -ENOMEM;
    354		goto err;
    355	}
    356
    357	data.fence = fd;
    358	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
    359		fput(sync_file->file);
    360		err = -EFAULT;
    361		goto err;
    362	}
    363
    364	fd_install(fd, sync_file->file);
    365
    366	return 0;
    367
    368err:
    369	put_unused_fd(fd);
    370	return err;
    371}
    372
    373static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg)
    374{
    375	u32 value;
    376
    377	if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
    378		return -EFAULT;
    379
    380	while (value > INT_MAX)  {
    381		sync_timeline_signal(obj, INT_MAX);
    382		value -= INT_MAX;
    383	}
    384
    385	sync_timeline_signal(obj, value);
    386
    387	return 0;
    388}
    389
    390static long sw_sync_ioctl(struct file *file, unsigned int cmd,
    391			  unsigned long arg)
    392{
    393	struct sync_timeline *obj = file->private_data;
    394
    395	switch (cmd) {
    396	case SW_SYNC_IOC_CREATE_FENCE:
    397		return sw_sync_ioctl_create_fence(obj, arg);
    398
    399	case SW_SYNC_IOC_INC:
    400		return sw_sync_ioctl_inc(obj, arg);
    401
    402	default:
    403		return -ENOTTY;
    404	}
    405}
    406
    407const struct file_operations sw_sync_debugfs_fops = {
    408	.open           = sw_sync_debugfs_open,
    409	.release        = sw_sync_debugfs_release,
    410	.unlocked_ioctl = sw_sync_ioctl,
    411	.compat_ioctl	= compat_ptr_ioctl,
    412};