diff -uprN -X dontdiff linux-2.6.8.1-aioevent/fs/aio.c linux-2.6.8.1-lioevent/fs/aio.c
--- linux-2.6.8.1-aioevent/fs/aio.c	2004-11-03 14:50:33.000000000 +0100
+++ linux-2.6.8.1-lioevent/fs/aio.c	2004-11-03 14:51:10.000000000 +0100
@@ -401,6 +401,7 @@ static struct kiocb fastcall *__aio_get_
 	req->ki_signo = 0;
 	req->ki_notify = 0;
 	req->ki_value = 0;
+	req->ki_lio_event = NULL;
 	req->private = NULL;
 
 	/* Check if the completion queue has enough free space to
@@ -452,6 +453,7 @@ static inline void really_put_req(struct
 	req->ki_signo = 0;
 	req->ki_notify = 0;
 	req->private = NULL;
+	req->ki_lio_event = NULL;
 	kmem_cache_free(kiocb_cachep, req);
 	ctx->reqs_active--;
 
@@ -755,6 +757,24 @@ int fastcall aio_complete(struct kiocb *
 		__aio_send_signal(iocb->ki_pid, iocb->ki_signo, iocb->ki_notify,
 				  (void*)(unsigned long)iocb->ki_value);
 
+	if (iocb->ki_lio_event) {
+		/* decrement and test users counter */
+
+		ret = atomic_dec_and_test(&iocb->ki_lio_event->lio_users);
+
+		if (likely(ret)) {
+			/* last one -> notify process */
+
+			if (iocb->ki_lio_event->lio_signo)
+				__aio_send_signal(iocb->ki_lio_event->lio_pid,
+						 iocb->ki_lio_event->lio_signo,
+						 iocb->ki_lio_event->lio_notify,
+						(void*)(unsigned long)iocb->ki_lio_event->lio_value);
+
+			kfree(iocb->ki_lio_event);
+		}
+	}
+
 	/* everything turned out well, dispose of the aiocb. */
 	ret = __aio_put_req(ctx, iocb);
 
@@ -1039,8 +1059,78 @@ asmlinkage long sys_io_destroy(aio_conte
 	return -EINVAL;
 }
 
+static int create_lio_event(struct lio_event **lio, pid_t pid, int signo, 
+			    void *value)
+{
+	int notify;
+
+	if (pid == 0) {
+		/* notify itself */
+
+		pid = current->pid;
+		notify = IO_NOTIFY_SIGNAL;
+	} else {
+		pid_t group_id;
+
+		/* notify given thread */
+
+		/* caller thread and target thread must be in same
+		 * thread group
+		 */
+
+		read_lock(&tasklist_lock);
+		group_id = find_task_by_pid(pid)->tgid;
+		read_unlock(&tasklist_lock);
+		if (group_id != current->tgid)
+			return -EINVAL;
+		notify = IO_NOTIFY_THREAD_ID;
+	}
+
+	if (*lio != NULL) {
+		int ret;
+
+		/* we break an existing sequence (lio exists)
+		 * close the sequence
+		 */
+
+		ret = atomic_dec_and_test(&((*lio)->lio_users));
+		if (likely(ret)) {
+			if ((*lio)->lio_signo)
+				__aio_send_signal((*lio)->lio_pid, 
+						  (*lio)->lio_signo, 
+					  	  (*lio)->lio_notify,
+						  (void*)(unsigned long)(*lio)->lio_value);
+			kfree(*lio);
+		}
+	}
+
+	if (signo == 0) {
+
+		/* it means we don't want to monitor following commands
+		 * usefull to break current sequence
+		 */
+
+		*lio = NULL;
+
+		return 0;
+	}
+
+
+	*lio = kmalloc(sizeof(*lio), GFP_KERNEL);
+	if (!*lio)
+		return -EAGAIN;
+
+	atomic_set(&((*lio)->lio_users), 1);
+	(*lio)->lio_pid = pid;
+	(*lio)->lio_signo = signo;
+	(*lio)->lio_notify = notify;
+	(*lio)->lio_value = (__u64)(unsigned long)value;
+
+	return 0;
+}
+
 int fastcall io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
-			 struct iocb *iocb)
+			 struct iocb *iocb, struct lio_event *lio)
 {
 	struct kiocb *req;
 	struct file *file;
@@ -1119,6 +1209,7 @@ int fastcall io_submit_one(struct kioctx
 		req->ki_notify = notify;
 		req->ki_value = iocb->aio_value;
 	}
+	req->ki_lio_event = lio;
 
 	buf = (char __user *)(unsigned long)iocb->aio_buf;
 
@@ -1195,6 +1286,7 @@ out_put_req:
 asmlinkage long sys_io_submit(aio_context_t ctx_id, long nr,
 			      struct iocb __user * __user *iocbpp)
 {
+	struct lio_event *lio;
 	struct kioctx *ctx;
 	long ret = 0;
 	int i;
@@ -1215,6 +1307,8 @@ asmlinkage long sys_io_submit(aio_contex
 	 * AKPM: should this return a partial result if some of the IOs were
 	 * successfully submitted?
 	 */
+
+	lio = NULL;
 	for (i=0; i<nr; i++) {
 		struct iocb __user *user_iocb;
 		struct iocb tmp;
@@ -1229,9 +1323,51 @@ asmlinkage long sys_io_submit(aio_contex
 			break;
 		}
 
-		ret = io_submit_one(ctx, user_iocb, &tmp);
-		if (ret)
-			break;
+		if (tmp.aio_lio_opcode == IOCB_CMD_EVENT) {
+
+			/* this command means that all following IO commands
+			 * are in the same group and when the group becomes
+			 * empty (all requests have been processed)
+			 * we must send a signal to a given process or thread
+			 */
+
+			ret = create_lio_event(&lio,
+					       tmp.aio_pid, tmp.aio_signo,
+					       user_iocb);
+			if (ret)
+				break;
+
+			continue;
+		}
+
+		if (lio && ((tmp.aio_lio_opcode == IOCB_CMD_PREAD) ||
+			   (tmp.aio_lio_opcode == IOCB_CMD_PWRITE)) ) {
+
+			atomic_inc(&lio->lio_users);
+			ret = io_submit_one(ctx, user_iocb, &tmp, lio);
+			if (ret)
+				atomic_dec(&lio->lio_users);
+
+			continue;
+
+		} else {
+			ret = io_submit_one(ctx, user_iocb, &tmp, NULL);
+			if (ret)
+				break;
+		}
+	}
+	if (lio) {
+
+		/* close the sequence */
+
+		ret = atomic_dec_and_test(&lio->lio_users);
+		if (likely(ret)) {
+			if (lio->lio_signo)
+				__aio_send_signal(lio->lio_pid, lio->lio_signo, 
+						  lio->lio_notify, 
+						  (void*)(unsigned long)lio->lio_value);
+			kfree(lio);
+		}
 	}
 
 	put_ioctx(ctx);
diff -uprN -X dontdiff linux-2.6.8.1-aioevent/include/linux/aio_abi.h linux-2.6.8.1-lioevent/include/linux/aio_abi.h
--- linux-2.6.8.1-aioevent/include/linux/aio_abi.h	2004-10-21 17:33:43.000000000 +0200
+++ linux-2.6.8.1-lioevent/include/linux/aio_abi.h	2004-10-26 11:27:42.000000000 +0200
@@ -41,6 +41,7 @@ enum {
 	 * IOCB_CMD_POLL = 5,
 	 */
 	IOCB_CMD_NOOP = 6,
+	IOCB_CMD_EVENT = 7,
 };
 
 /* read() from /dev/aio returns these structures. */
diff -uprN -X dontdiff linux-2.6.8.1-aioevent/include/linux/aio.h linux-2.6.8.1-lioevent/include/linux/aio.h
--- linux-2.6.8.1-aioevent/include/linux/aio.h	2004-10-26 11:48:39.000000000 +0200
+++ linux-2.6.8.1-lioevent/include/linux/aio.h	2004-10-28 14:35:48.000000000 +0200
@@ -48,6 +48,14 @@ struct kioctx;
 #define kiocbIsKicked(iocb)	test_bit(KIF_KICKED, &(iocb)->ki_flags)
 #define kiocbIsCancelled(iocb)	test_bit(KIF_CANCELLED, &(iocb)->ki_flags)
 
+struct lio_event {
+	atomic_t	lio_users;
+	__s32		lio_pid;
+        __u16		lio_signo;
+        __u16		lio_notify;
+	__u64		lio_value;
+};
+
 struct kiocb {
 	struct list_head	ki_run_list;
 	long			ki_flags;
@@ -74,6 +82,7 @@ struct kiocb {
 	__u16			ki_signo;
 	__u16			ki_notify;
 	__u64			ki_value;
+	struct lio_event	*ki_lio_event;
 	void			*private;
 };
 
@@ -161,12 +170,13 @@ struct mm_struct;
 extern void FASTCALL(exit_aio(struct mm_struct *mm));
 extern struct kioctx *lookup_ioctx(unsigned long ctx_id);
 extern int FASTCALL(io_submit_one(struct kioctx *ctx,
-			struct iocb __user *user_iocb, struct iocb *iocb));
+			struct iocb __user *user_iocb, struct iocb *iocb,
+			struct lio_event *lio));
 
 /* semi private, but used by the 32bit emulations: */
 struct kioctx *lookup_ioctx(unsigned long ctx_id);
 int FASTCALL(io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
-				  struct iocb *iocb));
+				  struct iocb *iocb, struct lio_event *lio));
 
 #define get_ioctx(kioctx)	do { if (unlikely(atomic_read(&(kioctx)->users) <= 0)) BUG(); atomic_inc(&(kioctx)->users); } while (0)
 #define put_ioctx(kioctx)	do { if (unlikely(atomic_dec_and_test(&(kioctx)->users))) __put_ioctx(kioctx); else if (unlikely(atomic_read(&(kioctx)->users) < 0)) BUG(); } while (0)

