WIP
diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c
index f5061de..74c131b 100644
--- a/drivers/dma-buf/sync_file.c
+++ b/drivers/dma-buf/sync_file.c
@@ -49,6 +49,8 @@
 	init_waitqueue_head(&sync_file->wq);
 
 	INIT_LIST_HEAD(&sync_file->cb.node);
+	INIT_LIST_HEAD(&sync_file->chain);
+	INIT_LIST_HEAD(&sync_file->chain_cb.node);
 
 	return sync_file;
 
@@ -66,6 +68,20 @@
 	wake_up_all(&sync_file->wq);
 }
 
+static void fence_chain_cb_func(struct fence *f, struct fence_cb *cb)
+{
+	struct sync_file *sync_file, *sync_chain;
+
+	sync_file = container_of(cb, struct sync_file, chain_cb);
+
+	list_for_each_entry(sync_chain, &sync_file->chain, chain) {
+		pr_err("sync_chain %p\n",  sync_chain);
+		fence_signal(sync_chain->fence);
+		fence_put(sync_chain->fence);
+	}
+	//XXX still not cleaning the list up
+}
+
 /**
  * sync_file_create() - creates a sync file
  * @fence:	fence to add to the sync_fence
@@ -446,6 +462,59 @@
 	return ret;
 }
 
+static long sync_file_ioctl_chain_fence(struct sync_file *sync_file,
+					unsigned long arg)
+{
+	struct sync_chain_data chain;
+	struct sync_file *sync_chain;
+	int *fences;
+	int i;
+
+	if (copy_from_user(&chain, (void __user *)arg, sizeof(chain)))
+		return -EFAULT;
+
+	if (!chain.fences_count)
+		return -EINVAL;
+
+	if (chain.flags)
+		return -EINVAL;
+
+	fences = kcalloc(chain.fences_count, sizeof(*fences), GFP_KERNEL);
+	if (!fences)
+		return -ENOMEM;
+
+	if (copy_from_user(fences, u64_to_user_ptr(chain.fences),
+			   sizeof(int) * chain.fences_count)) {
+		kfree(fences);
+		return -EFAULT;
+	}
+
+	for (i = 0 ; i < chain.fences_count ; i++) {
+		sync_chain = sync_file_fdget(fences[i]);
+		if (!sync_chain)
+			goto out;
+
+		if (fence_is_signaled(sync_chain->fence)) {
+		    fence_signal(sync_chain->fence);
+		    fence_put(sync_chain->fence);
+		} else {
+			if (list_empty(&sync_file->chain))
+				fence_add_callback(sync_file->fence,
+						   &sync_file->chain_cb,
+						   fence_chain_cb_func);
+			list_add(&sync_chain->chain, &sync_file->chain);
+			//FIXME: check fence_add_callback return
+		}
+	}
+
+	return 0;
+
+out:
+	//XXx put fences
+	kfree(fences);
+	return -EINVAL;
+}
+
 static long sync_file_ioctl(struct file *file, unsigned int cmd,
 			    unsigned long arg)
 {
@@ -458,6 +527,8 @@
 	case SYNC_IOC_FILE_INFO:
 		return sync_file_ioctl_fence_info(sync_file, arg);
 
+	case SYNC_IOC_CHAIN:
+		return sync_file_ioctl_chain_fence(sync_file, arg);
 	default:
 		return -ENOTTY;
 	}
diff --git a/include/linux/sync_file.h b/include/linux/sync_file.h
index aa17ccf..1605b0e 100644
--- a/include/linux/sync_file.h
+++ b/include/linux/sync_file.h
@@ -43,6 +43,8 @@
 
 	struct fence		*fence;
 	struct fence_cb cb;
+	struct list_head	chain;
+	struct fence_cb		chain_cb;
 };
 
 #define POLL_ENABLED FENCE_FLAG_USER_BITS
diff --git a/include/uapi/linux/sync_file.h b/include/uapi/linux/sync_file.h
index ee3971a..257d698 100644
--- a/include/uapi/linux/sync_file.h
+++ b/include/uapi/linux/sync_file.h
@@ -15,6 +15,16 @@
 #include <linux/types.h>
 
 /**
+ * struct sync_chain_data - data passed to merge ioctl
+ * @flags:	chain_data flags
+ */
+struct sync_chain_data {
+	__u32	fences_count;
+	__u32	flags;
+	__u64	fences;
+};
+
+/**
  * struct sync_merge_data - data passed to merge ioctl
  * @name:	name of new fence
  * @fd2:	file descriptor of second fence
@@ -105,7 +115,14 @@
 #define SYNC_IOC_FILE_INFO	_IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
 
 /**
+ * DOC: SYNC_IOC_CHAIN_FENCE - chain fences to a fence
+ *
+ */
+#define SYNC_IOC_CHAIN _IOWR(SYNC_IOC_MAGIC, 5, struct sync_chain_data)
+
+/**
  * DOC: SYNC_IOC_CREATE_FENCE - merge two fences
+ * XXX: this will become a syscall
  *
  */
 #define SYNC_IOC_CREATE_FENCE	_IOWR(SYNC_IOC_MAGIC, 5, struct sync_empty_fence)