Linux Audio

Check our new training course

Embedded Linux training

Mar 31-Apr 8, 2025
Register
Loading...
Note: File does not exist in v6.8.
   1/*
   2 * drivers/base/sync.c
   3 *
   4 * Copyright (C) 2012 Google, Inc.
   5 *
   6 * This software is licensed under the terms of the GNU General Public
   7 * License version 2, as published by the Free Software Foundation, and
   8 * may be copied, distributed, and modified under those terms.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 */
  16
  17#include <linux/debugfs.h>
  18#include <linux/export.h>
  19#include <linux/file.h>
  20#include <linux/fs.h>
  21#include <linux/kernel.h>
  22#include <linux/poll.h>
  23#include <linux/sched.h>
  24#include <linux/seq_file.h>
  25#include <linux/slab.h>
  26#include <linux/uaccess.h>
  27#include <linux/anon_inodes.h>
  28
  29#include "sync.h"
  30
  31#define CREATE_TRACE_POINTS
  32#include "trace/sync.h"
  33
  34static void sync_fence_signal_pt(struct sync_pt *pt);
  35static int _sync_pt_has_signaled(struct sync_pt *pt);
  36static void sync_fence_free(struct kref *kref);
  37static void sync_dump(void);
  38
  39static LIST_HEAD(sync_timeline_list_head);
  40static DEFINE_SPINLOCK(sync_timeline_list_lock);
  41
  42static LIST_HEAD(sync_fence_list_head);
  43static DEFINE_SPINLOCK(sync_fence_list_lock);
  44
  45struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
  46					   int size, const char *name)
  47{
  48	struct sync_timeline *obj;
  49	unsigned long flags;
  50
  51	if (size < sizeof(struct sync_timeline))
  52		return NULL;
  53
  54	obj = kzalloc(size, GFP_KERNEL);
  55	if (obj == NULL)
  56		return NULL;
  57
  58	kref_init(&obj->kref);
  59	obj->ops = ops;
  60	strlcpy(obj->name, name, sizeof(obj->name));
  61
  62	INIT_LIST_HEAD(&obj->child_list_head);
  63	spin_lock_init(&obj->child_list_lock);
  64
  65	INIT_LIST_HEAD(&obj->active_list_head);
  66	spin_lock_init(&obj->active_list_lock);
  67
  68	spin_lock_irqsave(&sync_timeline_list_lock, flags);
  69	list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
  70	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
  71
  72	return obj;
  73}
  74EXPORT_SYMBOL(sync_timeline_create);
  75
  76static void sync_timeline_free(struct kref *kref)
  77{
  78	struct sync_timeline *obj =
  79		container_of(kref, struct sync_timeline, kref);
  80	unsigned long flags;
  81
  82	spin_lock_irqsave(&sync_timeline_list_lock, flags);
  83	list_del(&obj->sync_timeline_list);
  84	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
  85
  86	if (obj->ops->release_obj)
  87		obj->ops->release_obj(obj);
  88
  89	kfree(obj);
  90}
  91
  92void sync_timeline_destroy(struct sync_timeline *obj)
  93{
  94	obj->destroyed = true;
  95	smp_wmb();
  96
  97	/*
  98	 * signal any children that their parent is going away.
  99	 */
 100	sync_timeline_signal(obj);
 101
 102	kref_put(&obj->kref, sync_timeline_free);
 103}
 104EXPORT_SYMBOL(sync_timeline_destroy);
 105
 106static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt)
 107{
 108	unsigned long flags;
 109
 110	pt->parent = obj;
 111
 112	spin_lock_irqsave(&obj->child_list_lock, flags);
 113	list_add_tail(&pt->child_list, &obj->child_list_head);
 114	spin_unlock_irqrestore(&obj->child_list_lock, flags);
 115}
 116
 117static void sync_timeline_remove_pt(struct sync_pt *pt)
 118{
 119	struct sync_timeline *obj = pt->parent;
 120	unsigned long flags;
 121
 122	spin_lock_irqsave(&obj->active_list_lock, flags);
 123	if (!list_empty(&pt->active_list))
 124		list_del_init(&pt->active_list);
 125	spin_unlock_irqrestore(&obj->active_list_lock, flags);
 126
 127	spin_lock_irqsave(&obj->child_list_lock, flags);
 128	if (!list_empty(&pt->child_list))
 129		list_del_init(&pt->child_list);
 130
 131	spin_unlock_irqrestore(&obj->child_list_lock, flags);
 132}
 133
 134void sync_timeline_signal(struct sync_timeline *obj)
 135{
 136	unsigned long flags;
 137	LIST_HEAD(signaled_pts);
 138	struct list_head *pos, *n;
 139
 140	trace_sync_timeline(obj);
 141
 142	spin_lock_irqsave(&obj->active_list_lock, flags);
 143
 144	list_for_each_safe(pos, n, &obj->active_list_head) {
 145		struct sync_pt *pt =
 146			container_of(pos, struct sync_pt, active_list);
 147
 148		if (_sync_pt_has_signaled(pt)) {
 149			list_del_init(pos);
 150			list_add(&pt->signaled_list, &signaled_pts);
 151			kref_get(&pt->fence->kref);
 152		}
 153	}
 154
 155	spin_unlock_irqrestore(&obj->active_list_lock, flags);
 156
 157	list_for_each_safe(pos, n, &signaled_pts) {
 158		struct sync_pt *pt =
 159			container_of(pos, struct sync_pt, signaled_list);
 160
 161		list_del_init(pos);
 162		sync_fence_signal_pt(pt);
 163		kref_put(&pt->fence->kref, sync_fence_free);
 164	}
 165}
 166EXPORT_SYMBOL(sync_timeline_signal);
 167
 168struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size)
 169{
 170	struct sync_pt *pt;
 171
 172	if (size < sizeof(struct sync_pt))
 173		return NULL;
 174
 175	pt = kzalloc(size, GFP_KERNEL);
 176	if (pt == NULL)
 177		return NULL;
 178
 179	INIT_LIST_HEAD(&pt->active_list);
 180	kref_get(&parent->kref);
 181	sync_timeline_add_pt(parent, pt);
 182
 183	return pt;
 184}
 185EXPORT_SYMBOL(sync_pt_create);
 186
 187void sync_pt_free(struct sync_pt *pt)
 188{
 189	if (pt->parent->ops->free_pt)
 190		pt->parent->ops->free_pt(pt);
 191
 192	sync_timeline_remove_pt(pt);
 193
 194	kref_put(&pt->parent->kref, sync_timeline_free);
 195
 196	kfree(pt);
 197}
 198EXPORT_SYMBOL(sync_pt_free);
 199
 200/* call with pt->parent->active_list_lock held */
 201static int _sync_pt_has_signaled(struct sync_pt *pt)
 202{
 203	int old_status = pt->status;
 204
 205	if (!pt->status)
 206		pt->status = pt->parent->ops->has_signaled(pt);
 207
 208	if (!pt->status && pt->parent->destroyed)
 209		pt->status = -ENOENT;
 210
 211	if (pt->status != old_status)
 212		pt->timestamp = ktime_get();
 213
 214	return pt->status;
 215}
 216
 217static struct sync_pt *sync_pt_dup(struct sync_pt *pt)
 218{
 219	return pt->parent->ops->dup(pt);
 220}
 221
 222/* Adds a sync pt to the active queue.  Called when added to a fence */
 223static void sync_pt_activate(struct sync_pt *pt)
 224{
 225	struct sync_timeline *obj = pt->parent;
 226	unsigned long flags;
 227	int err;
 228
 229	spin_lock_irqsave(&obj->active_list_lock, flags);
 230
 231	err = _sync_pt_has_signaled(pt);
 232	if (err != 0)
 233		goto out;
 234
 235	list_add_tail(&pt->active_list, &obj->active_list_head);
 236
 237out:
 238	spin_unlock_irqrestore(&obj->active_list_lock, flags);
 239}
 240
 241static int sync_fence_release(struct inode *inode, struct file *file);
 242static unsigned int sync_fence_poll(struct file *file, poll_table *wait);
 243static long sync_fence_ioctl(struct file *file, unsigned int cmd,
 244			     unsigned long arg);
 245
 246
 247static const struct file_operations sync_fence_fops = {
 248	.release = sync_fence_release,
 249	.poll = sync_fence_poll,
 250	.unlocked_ioctl = sync_fence_ioctl,
 251	.compat_ioctl = sync_fence_ioctl,
 252};
 253
 254static struct sync_fence *sync_fence_alloc(const char *name)
 255{
 256	struct sync_fence *fence;
 257	unsigned long flags;
 258
 259	fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL);
 260	if (fence == NULL)
 261		return NULL;
 262
 263	fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,
 264					 fence, 0);
 265	if (IS_ERR(fence->file))
 266		goto err;
 267
 268	kref_init(&fence->kref);
 269	strlcpy(fence->name, name, sizeof(fence->name));
 270
 271	INIT_LIST_HEAD(&fence->pt_list_head);
 272	INIT_LIST_HEAD(&fence->waiter_list_head);
 273	spin_lock_init(&fence->waiter_list_lock);
 274
 275	init_waitqueue_head(&fence->wq);
 276
 277	spin_lock_irqsave(&sync_fence_list_lock, flags);
 278	list_add_tail(&fence->sync_fence_list, &sync_fence_list_head);
 279	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
 280
 281	return fence;
 282
 283err:
 284	kfree(fence);
 285	return NULL;
 286}
 287
 288/* TODO: implement a create which takes more that one sync_pt */
 289struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
 290{
 291	struct sync_fence *fence;
 292
 293	if (pt->fence)
 294		return NULL;
 295
 296	fence = sync_fence_alloc(name);
 297	if (fence == NULL)
 298		return NULL;
 299
 300	pt->fence = fence;
 301	list_add(&pt->pt_list, &fence->pt_list_head);
 302	sync_pt_activate(pt);
 303
 304	/*
 305	 * signal the fence in case pt was activated before
 306	 * sync_pt_activate(pt) was called
 307	 */
 308	sync_fence_signal_pt(pt);
 309
 310	return fence;
 311}
 312EXPORT_SYMBOL(sync_fence_create);
 313
 314static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src)
 315{
 316	struct list_head *pos;
 317
 318	list_for_each(pos, &src->pt_list_head) {
 319		struct sync_pt *orig_pt =
 320			container_of(pos, struct sync_pt, pt_list);
 321		struct sync_pt *new_pt = sync_pt_dup(orig_pt);
 322
 323		if (new_pt == NULL)
 324			return -ENOMEM;
 325
 326		new_pt->fence = dst;
 327		list_add(&new_pt->pt_list, &dst->pt_list_head);
 328	}
 329
 330	return 0;
 331}
 332
 333static int sync_fence_merge_pts(struct sync_fence *dst, struct sync_fence *src)
 334{
 335	struct list_head *src_pos, *dst_pos, *n;
 336
 337	list_for_each(src_pos, &src->pt_list_head) {
 338		struct sync_pt *src_pt =
 339			container_of(src_pos, struct sync_pt, pt_list);
 340		bool collapsed = false;
 341
 342		list_for_each_safe(dst_pos, n, &dst->pt_list_head) {
 343			struct sync_pt *dst_pt =
 344				container_of(dst_pos, struct sync_pt, pt_list);
 345			/* collapse two sync_pts on the same timeline
 346			 * to a single sync_pt that will signal at
 347			 * the later of the two
 348			 */
 349			if (dst_pt->parent == src_pt->parent) {
 350				if (dst_pt->parent->ops->compare(dst_pt, src_pt)
 351						 == -1) {
 352					struct sync_pt *new_pt =
 353						sync_pt_dup(src_pt);
 354					if (new_pt == NULL)
 355						return -ENOMEM;
 356
 357					new_pt->fence = dst;
 358					list_replace(&dst_pt->pt_list,
 359						     &new_pt->pt_list);
 360					sync_pt_free(dst_pt);
 361				}
 362				collapsed = true;
 363				break;
 364			}
 365		}
 366
 367		if (!collapsed) {
 368			struct sync_pt *new_pt = sync_pt_dup(src_pt);
 369
 370			if (new_pt == NULL)
 371				return -ENOMEM;
 372
 373			new_pt->fence = dst;
 374			list_add(&new_pt->pt_list, &dst->pt_list_head);
 375		}
 376	}
 377
 378	return 0;
 379}
 380
 381static void sync_fence_detach_pts(struct sync_fence *fence)
 382{
 383	struct list_head *pos, *n;
 384
 385	list_for_each_safe(pos, n, &fence->pt_list_head) {
 386		struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
 387		sync_timeline_remove_pt(pt);
 388	}
 389}
 390
 391static void sync_fence_free_pts(struct sync_fence *fence)
 392{
 393	struct list_head *pos, *n;
 394
 395	list_for_each_safe(pos, n, &fence->pt_list_head) {
 396		struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
 397		sync_pt_free(pt);
 398	}
 399}
 400
 401struct sync_fence *sync_fence_fdget(int fd)
 402{
 403	struct file *file = fget(fd);
 404
 405	if (file == NULL)
 406		return NULL;
 407
 408	if (file->f_op != &sync_fence_fops)
 409		goto err;
 410
 411	return file->private_data;
 412
 413err:
 414	fput(file);
 415	return NULL;
 416}
 417EXPORT_SYMBOL(sync_fence_fdget);
 418
 419void sync_fence_put(struct sync_fence *fence)
 420{
 421	fput(fence->file);
 422}
 423EXPORT_SYMBOL(sync_fence_put);
 424
 425void sync_fence_install(struct sync_fence *fence, int fd)
 426{
 427	fd_install(fd, fence->file);
 428}
 429EXPORT_SYMBOL(sync_fence_install);
 430
 431static int sync_fence_get_status(struct sync_fence *fence)
 432{
 433	struct list_head *pos;
 434	int status = 1;
 435
 436	list_for_each(pos, &fence->pt_list_head) {
 437		struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
 438		int pt_status = pt->status;
 439
 440		if (pt_status < 0) {
 441			status = pt_status;
 442			break;
 443		} else if (status == 1) {
 444			status = pt_status;
 445		}
 446	}
 447
 448	return status;
 449}
 450
 451struct sync_fence *sync_fence_merge(const char *name,
 452				    struct sync_fence *a, struct sync_fence *b)
 453{
 454	struct sync_fence *fence;
 455	struct list_head *pos;
 456	int err;
 457
 458	fence = sync_fence_alloc(name);
 459	if (fence == NULL)
 460		return NULL;
 461
 462	err = sync_fence_copy_pts(fence, a);
 463	if (err < 0)
 464		goto err;
 465
 466	err = sync_fence_merge_pts(fence, b);
 467	if (err < 0)
 468		goto err;
 469
 470	list_for_each(pos, &fence->pt_list_head) {
 471		struct sync_pt *pt =
 472			container_of(pos, struct sync_pt, pt_list);
 473		sync_pt_activate(pt);
 474	}
 475
 476	/*
 477	 * signal the fence in case one of it's pts were activated before
 478	 * they were activated
 479	 */
 480	sync_fence_signal_pt(list_first_entry(&fence->pt_list_head,
 481					      struct sync_pt,
 482					      pt_list));
 483
 484	return fence;
 485err:
 486	sync_fence_free_pts(fence);
 487	kfree(fence);
 488	return NULL;
 489}
 490EXPORT_SYMBOL(sync_fence_merge);
 491
 492static void sync_fence_signal_pt(struct sync_pt *pt)
 493{
 494	LIST_HEAD(signaled_waiters);
 495	struct sync_fence *fence = pt->fence;
 496	struct list_head *pos;
 497	struct list_head *n;
 498	unsigned long flags;
 499	int status;
 500
 501	status = sync_fence_get_status(fence);
 502
 503	spin_lock_irqsave(&fence->waiter_list_lock, flags);
 504	/*
 505	 * this should protect against two threads racing on the signaled
 506	 * false -> true transition
 507	 */
 508	if (status && !fence->status) {
 509		list_for_each_safe(pos, n, &fence->waiter_list_head)
 510			list_move(pos, &signaled_waiters);
 511
 512		fence->status = status;
 513	} else {
 514		status = 0;
 515	}
 516	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
 517
 518	if (status) {
 519		list_for_each_safe(pos, n, &signaled_waiters) {
 520			struct sync_fence_waiter *waiter =
 521				container_of(pos, struct sync_fence_waiter,
 522					     waiter_list);
 523
 524			list_del(pos);
 525			waiter->callback(fence, waiter);
 526		}
 527		wake_up(&fence->wq);
 528	}
 529}
 530
 531int sync_fence_wait_async(struct sync_fence *fence,
 532			  struct sync_fence_waiter *waiter)
 533{
 534	unsigned long flags;
 535	int err = 0;
 536
 537	spin_lock_irqsave(&fence->waiter_list_lock, flags);
 538
 539	if (fence->status) {
 540		err = fence->status;
 541		goto out;
 542	}
 543
 544	list_add_tail(&waiter->waiter_list, &fence->waiter_list_head);
 545out:
 546	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
 547
 548	return err;
 549}
 550EXPORT_SYMBOL(sync_fence_wait_async);
 551
 552int sync_fence_cancel_async(struct sync_fence *fence,
 553			     struct sync_fence_waiter *waiter)
 554{
 555	struct list_head *pos;
 556	struct list_head *n;
 557	unsigned long flags;
 558	int ret = -ENOENT;
 559
 560	spin_lock_irqsave(&fence->waiter_list_lock, flags);
 561	/*
 562	 * Make sure waiter is still in waiter_list because it is possible for
 563	 * the waiter to be removed from the list while the callback is still
 564	 * pending.
 565	 */
 566	list_for_each_safe(pos, n, &fence->waiter_list_head) {
 567		struct sync_fence_waiter *list_waiter =
 568			container_of(pos, struct sync_fence_waiter,
 569				     waiter_list);
 570		if (list_waiter == waiter) {
 571			list_del(pos);
 572			ret = 0;
 573			break;
 574		}
 575	}
 576	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
 577	return ret;
 578}
 579EXPORT_SYMBOL(sync_fence_cancel_async);
 580
 581static bool sync_fence_check(struct sync_fence *fence)
 582{
 583	/*
 584	 * Make sure that reads to fence->status are ordered with the
 585	 * wait queue event triggering
 586	 */
 587	smp_rmb();
 588	return fence->status != 0;
 589}
 590
 591int sync_fence_wait(struct sync_fence *fence, long timeout)
 592{
 593	int err = 0;
 594	struct sync_pt *pt;
 595
 596	trace_sync_wait(fence, 1);
 597	list_for_each_entry(pt, &fence->pt_list_head, pt_list)
 598		trace_sync_pt(pt);
 599
 600	if (timeout > 0) {
 601		timeout = msecs_to_jiffies(timeout);
 602		err = wait_event_interruptible_timeout(fence->wq,
 603						       sync_fence_check(fence),
 604						       timeout);
 605	} else if (timeout < 0) {
 606		err = wait_event_interruptible(fence->wq,
 607					       sync_fence_check(fence));
 608	}
 609	trace_sync_wait(fence, 0);
 610
 611	if (err < 0)
 612		return err;
 613
 614	if (fence->status < 0) {
 615		pr_info("fence error %d on [%p]\n", fence->status, fence);
 616		sync_dump();
 617		return fence->status;
 618	}
 619
 620	if (fence->status == 0) {
 621		if (timeout > 0) {
 622			pr_info("fence timeout on [%p] after %dms\n", fence,
 623				jiffies_to_msecs(timeout));
 624			sync_dump();
 625		}
 626		return -ETIME;
 627	}
 628
 629	return 0;
 630}
 631EXPORT_SYMBOL(sync_fence_wait);
 632
 633static void sync_fence_free(struct kref *kref)
 634{
 635	struct sync_fence *fence = container_of(kref, struct sync_fence, kref);
 636
 637	sync_fence_free_pts(fence);
 638
 639	kfree(fence);
 640}
 641
 642static int sync_fence_release(struct inode *inode, struct file *file)
 643{
 644	struct sync_fence *fence = file->private_data;
 645	unsigned long flags;
 646
 647	/*
 648	 * We need to remove all ways to access this fence before droping
 649	 * our ref.
 650	 *
 651	 * start with its membership in the global fence list
 652	 */
 653	spin_lock_irqsave(&sync_fence_list_lock, flags);
 654	list_del(&fence->sync_fence_list);
 655	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
 656
 657	/*
 658	 * remove its pts from their parents so that sync_timeline_signal()
 659	 * can't reference the fence.
 660	 */
 661	sync_fence_detach_pts(fence);
 662
 663	kref_put(&fence->kref, sync_fence_free);
 664
 665	return 0;
 666}
 667
 668static unsigned int sync_fence_poll(struct file *file, poll_table *wait)
 669{
 670	struct sync_fence *fence = file->private_data;
 671
 672	poll_wait(file, &fence->wq, wait);
 673
 674	/*
 675	 * Make sure that reads to fence->status are ordered with the
 676	 * wait queue event triggering
 677	 */
 678	smp_rmb();
 679
 680	if (fence->status == 1)
 681		return POLLIN;
 682	else if (fence->status < 0)
 683		return POLLERR;
 684	else
 685		return 0;
 686}
 687
 688static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg)
 689{
 690	__s32 value;
 691
 692	if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
 693		return -EFAULT;
 694
 695	return sync_fence_wait(fence, value);
 696}
 697
 698static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg)
 699{
 700	int fd = get_unused_fd_flags(O_CLOEXEC);
 701	int err;
 702	struct sync_fence *fence2, *fence3;
 703	struct sync_merge_data data;
 704
 705	if (fd < 0)
 706		return fd;
 707
 708	if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
 709		err = -EFAULT;
 710		goto err_put_fd;
 711	}
 712
 713	fence2 = sync_fence_fdget(data.fd2);
 714	if (fence2 == NULL) {
 715		err = -ENOENT;
 716		goto err_put_fd;
 717	}
 718
 719	data.name[sizeof(data.name) - 1] = '\0';
 720	fence3 = sync_fence_merge(data.name, fence, fence2);
 721	if (fence3 == NULL) {
 722		err = -ENOMEM;
 723		goto err_put_fence2;
 724	}
 725
 726	data.fence = fd;
 727	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
 728		err = -EFAULT;
 729		goto err_put_fence3;
 730	}
 731
 732	sync_fence_install(fence3, fd);
 733	sync_fence_put(fence2);
 734	return 0;
 735
 736err_put_fence3:
 737	sync_fence_put(fence3);
 738
 739err_put_fence2:
 740	sync_fence_put(fence2);
 741
 742err_put_fd:
 743	put_unused_fd(fd);
 744	return err;
 745}
 746
 747static int sync_fill_pt_info(struct sync_pt *pt, void *data, int size)
 748{
 749	struct sync_pt_info *info = data;
 750	int ret;
 751
 752	if (size < sizeof(struct sync_pt_info))
 753		return -ENOMEM;
 754
 755	info->len = sizeof(struct sync_pt_info);
 756
 757	if (pt->parent->ops->fill_driver_data) {
 758		ret = pt->parent->ops->fill_driver_data(pt, info->driver_data,
 759							size - sizeof(*info));
 760		if (ret < 0)
 761			return ret;
 762
 763		info->len += ret;
 764	}
 765
 766	strlcpy(info->obj_name, pt->parent->name, sizeof(info->obj_name));
 767	strlcpy(info->driver_name, pt->parent->ops->driver_name,
 768		sizeof(info->driver_name));
 769	info->status = pt->status;
 770	info->timestamp_ns = ktime_to_ns(pt->timestamp);
 771
 772	return info->len;
 773}
 774
 775static long sync_fence_ioctl_fence_info(struct sync_fence *fence,
 776					unsigned long arg)
 777{
 778	struct sync_fence_info_data *data;
 779	struct list_head *pos;
 780	__u32 size;
 781	__u32 len = 0;
 782	int ret;
 783
 784	if (copy_from_user(&size, (void __user *)arg, sizeof(size)))
 785		return -EFAULT;
 786
 787	if (size < sizeof(struct sync_fence_info_data))
 788		return -EINVAL;
 789
 790	if (size > 4096)
 791		size = 4096;
 792
 793	data = kzalloc(size, GFP_KERNEL);
 794	if (data == NULL)
 795		return -ENOMEM;
 796
 797	strlcpy(data->name, fence->name, sizeof(data->name));
 798	data->status = fence->status;
 799	len = sizeof(struct sync_fence_info_data);
 800
 801	list_for_each(pos, &fence->pt_list_head) {
 802		struct sync_pt *pt =
 803			container_of(pos, struct sync_pt, pt_list);
 804
 805		ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len);
 806
 807		if (ret < 0)
 808			goto out;
 809
 810		len += ret;
 811	}
 812
 813	data->len = len;
 814
 815	if (copy_to_user((void __user *)arg, data, len))
 816		ret = -EFAULT;
 817	else
 818		ret = 0;
 819
 820out:
 821	kfree(data);
 822
 823	return ret;
 824}
 825
 826static long sync_fence_ioctl(struct file *file, unsigned int cmd,
 827			     unsigned long arg)
 828{
 829	struct sync_fence *fence = file->private_data;
 830	switch (cmd) {
 831	case SYNC_IOC_WAIT:
 832		return sync_fence_ioctl_wait(fence, arg);
 833
 834	case SYNC_IOC_MERGE:
 835		return sync_fence_ioctl_merge(fence, arg);
 836
 837	case SYNC_IOC_FENCE_INFO:
 838		return sync_fence_ioctl_fence_info(fence, arg);
 839
 840	default:
 841		return -ENOTTY;
 842	}
 843}
 844
 845#ifdef CONFIG_DEBUG_FS
 846static const char *sync_status_str(int status)
 847{
 848	if (status > 0)
 849		return "signaled";
 850	else if (status == 0)
 851		return "active";
 852	else
 853		return "error";
 854}
 855
 856static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
 857{
 858	int status = pt->status;
 859	seq_printf(s, "  %s%spt %s",
 860		   fence ? pt->parent->name : "",
 861		   fence ? "_" : "",
 862		   sync_status_str(status));
 863	if (pt->status) {
 864		struct timeval tv = ktime_to_timeval(pt->timestamp);
 865		seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec);
 866	}
 867
 868	if (pt->parent->ops->timeline_value_str &&
 869	    pt->parent->ops->pt_value_str) {
 870		char value[64];
 871		pt->parent->ops->pt_value_str(pt, value, sizeof(value));
 872		seq_printf(s, ": %s", value);
 873		if (fence) {
 874			pt->parent->ops->timeline_value_str(pt->parent, value,
 875						    sizeof(value));
 876			seq_printf(s, " / %s", value);
 877		}
 878	} else if (pt->parent->ops->print_pt) {
 879		seq_puts(s, ": ");
 880		pt->parent->ops->print_pt(s, pt);
 881	}
 882
 883	seq_puts(s, "\n");
 884}
 885
 886static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
 887{
 888	struct list_head *pos;
 889	unsigned long flags;
 890
 891	seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
 892
 893	if (obj->ops->timeline_value_str) {
 894		char value[64];
 895		obj->ops->timeline_value_str(obj, value, sizeof(value));
 896		seq_printf(s, ": %s", value);
 897	} else if (obj->ops->print_obj) {
 898		seq_puts(s, ": ");
 899		obj->ops->print_obj(s, obj);
 900	}
 901
 902	seq_puts(s, "\n");
 903
 904	spin_lock_irqsave(&obj->child_list_lock, flags);
 905	list_for_each(pos, &obj->child_list_head) {
 906		struct sync_pt *pt =
 907			container_of(pos, struct sync_pt, child_list);
 908		sync_print_pt(s, pt, false);
 909	}
 910	spin_unlock_irqrestore(&obj->child_list_lock, flags);
 911}
 912
 913static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
 914{
 915	struct list_head *pos;
 916	unsigned long flags;
 917
 918	seq_printf(s, "[%p] %s: %s\n", fence, fence->name,
 919		   sync_status_str(fence->status));
 920
 921	list_for_each(pos, &fence->pt_list_head) {
 922		struct sync_pt *pt =
 923			container_of(pos, struct sync_pt, pt_list);
 924		sync_print_pt(s, pt, true);
 925	}
 926
 927	spin_lock_irqsave(&fence->waiter_list_lock, flags);
 928	list_for_each(pos, &fence->waiter_list_head) {
 929		struct sync_fence_waiter *waiter =
 930			container_of(pos, struct sync_fence_waiter,
 931				     waiter_list);
 932
 933		seq_printf(s, "waiter %pF\n", waiter->callback);
 934	}
 935	spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
 936}
 937
 938static int sync_debugfs_show(struct seq_file *s, void *unused)
 939{
 940	unsigned long flags;
 941	struct list_head *pos;
 942
 943	seq_puts(s, "objs:\n--------------\n");
 944
 945	spin_lock_irqsave(&sync_timeline_list_lock, flags);
 946	list_for_each(pos, &sync_timeline_list_head) {
 947		struct sync_timeline *obj =
 948			container_of(pos, struct sync_timeline,
 949				     sync_timeline_list);
 950
 951		sync_print_obj(s, obj);
 952		seq_puts(s, "\n");
 953	}
 954	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
 955
 956	seq_puts(s, "fences:\n--------------\n");
 957
 958	spin_lock_irqsave(&sync_fence_list_lock, flags);
 959	list_for_each(pos, &sync_fence_list_head) {
 960		struct sync_fence *fence =
 961			container_of(pos, struct sync_fence, sync_fence_list);
 962
 963		sync_print_fence(s, fence);
 964		seq_puts(s, "\n");
 965	}
 966	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
 967	return 0;
 968}
 969
 970static int sync_debugfs_open(struct inode *inode, struct file *file)
 971{
 972	return single_open(file, sync_debugfs_show, inode->i_private);
 973}
 974
 975static const struct file_operations sync_debugfs_fops = {
 976	.open           = sync_debugfs_open,
 977	.read           = seq_read,
 978	.llseek         = seq_lseek,
 979	.release        = single_release,
 980};
 981
 982static __init int sync_debugfs_init(void)
 983{
 984	debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
 985	return 0;
 986}
 987late_initcall(sync_debugfs_init);
 988
 989#define DUMP_CHUNK 256
 990static char sync_dump_buf[64 * 1024];
 991static void sync_dump(void)
 992{
 993	struct seq_file s = {
 994		.buf = sync_dump_buf,
 995		.size = sizeof(sync_dump_buf) - 1,
 996	};
 997	int i;
 998
 999	sync_debugfs_show(&s, NULL);
1000
1001	for (i = 0; i < s.count; i += DUMP_CHUNK) {
1002		if ((s.count - i) > DUMP_CHUNK) {
1003			char c = s.buf[i + DUMP_CHUNK];
1004			s.buf[i + DUMP_CHUNK] = 0;
1005			pr_cont("%s", s.buf + i);
1006			s.buf[i + DUMP_CHUNK] = c;
1007		} else {
1008			s.buf[s.count] = 0;
1009			pr_cont("%s", s.buf + i);
1010		}
1011	}
1012}
1013#else
1014static void sync_dump(void)
1015{
1016}
1017#endif