RE: Poor thread performance on Linux vs. Solaris

"Hu, Boris" <[email protected]>
Newsgroups gmane.comp.lib.phil
Message-ID <37FBBA5F3A361C41AB7CE44558C3448E01C0B2F0@pdsmsx403.ccr.corp.intel.com>
You could try this patch. It is against linux-2.6.0-test4. :)


boris

--- linux-2.6.0-test4.orig/kernel/futex.c	2003-08-23
07:53:39.000000000 +0800
+++ linux-2.6.0-test4/kernel/futex.c	2003-09-08 11:12:16.000000000
+0800
@@ -57,9 +57,16 @@
 	struct file *filp;
 };
 
+/* 
+ * Split the global futex_lock into every hash list lock.
+ */ 
+struct futex_hash_bucket {
+	struct list_head	chain;
+	spinlock_t		lock;
+};
+
 /* The key for the hash is the address + index + offset within page */
-static struct list_head futex_queues[1<<FUTEX_HASHBITS];
-static spinlock_t futex_lock = SPIN_LOCK_UNLOCKED;
+static struct futex_hash_bucket futex_queues[1<<FUTEX_HASHBITS];
 
 extern void send_sigio(struct fown_struct *fown, int fd, int band);
 
@@ -74,12 +81,10 @@
 {
 	spin_lock(&current->mm->page_table_lock);
 	spin_lock(&vcache_lock);
-	spin_lock(&futex_lock);
 }
 
 static inline void unlock_futex_mm(void)
 {
-	spin_unlock(&futex_lock);
 	spin_unlock(&vcache_lock);
 	spin_unlock(&current->mm->page_table_lock);
 }
@@ -87,7 +92,7 @@
 /*
  * The physical page is shared, so we can hash on its address:
  */
-static inline struct list_head *hash_futex(struct page *page, int
offset)
+static inline struct futex_hash_bucket *hash_futex(struct page *page,
int offset)
 {
 	return &futex_queues[hash_long((unsigned long)page + offset,
 
FUTEX_HASHBITS)];
@@ -153,6 +158,7 @@
 static inline int futex_wake(unsigned long uaddr, int offset, int num)
 {
 	struct list_head *i, *next, *head;
+	struct futex_hash_bucket *bh;
 	struct page *page;
 	int ret = 0;
 
@@ -164,7 +170,9 @@
 		return -EFAULT;
 	}
 
-	head = hash_futex(page, offset);
+	bh = hash_futex(page, offset);
+	spin_lock(&bh->lock);
+	head = &bh->chain;
 
 	list_for_each_safe(i, next, head) {
 		struct futex_q *this = list_entry(i, struct futex_q,
list);
@@ -180,7 +188,7 @@
 				break;
 		}
 	}
-
+	spin_unlock(&bh->lock);
 	unlock_futex_mm();
 	put_page(page);
 
@@ -196,19 +204,19 @@
 static void futex_vcache_callback(vcache_t *vcache, struct page
*new_page)
 {
 	struct futex_q *q = container_of(vcache, struct futex_q,
vcache);
-	struct list_head *head = hash_futex(new_page, q->offset);
+	struct futex_hash_bucket *head = hash_futex(new_page,
q->offset);
 
-	spin_lock(&futex_lock);
+	spin_lock(&head->lock);
 
 	if (!list_empty(&q->list)) {
 		put_page(q->page);
 		q->page = new_page;
 		__pin_page_atomic(new_page);
 		list_del(&q->list);
-		list_add_tail(&q->list, head);
+		list_add_tail(&q->list, &head->chain);
 	}
 
-	spin_unlock(&futex_lock);
+	spin_unlock(&head->lock);
 }
 
 /*
@@ -219,6 +227,7 @@
 	unsigned long uaddr2, int offset2, int nr_wake, int nr_requeue)
 {
 	struct list_head *i, *next, *head1, *head2;
+	struct futex_hash_bucket *bh1, *bh2;
 	struct page *page1 = NULL, *page2 = NULL;
 	int ret = 0;
 
@@ -231,9 +240,13 @@
 	if (!page2)
 		goto out;
 
-	head1 = hash_futex(page1, offset1);
-	head2 = hash_futex(page2, offset2);
-
+	bh1 = hash_futex(page1, offset1);
+	bh2 = hash_futex(page2, offset2);
+	spin_lock(&bh1->lock);
+	spin_lock(&bh2->lock);
+	head1 = &bh1->chain;
+	head2 = &bh2->chain;
+       
 	list_for_each_safe(i, next, head1) {
 		struct futex_q *this = list_entry(i, struct futex_q,
list);
 
@@ -260,6 +273,8 @@
 	}
 
 out:
+	spin_unlock(&bh2->lock);
+	spin_unlock(&bh1->lock);
 	unlock_futex_mm();
 
 	if (page1)
@@ -274,7 +289,7 @@
 				unsigned long uaddr, int offset,
 				int fd, struct file *filp)
 {
-	struct list_head *head = hash_futex(page, offset);
+	struct list_head *head = &hash_futex(page, offset)->chain;
 
 	q->offset = offset;
 	q->fd = fd;
@@ -293,15 +308,16 @@
 static inline int unqueue_me(struct futex_q *q)
 {
 	int ret = 0;
+	struct futex_hash_bucket *bh = hash_futex(q->page, q->offset);
 
 	spin_lock(&vcache_lock);
-	spin_lock(&futex_lock);
+	spin_lock(&bh->lock);
 	if (!list_empty(&q->list)) {
 		list_del(&q->list);
 		__detach_vcache(&q->vcache);
 		ret = 1;
 	}
-	spin_unlock(&futex_lock);
+	spin_unlock(&bh->lock);
 	spin_unlock(&vcache_lock);
 	return ret;
 }
@@ -315,6 +331,7 @@
 	int ret = 0, curval;
 	struct page *page;
 	struct futex_q q;
+	struct futex_hash_bucket *bh;
 
 	init_waitqueue_head(&q.waiters);
 
@@ -325,6 +342,10 @@
 		unlock_futex_mm();
 		return -EFAULT;
 	}
+	
+	bh = hash_futex(page, offset);
+	spin_lock(&bh->lock);
+
 	__queue_me(&q, page, uaddr, offset, -1, NULL);
 
 	/*
@@ -332,11 +353,13 @@
 	 * It cannot schedule, so we access it with the spinlock held.
 	 */
 	if (get_user(curval, (int *)uaddr) != 0) {
+		spin_unlock(&bh->lock);
 		unlock_futex_mm();
 		ret = -EFAULT;
 		goto out;
 	}
 	if (curval != val) {
+		spin_unlock(&bh->lock);
 		unlock_futex_mm();
 		ret = -EWOULDBLOCK;
 		goto out;
@@ -351,6 +374,7 @@
 	add_wait_queue(&q.waiters, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 	if (!list_empty(&q.list)) {
+		spin_unlock(&bh->lock);
 		unlock_futex_mm();
 		time = schedule_timeout(time);
 	}
@@ -389,13 +413,14 @@
 			       struct poll_table_struct *wait)
 {
 	struct futex_q *q = filp->private_data;
+	struct futex_hash_bucket *bh = hash_futex(q->page, q->offset);
 	int ret = 0;
 
 	poll_wait(filp, &q->waiters, wait);
-	spin_lock(&futex_lock);
+	spin_lock(&bh->lock);
 	if (list_empty(&q->list))
 		ret = POLLIN | POLLRDNORM;
-	spin_unlock(&futex_lock);
+	spin_unlock(&bh->lock);
 
 	return ret;
 }
@@ -411,6 +436,7 @@
 {
 	struct page *page = NULL;
 	struct futex_q *q;
+	struct futex_hash_bucket *bh;
 	struct file *filp;
 	int ret;
 
@@ -466,8 +492,12 @@
 	init_waitqueue_head(&q->waiters);
 	filp->private_data = q;
 
-	__queue_me(q, page, uaddr, offset, ret, filp);
+	bh = hash_futex(page, offset);
+	spin_lock(&bh->lock);
 
+	__queue_me(q, page, uaddr, offset, ret, filp);
+	
+	spin_unlock(&bh->lock);
 	unlock_futex_mm();
 
 	/* Now we map fd to filp, so userspace can access it */
@@ -563,8 +593,10 @@
 	register_filesystem(&futex_fs_type);
 	futex_mnt = kern_mount(&futex_fs_type);
 
-	for (i = 0; i < ARRAY_SIZE(futex_queues); i++)
-		INIT_LIST_HEAD(&futex_queues[i]);
+	for (i = 0; i < ARRAY_SIZE(futex_queues); i++) {
+		futex_queues[i].lock = SPIN_LOCK_UNLOCKED;
+		INIT_LIST_HEAD(&futex_queues[i].chain);
+	}
 	return 0;
 }
 __initcall(init);


> 
> On Thursday 04 September 2003 8:36 pm, Perez-Gonzalez, Inaky wrote:
> 
> > > From: Bill Soudan [mailto:[email protected]]
> > >
> > > 4) try to eliminate the global locks in the kernel/futex.c file.
I'm
> > > afraid it might not be that easy though - the weird freeze make me
> > > wonder if the scheduler may play a role here as well.
> >
> > A coworker of mine played with the notion of removing global lock
> > for the futexes, replacing it with a per-hash-queue lock. He did it
> > on our rtfutex patch, and I don't know how easy would it be for
> > the mainstream futexes to adapt, but it might be worth a try; it
> > will not resolve fully the problem, but it might help a lot.
> 
> Any chance I could get my hands on this code?  From the list archives,
> I
> found this link:
> 
> http://developer.osdl.org/dev/robustmutexes/
> 
> but the rtfutex code I found still has the global lock.  Am I looking
> in
> the wrong place, or is this unreleased code?
> 
> Bill
> 
> 
> STATEMENT OF CONFIDENTIALITY
> 
> The information contained in this electronic message and any
attachments
> to this message are intended for the exclusive use of the addressee(s)
> and may contain confidential or privileged information. If you are not
> the intended recipient, please notify SunGard Trading Systems
> immediately
> at (201) 499-5900 and destroy all copies of this message and any
> attachments.
> 
> 
> --
> Phil-list mailing list
> [email protected]
> https://www.redhat.com/mailman/listinfo/phil-list
futex_q_lock.diff (application/octet-stream, 5.9 KB)
--- linux-2.6.0-test4.orig/kernel/futex.c	2003-08-23 07:53:39.000000000 +0800
+++ linux-2.6.0-test4/kernel/futex.c	2003-09-08 11:12:16.000000000 +0800
@@ -57,9 +57,16 @@
 	struct file *filp;
 };
 
+/* 
+ * Split the global futex_lock into every hash list lock.
+ */ 
+struct futex_hash_bucket {
+	struct list_head	chain;
+	spinlock_t		lock;
+};
+
 /* The key for the hash is the address + index + offset within page */
-static struct list_head futex_queues[1<<FUTEX_HASHBITS];
-static spinlock_t futex_lock = SPIN_LOCK_UNLOCKED;
+static struct futex_hash_bucket futex_queues[1<<FUTEX_HASHBITS];
 
 extern void send_sigio(struct fown_struct *fown, int fd, int band);
 
@@ -74,12 +81,10 @@
 {
 	spin_lock(&current->mm->page_table_lock);
 	spin_lock(&vcache_lock);
-	spin_lock(&futex_lock);
 }
 
 static inline void unlock_futex_mm(void)
 {
-	spin_unlock(&futex_lock);
 	spin_unlock(&vcache_lock);
 	spin_unlock(&current->mm->page_table_lock);
 }
@@ -87,7 +92,7 @@
 /*
  * The physical page is shared, so we can hash on its address:
  */
-static inline struct list_head *hash_futex(struct page *page, int offset)
+static inline struct futex_hash_bucket *hash_futex(struct page *page, int offset)
 {
 	return &futex_queues[hash_long((unsigned long)page + offset,
 							FUTEX_HASHBITS)];
@@ -153,6 +158,7 @@
 static inline int futex_wake(unsigned long uaddr, int offset, int num)
 {
 	struct list_head *i, *next, *head;
+	struct futex_hash_bucket *bh;
 	struct page *page;
 	int ret = 0;
 
@@ -164,7 +170,9 @@
 		return -EFAULT;
 	}
 
-	head = hash_futex(page, offset);
+	bh = hash_futex(page, offset);
+	spin_lock(&bh->lock);
+	head = &bh->chain;
 
 	list_for_each_safe(i, next, head) {
 		struct futex_q *this = list_entry(i, struct futex_q, list);
@@ -180,7 +188,7 @@
 				break;
 		}
 	}
-
+	spin_unlock(&bh->lock);
 	unlock_futex_mm();
 	put_page(page);
 
@@ -196,19 +204,19 @@
 static void futex_vcache_callback(vcache_t *vcache, struct page *new_page)
 {
 	struct futex_q *q = container_of(vcache, struct futex_q, vcache);
-	struct list_head *head = hash_futex(new_page, q->offset);
+	struct futex_hash_bucket *head = hash_futex(new_page, q->offset);
 
-	spin_lock(&futex_lock);
+	spin_lock(&head->lock);
 
 	if (!list_empty(&q->list)) {
 		put_page(q->page);
 		q->page = new_page;
 		__pin_page_atomic(new_page);
 		list_del(&q->list);
-		list_add_tail(&q->list, head);
+		list_add_tail(&q->list, &head->chain);
 	}
 
-	spin_unlock(&futex_lock);
+	spin_unlock(&head->lock);
 }
 
 /*
@@ -219,6 +227,7 @@
 	unsigned long uaddr2, int offset2, int nr_wake, int nr_requeue)
 {
 	struct list_head *i, *next, *head1, *head2;
+	struct futex_hash_bucket *bh1, *bh2;
 	struct page *page1 = NULL, *page2 = NULL;
 	int ret = 0;
 
@@ -231,9 +240,13 @@
 	if (!page2)
 		goto out;
 
-	head1 = hash_futex(page1, offset1);
-	head2 = hash_futex(page2, offset2);
-
+	bh1 = hash_futex(page1, offset1);
+	bh2 = hash_futex(page2, offset2);
+	spin_lock(&bh1->lock);
+	spin_lock(&bh2->lock);
+	head1 = &bh1->chain;
+	head2 = &bh2->chain;
+       
 	list_for_each_safe(i, next, head1) {
 		struct futex_q *this = list_entry(i, struct futex_q, list);
 
@@ -260,6 +273,8 @@
 	}
 
 out:
+	spin_unlock(&bh2->lock);
+	spin_unlock(&bh1->lock);
 	unlock_futex_mm();
 
 	if (page1)
@@ -274,7 +289,7 @@
 				unsigned long uaddr, int offset,
 				int fd, struct file *filp)
 {
-	struct list_head *head = hash_futex(page, offset);
+	struct list_head *head = &hash_futex(page, offset)->chain;
 
 	q->offset = offset;
 	q->fd = fd;
@@ -293,15 +308,16 @@
 static inline int unqueue_me(struct futex_q *q)
 {
 	int ret = 0;
+	struct futex_hash_bucket *bh = hash_futex(q->page, q->offset);
 
 	spin_lock(&vcache_lock);
-	spin_lock(&futex_lock);
+	spin_lock(&bh->lock);
 	if (!list_empty(&q->list)) {
 		list_del(&q->list);
 		__detach_vcache(&q->vcache);
 		ret = 1;
 	}
-	spin_unlock(&futex_lock);
+	spin_unlock(&bh->lock);
 	spin_unlock(&vcache_lock);
 	return ret;
 }
@@ -315,6 +331,7 @@
 	int ret = 0, curval;
 	struct page *page;
 	struct futex_q q;
+	struct futex_hash_bucket *bh;
 
 	init_waitqueue_head(&q.waiters);
 
@@ -325,6 +342,10 @@
 		unlock_futex_mm();
 		return -EFAULT;
 	}
+	
+	bh = hash_futex(page, offset);
+	spin_lock(&bh->lock);
+
 	__queue_me(&q, page, uaddr, offset, -1, NULL);
 
 	/*
@@ -332,11 +353,13 @@
 	 * It cannot schedule, so we access it with the spinlock held.
 	 */
 	if (get_user(curval, (int *)uaddr) != 0) {
+		spin_unlock(&bh->lock);
 		unlock_futex_mm();
 		ret = -EFAULT;
 		goto out;
 	}
 	if (curval != val) {
+		spin_unlock(&bh->lock);
 		unlock_futex_mm();
 		ret = -EWOULDBLOCK;
 		goto out;
@@ -351,6 +374,7 @@
 	add_wait_queue(&q.waiters, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 	if (!list_empty(&q.list)) {
+		spin_unlock(&bh->lock);
 		unlock_futex_mm();
 		time = schedule_timeout(time);
 	}
@@ -389,13 +413,14 @@
 			       struct poll_table_struct *wait)
 {
 	struct futex_q *q = filp->private_data;
+	struct futex_hash_bucket *bh = hash_futex(q->page, q->offset);
 	int ret = 0;
 
 	poll_wait(filp, &q->waiters, wait);
-	spin_lock(&futex_lock);
+	spin_lock(&bh->lock);
 	if (list_empty(&q->list))
 		ret = POLLIN | POLLRDNORM;
-	spin_unlock(&futex_lock);
+	spin_unlock(&bh->lock);
 
 	return ret;
 }
@@ -411,6 +436,7 @@
 {
 	struct page *page = NULL;
 	struct futex_q *q;
+	struct futex_hash_bucket *bh;
 	struct file *filp;
 	int ret;
 
@@ -466,8 +492,12 @@
 	init_waitqueue_head(&q->waiters);
 	filp->private_data = q;
 
-	__queue_me(q, page, uaddr, offset, ret, filp);
+	bh = hash_futex(page, offset);
+	spin_lock(&bh->lock);
 
+	__queue_me(q, page, uaddr, offset, ret, filp);
+	
+	spin_unlock(&bh->lock);
 	unlock_futex_mm();
 
 	/* Now we map fd to filp, so userspace can access it */
@@ -563,8 +593,10 @@
 	register_filesystem(&futex_fs_type);
 	futex_mnt = kern_mount(&futex_fs_type);
 
-	for (i = 0; i < ARRAY_SIZE(futex_queues); i++)
-		INIT_LIST_HEAD(&futex_queues[i]);
+	for (i = 0; i < ARRAY_SIZE(futex_queues); i++) {
+		futex_queues[i].lock = SPIN_LOCK_UNLOCKED;
+		INIT_LIST_HEAD(&futex_queues[i].chain);
+	}
 	return 0;
 }
 __initcall(init);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.