Commit f15fe424 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] hashed b_wait

Implements hashed waitqueues for buffer_heads.  Drops twelve bytes from
struct buffer_head.
parent 39e8cdf7
...@@ -166,7 +166,6 @@ static int grow_buffers(struct stripe_head *sh, int num, int b_size, int priorit ...@@ -166,7 +166,6 @@ static int grow_buffers(struct stripe_head *sh, int num, int b_size, int priorit
if (!bh) if (!bh)
return 1; return 1;
memset(bh, 0, sizeof (struct buffer_head)); memset(bh, 0, sizeof (struct buffer_head));
init_waitqueue_head(&bh->b_wait);
if ((page = alloc_page(priority))) if ((page = alloc_page(priority)))
bh->b_data = page_address(page); bh->b_data = page_address(page);
else { else {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/kernel.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -31,6 +32,7 @@ ...@@ -31,6 +32,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/writeback.h> #include <linux/writeback.h>
#include <linux/mempool.h> #include <linux/mempool.h>
#include <linux/hash.h>
#include <asm/bitops.h> #include <asm/bitops.h>
#define BH_ENTRY(list) list_entry((list), struct buffer_head, b_inode_buffers) #define BH_ENTRY(list) list_entry((list), struct buffer_head, b_inode_buffers)
...@@ -38,6 +40,14 @@ ...@@ -38,6 +40,14 @@
/* This is used by some architectures to estimate available memory. */ /* This is used by some architectures to estimate available memory. */
atomic_t buffermem_pages = ATOMIC_INIT(0); atomic_t buffermem_pages = ATOMIC_INIT(0);
/*
* Hashed waitqueue_head's for wait_on_buffer()
*/
#define BH_WAIT_TABLE_ORDER 7
static struct bh_wait_queue_head {
wait_queue_head_t wqh;
} ____cacheline_aligned_in_smp bh_wait_queue_heads[1<<BH_WAIT_TABLE_ORDER];
/* /*
* Several of these buffer list functions are exported to filesystems, * Several of these buffer list functions are exported to filesystems,
* so we do funny things with the spinlocking to support those * so we do funny things with the spinlocking to support those
...@@ -76,6 +86,35 @@ init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *private) ...@@ -76,6 +86,35 @@ init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *private)
bh->b_private = private; bh->b_private = private;
} }
/*
* Return the address of the waitqueue_head to be used for this
* buffer_head
*/
static wait_queue_head_t *bh_waitq_head(struct buffer_head *bh)
{
return &bh_wait_queue_heads[hash_ptr(bh, BH_WAIT_TABLE_ORDER)].wqh;
}
/*
* Wait on a buffer until someone does a wakeup on it. Needs
* lots of external locking. ext3 uses this. Fix it.
*/
void sleep_on_buffer(struct buffer_head *bh)
{
wait_queue_head_t *wq = bh_waitq_head(bh);
sleep_on(wq);
}
EXPORT_SYMBOL(sleep_on_buffer);
void wake_up_buffer(struct buffer_head *bh)
{
wait_queue_head_t *wq = bh_waitq_head(bh);
if (waitqueue_active(wq))
wake_up_all(wq);
}
EXPORT_SYMBOL(wake_up_buffer);
void unlock_buffer(struct buffer_head *bh) void unlock_buffer(struct buffer_head *bh)
{ {
/* /*
...@@ -89,8 +128,32 @@ void unlock_buffer(struct buffer_head *bh) ...@@ -89,8 +128,32 @@ void unlock_buffer(struct buffer_head *bh)
clear_buffer_locked(bh); clear_buffer_locked(bh);
smp_mb__after_clear_bit(); smp_mb__after_clear_bit();
if (waitqueue_active(&bh->b_wait)) wake_up_buffer(bh);
wake_up(&bh->b_wait); }
/*
* Block until a buffer comes unlocked. This doesn't stop it
* from becoming locked again - you have to lock it yourself
* if you want to preserve its state.
*/
void __wait_on_buffer(struct buffer_head * bh)
{
wait_queue_head_t *wq = bh_waitq_head(bh);
struct task_struct *tsk = current;
DECLARE_WAITQUEUE(wait, tsk);
get_bh(bh);
add_wait_queue(wq, &wait);
do {
run_task_queue(&tq_disk);
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
if (!buffer_locked(bh))
break;
schedule();
} while (buffer_locked(bh));
tsk->state = TASK_RUNNING;
remove_wait_queue(wq, &wait);
put_bh(bh);
} }
static inline void static inline void
...@@ -121,30 +184,6 @@ __clear_page_buffers(struct page *page) ...@@ -121,30 +184,6 @@ __clear_page_buffers(struct page *page)
page_cache_release(page); page_cache_release(page);
} }
/*
* Block until a buffer comes unlocked. This doesn't stop it
* from becoming locked again - you have to lock it yourself
* if you want to preserve its state.
*/
void __wait_on_buffer(struct buffer_head * bh)
{
struct task_struct *tsk = current;
DECLARE_WAITQUEUE(wait, tsk);
get_bh(bh);
add_wait_queue(&bh->b_wait, &wait);
do {
run_task_queue(&tq_disk);
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
if (!buffer_locked(bh))
break;
schedule();
} while (buffer_locked(bh));
tsk->state = TASK_RUNNING;
remove_wait_queue(&bh->b_wait, &wait);
put_bh(bh);
}
/* /*
* Default synchronous end-of-IO handler.. Just mark it up-to-date and * Default synchronous end-of-IO handler.. Just mark it up-to-date and
* unlock the buffer. This is what ll_rw_block uses too. * unlock the buffer. This is what ll_rw_block uses too.
...@@ -2265,7 +2304,6 @@ static void init_buffer_head(void *data, kmem_cache_t *cachep, unsigned long fla ...@@ -2265,7 +2304,6 @@ static void init_buffer_head(void *data, kmem_cache_t *cachep, unsigned long fla
memset(bh, 0, sizeof(*bh)); memset(bh, 0, sizeof(*bh));
bh->b_blocknr = -1; bh->b_blocknr = -1;
INIT_LIST_HEAD(&bh->b_inode_buffers); INIT_LIST_HEAD(&bh->b_inode_buffers);
init_waitqueue_head(&bh->b_wait);
} }
} }
...@@ -2284,9 +2322,13 @@ static void bh_mempool_free(void *element, void *pool_data) ...@@ -2284,9 +2322,13 @@ static void bh_mempool_free(void *element, void *pool_data)
void __init buffer_init(void) void __init buffer_init(void)
{ {
int i;
bh_cachep = kmem_cache_create("buffer_head", bh_cachep = kmem_cache_create("buffer_head",
sizeof(struct buffer_head), 0, sizeof(struct buffer_head), 0,
SLAB_HWCACHE_ALIGN, init_buffer_head, NULL); SLAB_HWCACHE_ALIGN, init_buffer_head, NULL);
bh_mempool = mempool_create(MAX_UNUSED_BUFFERS, bh_mempool_alloc, bh_mempool = mempool_create(MAX_UNUSED_BUFFERS, bh_mempool_alloc,
bh_mempool_free, NULL); bh_mempool_free, NULL);
for (i = 0; i < ARRAY_SIZE(bh_wait_queue_heads); i++)
init_waitqueue_head(&bh_wait_queue_heads[i].wqh);
} }
...@@ -530,7 +530,7 @@ void journal_commit_transaction(journal_t *journal) ...@@ -530,7 +530,7 @@ void journal_commit_transaction(journal_t *journal)
journal_file_buffer(jh, commit_transaction, BJ_Forget); journal_file_buffer(jh, commit_transaction, BJ_Forget);
/* Wake up any transactions which were waiting for this /* Wake up any transactions which were waiting for this
IO to complete */ IO to complete */
wake_up(&bh->b_wait); wake_up_buffer(bh);
JBUFFER_TRACE(jh, "brelse shadowed buffer"); JBUFFER_TRACE(jh, "brelse shadowed buffer");
__brelse(bh); __brelse(bh);
} }
......
...@@ -654,7 +654,7 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, int force_copy) ...@@ -654,7 +654,7 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, int force_copy)
spin_unlock(&journal_datalist_lock); spin_unlock(&journal_datalist_lock);
unlock_journal(journal); unlock_journal(journal);
/* commit wakes up all shadow buffers after IO */ /* commit wakes up all shadow buffers after IO */
sleep_on(&jh2bh(jh)->b_wait); sleep_on_buffer(jh2bh(jh));
lock_journal(journal); lock_journal(journal);
goto repeat; goto repeat;
} }
......
...@@ -56,9 +56,6 @@ struct buffer_head { ...@@ -56,9 +56,6 @@ struct buffer_head {
char * b_data; /* pointer to data block */ char * b_data; /* pointer to data block */
bh_end_io_t *b_end_io; /* I/O completion */ bh_end_io_t *b_end_io; /* I/O completion */
void *b_private; /* reserved for b_end_io */ void *b_private; /* reserved for b_end_io */
wait_queue_head_t b_wait;
struct list_head b_inode_buffers; /* list of inode dirty buffers */ struct list_head b_inode_buffers; /* list of inode dirty buffers */
}; };
...@@ -166,6 +163,8 @@ void invalidate_bdev(struct block_device *, int); ...@@ -166,6 +163,8 @@ void invalidate_bdev(struct block_device *, int);
void __invalidate_buffers(kdev_t dev, int); void __invalidate_buffers(kdev_t dev, int);
int sync_buffers(struct block_device *, int); int sync_buffers(struct block_device *, int);
void __wait_on_buffer(struct buffer_head *); void __wait_on_buffer(struct buffer_head *);
void sleep_on_buffer(struct buffer_head *bh);
void wake_up_buffer(struct buffer_head *bh);
int fsync_dev(kdev_t); int fsync_dev(kdev_t);
int fsync_bdev(struct block_device *); int fsync_bdev(struct block_device *);
int fsync_super(struct super_block *); int fsync_super(struct super_block *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment