Skip to contentSkip to conversation

Zerocopy networking patch, 2.4.0-1

PLEASE-TESTME Manfred Spraul 5 participants 7 messages Jan 10, 2001 – Jan 13, 2001 Network drivers

lore
Share & tools
lore
#0 lore raw
Quoted 11 lines
Quoted 5 lines

In user space, how do you know when its safe to reuse the buffer that was handed to sendmsg() with the MSG_NOCOPY flag? Or does sendmsg() with that flag block until the buffer isn't needed by the kernel any more? If it does block, doesn't that defeat the use of non-blocking I/O?

sendmsg() marks those pages COW and copies the original page into a new one for further usage. (the old page is used until the packet is released.) So for maximum performance user-space should not reuse such buffers immediately.

That means sendmsg() changes the page tables? I measures smp_call_function on my Dual Pentium 350, and it took around 1950 cpu ticks.
I'm sure that for an 8 way server the total lost time on all cpus (multi threaded server) is larger than the time required to copy the complete page.
(I've attached my patch, just run "insmod dummy p_shift=0")

--
Manfred

drivers/net/dummy.c +159 -0
@@ -95,9 +95,168 @@
 
 static struct net_device dev_dummy;
 
+/* ************************************* */
+int p_shift = -1;
+MODULE_PARM     (p_shift, "1i");
+MODULE_PARM_DESC(p_shift, "Shift for the profile buffer");
+
+int p_size = 0;
+MODULE_PARM     (p_size, "1i");
+MODULE_PARM_DESC(p_size, "size");
+
+
+#define STAT_TABLELEN		16384
+static unsigned long totals[STAT_TABLELEN];
+static unsigned int overflows;
+
+static unsigned long long stime;
+static void start_measure(void)
+{
+	 __asm__ __volatile__ (
+		".align 64\n\t"
+	 	"pushal\n\t"
+		"cpuid\n\t"
+		"popal\n\t"
+		"rdtsc\n\t"
+		"movl %%eax,(%0)\n\t"
+		"movl %%edx,4(%0)\n\t"
+		: /* no output */
+		: "c"(&stime)
+		: "eax", "edx", "memory" );
+}
+
+static void end_measure(void)
+{
+static unsigned long long etime;
+	__asm__ __volatile__ (
+		"pushal\n\t"
+		"cpuid\n\t"
+		"popal\n\t"
+		"rdtsc\n\t"
+		"movl %%eax,(%0)\n\t"
+		"movl %%edx,4(%0)\n\t"
+		: /* no output */
+		: "c"(&etime)
+		: "eax", "edx", "memory" );
+	{
+		unsigned long time = (unsigned long)(etime-stime);
+		time >>= p_shift;
+		if(time < STAT_TABLELEN) {
+			totals[time]++;
+		} else {
+			overflows++;
+		}
+	}
+}
+
+static void clean_buf(void)
+{
+	memset(totals,0,sizeof(totals));
+	overflows = 0;
+}
+
+static void print_line(unsigned long* array)
+{
+	int i;
+	for(i=0;i<32;i++) {
+		if((i%32)==16)
+			printk(":");
+		printk("%lx ",array[i]); 
+	}
+}
+
+static void print_buf(char* caption)
+{
+	int i, other = 0;
+	printk("Results - %s - shift %d",
+		caption, p_shift);
+
+	for(i=0;i<STAT_TABLELEN;i+=32) {
+		int j;
+		int local = 0;
+		for(j=0;j<32;j++)
+			local += totals[i+j];
+
+		if(local) {
+			printk("\n%3x: ",i);
+			print_line(&totals[i]);
+			other += local;
+		}
+	}
+	printk("\nOverflows: %d.\n",
+		overflows);
+	printk("Sum: %ld\n",other+overflows);
+}
+
+static void return_immediately(void* dummy)
+{
+	return;
+}
+
+static void just_one_page(void* dummy)
+{
+	__flush_tlb_one(0x12345678);
+	return;
+}
+
+
 static int __init dummy_init_module(void)
 {
 	int err;
+
+	if(p_shift != -1) {
+		int i;
+		void* p;
+		kmem_cache_t* cachep;
+		/* empty test measurement: */
+		printk("******** kernel cpu benchmark started **********\n");
+		clean_buf();
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(200);
+		for(i=0;i<100;i++) {
+			start_measure();
+			return_immediately(NULL);
+			return_immediately(NULL);
+			return_immediately(NULL);
+			return_immediately(NULL);
+			end_measure();
+		}
+		print_buf("zero");
+		clean_buf();
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(200);
+		for(i=0;i<100;i++) {
+			start_measure();
+			return_immediately(NULL);
+			return_immediately(NULL);
+			smp_call_function(return_immediately,NULL,
+						1, 1);
+			return_immediately(NULL);
+			return_immediately(NULL);
+			end_measure();
+		}
+		print_buf("empty smp_call_function()");
+		clean_buf();
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(200);
+		for(i=0;i<100;i++) {
+			start_measure();
+			return_immediately(NULL);
+			return_immediately(NULL);
+			smp_call_function(just_one_page,NULL,
+						1, 1);
+			just_one_page(NULL);
+			return_immediately(NULL);
+			return_immediately(NULL);
+			end_measure();
+		}
+		print_buf("flush_one_page()");
+		clean_buf();	
+
+		return -EINVAL;
+	}
 
 	dev_dummy.init = dummy_init;
 	SET_MODULE_OWNER(&dev_dummy);
#1 lore raw

Date: Wed, 10 Jan 2001 09:41:24 +0100
From: Manfred Spraul <[email protected]>

That means sendmsg() changes the page tables?

Not in the zerocopy patch I am proposing and asking people to test. I stated in another email that MSG_NOCOPY was considered experimental and thus left out of my patches.

I measures smp_call_function on my Dual Pentium 350, and it took around 1950 cpu ticks.

And this is one of several reasons why the MSG_NOCOPY facility is considered experimental.

Later,
David S. Miller
[email protected]
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#2 lore raw
In reply to Manfred Spraul
Quoted 3 lines · Manfred Spraul

That means sendmsg() changes the page tables? I measures smp_call_function on my Dual Pentium 350, and it took around 1950 cpu ticks.

well, this is a performance problem if you are using threads. For normal processes there is no need for a SMP cross-call, there TLB flushes are local only.

Ingo

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#3 lore raw
Quoted 11 lines · Ingo Molnar
Quoted 3 lines · Manfred Spraul

That means sendmsg() changes the page tables? I measures smp_call_function on my Dual Pentium 350, and it took around 1950 cpu ticks.

well, this is a performance problem if you are using threads. For normal processes there is no need for a SMP cross-call, there TLB flushes are local only.

But that would be ugly as hell:
so apache 2.0 would become slower with MSG_NOCOPY, whereas samba 2.2 would become faster.

Is is possible to move the responsibility for maitaining the copy to the caller?

e.g. use msg_control, and then the caller can request either that a signal is sent when that data is transfered, or that a variable is set to 0.

--
Manfred
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#4 lore raw
Quoted 7 lines · Manfred Spraul
Quoted 4 lines

well, this is a performance problem if you are using threads. For normal processes there is no need for a SMP cross-call, there TLB flushes are local only.

But that would be ugly as hell:
so apache 2.0 would become slower with MSG_NOCOPY, whereas samba 2.2 would become faster.

there *is* a cost of having a shared VM - and this is i suspect unavoidable.

Quoted 2 lines

Is is possible to move the responsibility for maitaining the copy to the caller?

this needs a completion event i believe.

Quoted 3 lines

e.g. use msg_control, and then the caller can request either that a signal is sent when that data is transfered, or that a variable is set to 0.

i believe a signal-based thing would be the right (and scalable) solution
- the signal handler could free() the buffer.

this makes sense even in the VM-assisted MSG_NOCOPY case, since one wants to do garbage collection of these in-flight buffers anyway. (not for correctness but for performance reasons - free()-ing and immediately reusing such a buffer would generate a COW.)

Ingo

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#5 lore raw
In reply to Manfred Spraul
Quoted 10 lines · Ingo Molnar
Quoted 7 lines
Quoted 4 lines

well, this is a performance problem if you are using threads. For normal processes there is no need for a SMP cross-call, there TLB flushes are local only.

But that would be ugly as hell:
so apache 2.0 would become slower with MSG_NOCOPY, whereas samba 2.2 would become faster.

there *is* a cost of having a shared VM - and this is i suspect unavoidable.

Is it possible to avoid the SMP cross-call in the case that the other threads have neither accessed nor dirtied the page in question?

One way to implement this is to share VMs but not the page tables, or to share parts of the page tables that don't contain writable pages.

Just a sudden inspired thought... I don't know if it is possible or worthwhile.

enjoy,
-- Jamie
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#6 lore raw
In reply to Manfred Spraul

FWIW: POSIX mq_send does not promise that the buffer is safe, it only promises that the message is queued. Interesting interface.

Quoted 194 lines · Manfred Spraul
Quoted 11 lines
Quoted 5 lines

In user space, how do you know when its safe to reuse the buffer that was handed to sendmsg() with the MSG_NOCOPY flag? Or does sendmsg() with that flag block until the buffer isn't needed by the kernel any more? If it does block, doesn't that defeat the use of non-blocking I/O?

sendmsg() marks those pages COW and copies the original page into a new one for further usage. (the old page is used until the packet is released.) So for maximum performance user-space should not reuse such buffers immediately.

That means sendmsg() changes the page tables? I measures smp_call_function on my Dual Pentium 350, and it took around 1950 cpu ticks.
I'm sure that for an 8 way server the total lost time on all cpus (multi threaded server) is larger than the time required to copy the complete page.
(I've attached my patch, just run "insmod dummy p_shift=0")

--
Manfred

drivers/net/dummy.c +159 -0
@@ -95,9 +95,168 @@
 
 static struct net_device dev_dummy;
 
+/* ************************************* */
+int p_shift = -1;
+MODULE_PARM     (p_shift, "1i");
+MODULE_PARM_DESC(p_shift, "Shift for the profile buffer");
+
+int p_size = 0;
+MODULE_PARM     (p_size, "1i");
+MODULE_PARM_DESC(p_size, "size");
+
+
+#define STAT_TABLELEN		16384
+static unsigned long totals[STAT_TABLELEN];
+static unsigned int overflows;
+
+static unsigned long long stime;
+static void start_measure(void)
+{
+	 __asm__ __volatile__ (
+		".align 64\n\t"
+	 	"pushal\n\t"
+		"cpuid\n\t"
+		"popal\n\t"
+		"rdtsc\n\t"
+		"movl %%eax,(%0)\n\t"
+		"movl %%edx,4(%0)\n\t"
+		: /* no output */
+		: "c"(&stime)
+		: "eax", "edx", "memory" );
+}
+
+static void end_measure(void)
+{
+static unsigned long long etime;
+	__asm__ __volatile__ (
+		"pushal\n\t"
+		"cpuid\n\t"
+		"popal\n\t"
+		"rdtsc\n\t"
+		"movl %%eax,(%0)\n\t"
+		"movl %%edx,4(%0)\n\t"
+		: /* no output */
+		: "c"(&etime)
+		: "eax", "edx", "memory" );
+	{
+		unsigned long time = (unsigned long)(etime-stime);
+		time >>= p_shift;
+		if(time < STAT_TABLELEN) {
+			totals[time]++;
+		} else {
+			overflows++;
+		}
+	}
+}
+
+static void clean_buf(void)
+{
+	memset(totals,0,sizeof(totals));
+	overflows = 0;
+}
+
+static void print_line(unsigned long* array)
+{
+	int i;
+	for(i=0;i<32;i++) {
+		if((i%32)==16)
+			printk(":");
+		printk("%lx ",array[i]); 
+	}
+}
+
+static void print_buf(char* caption)
+{
+	int i, other = 0;
+	printk("Results - %s - shift %d",
+		caption, p_shift);
+
+	for(i=0;i<STAT_TABLELEN;i+=32) {
+		int j;
+		int local = 0;
+		for(j=0;j<32;j++)
+			local += totals[i+j];
+
+		if(local) {
+			printk("\n%3x: ",i);
+			print_line(&totals[i]);
+			other += local;
+		}
+	}
+	printk("\nOverflows: %d.\n",
+		overflows);
+	printk("Sum: %ld\n",other+overflows);
+}
+
+static void return_immediately(void* dummy)
+{
+	return;
+}
+
+static void just_one_page(void* dummy)
+{
+	__flush_tlb_one(0x12345678);
+	return;
+}
+
+
 static int __init dummy_init_module(void)
 {
 	int err;
+
+	if(p_shift != -1) {
+		int i;
+		void* p;
+		kmem_cache_t* cachep;
+		/* empty test measurement: */
+		printk("******** kernel cpu benchmark started **********\n");
+		clean_buf();
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(200);
+		for(i=0;i<100;i++) {
+			start_measure();
+			return_immediately(NULL);
+			return_immediately(NULL);
+			return_immediately(NULL);
+			return_immediately(NULL);
+			end_measure();
+		}
+		print_buf("zero");
+		clean_buf();
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(200);
+		for(i=0;i<100;i++) {
+			start_measure();
+			return_immediately(NULL);
+			return_immediately(NULL);
+			smp_call_function(return_immediately,NULL,
+						1, 1);
+			return_immediately(NULL);
+			return_immediately(NULL);
+			end_measure();
+		}
+		print_buf("empty smp_call_function()");
+		clean_buf();
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(200);
+		for(i=0;i<100;i++) {
+			start_measure();
+			return_immediately(NULL);
+			return_immediately(NULL);
+			smp_call_function(just_one_page,NULL,
+						1, 1);
+			just_one_page(NULL);
+			return_immediately(NULL);
+			return_immediately(NULL);
+			end_measure();
+		}
+		print_buf("flush_one_page()");
+		clean_buf();	
+
+		return -EINVAL;
+	}
 
 	dev_dummy.init = dummy_init;
 	SET_MODULE_OWNER(&dev_dummy);
Signature · 9 lines
---------------------------------------------------------
Victor Yodaiken 
Finite State Machine Labs: The RTLinux Company.
 www.fsmlabs.com  www.rtlinux.com

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/