Index: kernel/generic/include/debug.h
===================================================================
--- kernel/generic/include/debug.h	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/include/debug.h	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -55,5 +55,6 @@
 	do { \
 		if (!(expr)) \
-			panic_assert("%s", #expr); \
+			panic_assert("%s() at %s:%u:\n%s", \
+			    __func__, __FILE__, __LINE__, #expr); \
 	} while (0)
 
@@ -72,5 +73,6 @@
 	do { \
 		if (!(expr)) \
-			panic_assert("%s, %s", #expr, msg); \
+			panic_assert("%s() at %s:%u:\n%s, %s", \
+			    __func__, __FILE__, __LINE__, #expr, msg); \
 	} while (0)
 
Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/include/ipc/ipc.h	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -165,5 +165,7 @@
  *                       error is sent back to caller. Otherwise 
  *                       the call is accepted and the response is sent back.
- *                     - the allocated phoneid is passed to userspace 
+ *                     - the hash of the client task is passed to userspace
+ *                       (on the receiving side) as ARG4 of the call.
+ *                     - the hash of the allocated phone is passed to userspace
  *                       (on the receiving side) as ARG5 of the call.
  *
@@ -319,4 +321,7 @@
 typedef struct {
 	sysarg_t args[IPC_CALL_LEN];
+	/** Task which made or forwarded the call with IPC_FF_ROUTE_FROM_ME. */
+	struct task *task;
+	/** Phone which made or last masqueraded this call. */
 	phone_t *phone;
 } ipc_data_t;
@@ -333,5 +338,4 @@
 	 * The caller box is different from sender->answerbox
 	 * for synchronous calls.
-	 *
 	 */
 	answerbox_t *callerbox;
@@ -350,5 +354,4 @@
 	 * cases, we must keep it aside so that the answer is processed
 	 * correctly.
-	 *
 	 */
 	phone_t *caller_phone;
Index: kernel/generic/include/proc/thread.h
===================================================================
--- kernel/generic/include/proc/thread.h	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/include/proc/thread.h	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -91,21 +91,23 @@
 	
 	/** Function implementing the thread. */
-	void (* thread_code)(void *);
+	void (*thread_code)(void *);
 	/** Argument passed to thread_code() function. */
 	void *thread_arg;
 	
 	/**
-	 * From here, the stored context is restored when the thread is
-	 * scheduled.
+	 * From here, the stored context is restored
+	 * when the thread is scheduled.
 	 */
 	context_t saved_context;
-	/**
-	 * From here, the stored timeout context is restored when sleep times
-	 * out.
+	
+	/**
+	 * From here, the stored timeout context
+	 * is restored when sleep times out.
 	 */
 	context_t sleep_timeout_context;
-	/**
-	 * From here, the stored interruption context is restored when sleep is
-	 * interrupted.
+	
+	/**
+	 * From here, the stored interruption context
+	 * is restored when sleep is interrupted.
 	 */
 	context_t sleep_interruption_context;
@@ -125,4 +127,5 @@
 	 */
 	bool in_copy_from_uspace;
+	
 	/**
 	 * True if this thread is executing copy_to_uspace().
@@ -187,4 +190,10 @@
 	
 #ifdef CONFIG_UDEBUG
+	/**
+	 * If true, the scheduler will print a stack trace
+	 * to the kernel console upon scheduling this thread.
+	 */
+	bool btrace;
+	
 	/** Debugging stuff */
 	udebug_thread_t udebug;
@@ -237,4 +246,8 @@
 extern bool thread_exists(thread_t *);
 
+#ifdef CONFIG_UDEBUG
+extern void thread_stack_trace(thread_id_t);
+#endif
+
 /** Fpu context slab cache. */
 extern slab_cache_t *fpu_context_slab;
Index: kernel/generic/src/console/cmd.c
===================================================================
--- kernel/generic/src/console/cmd.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/src/console/cmd.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -78,25 +78,28 @@
 static cmd_info_t help_info = {
 	.name = "help",
-	.description = "List of supported commands.",
+	.description = "List supported commands.",
 	.func = cmd_help,
 	.argc = 0
 };
 
+/* Data and methods for 'reboot' command. */
 static int cmd_reboot(cmd_arg_t *argv);
 static cmd_info_t reboot_info = {
 	.name = "reboot",
-	.description = "Reboot.",
+	.description = "Reboot system.",
 	.func = cmd_reboot,
 	.argc = 0
 };
 
+/* Data and methods for 'uptime' command. */
 static int cmd_uptime(cmd_arg_t *argv);
 static cmd_info_t uptime_info = {
 	.name = "uptime",
-	.description = "Print uptime information.",
+	.description = "Show system uptime.",
 	.func = cmd_uptime,
 	.argc = 0
 };
 
+/* Data and methods for 'continue' command. */
 static int cmd_continue(cmd_arg_t *argv);
 static cmd_info_t continue_info = {
@@ -108,4 +111,6 @@
 
 #ifdef CONFIG_TEST
+
+/* Data and methods for 'test' command. */
 static char test_buf[MAX_CMDLINE + 1];
 static int cmd_test(cmd_arg_t *argv);
@@ -119,5 +124,5 @@
 static cmd_info_t test_info = {
 	.name = "test",
-	.description = "Print list of kernel tests or run a test.",
+	.description = "<test> List kernel tests or run a test.",
 	.func = cmd_test,
 	.argc = 1,
@@ -125,4 +130,5 @@
 };
 
+/* Data and methods for 'bench' command. */
 static int cmd_bench(cmd_arg_t *argv);
 static cmd_arg_t bench_argv[] = {
@@ -138,15 +144,16 @@
 static cmd_info_t bench_info = {
 	.name = "bench",
-	.description = "Run kernel test as benchmark.",
+	.description = "<test> <count> Run kernel test as benchmark.",
 	.func = cmd_bench,
 	.argc = 2,
 	.argv = bench_argv
 };
-#endif
+
+#endif /* CONFIG_TEST */
 
 /* Data and methods for 'description' command. */
 static int cmd_desc(cmd_arg_t *argv);
 static void desc_help(void);
-static char desc_buf[MAX_CMDLINE+1];
+static char desc_buf[MAX_CMDLINE + 1];
 static cmd_arg_t desc_argv = {
 	.type = ARG_TYPE_STRING,
@@ -156,5 +163,5 @@
 static cmd_info_t desc_info = {
 	.name = "describe",
-	.description = "Describe specified command.",
+	.description = "<command> Describe specified command.",
 	.help = desc_help,
 	.func = cmd_desc,
@@ -165,5 +172,5 @@
 /* Data and methods for 'symaddr' command. */
 static int cmd_symaddr(cmd_arg_t *argv);
-static char symaddr_buf[MAX_CMDLINE+1];
+static char symaddr_buf[MAX_CMDLINE + 1];
 static cmd_arg_t symaddr_argv = {
 	.type = ARG_TYPE_STRING,
@@ -173,5 +180,5 @@
 static cmd_info_t symaddr_info = {
 	.name = "symaddr",
-	.description = "Return symbol address.",
+	.description = "<symbol> Return symbol address.",
 	.func = cmd_symaddr,
 	.argc = 1,
@@ -179,5 +186,6 @@
 };
 
-static char set_buf[MAX_CMDLINE+1];
+/* Data and methods for 'set4' command. */
+static char set_buf[MAX_CMDLINE + 1];
 static int cmd_set4(cmd_arg_t *argv);
 static cmd_arg_t set4_argv[] = {
@@ -193,5 +201,5 @@
 static cmd_info_t set4_info = {
 	.name = "set4",
-	.description = "set <dest_addr> <value> - 4byte version",
+	.description = "<addr> <value> Set 4B memory location to a value.",
 	.func = cmd_set4,
 	.argc = 2,
@@ -213,5 +221,5 @@
 static cmd_info_t call0_info = {
 	.name = "call0",
-	.description = "call0 <function> -> call function().",
+	.description = "<function> Call function().",
 	.func = cmd_call0,
 	.argc = 1,
@@ -228,5 +236,5 @@
 static cmd_info_t mcall0_info = {
 	.name = "mcall0",
-	.description = "mcall0 <function> -> call function() on each CPU.",
+	.description = "<function> Call function() on each CPU.",
 	.func = cmd_mcall0,
 	.argc = 1,
@@ -250,5 +258,5 @@
 static cmd_info_t call1_info = {
 	.name = "call1",
-	.description = "call1 <function> <arg1> -> call function(arg1).",
+	.description = "<function> <arg1> Call function(arg1).",
 	.func = cmd_call1,
 	.argc = 2,
@@ -277,5 +285,5 @@
 static cmd_info_t call2_info = {
 	.name = "call2",
-	.description = "call2 <function> <arg1> <arg2> -> call function(arg1,arg2).",
+	.description = "<function> <arg1> <arg2> Call function(arg1, arg2).",
 	.func = cmd_call2,
 	.argc = 3,
@@ -310,5 +318,5 @@
 static cmd_info_t call3_info = {
 	.name = "call3",
-	.description = "call3 <function> <arg1> <arg2> <arg3> -> call function(arg1,arg2,arg3).",
+	.description = "<function> <arg1> <arg2> <arg3> Call function(arg1, arg2, arg3).",
 	.func = cmd_call3,
 	.argc = 4,
@@ -340,5 +348,5 @@
 cmd_info_t tlb_info = {
 	.name = "tlb",
-	.description = "Print TLB of current processor.",
+	.description = "Print TLB of the current CPU.",
 	.help = NULL,
 	.func = cmd_tlb,
@@ -377,9 +385,25 @@
 };
 
+#ifdef CONFIG_UDEBUG
+
+/* Data and methods for 'btrace' command */
+static int cmd_btrace(cmd_arg_t *argv);
+static cmd_arg_t btrace_argv = {
+	.type = ARG_TYPE_INT,
+};
+static cmd_info_t btrace_info = {
+	.name = "btrace",
+	.description = "<threadid> Show thread stack trace.",
+	.func = cmd_btrace,
+	.argc = 1,
+	.argv = &btrace_argv
+};
+
+#endif /* CONFIG_UDEBUG */
 
 static int cmd_sched(cmd_arg_t *argv);
 static cmd_info_t sched_info = {
 	.name = "scheduler",
-	.description = "List all scheduler information.",
+	.description = "Show scheduler information.",
 	.func = cmd_sched,
 	.argc = 0
@@ -406,7 +430,21 @@
 static cmd_info_t zones_info = {
 	.name = "zones",
-	.description = "List of memory zones.",
+	.description = "List memory zones.",
 	.func = cmd_zones,
 	.argc = 0
+};
+
+/* Data and methods for 'zone' command */
+static int cmd_zone(cmd_arg_t *argv);
+static cmd_arg_t zone_argv = {
+	.type = ARG_TYPE_INT,
+};
+
+static cmd_info_t zone_info = {
+	.name = "zone",
+	.description = "<zone> Show memory zone structure.",
+	.func = cmd_zone,
+	.argc = 1,
+	.argv = &zone_argv
 };
 
@@ -418,5 +456,5 @@
 static cmd_info_t ipc_info = {
 	.name = "ipc",
-	.description = "ipc <taskid> Show IPC information of given task.",
+	.description = "<taskid> Show IPC information of a task.",
 	.func = cmd_ipc,
 	.argc = 1,
@@ -431,22 +469,8 @@
 static cmd_info_t kill_info = {
 	.name = "kill",
-	.description = "kill <taskid> Kill a task.",
+	.description = "<taskid> Kill a task.",
 	.func = cmd_kill,
 	.argc = 1,
 	.argv = &kill_argv
-};
-
-/* Data and methods for 'zone' command */
-static int cmd_zone(cmd_arg_t *argv);
-static cmd_arg_t zone_argv = {
-	.type = ARG_TYPE_INT,
-};
-
-static cmd_info_t zone_info = {
-	.name = "zone",
-	.description = "Show memory zone structure.",
-	.func = cmd_zone,
-	.argc = 1,
-	.argv = &zone_argv
 };
 
@@ -482,19 +506,19 @@
 	&cpus_info,
 	&desc_info,
-	&reboot_info,
-	&uptime_info,
 	&halt_info,
 	&help_info,
 	&ipc_info,
 	&kill_info,
+	&physmem_info,
+	&reboot_info,
+	&sched_info,
 	&set4_info,
 	&slabs_info,
+	&symaddr_info,
 	&sysinfo_info,
-	&symaddr_info,
-	&sched_info,
+	&tasks_info,
 	&threads_info,
-	&tasks_info,
-	&physmem_info,
 	&tlb_info,
+	&uptime_info,
 	&version_info,
 	&zones_info,
@@ -504,4 +528,7 @@
 	&bench_info,
 #endif
+#ifdef CONFIG_UDEBUG
+	&btrace_info,
+#endif
 	NULL
 };
@@ -530,5 +557,4 @@
 	}
 }
-
 
 /** List supported commands.
@@ -574,5 +600,4 @@
 }
 
-
 /** Reboot the system.
  *
@@ -588,5 +613,4 @@
 	return 1;
 }
-
 
 /** Print system uptime information.
@@ -824,5 +848,4 @@
 }
 
-
 /** Print detailed description of 'describe' command. */
 void desc_help(void)
@@ -911,5 +934,5 @@
  * @return Always 1
  */
-int cmd_slabs(cmd_arg_t * argv)
+int cmd_slabs(cmd_arg_t *argv)
 {
 	slab_print_list();
@@ -923,5 +946,5 @@
  * @return Always 1
  */
-int cmd_sysinfo(cmd_arg_t * argv)
+int cmd_sysinfo(cmd_arg_t *argv)
 {
 	sysinfo_dump(NULL);
@@ -929,6 +952,5 @@
 }
 
-
-/** Command for listings Thread information
+/** Command for listing thread information
  *
  * @param argv Ignored
@@ -948,5 +970,5 @@
 }
 
-/** Command for listings Task information
+/** Command for listing task information
  *
  * @param argv Ignored
@@ -966,5 +988,22 @@
 }
 
-/** Command for listings Thread information
+#ifdef CONFIG_UDEBUG
+
+/** Command for printing thread stack trace
+ *
+ * @param argv Integer argument from cmdline expected
+ *
+ * return Always 1
+ *
+ */
+int cmd_btrace(cmd_arg_t *argv)
+{
+	thread_stack_trace(argv[0].intval);
+	return 1;
+}
+
+#endif /* CONFIG_UDEBUG */
+
+/** Command for printing scheduler information
  *
  * @param argv Ignores
@@ -972,5 +1011,5 @@
  * @return Always 1
  */
-int cmd_sched(cmd_arg_t * argv)
+int cmd_sched(cmd_arg_t *argv)
 {
 	sched_print_list();
@@ -984,5 +1023,5 @@
  * return Always 1
  */
-int cmd_zones(cmd_arg_t * argv)
+int cmd_zones(cmd_arg_t *argv)
 {
 	zones_print_list();
@@ -996,5 +1035,5 @@
  * return Always 1
  */
-int cmd_zone(cmd_arg_t * argv)
+int cmd_zone(cmd_arg_t *argv)
 {
 	zone_print_one(argv[0].intval);
@@ -1002,5 +1041,5 @@
 }
 
-/** Command for printing task ipc details
+/** Command for printing task IPC details
  *
  * @param argv Integer argument from cmdline expected
@@ -1008,5 +1047,5 @@
  * return Always 1
  */
-int cmd_ipc(cmd_arg_t * argv)
+int cmd_ipc(cmd_arg_t *argv)
 {
 	ipc_print_task(argv[0].intval);
@@ -1020,5 +1059,5 @@
  * return 0 on failure, 1 on success.
  */
-int cmd_kill(cmd_arg_t * argv)
+int cmd_kill(cmd_arg_t *argv)
 {
 	if (task_kill(argv[0].intval) != EOK)
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/src/ipc/ipc.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -295,4 +295,5 @@
 		atomic_inc(&phone->active_calls);
 		call->data.phone = phone;
+		call->data.task = TASK;
 	}
 	
@@ -406,4 +407,5 @@
 			call->caller_phone = call->data.phone;
 		call->data.phone = newphone;
+		call->data.task = TASK;
 	}
 	
@@ -688,32 +690,32 @@
 	irq_spinlock_exchange(&tasks_lock, &task->lock);
 	
-	/* Print opened phones & details */
-	printf("PHONE:\n");
+	printf("[phone id] [calls] [state\n");
 	
 	size_t i;
 	for (i = 0; i < IPC_MAX_PHONES; i++) {
 		if (SYNCH_FAILED(mutex_trylock(&task->phones[i].lock))) {
-			printf("%zu: mutex busy\n", i);
+			printf("%-10zu (mutex busy)\n", i);
 			continue;
 		}
 		
 		if (task->phones[i].state != IPC_PHONE_FREE) {
-			printf("%zu: ", i);
+			printf("%-10zu %7" PRIun " ", i,
+			    atomic_get(&task->phones[i].active_calls));
 			
 			switch (task->phones[i].state) {
 			case IPC_PHONE_CONNECTING:
-				printf("connecting ");
+				printf("connecting");
 				break;
 			case IPC_PHONE_CONNECTED:
-				printf("connected to: %p (%" PRIu64 ") ",
-				    task->phones[i].callee,
-				    task->phones[i].callee->task->taskid);
+				printf("connected to %" PRIu64 " (%s)",
+				    task->phones[i].callee->task->taskid,
+				    task->phones[i].callee->task->name);
 				break;
 			case IPC_PHONE_SLAMMED:
-				printf("slammed by: %p ", 
+				printf("slammed by %p",
 				    task->phones[i].callee);
 				break;
 			case IPC_PHONE_HUNGUP:
-				printf("hung up - was: %p ", 
+				printf("hung up by %p",
 				    task->phones[i].callee);
 				break;
@@ -722,6 +724,5 @@
 			}
 			
-			printf("active: %" PRIun "\n",
-			    atomic_get(&task->phones[i].active_calls));
+			printf("\n");
 		}
 		
@@ -731,49 +732,79 @@
 	irq_spinlock_lock(&task->answerbox.lock, false);
 	
+#ifdef __32_BITS__
+	printf("[call id ] [method] [arg1] [arg2] [arg3] [arg4] [arg5]"
+	    " [flags] [sender\n");
+#endif
+	
+#ifdef __64_BITS__
+	printf("[call id         ] [method] [arg1] [arg2] [arg3] [arg4]"
+	    " [arg5] [flags] [sender\n");
+#endif
+	
 	link_t *cur;
 	
-	/* Print answerbox - calls */
-	printf("ABOX - CALLS:\n");
+	printf(" --- incomming calls ---\n");
 	for (cur = task->answerbox.calls.next; cur != &task->answerbox.calls;
 	    cur = cur->next) {
 		call_t *call = list_get_instance(cur, call_t, link);
-		printf("Callid: %p Srctask:%" PRIu64 " M:%" PRIun 
-		    " A1:%" PRIun " A2:%" PRIun " A3:%" PRIun
-		    " A4:%" PRIun " A5:%" PRIun " Flags:%x\n", call,
-		    call->sender->taskid,
+		
+#ifdef __32_BITS__
+		printf("%10p ", call);
+#endif
+		
+#ifdef __64_BITS__
+		printf("%18p ", call);
+#endif
+		
+		printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
+		    " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n",
 		    IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
 		    IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
 		    IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
-		    call->flags);
-	}
-	
-	/* Print answerbox - dispatched calls */
-	printf("ABOX - DISPATCHED CALLS:\n");
+		    call->flags, call->sender->taskid, call->sender->name);
+	}
+	
+	printf(" --- dispatched calls ---\n");
 	for (cur = task->answerbox.dispatched_calls.next;
 	    cur != &task->answerbox.dispatched_calls;
 	    cur = cur->next) {
 		call_t *call = list_get_instance(cur, call_t, link);
-		printf("Callid: %p Srctask:%" PRIu64 " M:%" PRIun
-		    " A1:%" PRIun " A2:%" PRIun " A3:%" PRIun
-		    " A4:%" PRIun " A5:%" PRIun " Flags:%x\n", call,
-		    call->sender->taskid,
+		
+#ifdef __32_BITS__
+		printf("%10p ", call);
+#endif
+		
+#ifdef __64_BITS__
+		printf("%18p ", call);
+#endif
+		
+		printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
+		    " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n",
 		    IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
 		    IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
 		    IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
-		    call->flags);
-	}
-	
-	/* Print answerbox - answers */
-	printf("ABOX - ANSWERS:\n");
+		    call->flags, call->sender->taskid, call->sender->name);
+	}
+	
+	printf(" --- outgoing answers ---\n");
 	for (cur = task->answerbox.answers.next;
 	    cur != &task->answerbox.answers;
 	    cur = cur->next) {
 		call_t *call = list_get_instance(cur, call_t, link);
-		printf("Callid:%p M:%" PRIun " A1:%" PRIun " A2:%" PRIun
-		    " A3:%" PRIun " A4:%" PRIun " A5:%" PRIun " Flags:%x\n",
-		    call, IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
+		
+#ifdef __32_BITS__
+		printf("%10p ", call);
+#endif
+		
+#ifdef __64_BITS__
+		printf("%18p ", call);
+#endif
+		
+		printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun
+		    " %-6" PRIun " %-6" PRIun " %-7x %" PRIu64 " (%s)\n",
+		    IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data),
 		    IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
 		    IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
-		    call->flags);
+		    call->flags, call->sender->taskid, call->sender->name);
 	}
 	
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/src/ipc/sysipc.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -248,4 +248,6 @@
 			/* The connection was accepted */
 			phone_connect(phoneid, &answer->sender->answerbox);
+			/* Set 'task hash' as arg4 of response */
+			IPC_SET_ARG4(answer->data, (sysarg_t) TASK);
 			/* Set 'phone hash' as arg5 of response */
 			IPC_SET_ARG5(answer->data,
Index: kernel/generic/src/proc/scheduler.c
===================================================================
--- kernel/generic/src/proc/scheduler.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/src/proc/scheduler.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -62,8 +62,6 @@
 #include <print.h>
 #include <debug.h>
-
-static void before_task_runs(void);
-static void before_thread_runs(void);
-static void after_thread_ran(void);
+#include <stacktrace.h>
+
 static void scheduler_separated_stack(void);
 
@@ -71,5 +69,5 @@
 
 /** Carry out actions before new task runs. */
-void before_task_runs(void)
+static void before_task_runs(void)
 {
 	before_task_runs_arch();
@@ -80,14 +78,15 @@
  * Perform actions that need to be
  * taken before the newly selected
- * tread is passed control.
+ * thread is passed control.
  *
  * THREAD->lock is locked on entry
  *
  */
-void before_thread_runs(void)
+static void before_thread_runs(void)
 {
 	before_thread_runs_arch();
+	
 #ifdef CONFIG_FPU_LAZY
-	if(THREAD == CPU->fpu_owner)
+	if (THREAD == CPU->fpu_owner)
 		fpu_enable();
 	else
@@ -102,4 +101,16 @@
 	}
 #endif
+	
+#ifdef CONFIG_UDEBUG
+	if (THREAD->btrace) {
+		istate_t *istate = THREAD->udebug.uspace_state;
+		if (istate != NULL) {
+			printf("Thread %" PRIu64 " stack trace:\n", THREAD->tid);
+			stack_trace_istate(istate);
+		}
+		
+		THREAD->btrace = false;
+	}
+#endif
 }
 
@@ -113,5 +124,5 @@
  *
  */
-void after_thread_ran(void)
+static void after_thread_ran(void)
 {
 	after_thread_ran_arch();
@@ -391,5 +402,4 @@
 	 * possible destruction should thread_destroy() be called on this or any
 	 * other processor while the scheduler is still using them.
-	 *
 	 */
 	if (old_task)
@@ -417,10 +427,8 @@
 				 * The thread structure is kept allocated until
 				 * somebody calls thread_detach() on it.
-				 *
 				 */
 				if (!irq_spinlock_trylock(&THREAD->join_wq.lock)) {
 					/*
 					 * Avoid deadlock.
-					 *
 					 */
 					irq_spinlock_unlock(&THREAD->lock, false);
@@ -443,5 +451,4 @@
 			/*
 			 * Prefer the thread after it's woken up.
-			 *
 			 */
 			THREAD->priority = -1;
@@ -451,5 +458,4 @@
 			 * waitq_sleep(). Address of wq->lock is kept in
 			 * THREAD->sleep_queue.
-			 *
 			 */
 			irq_spinlock_unlock(&THREAD->sleep_queue->lock, false);
@@ -461,5 +467,4 @@
 			/*
 			 * Entering state is unexpected.
-			 *
 			 */
 			panic("tid%" PRIu64 ": unexpected state %s.",
@@ -480,7 +485,6 @@
 	
 	/*
-	 * If both the old and the new task are the same, lots of work is
-	 * avoided.
-	 *
+	 * If both the old and the new task are the same,
+	 * lots of work is avoided.
 	 */
 	if (TASK != THREAD->task) {
@@ -488,7 +492,6 @@
 		
 		/*
-		 * Note that it is possible for two tasks to share one address
-		 * space.
-		 (
+		 * Note that it is possible for two tasks
+		 * to share one address space.
 		 */
 		if (old_as != new_as) {
@@ -496,5 +499,4 @@
 			 * Both tasks and address spaces are different.
 			 * Replace the old one with the new one.
-			 *
 			 */
 			as_switch(old_as, new_as);
@@ -527,5 +529,4 @@
 	 * necessary, is to be mapped in before_thread_runs(). This
 	 * function must be executed before the switch to the new stack.
-	 *
 	 */
 	before_thread_runs();
@@ -534,5 +535,4 @@
 	 * Copy the knowledge of CPU, TASK, THREAD and preemption counter to
 	 * thread's stack.
-	 *
 	 */
 	the_copy(THE, (the_t *) THREAD->kstack);
@@ -658,5 +658,4 @@
 				/*
 				 * Ready thread on local CPU
-				 *
 				 */
 				
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/src/proc/task.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -342,18 +342,32 @@
 sysarg_t sys_task_set_name(const char *uspace_name, size_t name_len)
 {
-	int rc;
 	char namebuf[TASK_NAME_BUFLEN];
 	
 	/* Cap length of name and copy it from userspace. */
-	
 	if (name_len > TASK_NAME_BUFLEN - 1)
 		name_len = TASK_NAME_BUFLEN - 1;
 	
-	rc = copy_from_uspace(namebuf, uspace_name, name_len);
+	int rc = copy_from_uspace(namebuf, uspace_name, name_len);
 	if (rc != 0)
 		return (sysarg_t) rc;
 	
 	namebuf[name_len] = '\0';
+	
+	/*
+	 * As the task name is referenced also from the
+	 * threads, lock the threads' lock for the course
+	 * of the update.
+	 */
+	
+	irq_spinlock_lock(&tasks_lock, true);
+	irq_spinlock_lock(&TASK->lock, false);
+	irq_spinlock_lock(&threads_lock, false);
+	
+	/* Set task name */
 	str_cpy(TASK->name, TASK_NAME_BUFLEN, namebuf);
+	
+	irq_spinlock_unlock(&threads_lock, false);
+	irq_spinlock_unlock(&TASK->lock, false);
+	irq_spinlock_unlock(&tasks_lock, true);
 	
 	return EOK;
@@ -449,10 +463,12 @@
 static void task_kill_internal(task_t *task)
 {
+	irq_spinlock_lock(&task->lock, false);
+	irq_spinlock_lock(&threads_lock, false);
+	
+	/*
+	 * Interrupt all threads.
+	 */
+	
 	link_t *cur;
-	
-	/*
-	 * Interrupt all threads.
-	 */
-	irq_spinlock_lock(&task->lock, false);
 	for (cur = task->th_head.next; cur != &task->th_head; cur = cur->next) {
 		thread_t *thread = list_get_instance(cur, thread_t, th_link);
@@ -471,4 +487,5 @@
 	}
 	
+	irq_spinlock_unlock(&threads_lock, false);
 	irq_spinlock_unlock(&task->lock, false);
 }
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/src/proc/thread.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -239,5 +239,5 @@
  * Switch thread to the ready state.
  *
- * @param t Thread to make ready.
+ * @param thread Thread to make ready.
  *
  */
@@ -246,5 +246,5 @@
 	irq_spinlock_lock(&thread->lock, true);
 	
-	ASSERT(!(thread->state == Ready));
+	ASSERT(thread->state != Ready);
 	
 	int i = (thread->priority < RQ_COUNT - 1)
@@ -350,5 +350,6 @@
 	
 #ifdef CONFIG_UDEBUG
-	/* Init debugging stuff */
+	/* Initialize debugging stuff */
+	thread->btrace = false;
 	udebug_thread_initialize(&thread->udebug);
 #endif
@@ -535,6 +536,6 @@
 /** Detach thread.
  *
- * Mark the thread as detached, if the thread is already in the Lingering
- * state, deallocate its resources.
+ * Mark the thread as detached. If the thread is already
+ * in the Lingering state, deallocate its resources.
  *
  * @param thread Thread to be detached.
@@ -590,24 +591,30 @@
 	order_suffix(thread->kcycles, &kcycles, &ksuffix);
 	
+	char *name;
+	if (str_cmp(thread->name, "uinit") == 0)
+		name = thread->task->name;
+	else
+		name = thread->name;
+	
 #ifdef __32_BITS__
 	if (*additional)
-		printf("%-8" PRIu64" %10p %9" PRIu64 "%c %9" PRIu64 "%c ",
-		    thread->tid, thread->kstack, ucycles, usuffix,
-		    kcycles, ksuffix);
+		printf("%-8" PRIu64 " %10p %10p %9" PRIu64 "%c %9" PRIu64 "%c ",
+		    thread->tid, thread->thread_code, thread->kstack,
+		    ucycles, usuffix, kcycles, ksuffix);
 	else
-		printf("%-8" PRIu64" %-14s %10p %-8s %10p %-5" PRIu32 " %10p\n",
-		    thread->tid, thread->name, thread, thread_states[thread->state],
-		    thread->task, thread->task->context, thread->thread_code);
+		printf("%-8" PRIu64 " %-14s %10p %-8s %10p %-5" PRIu32 "\n",
+		    thread->tid, name, thread, thread_states[thread->state],
+		    thread->task, thread->task->context);
 #endif
 	
 #ifdef __64_BITS__
 	if (*additional)
-		printf("%-8" PRIu64" %18p %18p\n"
+		printf("%-8" PRIu64 " %18p %18p\n"
 		    "         %9" PRIu64 "%c %9" PRIu64 "%c ",
 		    thread->tid, thread->thread_code, thread->kstack,
 		    ucycles, usuffix, kcycles, ksuffix);
 	else
-		printf("%-8" PRIu64" %-14s %18p %-8s %18p %-5" PRIu32 "\n",
-		    thread->tid, thread->name, thread, thread_states[thread->state],
+		printf("%-8" PRIu64 " %-14s %18p %-8s %18p %-5" PRIu32 "\n",
+		    thread->tid, name, thread, thread_states[thread->state],
 		    thread->task, thread->task->context);
 #endif
@@ -647,9 +654,9 @@
 #ifdef __32_BITS__
 	if (additional)
-		printf("[id    ] [stack   ] [ucycles ] [kcycles ] [cpu]"
-		    " [waitqueue]\n");
+		printf("[id    ] [code    ] [stack   ] [ucycles ] [kcycles ]"
+		    " [cpu] [waitqueue]\n");
 	else
 		printf("[id    ] [name        ] [address ] [state ] [task    ]"
-		    " [ctx] [code    ]\n");
+		    " [ctx]\n");
 #endif
 	
@@ -740,5 +747,5 @@
 	ASSERT(interrupts_disabled());
 	ASSERT(irq_spinlock_locked(&threads_lock));
-
+	
 	thread_iterator_t iterator;
 	
@@ -751,4 +758,50 @@
 }
 
+#ifdef CONFIG_UDEBUG
+
+void thread_stack_trace(thread_id_t thread_id)
+{
+	irq_spinlock_lock(&threads_lock, true);
+	
+	thread_t *thread = thread_find_by_id(thread_id);
+	if (thread == NULL) {
+		printf("No such thread.\n");
+		irq_spinlock_unlock(&threads_lock, true);
+		return;
+	}
+	
+	irq_spinlock_lock(&thread->lock, false);
+	
+	/*
+	 * Schedule a stack trace to be printed
+	 * just before the thread is scheduled next.
+	 *
+	 * If the thread is sleeping then try to interrupt
+	 * the sleep. Any request for printing an uspace stack
+	 * trace from within the kernel should be always
+	 * considered a last resort debugging means, therefore
+	 * forcing the thread's sleep to be interrupted
+	 * is probably justifiable.
+	 */
+	
+	bool sleeping = false;
+	istate_t *istate = thread->udebug.uspace_state;
+	if (istate != NULL) {
+		printf("Scheduling thread stack trace.\n");
+		thread->btrace = true;
+		if (thread->state == Sleeping)
+			sleeping = true;
+	} else
+		printf("Thread interrupt state not available.\n");
+	
+	irq_spinlock_unlock(&thread->lock, false);
+	
+	if (sleeping)
+		waitq_interrupt_sleep(thread);
+	
+	irq_spinlock_unlock(&threads_lock, true);
+}
+
+#endif /* CONFIG_UDEBUG */
 
 /** Process syscall to create new thread.
@@ -793,5 +846,4 @@
 				 * has already been created. We need to undo its
 				 * creation now.
-				 *
 				 */
 				
@@ -815,5 +867,4 @@
 		 * THREAD_B events for threads that already existed
 		 * and could be detected with THREAD_READ before.
-		 *
 		 */
 		udebug_thread_b_event_attach(thread, TASK);
Index: kernel/generic/src/synch/waitq.c
===================================================================
--- kernel/generic/src/synch/waitq.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ kernel/generic/src/synch/waitq.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -127,6 +127,10 @@
 /** Interrupt sleeping thread.
  *
- * This routine attempts to interrupt a thread from its sleep in a waitqueue.
- * If the thread is not found sleeping, no action is taken.
+ * This routine attempts to interrupt a thread from its sleep in
+ * a waitqueue. If the thread is not found sleeping, no action
+ * is taken.
+ *
+ * The threads_lock must be already held and interrupts must be
+ * disabled upon calling this function.
  *
  * @param thread Thread to be interrupted.
@@ -138,7 +142,8 @@
 	DEADLOCK_PROBE_INIT(p_wqlock);
 	
-	irq_spinlock_lock(&threads_lock, true);
-	if (!thread_exists(thread))
-		goto out;
+	/*
+	 * The thread is quaranteed to exist because
+	 * threads_lock is held.
+	 */
 	
 grab_locks:
@@ -150,14 +155,13 @@
 			/*
 			 * The sleep cannot be interrupted.
-			 *
 			 */
 			irq_spinlock_unlock(&thread->lock, false);
-			goto out;
+			return;
 		}
 		
 		if (!irq_spinlock_trylock(&wq->lock)) {
+			/* Avoid deadlock */
 			irq_spinlock_unlock(&thread->lock, false);
 			DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
-			/* Avoid deadlock */
 			goto grab_locks;
 		}
@@ -173,11 +177,9 @@
 		irq_spinlock_unlock(&wq->lock, false);
 	}
+	
 	irq_spinlock_unlock(&thread->lock, false);
 	
 	if (do_wakeup)
 		thread_ready(thread);
-	
-out:
-	irq_spinlock_unlock(&threads_lock, true);
 }
 
@@ -370,5 +372,4 @@
 		 * If the thread was already interrupted,
 		 * don't go to sleep at all.
-		 *
 		 */
 		if (THREAD->interrupted) {
@@ -381,5 +382,4 @@
 		 * Set context that will be restored if the sleep
 		 * of this thread is ever interrupted.
-		 *
 		 */
 		THREAD->sleep_interruptible = true;
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/Makefile	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -46,5 +46,4 @@
 	app/stats \
 	app/taskdump \
-	app/tasks \
 	app/tester \
 	app/tetris \
Index: uspace/app/stats/stats.c
===================================================================
--- uspace/app/stats/stats.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/app/stats/stats.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Stanislav Kozina
+ * Copyright (c) 2010 Martin Decky
  * All rights reserved.
  *
@@ -36,10 +37,16 @@
 
 #include <stdio.h>
+#include <task.h>
+#include <thread.h>
 #include <stats.h>
-#include <sys/time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <malloc.h>
 #include <inttypes.h>
-#include <malloc.h>
-
-#define NAME  "sysstat"
+#include <bool.h>
+#include <str.h>
+#include <arg_parse.h>
+
+#define NAME  "stats"
 
 #define DAY     86400
@@ -47,39 +54,260 @@
 #define MINUTE  60
 
-int main(int argc, char *argv[])
-{
-	struct timeval time;
-	if (gettimeofday(&time, NULL) != 0) {
-		fprintf(stderr, "%s: Cannot get time of day\n", NAME);
-		return -1;
-	}
-	
-	uint64_t sec = time.tv_sec;
-	printf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64,
-	    (sec % DAY) / HOUR, (sec % HOUR) / MINUTE, sec % MINUTE);
-	
+static void list_tasks(void)
+{
+	size_t count;
+	stats_task_t *stats_tasks = stats_get_tasks(&count);
+	
+	if (stats_tasks == NULL) {
+		fprintf(stderr, "%s: Unable to get tasks\n", NAME);
+		return;
+	}
+	
+	printf("[taskid] [thrds] [resident] [virtual] [ucycles]"
+	    " [kcycles] [name\n");
+	
+	size_t i;
+	for (i = 0; i < count; i++) {
+		uint64_t resmem, virtmem, ucycles, kcycles;
+		char resmem_suffix, virtmem_suffix, usuffix, ksuffix;
+		
+		order_suffix(stats_tasks[i].resmem, &resmem, &resmem_suffix);
+		order_suffix(stats_tasks[i].virtmem, &virtmem, &virtmem_suffix);
+		order_suffix(stats_tasks[i].ucycles, &ucycles, &usuffix);
+		order_suffix(stats_tasks[i].kcycles, &kcycles, &ksuffix);
+		
+		printf("%-8" PRIu64 " %7zu %9" PRIu64 "%c %8" PRIu64 "%c"
+		    " %8" PRIu64 "%c %8" PRIu64 "%c %s\n",
+		    stats_tasks[i].task_id, stats_tasks[i].threads,
+		    resmem, resmem_suffix, virtmem, virtmem_suffix,
+		    ucycles, usuffix, kcycles, ksuffix, stats_tasks[i].name);
+	}
+	
+	free(stats_tasks);
+}
+
+static void list_threads(task_id_t task_id, bool all)
+{
+	size_t count;
+	stats_thread_t *stats_threads = stats_get_threads(&count);
+	
+	if (stats_threads == NULL) {
+		fprintf(stderr, "%s: Unable to get threads\n", NAME);
+		return;
+	}
+	
+	printf("[taskid] [threadid] [state ] [prio] [cpu ] [ucycles] [kcycles]\n");
+	
+	size_t i;
+	for (i = 0; i < count; i++) {
+		if ((all) || (stats_threads[i].task_id == task_id)) {
+			uint64_t ucycles, kcycles;
+			char usuffix, ksuffix;
+			
+			order_suffix(stats_threads[i].ucycles, &ucycles, &usuffix);
+			order_suffix(stats_threads[i].kcycles, &kcycles, &ksuffix);
+			
+			printf("%-8" PRIu64 " %-10" PRIu64 " %-8s %6d ",
+			    stats_threads[i].task_id, stats_threads[i].thread_id,
+			    thread_get_state(stats_threads[i].state),
+			    stats_threads[i].priority);
+			
+			if (stats_threads[i].on_cpu)
+				printf("%6u ", stats_threads[i].cpu);
+			else
+				printf("(none) ");
+			
+			printf("%8" PRIu64"%c %8" PRIu64"%c\n",
+			    ucycles, usuffix, kcycles, ksuffix);
+		}
+	}
+	
+	free(stats_threads);
+}
+
+static void list_cpus(void)
+{
+	size_t count;
+	stats_cpu_t *cpus = stats_get_cpus(&count);
+	
+	if (cpus == NULL) {
+		fprintf(stderr, "%s: Unable to get CPU statistics\n", NAME);
+		return;
+	}
+	
+	printf("[id] [MHz     ] [busy cycles] [idle cycles]\n");
+	
+	size_t i;
+	for (i = 0; i < count; i++) {
+		printf("%-4u ", cpus[i].id);
+		if (cpus[i].active) {
+			uint64_t bcycles, icycles;
+			char bsuffix, isuffix;
+			
+			order_suffix(cpus[i].busy_cycles, &bcycles, &bsuffix);
+			order_suffix(cpus[i].idle_cycles, &icycles, &isuffix);
+			
+			printf("%10" PRIu16 " %12" PRIu64 "%c %12" PRIu64 "%c\n",
+			    cpus[i].frequency_mhz, bcycles, bsuffix,
+			    icycles, isuffix);
+		} else
+			printf("inactive\n");
+	}
+	
+	free(cpus);
+}
+
+static void print_load(void)
+{
+	size_t count;
+	load_t *load = stats_get_load(&count);
+	
+	if (load == NULL) {
+		fprintf(stderr, "%s: Unable to get load\n", NAME);
+		return;
+	}
+	
+	printf("%s: Load average: ", NAME);
+	
+	size_t i;
+	for (i = 0; i < count; i++) {
+		if (i > 0)
+			printf(" ");
+		
+		stats_print_load_fragment(load[i], 2);
+	}
+	
+	printf("\n");
+	
+	free(load);
+}
+
+static void print_uptime(void)
+{
 	sysarg_t uptime = stats_get_uptime();
-	printf(", up %" PRIun " days, %" PRIun " hours, "
-	    "%" PRIun " minutes, %" PRIun " seconds",
+	printf("%s: Up %" PRIun " days, %" PRIun " hours, "
+	    "%" PRIun " minutes, %" PRIun " seconds\n", NAME,
 	    uptime / DAY, (uptime % DAY) / HOUR,
 	    (uptime % HOUR) / MINUTE, uptime % MINUTE);
-	
-	size_t count;
-	load_t *load = stats_get_load(&count);
-	if (load != NULL) {
-		printf(", load average: ");
-		
-		size_t i;
-		for (i = 0; i < count; i++) {
-			if (i > 0)
-				printf(" ");
-			
-			stats_print_load_fragment(load[i], 2);
-		}
-		
-		free(load);
-	}
-	
-	printf("\n");
+}
+
+static void usage(const char *name)
+{
+	printf(
+	    "Usage: %s [-t task_id] [-a] [-c] [-l] [-u]\n" \
+	    "\n" \
+	    "Options:\n" \
+	    "\t-t task_id\n" \
+	    "\t--task=task_id\n" \
+	    "\t\tList threads of the given task\n" \
+	    "\n" \
+	    "\t-a\n" \
+	    "\t--all\n" \
+	    "\t\tList all threads\n" \
+	    "\n" \
+	    "\t-c\n" \
+	    "\t--cpus\n" \
+	    "\t\tList CPUs\n" \
+	    "\n" \
+	    "\t-l\n" \
+	    "\t--load\n" \
+	    "\t\tPrint system load\n" \
+	    "\n" \
+	    "\t-u\n" \
+	    "\t--uptime\n" \
+	    "\t\tPrint system uptime\n" \
+	    "\n" \
+	    "\t-h\n" \
+	    "\t--help\n" \
+	    "\t\tPrint this usage information\n"
+	    "\n" \
+	    "Without any options all tasks are listed\n",
+	    name
+	);
+}
+
+int main(int argc, char *argv[])
+{
+	bool toggle_tasks = true;
+	bool toggle_threads = false;
+	bool toggle_all = false;
+	bool toggle_cpus = false;
+	bool toggle_load = false;
+	bool toggle_uptime = false;
+	
+	task_id_t task_id = 0;
+	
+	int i;
+	for (i = 1; i < argc; i++) {
+		int off;
+		
+		/* Usage */
+		if ((off = arg_parse_short_long(argv[i], "-h", "--help")) != -1) {
+			usage(argv[0]);
+			return 0;
+		}
+		
+		/* All threads */
+		if ((off = arg_parse_short_long(argv[i], "-a", "--all")) != -1) {
+			toggle_tasks = false;
+			toggle_threads = true;
+			toggle_all = true;
+			continue;
+		}
+		
+		/* CPUs */
+		if ((off = arg_parse_short_long(argv[i], "-c", "--cpus")) != -1) {
+			toggle_tasks = false;
+			toggle_cpus = true;
+			continue;
+		}
+		
+		/* Threads */
+		if ((off = arg_parse_short_long(argv[i], "-t", "--task=")) != -1) {
+			// TODO: Support for 64b range
+			int tmp;
+			int ret = arg_parse_int(argc, argv, &i, &tmp, off);
+			if (ret != EOK) {
+				printf("%s: Malformed task_id '%s'\n", NAME, argv[i]);
+				return -1;
+			}
+			
+			task_id = tmp;
+			
+			toggle_tasks = false;
+			toggle_threads = true;
+			continue;
+		}
+		
+		/* Load */
+		if ((off = arg_parse_short_long(argv[i], "-l", "--load")) != -1) {
+			toggle_tasks = false;
+			toggle_load = true;
+			continue;
+		}
+		
+		/* Uptime */
+		if ((off = arg_parse_short_long(argv[i], "-u", "--uptime")) != -1) {
+			toggle_tasks = false;
+			toggle_uptime = true;
+			continue;
+		}
+	}
+	
+	if (toggle_tasks)
+		list_tasks();
+	
+	if (toggle_threads)
+		list_threads(task_id, toggle_all);
+	
+	if (toggle_cpus)
+		list_cpus();
+	
+	if (toggle_load)
+		print_load();
+	
+	if (toggle_uptime)
+		print_uptime();
+	
 	return 0;
 }
Index: pace/app/tasks/Makefile
===================================================================
--- uspace/app/tasks/Makefile	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ 	(revision )
@@ -1,36 +1,0 @@
-#
-# Copyright (c) 2005 Martin Decky
-# Copyright (c) 2007 Jakub Jermar
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# - Redistributions of source code must retain the above copyright
-#   notice, this list of conditions and the following disclaimer.
-# - Redistributions in binary form must reproduce the above copyright
-#   notice, this list of conditions and the following disclaimer in the
-#   documentation and/or other materials provided with the distribution.
-# - The name of the author may not be used to endorse or promote products
-#   derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-
-USPACE_PREFIX = ../..
-BINARY = tasks
-
-SOURCES = \
-	tasks.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/tasks/tasks.c
===================================================================
--- uspace/app/tasks/tasks.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ 	(revision )
@@ -1,285 +1,0 @@
-/*
- * Copyright (c) 2010 Stanislav Kozina
- * Copyright (c) 2010 Martin Decky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup tasks
- * @brief Task lister.
- * @{
- */
-/**
- * @file
- */
-
-#include <stdio.h>
-#include <task.h>
-#include <thread.h>
-#include <stats.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <malloc.h>
-#include <inttypes.h>
-#include <bool.h>
-#include <str.h>
-#include <arg_parse.h>
-
-#define NAME  "tasks"
-
-#define TASK_COUNT    10
-#define THREAD_COUNT  50
-
-#define PRINT_LOAD1(x)  ((x) >> 11)
-#define PRINT_LOAD2(x)  (((x) & 0x7ff) / 2)
-
-static void list_tasks(void)
-{
-	size_t count;
-	stats_task_t *stats_tasks = stats_get_tasks(&count);
-	
-	if (stats_tasks == NULL) {
-		fprintf(stderr, "%s: Unable to get tasks\n", NAME);
-		return;
-	}
-	
-	printf(" Task ID  Threads      Mem       uCycles       kCycles   Name\n");
-	
-	size_t i;
-	for (i = 0; i < count; i++) {
-		uint64_t virtmem, ucycles, kcycles;
-		char vmsuffix, usuffix, ksuffix;
-		
-		order_suffix(stats_tasks[i].virtmem, &virtmem, &vmsuffix);
-		order_suffix(stats_tasks[i].ucycles, &ucycles, &usuffix);
-		order_suffix(stats_tasks[i].kcycles, &kcycles, &ksuffix);
-		
-		printf("%8" PRIu64 "%8zu %8" PRIu64 "%c %12"
-		    PRIu64 "%c %12" PRIu64 "%c %s\n", stats_tasks[i].task_id,
-		    stats_tasks[i].threads, virtmem, vmsuffix, ucycles, usuffix,
-		    kcycles, ksuffix, stats_tasks[i].name);
-	}
-	
-	free(stats_tasks);
-}
-
-static void list_threads(task_id_t task_id, bool all)
-{
-	size_t count;
-	stats_thread_t *stats_threads = stats_get_threads(&count);
-	
-	if (stats_threads == NULL) {
-		fprintf(stderr, "%s: Unable to get threads\n", NAME);
-		return;
-	}
-	
-	printf(" ThrID    State  CPU   Prio    [k]uCycles    [k]kcycles   Cycle fault\n");
-	size_t i;
-	for (i = 0; i < count; i++) {
-		if ((all) || (stats_threads[i].task_id == task_id)) {
-			uint64_t ucycles, kcycles;
-			char usuffix, ksuffix;
-			
-			order_suffix(stats_threads[i].ucycles, &ucycles, &usuffix);
-			order_suffix(stats_threads[i].kcycles, &kcycles, &ksuffix);
-			
-			if (stats_threads[i].on_cpu) {
-				printf("%8" PRIu64 " %-8s %4u %6d %12"
-				    PRIu64"%c %12" PRIu64"%c\n", stats_threads[i].thread_id,
-				    thread_get_state(stats_threads[i].state),
-				    stats_threads[i].cpu, stats_threads[i].priority,
-				    ucycles, usuffix, kcycles, ksuffix);
-			} else {
-				printf("%8" PRIu64 " %-8s ---- %6d %12"
-				    PRIu64"%c %12" PRIu64"%c\n", stats_threads[i].thread_id,
-				    thread_get_state(stats_threads[i].state),
-				    stats_threads[i].priority,
-				    ucycles, usuffix, kcycles, ksuffix);
-			}
-		}
-	}
-	
-	free(stats_threads);
-}
-
-static void print_load(void)
-{
-	size_t count;
-	load_t *load = stats_get_load(&count);
-	
-	if (load == NULL) {
-		fprintf(stderr, "%s: Unable to get load\n", NAME);
-		return;
-	}
-	
-	printf("%s: Load average: ", NAME);
-	
-	size_t i;
-	for (i = 0; i < count; i++) {
-		if (i > 0)
-			printf(" ");
-		
-		stats_print_load_fragment(load[i], 2);
-	}
-	
-	printf("\n");
-	
-	free(load);
-}
-
-static void list_cpus(void)
-{
-	size_t count;
-	stats_cpu_t *cpus = stats_get_cpus(&count);
-	
-	if (cpus == NULL) {
-		fprintf(stderr, "%s: Unable to get CPU statistics\n", NAME);
-		return;
-	}
-	
-	printf("%s: %zu CPU(s) detected\n", NAME, count);
-	
-	size_t i;
-	for (i = 0; i < count; i++) {
-		if (cpus[i].active) {
-			printf("cpu%u: %" PRIu16 " MHz, busy cycles: "
-			    "%" PRIu64 ", idle cycles: %" PRIu64 "\n",
-			    cpus[i].id, cpus[i].frequency_mhz, cpus[i].busy_cycles,
-			    cpus[i].idle_cycles);
-		} else {
-			printf("cpu%u: inactive\n", cpus[i].id);
-		}
-	}
-	
-	free(cpus);
-}
-
-static void usage()
-{
-	printf(
-	    "Usage: tasks [-t task_id] [-a] [-l] [-c]\n" \
-	    "\n" \
-	    "Options:\n" \
-	    "\t-t task_id\n" \
-	    "\t--task=task_id\n" \
-	    "\t\tList threads of the given task\n" \
-	    "\n" \
-	    "\t-a\n" \
-	    "\t--all\n" \
-	    "\t\tList all threads\n" \
-	    "\n" \
-	    "\t-l\n" \
-	    "\t--load\n" \
-	    "\t\tPrint system load\n" \
-	    "\n" \
-	    "\t-c\n" \
-	    "\t--cpu\n" \
-	    "\t\tList CPUs\n" \
-	    "\n" \
-	    "\t-h\n" \
-	    "\t--help\n" \
-	    "\t\tPrint this usage information\n"
-	    "\n" \
-	    "Without any options all tasks are listed\n"
-	);
-}
-
-int main(int argc, char *argv[])
-{
-	bool toggle_tasks = true;
-	bool toggle_threads = false;
-	bool toggle_all = false;
-	bool toggle_load = false;
-	bool toggle_cpus = false;
-	
-	task_id_t task_id = 0;
-	
-	int i;
-	for (i = 1; i < argc; i++) {
-		int off;
-		
-		/* Usage */
-		if ((off = arg_parse_short_long(argv[i], "-h", "--help")) != -1) {
-			usage();
-			return 0;
-		}
-		
-		/* All threads */
-		if ((off = arg_parse_short_long(argv[i], "-a", "--all")) != -1) {
-			toggle_tasks = false;
-			toggle_threads = true;
-			toggle_all = true;
-			continue;
-		}
-		
-		/* Load */
-		if ((off = arg_parse_short_long(argv[i], "-l", "--load")) != -1) {
-			toggle_tasks = false;
-			toggle_load = true;
-			continue;
-		}
-		
-		/* CPUs */
-		if ((off = arg_parse_short_long(argv[i], "-c", "--cpus")) != -1) {
-			toggle_tasks = false;
-			toggle_cpus = true;
-			continue;
-		}
-		
-		/* Threads */
-		if ((off = arg_parse_short_long(argv[i], "-t", "--task=")) != -1) {
-			// TODO: Support for 64b range
-			int tmp;
-			int ret = arg_parse_int(argc, argv, &i, &tmp, off);
-			if (ret != EOK) {
-				printf("%s: Malformed task_id '%s'\n", NAME, argv[i]);
-				return -1;
-			}
-			
-			task_id = tmp;
-			
-			toggle_tasks = false;
-			toggle_threads = true;
-			continue;
-		}
-	}
-	
-	if (toggle_tasks)
-		list_tasks();
-	
-	if (toggle_threads)
-		list_threads(task_id, toggle_all);
-	
-	if (toggle_load)
-		print_load();
-	
-	if (toggle_cpus)
-		list_cpus();
-	
-	return 0;
-}
-
-/** @}
- */
Index: uspace/app/top/screen.c
===================================================================
--- uspace/app/top/screen.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/app/top/screen.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -274,5 +274,5 @@
 {
 	screen_style_inverted();
-	printf("[taskid] [threads] [resident] [%%resi] [virtual] [%%virt]"
+	printf("[taskid] [thrds] [resident] [%%resi] [virtual] [%%virt]"
 	    " [%%user] [%%kern] [name");
 	screen_newline();
@@ -303,5 +303,5 @@
 		order_suffix(task->virtmem, &virtmem, &virtmem_suffix);
 		
-		printf("%-8" PRIu64 " %9zu %9" PRIu64 "%c ",
+		printf("%-8" PRIu64 " %7zu %9" PRIu64 "%c ",
 		    task->task_id, task->threads, resmem, resmem_suffix);
 		print_percent(perc->resmem, 2);
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/c/generic/async.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -134,4 +134,11 @@
 
 typedef struct {
+	sysarg_t in_task_hash;
+	link_t link;
+	int refcnt;
+	void *data;
+} client_t;
+
+typedef struct {
 	awaiter_t wdata;
 	
@@ -139,7 +146,12 @@
 	link_t link;
 	
+	/** Incoming client task hash. */
+	sysarg_t in_task_hash;
 	/** Incoming phone hash. */
 	sysarg_t in_phone_hash;
 	
+	/** Link to the client tracking structure. */
+	client_t *client;
+
 	/** Messages that should be delivered to this fibril. */
 	link_t msg_queue;
@@ -160,4 +172,35 @@
 fibril_local connection_t *FIBRIL_connection;
 
+static void *default_client_data_constructor(void)
+{
+	return NULL;
+}
+
+static void default_client_data_destructor(void *data)
+{
+}
+
+static async_client_data_ctor_t async_client_data_create =
+    default_client_data_constructor;
+static async_client_data_dtor_t async_client_data_destroy =
+    default_client_data_destructor;
+
+void async_set_client_data_constructor(async_client_data_ctor_t ctor)
+{
+	async_client_data_create = ctor;
+}
+
+void async_set_client_data_destructor(async_client_data_dtor_t dtor)
+{
+	async_client_data_destroy = dtor;
+}
+
+void *async_client_data_get(void)
+{
+	assert(FIBRIL_connection);
+
+	return FIBRIL_connection->client->data;
+}
+
 static void default_client_connection(ipc_callid_t callid, ipc_call_t *call);
 static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call);
@@ -174,8 +217,33 @@
 static async_client_conn_t interrupt_received = default_interrupt_received;
 
+static hash_table_t client_hash_table;
 static hash_table_t conn_hash_table;
 static LIST_INITIALIZE(timeout_list);
 
-#define CONN_HASH_TABLE_CHAINS  32
+#define CLIENT_HASH_TABLE_BUCKETS	32
+#define CONN_HASH_TABLE_BUCKETS		32
+
+static hash_index_t client_hash(unsigned long *key)
+{
+	assert(key);
+	return (((*key) >> 4) % CLIENT_HASH_TABLE_BUCKETS); 
+}
+
+static int client_compare(unsigned long key[], hash_count_t keys, link_t *item)
+{
+	client_t *cl = hash_table_get_instance(item, client_t, link);
+	return (key[0] == cl->in_task_hash);
+}
+
+static void client_remove(link_t *item)
+{
+}
+
+/** Operations for the client hash table. */
+static hash_table_operations_t client_hash_table_ops = {
+	.hash = client_hash,
+	.compare = client_compare,
+	.remove_callback = client_remove
+};
 
 /** Compute hash into the connection hash table based on the source phone hash.
@@ -189,5 +257,5 @@
 {
 	assert(key);
-	return (((*key) >> 4) % CONN_HASH_TABLE_CHAINS);
+	return (((*key) >> 4) % CONN_HASH_TABLE_BUCKETS);
 }
 
@@ -480,19 +548,76 @@
 static int connection_fibril(void *arg)
 {
+	unsigned long key;
+	client_t *cl;
+	link_t *lnk;
+	bool destroy = false;
+
 	/*
-	 * Setup fibril-local connection pointer and call client_connection().
-	 *
+	 * Setup fibril-local connection pointer.
 	 */
 	FIBRIL_connection = (connection_t *) arg;
+
+	/*
+	 * Add our reference for the current connection in the client task
+	 * tracking structure. If this is the first reference, create and
+	 * hash in a new tracking structure.
+	 */
+	futex_down(&async_futex);
+	key = FIBRIL_connection->in_task_hash;
+	lnk = hash_table_find(&client_hash_table, &key);
+	if (lnk) {
+		cl = hash_table_get_instance(lnk, client_t, link);
+		cl->refcnt++;
+	} else {
+		cl = malloc(sizeof(client_t));
+		if (!cl) {
+			ipc_answer_0(FIBRIL_connection->callid, ENOMEM);
+			futex_up(&async_futex);
+			return 0;
+		}
+		cl->in_task_hash = FIBRIL_connection->in_task_hash;
+		async_serialize_start();
+		cl->data = async_client_data_create();
+		async_serialize_end();
+		cl->refcnt = 1;
+		hash_table_insert(&client_hash_table, &key, &cl->link);
+	}
+	futex_up(&async_futex);
+
+	FIBRIL_connection->client = cl;
+
+	/*
+	 * Call the connection handler function.
+	 */
 	FIBRIL_connection->cfibril(FIBRIL_connection->callid,
 	    &FIBRIL_connection->call);
 	
-	/* Remove myself from the connection hash table */
+	/*
+	 * Remove the reference for this client task connection.
+	 */
 	futex_down(&async_futex);
-	unsigned long key = FIBRIL_connection->in_phone_hash;
+	if (--cl->refcnt == 0) {
+		hash_table_remove(&client_hash_table, &key, 1);
+		destroy = true;
+	}
+	futex_up(&async_futex);
+
+	if (destroy) {
+		if (cl->data)
+			async_client_data_destroy(cl->data);
+		free(cl);
+	}
+
+	/*
+	 * Remove myself from the connection hash table.
+	 */
+	futex_down(&async_futex);
+	key = FIBRIL_connection->in_phone_hash;
 	hash_table_remove(&conn_hash_table, &key, 1);
 	futex_up(&async_futex);
 	
-	/* Answer all remaining messages with EHANGUP */
+	/*
+	 * Answer all remaining messages with EHANGUP.
+	 */
 	while (!list_empty(&FIBRIL_connection->msg_queue)) {
 		msg_t *msg;
@@ -505,4 +630,8 @@
 	}
 	
+	/*
+	 * If the connection was hung-up, answer the last call,
+	 * i.e. IPC_M_PHONE_HUNGUP.
+	 */
 	if (FIBRIL_connection->close_callid)
 		ipc_answer_0(FIBRIL_connection->close_callid, EOK);
@@ -517,4 +646,5 @@
  * particular fibrils.
  *
+ * @param in_task_hash  Identification of the incoming connection.
  * @param in_phone_hash Identification of the incoming connection.
  * @param callid        Hash of the opening IPC_M_CONNECT_ME_TO call.
@@ -529,6 +659,7 @@
  *
  */
-fid_t async_new_connection(sysarg_t in_phone_hash, ipc_callid_t callid,
-    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
+fid_t async_new_connection(sysarg_t in_task_hash, sysarg_t in_phone_hash,
+    ipc_callid_t callid, ipc_call_t *call,
+    void (*cfibril)(ipc_callid_t, ipc_call_t *))
 {
 	connection_t *conn = malloc(sizeof(*conn));
@@ -539,4 +670,5 @@
 	}
 	
+	conn->in_task_hash = in_task_hash;
 	conn->in_phone_hash = in_phone_hash;
 	list_initialize(&conn->msg_queue);
@@ -592,6 +724,6 @@
 	case IPC_M_CONNECT_ME_TO:
 		/* Open new connection with fibril etc. */
-		async_new_connection(IPC_GET_ARG5(*call), callid, call,
-		    client_connection);
+		async_new_connection(call->in_task_hash, IPC_GET_ARG5(*call),
+		    callid, call, client_connection);
 		goto out;
 	}
@@ -744,7 +876,7 @@
 int __async_init(void)
 {
-	if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
-	    &conn_hash_table_ops)) {
-		printf("%s: Cannot create async hash table\n", "libc");
+	if (!hash_table_create(&client_hash_table, CLIENT_HASH_TABLE_BUCKETS, 1,
+	    &client_hash_table_ops) || !hash_table_create(&conn_hash_table,
+	    CONN_HASH_TABLE_BUCKETS, 1, &conn_hash_table_ops)) {
 		return ENOMEM;
 	}
Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/c/generic/devman.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -116,6 +116,5 @@
 	async_set_client_connection(conn);
 	
-	sysarg_t callback_phonehash;
-	ipc_connect_to_me(phone, 0, 0, 0, &callback_phonehash);
+	ipc_connect_to_me(phone, 0, 0, 0, NULL, NULL);
 	async_wait_for(req, &retval);
 	
Index: uspace/lib/c/generic/devmap.c
===================================================================
--- uspace/lib/c/generic/devmap.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/c/generic/devmap.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -116,6 +116,5 @@
 	async_set_client_connection(conn);
 	
-	sysarg_t callback_phonehash;
-	ipc_connect_to_me(phone, 0, 0, 0, &callback_phonehash);
+	ipc_connect_to_me(phone, 0, 0, 0, NULL, NULL);
 	async_wait_for(req, &retval);
 	
Index: uspace/lib/c/generic/ipc.c
===================================================================
--- uspace/lib/c/generic/ipc.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/c/generic/ipc.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -578,5 +578,7 @@
  * @param arg2		Service-defined argument.
  * @param arg3		Service-defined argument.
- * @param phonehash	Storage where the library will store an opaque
+ * @param taskhash	Storage where the kernel will store an opaque
+ *			identifier of the client task.
+ * @param phonehash	Storage where the kernel will store an opaque
  *			identifier of the phone that will be used for incoming
  *			calls. This identifier can be used for connection
@@ -586,8 +588,8 @@
  */
 int ipc_connect_to_me(int phoneid, int arg1, int arg2, int arg3, 
-    sysarg_t *phonehash)
+    sysarg_t *taskhash, sysarg_t *phonehash)
 {
 	return ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2,
-	    arg3, NULL, NULL, NULL, NULL, phonehash);
+	    arg3, NULL, NULL, NULL, taskhash, phonehash);
 }
 
Index: uspace/lib/c/generic/net/modules.c
===================================================================
--- uspace/lib/c/generic/net/modules.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/c/generic/net/modules.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -143,12 +143,15 @@
 	if (phone >= 0) {
 		/* Request the bidirectional connection */
+		sysarg_t taskhash;
 		sysarg_t phonehash;
 		
-		rc = ipc_connect_to_me(phone, arg1, arg2, arg3, &phonehash);
+		rc = ipc_connect_to_me(phone, arg1, arg2, arg3, &taskhash,
+		    &phonehash);
 		if (rc != EOK) {
 			ipc_hangup(phone);
 			return rc;
 		}
-		async_new_connection(phonehash, 0, NULL, client_receiver);
+		async_new_connection(taskhash, phonehash, 0, NULL,
+		    client_receiver);
 	}
 	
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -46,7 +46,7 @@
 #include <ipc/services.h>
 #include <async.h>
-#include <atomic.h>
-#include <futex.h>
+#include <fibril_synch.h>
 #include <errno.h>
+#include <assert.h>
 #include <str.h>
 #include <devmap.h>
@@ -54,7 +54,10 @@
 #include <ipc/devmap.h>
 
+static async_sess_t vfs_session;
+
+static FIBRIL_MUTEX_INITIALIZE(vfs_phone_mutex);
 static int vfs_phone = -1;
-static futex_t vfs_phone_futex = FUTEX_INITIALIZER;
-static futex_t cwd_futex = FUTEX_INITIALIZER;
+
+static FIBRIL_MUTEX_INITIALIZE(cwd_mutex);
 
 static int cwd_fd = -1;
@@ -67,14 +70,14 @@
 	char *ncwd_path_nc;
 
-	futex_down(&cwd_futex);
+	fibril_mutex_lock(&cwd_mutex);
 	size_t size = str_size(path);
 	if (*path != '/') {
 		if (!cwd_path) {
-			futex_up(&cwd_futex);
+			fibril_mutex_unlock(&cwd_mutex);
 			return NULL;
 		}
 		ncwd_path_nc = malloc(cwd_size + 1 + size + 1);
 		if (!ncwd_path_nc) {
-			futex_up(&cwd_futex);
+			fibril_mutex_unlock(&cwd_mutex);
 			return NULL;
 		}
@@ -85,5 +88,5 @@
 		ncwd_path_nc = malloc(size + 1);
 		if (!ncwd_path_nc) {
-			futex_up(&cwd_futex);
+			fibril_mutex_unlock(&cwd_mutex);
 			return NULL;
 		}
@@ -93,5 +96,5 @@
 	ncwd_path = canonify(ncwd_path_nc, retlen);
 	if (!ncwd_path) {
-		futex_up(&cwd_futex);
+		fibril_mutex_unlock(&cwd_mutex);
 		free(ncwd_path_nc);
 		return NULL;
@@ -105,15 +108,43 @@
 	free(ncwd_path_nc);
 	if (!ncwd_path) {
-		futex_up(&cwd_futex);
+		fibril_mutex_unlock(&cwd_mutex);
 		return NULL;
 	}
-	futex_up(&cwd_futex);
+	fibril_mutex_unlock(&cwd_mutex);
 	return ncwd_path;
 }
 
+/** Connect to VFS service and create session. */
 static void vfs_connect(void)
 {
-	while (vfs_phone < 0)
-		vfs_phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_VFS, 0, 0);
+	while (vfs_phone < 0) {
+		vfs_phone = async_connect_me_to_blocking(PHONE_NS, SERVICE_VFS,
+		    0, 0);
+	}
+	
+	async_session_create(&vfs_session, vfs_phone, 0);
+}
+
+/** Start an async exchange on the VFS session.
+ *
+ * @return		New phone to be used during the exchange.
+ */
+static int vfs_exchange_begin(void)
+{
+	fibril_mutex_lock(&vfs_phone_mutex);
+	if (vfs_phone < 0)
+		vfs_connect();
+	fibril_mutex_unlock(&vfs_phone_mutex);
+
+	return async_exchange_begin(&vfs_session);
+}
+
+/** End an async exchange on the VFS session.
+ *
+ * @param phone		Phone used during the exchange.
+ */
+static void vfs_exchange_end(int phone)
+{
+	async_exchange_end(&vfs_session, phone);
 }
 
@@ -154,16 +185,13 @@
 	}
 	
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
-	
+	int vfs_phone = vfs_exchange_begin();
+
 	sysarg_t rc_orig;
 	aid_t req = async_send_2(vfs_phone, VFS_IN_MOUNT, devmap_handle, flags, NULL);
 	sysarg_t rc = async_data_write_start(vfs_phone, (void *) mpa, mpa_size);
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(mpa);
+		async_wait_for(req, &rc_orig);
 		
 		if (null_id != -1)
@@ -178,8 +206,7 @@
 	rc = async_data_write_start(vfs_phone, (void *) opts, str_size(opts));
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(mpa);
+		async_wait_for(req, &rc_orig);
 		
 		if (null_id != -1)
@@ -194,8 +221,7 @@
 	rc = async_data_write_start(vfs_phone, (void *) fs_name, str_size(fs_name));
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(mpa);
+		async_wait_for(req, &rc_orig);
 		
 		if (null_id != -1)
@@ -211,8 +237,7 @@
 	rc = async_req_0_0(vfs_phone, IPC_M_PING);
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(mpa);
+		async_wait_for(req, &rc_orig);
 		
 		if (null_id != -1)
@@ -225,8 +250,7 @@
 	}
 	
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
 	free(mpa);
+	async_wait_for(req, &rc);
 	
 	if ((rc != EOK) && (null_id != -1))
@@ -248,26 +272,22 @@
 		return ENOMEM;
 	
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	req = async_send_0(vfs_phone, VFS_IN_UNMOUNT, NULL);
 	rc = async_data_write_start(vfs_phone, (void *) mpa, mpa_size);
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(mpa);
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	
-
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+		async_wait_for(req, &rc_orig);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+
+	vfs_exchange_end(vfs_phone);
 	free(mpa);
+	async_wait_for(req, &rc);
 	
 	return (int) rc;
@@ -276,7 +296,5 @@
 static int open_internal(const char *abs, size_t abs_size, int lflag, int oflag)
 {
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	ipc_call_t answer;
@@ -285,19 +303,17 @@
 	
 	if (rc != EOK) {
+		vfs_exchange_end(vfs_phone);
+
 		sysarg_t rc_orig;
 		async_wait_for(req, &rc_orig);
 		
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
-		
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+	vfs_exchange_end(vfs_phone);
+	async_wait_for(req, &rc);
 	
 	if (rc != EOK)
@@ -322,7 +338,5 @@
 int open_node(fdi_node_t *node, int oflag)
 {
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	ipc_call_t answer;
@@ -330,8 +344,8 @@
 	    node->devmap_handle, node->index, oflag, &answer);
 	
-	sysarg_t rc;
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
+
+	sysarg_t rc;
+	async_wait_for(req, &rc);
 	
 	if (rc != EOK)
@@ -345,12 +359,9 @@
 	sysarg_t rc;
 	
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	rc = async_req_1_0(vfs_phone, VFS_IN_CLOSE, fildes);
 	
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
 	
 	return (int)rc;
@@ -363,16 +374,14 @@
 	aid_t req;
 
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	req = async_send_1(vfs_phone, VFS_IN_READ, fildes, &answer);
 	rc = async_data_read_start(vfs_phone, (void *)buf, nbyte);
 	if (rc != EOK) {
+		vfs_exchange_end(vfs_phone);
+
 		sysarg_t rc_orig;
-	
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		async_wait_for(req, &rc_orig);
+
 		if (rc_orig == EOK)
 			return (ssize_t) rc;
@@ -380,7 +389,6 @@
 			return (ssize_t) rc_orig;
 	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
+	async_wait_for(req, &rc);
 	if (rc == EOK)
 		return (ssize_t) IPC_GET_ARG1(answer);
@@ -395,16 +403,14 @@
 	aid_t req;
 
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	req = async_send_1(vfs_phone, VFS_IN_WRITE, fildes, &answer);
 	rc = async_data_write_start(vfs_phone, (void *)buf, nbyte);
 	if (rc != EOK) {
+		vfs_exchange_end(vfs_phone);
+
 		sysarg_t rc_orig;
-	
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		async_wait_for(req, &rc_orig);
+
 		if (rc_orig == EOK)
 			return (ssize_t) rc;
@@ -412,7 +418,6 @@
 			return (ssize_t) rc_orig;
 	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
+	async_wait_for(req, &rc);
 	if (rc == EOK)
 		return (ssize_t) IPC_GET_ARG1(answer);
@@ -423,12 +428,9 @@
 int fsync(int fildes)
 {
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	sysarg_t rc = async_req_1_0(vfs_phone, VFS_IN_SYNC, fildes);
 	
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
 	
 	return (int) rc;
@@ -437,7 +439,5 @@
 off64_t lseek(int fildes, off64_t offset, int whence)
 {
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	sysarg_t newoff_lo;
@@ -447,6 +447,5 @@
 	    &newoff_lo, &newoff_hi);
 	
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
 	
 	if (rc != EOK)
@@ -460,12 +459,9 @@
 	sysarg_t rc;
 	
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	rc = async_req_3_0(vfs_phone, VFS_IN_TRUNCATE, fildes,
 	    LOWER32(length), UPPER32(length));
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
 	
 	return (int) rc;
@@ -477,16 +473,14 @@
 	aid_t req;
 
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	req = async_send_1(vfs_phone, VFS_IN_FSTAT, fildes, NULL);
 	rc = async_data_read_start(vfs_phone, (void *) stat, sizeof(struct stat));
 	if (rc != EOK) {
+		vfs_exchange_end(vfs_phone);
+
 		sysarg_t rc_orig;
-		
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		async_wait_for(req, &rc_orig);
+
 		if (rc_orig == EOK)
 			return (ssize_t) rc;
@@ -494,7 +488,6 @@
 			return (ssize_t) rc_orig;
 	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
+	async_wait_for(req, &rc);
 
 	return rc;
@@ -512,15 +505,12 @@
 		return ENOMEM;
 	
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	req = async_send_0(vfs_phone, VFS_IN_STAT, NULL);
 	rc = async_data_write_start(vfs_phone, pa, pa_size);
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(pa);
+		async_wait_for(req, &rc_orig);
 		if (rc_orig == EOK)
 			return (int) rc;
@@ -530,17 +520,15 @@
 	rc = async_data_read_start(vfs_phone, stat, sizeof(struct stat));
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(pa);
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+		async_wait_for(req, &rc_orig);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	vfs_exchange_end(vfs_phone);
 	free(pa);
+	async_wait_for(req, &rc);
 	return rc;
 }
@@ -601,26 +589,23 @@
 		return ENOMEM;
 	
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	req = async_send_1(vfs_phone, VFS_IN_MKDIR, mode, NULL);
 	rc = async_data_write_start(vfs_phone, pa, pa_size);
 	if (rc != EOK) {
+		vfs_exchange_end(vfs_phone);
+		free(pa);
+
 		sysarg_t rc_orig;
-	
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
-		free(pa);
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+		async_wait_for(req, &rc_orig);
+
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	vfs_exchange_end(vfs_phone);
 	free(pa);
+	async_wait_for(req, &rc);
 	return rc;
 }
@@ -636,26 +621,23 @@
 		return ENOMEM;
 
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	req = async_send_0(vfs_phone, VFS_IN_UNLINK, NULL);
 	rc = async_data_write_start(vfs_phone, pa, pa_size);
 	if (rc != EOK) {
+		vfs_exchange_end(vfs_phone);
+		free(pa);
+
 		sysarg_t rc_orig;
-
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
-		free(pa);
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+		async_wait_for(req, &rc_orig);
+
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	vfs_exchange_end(vfs_phone);
 	free(pa);
+	async_wait_for(req, &rc);
 	return rc;
 }
@@ -689,16 +671,13 @@
 	}
 
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	req = async_send_0(vfs_phone, VFS_IN_RENAME, NULL);
 	rc = async_data_write_start(vfs_phone, olda, olda_size);
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(olda);
 		free(newa);
+		async_wait_for(req, &rc_orig);
 		if (rc_orig == EOK)
 			return (int) rc;
@@ -708,19 +687,17 @@
 	rc = async_data_write_start(vfs_phone, newa, newa_size);
 	if (rc != EOK) {
-		async_wait_for(req, &rc_orig);
-		async_serialize_end();
-		futex_up(&vfs_phone_futex);
+		vfs_exchange_end(vfs_phone);
 		free(olda);
 		free(newa);
-		if (rc_orig == EOK)
-			return (int) rc;
-		else
-			return (int) rc_orig;
-	}
-	async_wait_for(req, &rc);
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+		async_wait_for(req, &rc_orig);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	vfs_exchange_end(vfs_phone);
 	free(olda);
 	free(newa);
+	async_wait_for(req, &rc);
 	return rc;
 }
@@ -740,5 +717,5 @@
 	}
 	
-	futex_down(&cwd_futex);
+	fibril_mutex_lock(&cwd_mutex);
 	
 	if (cwd_fd >= 0)
@@ -753,5 +730,5 @@
 	cwd_size = abs_size;
 	
-	futex_up(&cwd_futex);
+	fibril_mutex_unlock(&cwd_mutex);
 	return EOK;
 }
@@ -762,13 +739,13 @@
 		return NULL;
 	
-	futex_down(&cwd_futex);
+	fibril_mutex_lock(&cwd_mutex);
 	
 	if ((cwd_size == 0) || (size < cwd_size + 1)) {
-		futex_up(&cwd_futex);
+		fibril_mutex_unlock(&cwd_mutex);
 		return NULL;
 	}
 	
 	str_cpy(buf, size, cwd_path);
-	futex_up(&cwd_futex);
+	fibril_mutex_unlock(&cwd_mutex);
 	
 	return buf;
@@ -806,13 +783,10 @@
 int dup2(int oldfd, int newfd)
 {
-	futex_down(&vfs_phone_futex);
-	async_serialize_start();
-	vfs_connect();
+	int vfs_phone = vfs_exchange_begin();
 	
 	sysarg_t ret;
 	sysarg_t rc = async_req_2_1(vfs_phone, VFS_IN_DUP, oldfd, newfd, &ret);
 	
-	async_serialize_end();
-	futex_up(&vfs_phone_futex);
+	vfs_exchange_end(vfs_phone);
 	
 	if (rc == EOK)
Index: uspace/lib/c/include/async.h
===================================================================
--- uspace/lib/c/include/async.h	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/c/include/async.h	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -44,5 +44,9 @@
 
 typedef ipc_callid_t aid_t;
-typedef void (*async_client_conn_t)(ipc_callid_t callid, ipc_call_t *call);
+
+typedef void *(*async_client_data_ctor_t)(void);
+typedef void (*async_client_data_dtor_t)(void *);
+
+typedef void (*async_client_conn_t)(ipc_callid_t, ipc_call_t *);
 
 extern atomic_t async_futex;
@@ -51,5 +55,5 @@
 
 extern int __async_init(void);
-extern ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs);
+extern ipc_callid_t async_get_call_timeout(ipc_call_t *, suseconds_t);
 
 static inline ipc_callid_t async_get_call(ipc_call_t *data)
@@ -85,21 +89,24 @@
 	    (arg5), (dataptr))
 
-extern aid_t async_send_fast(int phoneid, sysarg_t method, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr);
-extern aid_t async_send_slow(int phoneid, sysarg_t method, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
-    ipc_call_t *dataptr);
-extern void async_wait_for(aid_t amsgid, sysarg_t *result);
-extern int async_wait_timeout(aid_t amsgid, sysarg_t *retval,
-    suseconds_t timeout);
-
-extern fid_t async_new_connection(sysarg_t in_phone_hash, ipc_callid_t callid,
-    ipc_call_t *call, void (*cthread)(ipc_callid_t, ipc_call_t *));
-extern void async_usleep(suseconds_t timeout);
+extern aid_t async_send_fast(int, sysarg_t, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t, ipc_call_t *);
+extern aid_t async_send_slow(int, sysarg_t, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t, sysarg_t, ipc_call_t *);
+extern void async_wait_for(aid_t, sysarg_t *);
+extern int async_wait_timeout(aid_t, sysarg_t *, suseconds_t);
+
+extern fid_t async_new_connection(sysarg_t, sysarg_t, ipc_callid_t,
+    ipc_call_t *, void (*)(ipc_callid_t, ipc_call_t *));
+extern void async_usleep(suseconds_t);
 extern void async_create_manager(void);
 extern void async_destroy_manager(void);
 
-extern void async_set_client_connection(async_client_conn_t conn);
-extern void async_set_interrupt_received(async_client_conn_t conn);
+extern void async_set_client_data_constructor(async_client_data_ctor_t);
+extern void async_set_client_data_destructor(async_client_data_dtor_t);
+
+extern void *async_client_data_get(void);
+
+extern void async_set_client_connection(async_client_conn_t);
+extern void async_set_interrupt_received(async_client_conn_t);
 
 /* Wrappers for simple communication */
@@ -243,10 +250,9 @@
 	    (arg5), (rc1), (rc2), (rc3), (rc4), (rc5))
 
-extern sysarg_t async_req_fast(int phoneid, sysarg_t method, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
-    sysarg_t *r3, sysarg_t *r4, sysarg_t *r5);
-extern sysarg_t async_req_slow(int phoneid, sysarg_t method, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
-    sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5);
+extern sysarg_t async_req_fast(int, sysarg_t, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t, sysarg_t *, sysarg_t *, sysarg_t *, sysarg_t *, sysarg_t *);
+extern sysarg_t async_req_slow(int, sysarg_t, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t, sysarg_t, sysarg_t *, sysarg_t *, sysarg_t *, sysarg_t *,
+    sysarg_t *);
 
 static inline void async_serialize_start(void)
Index: uspace/lib/c/include/ipc/ipc.h
===================================================================
--- uspace/lib/c/include/ipc/ipc.h	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/c/include/ipc/ipc.h	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -46,4 +46,5 @@
 typedef struct {
 	sysarg_t args[IPC_CALL_LEN];
+	sysarg_t in_task_hash;
 	sysarg_t in_phone_hash;
 } ipc_call_t;
@@ -258,5 +259,5 @@
     sysarg_t, sysarg_t, void *, ipc_async_callback_t, int);
 
-extern int ipc_connect_to_me(int, int, int, int, sysarg_t *);
+extern int ipc_connect_to_me(int, int, int, int, sysarg_t *, sysarg_t *);
 extern int ipc_connect_me_to(int, int, int, int);
 extern int ipc_connect_me_to_blocking(int, int, int, int);
Index: uspace/lib/fs/libfs.c
===================================================================
--- uspace/lib/fs/libfs.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/fs/libfs.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -102,5 +102,6 @@
 	 * Ask VFS for callback connection.
 	 */
-	ipc_connect_to_me(vfs_phone, 0, 0, 0, &reg->vfs_phonehash);
+	sysarg_t taskhash;
+	ipc_connect_to_me(vfs_phone, 0, 0, 0, &taskhash, &reg->vfs_phonehash);
 	
 	/*
@@ -131,5 +132,5 @@
 	 * Create a connection fibril to handle the callback connection.
 	 */
-	async_new_connection(reg->vfs_phonehash, 0, NULL, conn);
+	async_new_connection(taskhash, reg->vfs_phonehash, 0, NULL, conn);
 	
 	/*
Index: uspace/lib/net/il/il_skel.c
===================================================================
--- uspace/lib/net/il/il_skel.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/net/il/il_skel.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -115,6 +115,5 @@
 		goto out;
 	
-	sysarg_t phonehash;
-	rc = ipc_connect_to_me(PHONE_NS, service, 0, 0, &phonehash);
+	rc = ipc_connect_to_me(PHONE_NS, service, 0, 0, NULL, NULL);
 	if (rc != EOK)
 		goto out;
Index: uspace/lib/net/nil/nil_skel.c
===================================================================
--- uspace/lib/net/nil/nil_skel.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/net/nil/nil_skel.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -115,6 +115,5 @@
 		goto out;
 	
-	sysarg_t phonehash;
-	rc = ipc_connect_to_me(PHONE_NS, service, 0, 0, &phonehash);
+	rc = ipc_connect_to_me(PHONE_NS, service, 0, 0, NULL, NULL);
 	if (rc != EOK)
 		goto out;
Index: uspace/lib/net/tl/tl_skel.c
===================================================================
--- uspace/lib/net/tl/tl_skel.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/net/tl/tl_skel.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -117,6 +117,5 @@
 		goto out;
 	
-	sysarg_t phonehash;
-	rc = ipc_connect_to_me(PHONE_NS, service, 0, 0, &phonehash);
+	rc = ipc_connect_to_me(PHONE_NS, service, 0, 0, NULL, NULL);
 	if (rc != EOK)
 		goto out;
Index: uspace/lib/usbvirt/src/main.c
===================================================================
--- uspace/lib/usbvirt/src/main.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/lib/usbvirt/src/main.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -221,5 +221,6 @@
 	
 	sysarg_t phonehash;
-	rc = ipc_connect_to_me(hcd_phone, 0, 0, 0, &phonehash);
+	sysarg_t taskhash;
+	rc = ipc_connect_to_me(hcd_phone, 0, 0, 0, &taskhash, &phonehash);
 	if (rc != EOK) {
 		printf("ipc_connect_to_me() failed\n");
@@ -233,5 +234,5 @@
 	virtual_device->id = 0;
 	
-	async_new_connection(phonehash, 0, NULL, callback_connection);
+	async_new_connection(taskhash, phonehash, 0, NULL, callback_connection);
 	
 	return EOK;
Index: uspace/srv/clip/clip.c
===================================================================
--- uspace/srv/clip/clip.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/clip/clip.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -183,6 +183,5 @@
 	async_set_client_connection(clip_connection);
 	
-	sysarg_t phonead;
-	if (ipc_connect_to_me(PHONE_NS, SERVICE_CLIPBOARD, 0, 0, &phonead) != 0) 
+	if (ipc_connect_to_me(PHONE_NS, SERVICE_CLIPBOARD, 0, 0, NULL, NULL)) 
 		return -1;
 	
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/devman/main.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -587,6 +587,5 @@
 
 	/* Register device manager at naming service. */
-	sysarg_t phonead;
-	if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAN, 0, 0, &phonead) != 0)
+	if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAN, 0, 0, NULL, NULL) != 0)
 		return -1;
 
Index: uspace/srv/devmap/devmap.c
===================================================================
--- uspace/srv/devmap/devmap.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/devmap/devmap.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -1150,6 +1150,5 @@
 	
 	/* Register device mapper at naming service */
-	sysarg_t phonead;
-	if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0) 
+	if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, NULL, NULL) != 0)
 		return -1;
 	
Index: uspace/srv/hid/adb_mouse/adb_dev.c
===================================================================
--- uspace/srv/hid/adb_mouse/adb_dev.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hid/adb_mouse/adb_dev.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -68,11 +68,12 @@
 
 	/* NB: The callback connection is slotted for removal */
+	sysarg_t taskhash;
 	sysarg_t phonehash;
-	if (ipc_connect_to_me(dev_phone, 0, 0, 0, &phonehash) != 0) {
+	if (ipc_connect_to_me(dev_phone, 0, 0, 0, &taskhash, &phonehash) != 0) {
 		printf(NAME ": Failed to create callback from device\n");
 		return false;
 	}
 
-	async_new_connection(phonehash, 0, NULL, adb_dev_events);
+	async_new_connection(taskhash, phonehash, 0, NULL, adb_dev_events);
 
 	return 0;
Index: uspace/srv/hid/char_mouse/chardev.c
===================================================================
--- uspace/srv/hid/char_mouse/chardev.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hid/char_mouse/chardev.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -70,11 +70,12 @@
 
 	/* NB: The callback connection is slotted for removal */
+	sysarg_t taskhash;
 	sysarg_t phonehash;
-	if (ipc_connect_to_me(dev_phone, 0, 0, 0, &phonehash) != 0) {
+	if (ipc_connect_to_me(dev_phone, 0, 0, 0, &taskhash, &phonehash) != 0) {
 		printf(NAME ": Failed to create callback from device\n");
 		return false;
 	}
 
-	async_new_connection(phonehash, 0, NULL, chardev_events);
+	async_new_connection(taskhash, phonehash, 0, NULL, chardev_events);
 
 	return 0;
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hid/console/console.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -727,6 +727,7 @@
 	/* NB: The callback connection is slotted for removal */
 	sysarg_t phonehash;
+	sysarg_t taskhash;
 	int rc = async_req_3_5(phone, IPC_M_CONNECT_TO_ME, SERVICE_CONSOLE,
-	    0, 0, NULL, NULL, NULL, NULL, &phonehash);
+	    0, 0, NULL, NULL, NULL, &taskhash, &phonehash);
 	if (rc != EOK) {
 		printf(NAME ": Failed to create callback from input device\n");
@@ -734,5 +735,5 @@
 	}
 	
-	async_new_connection(phonehash, 0, NULL, keyboard_events);
+	async_new_connection(taskhash, phonehash, 0, NULL, keyboard_events);
 
 	printf(NAME ": we got a hit (new keyboard \"%s\").\n", path);
@@ -784,6 +785,7 @@
 	/* NB: The callback connection is slotted for removal */
 	sysarg_t phonehash;
+	sysarg_t taskhash;
 	int rc = async_req_3_5(phone, IPC_M_CONNECT_TO_ME, SERVICE_CONSOLE,
-	    0, 0, NULL, NULL, NULL, NULL, &phonehash);
+	    0, 0, NULL, NULL, NULL, &taskhash, &phonehash);
 	if (rc != EOK) {
 		printf(NAME ": Failed to create callback from input device\n");
@@ -791,5 +793,5 @@
 	}
 
-	async_new_connection(phonehash, 0, NULL, keyboard_events);
+	async_new_connection(taskhash, phonehash, 0, NULL, keyboard_events);
 
 	printf(NAME ": we got a hit (new keyboard \"/dev/%s\").\n",
@@ -862,6 +864,8 @@
 	}
 	
+	sysarg_t taskhash;
 	sysarg_t phonehash;
-	if (ipc_connect_to_me(mouse_phone, SERVICE_CONSOLE, 0, 0, &phonehash) != 0) {
+	if (ipc_connect_to_me(mouse_phone, SERVICE_CONSOLE, 0, 0, &taskhash,
+	    &phonehash) != 0) {
 		printf(NAME ": Failed to create callback from mouse device\n");
 		mouse_phone = -1;
@@ -869,5 +873,5 @@
 	}
 	
-	async_new_connection(phonehash, 0, NULL, mouse_events);
+	async_new_connection(taskhash, phonehash, 0, NULL, mouse_events);
 skip_mouse:
 	
Index: uspace/srv/hid/fb/main.c
===================================================================
--- uspace/srv/hid/fb/main.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hid/fb/main.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -114,6 +114,5 @@
 		return -1;
 	
-	sysarg_t phonead;
-	if (ipc_connect_to_me(PHONE_NS, SERVICE_VIDEO, 0, 0, &phonead) != 0) 
+	if (ipc_connect_to_me(PHONE_NS, SERVICE_VIDEO, 0, 0, NULL, NULL) != 0)
 		return -1;
 	
Index: uspace/srv/hid/kbd/port/adb.c
===================================================================
--- uspace/srv/hid/kbd/port/adb.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hid/kbd/port/adb.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -71,11 +71,12 @@
 
 	/* NB: The callback connection is slotted for removal */
+	sysarg_t taskhash;
 	sysarg_t phonehash;
-	if (ipc_connect_to_me(dev_phone, 0, 0, 0, &phonehash) != 0) {
+	if (ipc_connect_to_me(dev_phone, 0, 0, 0, &taskhash, &phonehash) != 0) {
 		printf(NAME ": Failed to create callback from device\n");
 		return false;
 	}
 
-	async_new_connection(phonehash, 0, NULL, kbd_port_events);
+	async_new_connection(taskhash, phonehash, 0, NULL, kbd_port_events);
 
 	return 0;
Index: uspace/srv/hid/kbd/port/chardev.c
===================================================================
--- uspace/srv/hid/kbd/port/chardev.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hid/kbd/port/chardev.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -91,11 +91,12 @@
 
 	/* NB: The callback connection is slotted for removal */
+	sysarg_t taskhash;
 	sysarg_t phonehash;
-	if (ipc_connect_to_me(dev_phone, 0, 0, 0, &phonehash) != 0) {
+	if (ipc_connect_to_me(dev_phone, 0, 0, 0, &taskhash, &phonehash) != 0) {
 		printf(NAME ": Failed to create callback from device\n");
 		return -1;
 	}
 
-	async_new_connection(phonehash, 0, NULL, kbd_port_events);
+	async_new_connection(taskhash, phonehash, 0, NULL, kbd_port_events);
 
 	return 0;
Index: uspace/srv/hw/irc/apic/apic.c
===================================================================
--- uspace/srv/hw/irc/apic/apic.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hw/irc/apic/apic.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -108,6 +108,5 @@
 	
 	async_set_client_connection(apic_connection);
-	sysarg_t phonead;
-	ipc_connect_to_me(PHONE_NS, SERVICE_APIC, 0, 0, &phonead);
+	ipc_connect_to_me(PHONE_NS, SERVICE_APIC, 0, 0, NULL, NULL);
 	
 	return true;
Index: uspace/srv/hw/irc/fhc/fhc.c
===================================================================
--- uspace/srv/hw/irc/fhc/fhc.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hw/irc/fhc/fhc.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -137,6 +137,5 @@
 	
 	async_set_client_connection(fhc_connection);
-	sysarg_t phonead;
-	ipc_connect_to_me(PHONE_NS, SERVICE_FHC, 0, 0, &phonead);
+	ipc_connect_to_me(PHONE_NS, SERVICE_FHC, 0, 0, NULL, NULL);
 	
 	return true;
Index: uspace/srv/hw/irc/i8259/i8259.c
===================================================================
--- uspace/srv/hw/irc/i8259/i8259.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hw/irc/i8259/i8259.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -150,6 +150,5 @@
 	
 	async_set_client_connection(i8259_connection);
-	sysarg_t phonead;
-	ipc_connect_to_me(PHONE_NS, SERVICE_I8259, 0, 0, &phonead);
+	ipc_connect_to_me(PHONE_NS, SERVICE_I8259, 0, 0, NULL, NULL);
 	
 	return true;
Index: uspace/srv/hw/irc/obio/obio.c
===================================================================
--- uspace/srv/hw/irc/obio/obio.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hw/irc/obio/obio.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -138,6 +138,5 @@
 	
 	async_set_client_connection(obio_connection);
-	sysarg_t phonead;
-	ipc_connect_to_me(PHONE_NS, SERVICE_OBIO, 0, 0, &phonead);
+	ipc_connect_to_me(PHONE_NS, SERVICE_OBIO, 0, 0, NULL, NULL);
 	
 	return true;
Index: uspace/srv/hw/netif/ne2000/ne2000.c
===================================================================
--- uspace/srv/hw/netif/ne2000/ne2000.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/hw/netif/ne2000/ne2000.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -397,6 +397,5 @@
 	async_set_interrupt_received(irq_handler);
 	
-	sysarg_t phonehash;
-	return ipc_connect_to_me(PHONE_NS, SERVICE_NE2000, 0, 0, &phonehash);
+	return ipc_connect_to_me(PHONE_NS, SERVICE_NE2000, 0, 0, NULL, NULL);
 }
 
Index: uspace/srv/loader/main.c
===================================================================
--- uspace/srv/loader/main.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/loader/main.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -423,5 +423,4 @@
 int main(int argc, char *argv[])
 {
-	sysarg_t phonead;
 	task_id_t id;
 	int rc;
@@ -439,5 +438,5 @@
 	
 	/* Register at naming service. */
-	if (ipc_connect_to_me(PHONE_NS, SERVICE_LOAD, 0, 0, &phonead) != 0) 
+	if (ipc_connect_to_me(PHONE_NS, SERVICE_LOAD, 0, 0, NULL, NULL) != 0)
 		return -2;
 
Index: uspace/srv/net/net/net.c
===================================================================
--- uspace/srv/net/net/net.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/net/net/net.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -326,5 +326,4 @@
 static int net_module_start(async_client_conn_t client_connection)
 {
-	sysarg_t phonehash;
 	int rc;
 	
@@ -338,5 +337,5 @@
 		goto out;
 	
-	rc = ipc_connect_to_me(PHONE_NS, SERVICE_NETWORKING, 0, 0, &phonehash);
+	rc = ipc_connect_to_me(PHONE_NS, SERVICE_NETWORKING, 0, 0, NULL, NULL);
 	if (rc != EOK)
 		goto out;
Index: uspace/srv/net/netif/lo/lo.c
===================================================================
--- uspace/srv/net/netif/lo/lo.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/net/netif/lo/lo.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -167,6 +167,5 @@
 int netif_initialize(void)
 {
-	sysarg_t phonehash;
-	return ipc_connect_to_me(PHONE_NS, SERVICE_LO, 0, 0, &phonehash);
+	return ipc_connect_to_me(PHONE_NS, SERVICE_LO, 0, 0, NULL, NULL);
 }
 
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/vfs/vfs.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -59,14 +59,4 @@
 	ipc_answer_0(iid, EOK);
 	
-	/*
-	 * Here we enter the main connection fibril loop.
-	 * The logic behind this loop and the protocol is that we'd like to keep
-	 * each connection open until the client hangs up. When the client hangs
-	 * up, we will free its VFS state. The act of hanging up the connection
-	 * by the client is equivalent to client termination because we cannot
-	 * distinguish one from the other. On the other hand, the client can
-	 * hang up arbitrarily if it has no open files and reestablish the
-	 * connection later.
-	 */
 	while (keep_on_going) {
 		ipc_call_t call;
@@ -133,6 +123,9 @@
 		}
 	}
-	
-	vfs_files_done();
+
+	/*
+	 * Open files for this client will be cleaned up when its last
+	 * connection fibril terminates.
+	 */
 }
 
@@ -166,4 +159,10 @@
 	
 	/*
+	 * Set client data constructor and destructor.
+	 */
+	async_set_client_data_constructor(vfs_client_data_create);
+	async_set_client_data_destructor(vfs_client_data_destroy);
+
+	/*
 	 * Set a connection handling function/fibril.
 	 */
@@ -173,6 +172,5 @@
 	 * Register at the naming service.
 	 */
-	sysarg_t phonead;
-	ipc_connect_to_me(PHONE_NS, SERVICE_VFS, 0, 0, &phonead);
+	ipc_connect_to_me(PHONE_NS, SERVICE_VFS, 0, 0, NULL, NULL);
 	
 	/*
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/vfs/vfs.h	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -188,13 +188,12 @@
 #define MAX_OPEN_FILES	128
 
-extern bool vfs_files_init(void);
-extern void vfs_files_done(void);
+extern void *vfs_client_data_create(void);
+extern void vfs_client_data_destroy(void *);
+
 extern vfs_file_t *vfs_file_get(int);
-extern int vfs_fd_assign(vfs_file_t *file, int fd);
+extern void vfs_file_put(vfs_file_t *);
+extern int vfs_fd_assign(vfs_file_t *, int);
 extern int vfs_fd_alloc(bool desc);
 extern int vfs_fd_free(int);
-
-extern void vfs_file_addref(vfs_file_t *);
-extern void vfs_file_delref(vfs_file_t *);
 
 extern void vfs_node_addref(vfs_node_t *);
Index: uspace/srv/vfs/vfs_file.c
===================================================================
--- uspace/srv/vfs/vfs_file.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/vfs/vfs_file.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -45,50 +45,98 @@
 #include "vfs.h"
 
-/**
- * This is a per-connection table of open files.
- * Our assumption is that each client opens only one connection and therefore
- * there is one table of open files per task. However, this may not be the case
- * and the client can open more connections to VFS. In that case, there will be
- * several tables and several file handle name spaces per task. Besides of this,
- * the functionality will stay unchanged. So unless the client knows what it is
- * doing, it should open one connection to VFS only.
- *
- * Allocation of the open files table is deferred until the client makes the
- * first VFS_OPEN operation.
- *
- * This resource being per-connection and, in the first place, per-fibril, we
- * don't need to protect it by a mutex.
- */
-fibril_local vfs_file_t **files = NULL;
+#define VFS_DATA	((vfs_client_data_t *) async_client_data_get())
+#define FILES		(VFS_DATA->files)
+
+typedef struct {
+	fibril_mutex_t lock;
+	vfs_file_t **files;
+} vfs_client_data_t;
 
 /** Initialize the table of open files. */
-bool vfs_files_init(void)
-{
-	if (!files) {
-		files = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
-		if (!files)
+static bool vfs_files_init(void)
+{
+	fibril_mutex_lock(&VFS_DATA->lock);
+	if (!FILES) {
+		FILES = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
+		if (!FILES) {
+			fibril_mutex_unlock(&VFS_DATA->lock);
 			return false;
-		memset(files, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
-	}
+		}
+		memset(FILES, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
+	}
+	fibril_mutex_unlock(&VFS_DATA->lock);
 	return true;
 }
 
 /** Cleanup the table of open files. */
-void vfs_files_done(void)
+static void vfs_files_done(void)
 {
 	int i;
 
-	if (!files)
+	if (!FILES)
 		return;
 
 	for (i = 0; i < MAX_OPEN_FILES; i++) {
-		if (files[i]) {
-			(void) vfs_close_internal(files[i]);
+		if (FILES[i]) {
+			(void) vfs_close_internal(FILES[i]);
 			(void) vfs_fd_free(i);
 		}
 	}
 	
-	free(files);
-}
+	free(FILES);
+}
+
+void *vfs_client_data_create(void)
+{
+	vfs_client_data_t *vfs_data;
+
+	vfs_data = malloc(sizeof(vfs_client_data_t));
+	if (vfs_data) {
+		fibril_mutex_initialize(&vfs_data->lock);
+		vfs_data->files = NULL;
+	}
+	
+	return vfs_data;
+}
+
+void vfs_client_data_destroy(void *data)
+{
+	vfs_client_data_t *vfs_data = (vfs_client_data_t *) data;
+
+	vfs_files_done();
+	free(vfs_data);
+}
+
+/** Increment reference count of VFS file structure.
+ *
+ * @param file		File structure that will have reference count
+ *			incremented.
+ */
+static void vfs_file_addref(vfs_file_t *file)
+{
+	assert(fibril_mutex_is_locked(&VFS_DATA->lock));
+
+	file->refcnt++;
+}
+
+/** Decrement reference count of VFS file structure.
+ *
+ * @param file		File structure that will have reference count
+ *			decremented.
+ */
+static void vfs_file_delref(vfs_file_t *file)
+{
+	assert(fibril_mutex_is_locked(&VFS_DATA->lock));
+
+	if (file->refcnt-- == 1) {
+		/*
+		 * Lost the last reference to a file, need to drop our reference
+		 * to the underlying VFS node.
+		 */
+		vfs_node_delref(file->node);
+		free(file);
+	}
+}
+
 
 /** Allocate a file descriptor.
@@ -111,13 +159,17 @@
 		i = 0;
 	
+	fibril_mutex_lock(&VFS_DATA->lock);
 	while (true) {
-		if (!files[i]) {
-			files[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
-			if (!files[i])
+		if (!FILES[i]) {
+			FILES[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
+			if (!FILES[i]) {
+				fibril_mutex_unlock(&VFS_DATA->lock);
 				return ENOMEM;
+			}
 			
-			memset(files[i], 0, sizeof(vfs_file_t));
-			fibril_mutex_initialize(&files[i]->lock);
-			vfs_file_addref(files[i]);
+			memset(FILES[i], 0, sizeof(vfs_file_t));
+			fibril_mutex_initialize(&FILES[i]->lock);
+			vfs_file_addref(FILES[i]);
+			fibril_mutex_unlock(&VFS_DATA->lock);
 			return (int) i;
 		}
@@ -135,4 +187,5 @@
 		}
 	}
+	fibril_mutex_unlock(&VFS_DATA->lock);
 	
 	return EMFILE;
@@ -150,10 +203,14 @@
 	if (!vfs_files_init())
 		return ENOMEM;
-	
-	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || (files[fd] == NULL))
+
+	fibril_mutex_lock(&VFS_DATA->lock);	
+	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || (FILES[fd] == NULL)) {
+		fibril_mutex_unlock(&VFS_DATA->lock);
 		return EBADF;
-	
-	vfs_file_delref(files[fd]);
-	files[fd] = NULL;
+	}
+	
+	vfs_file_delref(FILES[fd]);
+	FILES[fd] = NULL;
+	fibril_mutex_unlock(&VFS_DATA->lock);
 	
 	return EOK;
@@ -173,46 +230,18 @@
 	if (!vfs_files_init())
 		return ENOMEM;
-	
-	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || (files[fd] != NULL))
+
+	fibril_mutex_lock(&VFS_DATA->lock);	
+	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || (FILES[fd] != NULL)) {
+		fibril_mutex_unlock(&VFS_DATA->lock);
 		return EINVAL;
-	
-	files[fd] = file;
-	vfs_file_addref(files[fd]);
+	}
+	
+	FILES[fd] = file;
+	vfs_file_addref(FILES[fd]);
+	fibril_mutex_unlock(&VFS_DATA->lock);
 	
 	return EOK;
 }
 
-/** Increment reference count of VFS file structure.
- *
- * @param file		File structure that will have reference count
- *			incremented.
- */
-void vfs_file_addref(vfs_file_t *file)
-{
-	/*
-	 * File structures are per-connection, so no-one, except the current
-	 * fibril, should have a reference to them. This is the reason we don't
-	 * do any synchronization here.
-	 */
-	file->refcnt++;
-}
-
-/** Decrement reference count of VFS file structure.
- *
- * @param file		File structure that will have reference count
- *			decremented.
- */
-void vfs_file_delref(vfs_file_t *file)
-{
-	if (file->refcnt-- == 1) {
-		/*
-		 * Lost the last reference to a file, need to drop our reference
-		 * to the underlying VFS node.
-		 */
-		vfs_node_delref(file->node);
-		free(file);
-	}
-}
-
 /** Find VFS file structure for a given file descriptor.
  *
@@ -226,8 +255,25 @@
 		return NULL;
 	
-	if ((fd >= 0) && (fd < MAX_OPEN_FILES))
-		return files[fd];
+	fibril_mutex_lock(&VFS_DATA->lock);
+	if ((fd >= 0) && (fd < MAX_OPEN_FILES)) {
+		vfs_file_t *file = FILES[fd];
+		vfs_file_addref(file);
+		fibril_mutex_unlock(&VFS_DATA->lock);
+		return file;
+	}
+	fibril_mutex_unlock(&VFS_DATA->lock);
 	
 	return NULL;
+}
+
+/** Stop using a file structure.
+ *
+ * @param file		VFS file structure.
+ */
+void vfs_file_put(vfs_file_t *file)
+{
+	fibril_mutex_lock(&VFS_DATA->lock);
+	vfs_file_delref(file);
+	fibril_mutex_unlock(&VFS_DATA->lock);
 }
 
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision ef8d6558c58526391170f038efdfc4dfa25d034f)
+++ uspace/srv/vfs/vfs_ops.c	(revision 2b9646335943ad9872daddeaffee9b749d2830c1)
@@ -491,9 +491,4 @@
 void vfs_open(ipc_callid_t rid, ipc_call_t *request)
 {
-	if (!vfs_files_init()) {
-		ipc_answer_0(rid, ENOMEM);
-		return;
-	}
-	
 	/*
 	 * The POSIX interface is open(path, oflag, mode).
@@ -609,4 +604,5 @@
 	vfs_node_addref(node);
 	vfs_node_put(node);
+	vfs_file_put(file);
 	
 	/* Success! Return the new file descriptor to the client. */
@@ -617,9 +613,4 @@
 {
 	// FIXME: check for sanity of the supplied fs, dev and index
-	
-	if (!vfs_files_init()) {
-		ipc_answer_0(rid, ENOMEM);
-		return;
-	}
 	
 	/*
@@ -686,4 +677,5 @@
 	vfs_node_addref(node);
 	vfs_node_put(node);
+	vfs_file_put(file);
 	
 	/* Success! Return the new file descriptor to the client. */
@@ -721,5 +713,6 @@
 	vfs_release_phone(file->node->fs_handle, fs_phone);
 	fibril_mutex_unlock(&file->lock);
-	
+
+	vfs_file_put(file);
 	ipc_answer_0(rid, rc);
 }
@@ -775,4 +768,5 @@
 		ipc_answer_0(rid, ret);
 	
+	vfs_file_put(file);
 	ret = vfs_fd_free(fd);
 	ipc_answer_0(rid, ret);
@@ -875,5 +869,6 @@
 		file->pos += bytes;
 	fibril_mutex_unlock(&file->lock);
-	
+	vfs_file_put(file);	
+
 	/*
 	 * FS server's reply is the final result of the whole operation we
@@ -915,4 +910,5 @@
 			file->pos = (aoff64_t) off;
 			fibril_mutex_unlock(&file->lock);
+			vfs_file_put(file);
 			ipc_answer_1(rid, EOK, off);
 			return;
@@ -922,4 +918,5 @@
 		if ((off >= 0) && (file->pos + off < file->pos)) {
 			fibril_mutex_unlock(&file->lock);
+			vfs_file_put(file);
 			ipc_answer_0(rid, EOVERFLOW);
 			return;
@@ -928,4 +925,5 @@
 		if ((off < 0) && (file->pos < (aoff64_t) -off)) {
 			fibril_mutex_unlock(&file->lock);
+			vfs_file_put(file);
 			ipc_answer_0(rid, EOVERFLOW);
 			return;
@@ -936,4 +934,5 @@
 		
 		fibril_mutex_unlock(&file->lock);
+		vfs_file_put(file);
 		ipc_answer_2(rid, EOK, LOWER32(newoff),
 		    UPPER32(newoff));
@@ -946,4 +945,5 @@
 			fibril_rwlock_read_unlock(&file->node->contents_rwlock);
 			fibril_mutex_unlock(&file->lock);
+			vfs_file_put(file);
 			ipc_answer_0(rid, EOVERFLOW);
 			return;
@@ -953,4 +953,5 @@
 			fibril_rwlock_read_unlock(&file->node->contents_rwlock);
 			fibril_mutex_unlock(&file->lock);
+			vfs_file_put(file);
 			ipc_answer_0(rid, EOVERFLOW);
 			return;
@@ -962,4 +963,5 @@
 		fibril_rwlock_read_unlock(&file->node->contents_rwlock);
 		fibril_mutex_unlock(&file->lock);
+		vfs_file_put(file);
 		ipc_answer_2(rid, EOK, LOWER32(newoff), UPPER32(newoff));
 		return;
@@ -967,4 +969,5 @@
 	
 	fibril_mutex_unlock(&file->lock);
+	vfs_file_put(file);
 	ipc_answer_0(rid, EINVAL);
 }
@@ -1005,4 +1008,5 @@
 
 	fibril_mutex_unlock(&file->lock);
+	vfs_file_put(file);
 	ipc_answer_0(rid, (sysarg_t)rc);
 }
@@ -1021,4 +1025,5 @@
 	ipc_callid_t callid;
 	if (!async_data_read_receive(&callid, NULL)) {
+		vfs_file_put(file);
 		ipc_answer_0(callid, EINVAL);
 		ipc_answer_0(rid, EINVAL);
@@ -1038,4 +1043,5 @@
 
 	fibril_mutex_unlock(&file->lock);
+	vfs_file_put(file);
 	ipc_answer_0(rid, rc);
 }
@@ -1339,14 +1345,14 @@
 	int newfd = IPC_GET_ARG2(*request);
 	
+	/* If the file descriptors are the same, do nothing. */
+	if (oldfd == newfd) {
+		ipc_answer_1(rid, EOK, newfd);
+		return;
+	}
+	
 	/* Lookup the file structure corresponding to oldfd. */
 	vfs_file_t *oldfile = vfs_file_get(oldfd);
 	if (!oldfile) {
 		ipc_answer_0(rid, EBADF);
-		return;
-	}
-	
-	/* If the file descriptors are the same, do nothing. */
-	if (oldfd == newfd) {
-		ipc_answer_1(rid, EOK, newfd);
 		return;
 	}
@@ -1365,4 +1371,6 @@
 		if (ret != EOK) {
 			fibril_mutex_unlock(&oldfile->lock);
+			vfs_file_put(oldfile);
+			vfs_file_put(newfile);
 			ipc_answer_0(rid, ret);
 			return;
@@ -1372,7 +1380,10 @@
 		if (ret != EOK) {
 			fibril_mutex_unlock(&oldfile->lock);
+			vfs_file_put(oldfile);
+			vfs_file_put(newfile);
 			ipc_answer_0(rid, ret);
 			return;
 		}
+		vfs_file_put(newfile);
 	}
 	
@@ -1380,4 +1391,5 @@
 	int ret = vfs_fd_assign(oldfile, newfd);
 	fibril_mutex_unlock(&oldfile->lock);
+	vfs_file_put(oldfile);
 	
 	if (ret != EOK)
