* [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr
@ 2007-03-07 10:13 Christoph Hellwig
2007-03-07 12:14 ` Shailendra Tripathi
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Christoph Hellwig @ 2007-03-07 10:13 UTC (permalink / raw)
To: xfs, ecashin, akpm; +Cc: linux-kernel
Currently xfs_buf_get_noaddr allocates memory using kmem_alloc which
can end up either in kmalloc or vmalloc and assigns it to the buffer.
This patch changes it to allocate individual pages and if there is
more then one maps it into kernel virtual space using vmap.
This means the minimum buffer allocation is PAGE_SIZE now. For two
of the three caller (log buffers, log recovery) that is perfectly
fine, because they always allocate buffers that are a power of two
of the page size anyway. For xfs_zero_remaining_bytes the minimum
allocation goes up from blocksize to pagesize and thus there is
a potential waste of memory for blocksize < pagesize allocations,
which is unfortunate but not directly solveable when block
drivers expect reference countable pages. To fix this waste
xfs_zero_remaining_bytes could be rewritten to zero more than
a single block at a time, which sounds like a good idea in general.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.c
===================================================================
--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.c 2007-03-05 15:54:40.000000000 +0100
+++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.c 2007-03-05 15:54:47.000000000 +0100
@@ -314,7 +314,7 @@
ASSERT(list_empty(&bp->b_hash_list));
- if (bp->b_flags & _XBF_PAGE_CACHE) {
+ if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) {
uint i;
if ((bp->b_flags & XBF_MAPPED) && (bp->b_page_count > 1))
@@ -323,18 +323,11 @@
for (i = 0; i < bp->b_page_count; i++) {
struct page *page = bp->b_pages[i];
- ASSERT(!PagePrivate(page));
+ if (bp->b_flags & _XBF_PAGE_CACHE)
+ ASSERT(!PagePrivate(page));
page_cache_release(page);
}
_xfs_buf_free_pages(bp);
- } else if (bp->b_flags & _XBF_KMEM_ALLOC) {
- /*
- * XXX(hch): bp->b_count_desired might be incorrect (see
- * xfs_buf_associate_memory for details), but fortunately
- * the Linux version of kmem_free ignores the len argument..
- */
- kmem_free(bp->b_addr, bp->b_count_desired);
- _xfs_buf_free_pages(bp);
}
xfs_buf_deallocate(bp);
@@ -764,41 +757,41 @@
size_t len,
xfs_buftarg_t *target)
{
- size_t malloc_len = len;
+ unsigned long page_count = PAGE_ALIGN(len) >> PAGE_SHIFT;
+ int error, i;
xfs_buf_t *bp;
- void *data;
- int error;
bp = xfs_buf_allocate(0);
if (unlikely(bp == NULL))
goto fail;
_xfs_buf_initialize(bp, target, 0, len, 0);
- try_again:
- data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL | KM_LARGE);
- if (unlikely(data == NULL))
+ error = _xfs_buf_get_pages(bp, page_count, 0);
+ if (error)
goto fail_free_buf;
- /* check whether alignment matches.. */
- if ((__psunsigned_t)data !=
- ((__psunsigned_t)data & ~target->bt_smask)) {
- /* .. else double the size and try again */
- kmem_free(data, malloc_len);
- malloc_len <<= 1;
- goto try_again;
- }
-
- error = xfs_buf_associate_memory(bp, data, len);
- if (error)
+ for (i = 0; i < page_count; i++) {
+ bp->b_pages[i] = alloc_page(GFP_KERNEL);
+ if (!bp->b_pages[i])
+ goto fail_free_mem;
+ }
+ bp->b_flags |= _XBF_PAGES;
+
+ error = _xfs_buf_map_pages(bp, XBF_MAPPED);
+ if (unlikely(error)) {
+ printk(KERN_WARNING "%s: failed to map pages\n",
+ __FUNCTION__);
goto fail_free_mem;
- bp->b_flags |= _XBF_KMEM_ALLOC;
+ }
xfs_buf_unlock(bp);
XB_TRACE(bp, "no_daddr", data);
return bp;
+
fail_free_mem:
- kmem_free(data, malloc_len);
+ for ( ; i >= 0; i++)
+ __free_page(bp->b_pages[i]);
fail_free_buf:
xfs_buf_free(bp);
fail:
Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.h
===================================================================
--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.h 2007-03-05 15:54:40.000000000 +0100
+++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.h 2007-03-05 15:55:06.000000000 +0100
@@ -63,7 +63,7 @@
/* flags used only internally */
_XBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */
- _XBF_KMEM_ALLOC = (1 << 18),/* backed by kmem_alloc() */
+ _XBF_PAGES = (1 << 18), /* backed by refcounted pages */
_XBF_RUN_QUEUES = (1 << 19),/* run block device task queue */
_XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */
} xfs_buf_flags_t;
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr
2007-03-07 10:13 [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr Christoph Hellwig
@ 2007-03-07 12:14 ` Shailendra Tripathi
2007-03-07 12:38 ` Christoph Hellwig
2007-03-07 17:04 ` Michael Nishimoto
2007-03-09 11:55 ` Christoph Hellwig
2 siblings, 1 reply; 8+ messages in thread
From: Shailendra Tripathi @ 2007-03-07 12:14 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: xfs, ecashin, akpm, linux-kernel
Hi Christoph,
Did you do some testing for recovery when end of the physical log
is seen ?
When you will be dealing with striped ICLOG buffers or big sized
ICLOGs, header size might range from 512 to 2k. Also, this header might
be split into 2 parts at the end of physical log. Then, you don't have
page size buffer. Please verify that XFS_BUF_SETP_PTRs work correctly
for those cases.
Same thing is true when data section is split around physical log. You
can get one part which is not PAGE sized.
(I am referring to the function xlog_do_recovery_pass).
Regards,
Shailendra
Christoph Hellwig wrote:
> Currently xfs_buf_get_noaddr allocates memory using kmem_alloc which
> can end up either in kmalloc or vmalloc and assigns it to the buffer.
> This patch changes it to allocate individual pages and if there is
> more then one maps it into kernel virtual space using vmap.
>
> This means the minimum buffer allocation is PAGE_SIZE now. For two
> of the three caller (log buffers, log recovery) that is perfectly
> fine, because they always allocate buffers that are a power of two
> of the page size anyway. For xfs_zero_remaining_bytes the minimum
> allocation goes up from blocksize to pagesize and thus there is
> a potential waste of memory for blocksize < pagesize allocations,
> which is unfortunate but not directly solveable when block
> drivers expect reference countable pages. To fix this waste
> xfs_zero_remaining_bytes could be rewritten to zero more than
> a single block at a time, which sounds like a good idea in general.
>
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
>
> Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.c
> ===================================================================
> --- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.c 2007-03-05 15:54:40.000000000 +0100
> +++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.c 2007-03-05 15:54:47.000000000 +0100
> @@ -314,7 +314,7 @@
>
> ASSERT(list_empty(&bp->b_hash_list));
>
> - if (bp->b_flags & _XBF_PAGE_CACHE) {
> + if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) {
> uint i;
>
> if ((bp->b_flags & XBF_MAPPED) && (bp->b_page_count > 1))
> @@ -323,18 +323,11 @@
> for (i = 0; i < bp->b_page_count; i++) {
> struct page *page = bp->b_pages[i];
>
> - ASSERT(!PagePrivate(page));
> + if (bp->b_flags & _XBF_PAGE_CACHE)
> + ASSERT(!PagePrivate(page));
> page_cache_release(page);
> }
> _xfs_buf_free_pages(bp);
> - } else if (bp->b_flags & _XBF_KMEM_ALLOC) {
> - /*
> - * XXX(hch): bp->b_count_desired might be incorrect (see
> - * xfs_buf_associate_memory for details), but fortunately
> - * the Linux version of kmem_free ignores the len argument..
> - */
> - kmem_free(bp->b_addr, bp->b_count_desired);
> - _xfs_buf_free_pages(bp);
> }
>
> xfs_buf_deallocate(bp);
> @@ -764,41 +757,41 @@
> size_t len,
> xfs_buftarg_t *target)
> {
> - size_t malloc_len = len;
> + unsigned long page_count = PAGE_ALIGN(len) >> PAGE_SHIFT;
> + int error, i;
> xfs_buf_t *bp;
> - void *data;
> - int error;
>
> bp = xfs_buf_allocate(0);
> if (unlikely(bp == NULL))
> goto fail;
> _xfs_buf_initialize(bp, target, 0, len, 0);
>
> - try_again:
> - data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL | KM_LARGE);
> - if (unlikely(data == NULL))
> + error = _xfs_buf_get_pages(bp, page_count, 0);
> + if (error)
> goto fail_free_buf;
>
> - /* check whether alignment matches.. */
> - if ((__psunsigned_t)data !=
> - ((__psunsigned_t)data & ~target->bt_smask)) {
> - /* .. else double the size and try again */
> - kmem_free(data, malloc_len);
> - malloc_len <<= 1;
> - goto try_again;
> - }
> -
> - error = xfs_buf_associate_memory(bp, data, len);
> - if (error)
> + for (i = 0; i < page_count; i++) {
> + bp->b_pages[i] = alloc_page(GFP_KERNEL);
> + if (!bp->b_pages[i])
> + goto fail_free_mem;
> + }
> + bp->b_flags |= _XBF_PAGES;
> +
> + error = _xfs_buf_map_pages(bp, XBF_MAPPED);
> + if (unlikely(error)) {
> + printk(KERN_WARNING "%s: failed to map pages\n",
> + __FUNCTION__);
> goto fail_free_mem;
> - bp->b_flags |= _XBF_KMEM_ALLOC;
> + }
>
> xfs_buf_unlock(bp);
>
> XB_TRACE(bp, "no_daddr", data);
> return bp;
> +
> fail_free_mem:
> - kmem_free(data, malloc_len);
> + for ( ; i >= 0; i++)
> + __free_page(bp->b_pages[i]);
> fail_free_buf:
> xfs_buf_free(bp);
> fail:
> Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.h
> ===================================================================
> --- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.h 2007-03-05 15:54:40.000000000 +0100
> +++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.h 2007-03-05 15:55:06.000000000 +0100
> @@ -63,7 +63,7 @@
>
> /* flags used only internally */
> _XBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */
> - _XBF_KMEM_ALLOC = (1 << 18),/* backed by kmem_alloc() */
> + _XBF_PAGES = (1 << 18), /* backed by refcounted pages */
> _XBF_RUN_QUEUES = (1 << 19),/* run block device task queue */
> _XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */
> } xfs_buf_flags_t;
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr
2007-03-07 12:14 ` Shailendra Tripathi
@ 2007-03-07 12:38 ` Christoph Hellwig
0 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2007-03-07 12:38 UTC (permalink / raw)
To: Shailendra Tripathi; +Cc: Christoph Hellwig, xfs, ecashin, akpm, linux-kernel
On Wed, Mar 07, 2007 at 05:44:24PM +0530, Shailendra Tripathi wrote:
> Hi Christoph,
> Did you do some testing for recovery when end of the physical
> log is seen ?
I ran xfsqa over it, which should catch this case.
> When you will be dealing with striped ICLOG buffers or big
> sized ICLOGs, header size might range from 512 to 2k. Also, this header
> might be split into 2 parts at the end of physical log. Then, you don't
> have page size buffer. Please verify that XFS_BUF_SETP_PTRs work correctly
> for those cases.
> Same thing is true when data section is split around physical log.
> You can get one part which is not PAGE sized.
I should have made my wording more clear, we always do PAGE_SIZE +
buffer allocations. After XFS_BUF_SETP_PTR the actually used buffer
might be smaller. I tested XFS_BUF_SETP_PTR manually with
artifical test code aswell, and made sure it still works.
Long term I have a plan to replace XFS_BUF_SETP_PTR with better
schemes, but that's irrelevant for this patch.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr
2007-03-07 10:13 [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr Christoph Hellwig
2007-03-07 12:14 ` Shailendra Tripathi
@ 2007-03-07 17:04 ` Michael Nishimoto
2007-03-07 17:15 ` Christoph Hellwig
2007-03-09 11:55 ` Christoph Hellwig
2 siblings, 1 reply; 8+ messages in thread
From: Michael Nishimoto @ 2007-03-07 17:04 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: xfs, ecashin, akpm, linux-kernel
Incore log buffers are not always a power of two of the page size.
In particular, when xfs is running over software raid devices, the
log buffers are allocated to match the size of a stripe.
However, they are always a multiple of PAGE_SIZE, so we are still safe.
Michael
Christoph Hellwig wrote:
>Currently xfs_buf_get_noaddr allocates memory using kmem_alloc which
>can end up either in kmalloc or vmalloc and assigns it to the buffer.
>This patch changes it to allocate individual pages and if there is
>more then one maps it into kernel virtual space using vmap.
>
>This means the minimum buffer allocation is PAGE_SIZE now. For two
>of the three caller (log buffers, log recovery) that is perfectly
>fine, because they always allocate buffers that are a power of two
>of the page size anyway. For xfs_zero_remaining_bytes the minimum
>allocation goes up from blocksize to pagesize and thus there is
>a potential waste of memory for blocksize < pagesize allocations,
>which is unfortunate but not directly solveable when block
>drivers expect reference countable pages. To fix this waste
>xfs_zero_remaining_bytes could be rewritten to zero more than
>a single block at a time, which sounds like a good idea in general.
>
>
>Signed-off-by: Christoph Hellwig <hch@lst.de>
>
>Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.c
>===================================================================
>--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.c 2007-03-05 15:54:40.000000000 +0100
>+++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.c 2007-03-05 15:54:47.000000000 +0100
>@@ -314,7 +314,7 @@
>
> ASSERT(list_empty(&bp->b_hash_list));
>
>- if (bp->b_flags & _XBF_PAGE_CACHE) {
>+ if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) {
> uint i;
>
> if ((bp->b_flags & XBF_MAPPED) && (bp->b_page_count > 1))
>@@ -323,18 +323,11 @@
> for (i = 0; i < bp->b_page_count; i++) {
> struct page *page = bp->b_pages[i];
>
>- ASSERT(!PagePrivate(page));
>+ if (bp->b_flags & _XBF_PAGE_CACHE)
>+ ASSERT(!PagePrivate(page));
> page_cache_release(page);
> }
> _xfs_buf_free_pages(bp);
>- } else if (bp->b_flags & _XBF_KMEM_ALLOC) {
>- /*
>- * XXX(hch): bp->b_count_desired might be incorrect (see
>- * xfs_buf_associate_memory for details), but fortunately
>- * the Linux version of kmem_free ignores the len argument..
>- */
>- kmem_free(bp->b_addr, bp->b_count_desired);
>- _xfs_buf_free_pages(bp);
> }
>
> xfs_buf_deallocate(bp);
>@@ -764,41 +757,41 @@
> size_t len,
> xfs_buftarg_t *target)
> {
>- size_t malloc_len = len;
>+ unsigned long page_count = PAGE_ALIGN(len) >> PAGE_SHIFT;
>+ int error, i;
> xfs_buf_t *bp;
>- void *data;
>- int error;
>
> bp = xfs_buf_allocate(0);
> if (unlikely(bp == NULL))
> goto fail;
> _xfs_buf_initialize(bp, target, 0, len, 0);
>
>- try_again:
>- data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL | KM_LARGE);
>- if (unlikely(data == NULL))
>+ error = _xfs_buf_get_pages(bp, page_count, 0);
>+ if (error)
> goto fail_free_buf;
>
>- /* check whether alignment matches.. */
>- if ((__psunsigned_t)data !=
>- ((__psunsigned_t)data & ~target->bt_smask)) {
>- /* .. else double the size and try again */
>- kmem_free(data, malloc_len);
>- malloc_len <<= 1;
>- goto try_again;
>- }
>-
>- error = xfs_buf_associate_memory(bp, data, len);
>- if (error)
>+ for (i = 0; i < page_count; i++) {
>+ bp->b_pages[i] = alloc_page(GFP_KERNEL);
>+ if (!bp->b_pages[i])
>+ goto fail_free_mem;
>+ }
>+ bp->b_flags |= _XBF_PAGES;
>+
>+ error = _xfs_buf_map_pages(bp, XBF_MAPPED);
>+ if (unlikely(error)) {
>+ printk(KERN_WARNING "%s: failed to map pages\n",
>+ __FUNCTION__);
> goto fail_free_mem;
>- bp->b_flags |= _XBF_KMEM_ALLOC;
>+ }
>
> xfs_buf_unlock(bp);
>
> XB_TRACE(bp, "no_daddr", data);
> return bp;
>+
> fail_free_mem:
>- kmem_free(data, malloc_len);
>+ for ( ; i >= 0; i++)
>+ __free_page(bp->b_pages[i]);
> fail_free_buf:
> xfs_buf_free(bp);
> fail:
>Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.h
>===================================================================
>--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.h 2007-03-05 15:54:40.000000000 +0100
>+++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.h 2007-03-05 15:55:06.000000000 +0100
>@@ -63,7 +63,7 @@
>
> /* flags used only internally */
> _XBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */
>- _XBF_KMEM_ALLOC = (1 << 18),/* backed by kmem_alloc() */
>+ _XBF_PAGES = (1 << 18), /* backed by refcounted pages */
> _XBF_RUN_QUEUES = (1 << 19),/* run block device task queue */
> _XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */
> } xfs_buf_flags_t;
>
>
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr
2007-03-07 17:04 ` Michael Nishimoto
@ 2007-03-07 17:15 ` Christoph Hellwig
0 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2007-03-07 17:15 UTC (permalink / raw)
To: Michael Nishimoto; +Cc: Christoph Hellwig, xfs, ecashin, akpm, linux-kernel
On Wed, Mar 07, 2007 at 09:04:53AM -0800, Michael Nishimoto wrote:
> Incore log buffers are not always a power of two of the page size.
> In particular, when xfs is running over software raid devices, the
> log buffers are allocated to match the size of a stripe.
>
> However, they are always a multiple of PAGE_SIZE, so we are still safe.
It's not actually about beeing safe - any allocation is still safe
with this patch. The issue is just that we waste memory because
we round up allocations to the next page size.
The power of two bit is actually wrong in this mail, it was about
another optimization I have that needs some more testing first.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr
2007-03-07 10:13 [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr Christoph Hellwig
2007-03-07 12:14 ` Shailendra Tripathi
2007-03-07 17:04 ` Michael Nishimoto
@ 2007-03-09 11:55 ` Christoph Hellwig
2007-03-13 0:08 ` Timothy Shimmin
2 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2007-03-09 11:55 UTC (permalink / raw)
To: xfs, ecashin, akpm; +Cc: linux-kernel
Ed Cashin found a bug in the error handling code for the case where
a page allocation fails. Here's the updated version:
Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.c
===================================================================
--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.c 2007-03-08 19:08:38.000000000 +0100
+++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.c 2007-03-09 08:59:15.000000000 +0100
@@ -314,7 +314,7 @@
ASSERT(list_empty(&bp->b_hash_list));
- if (bp->b_flags & _XBF_PAGE_CACHE) {
+ if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) {
uint i;
if ((bp->b_flags & XBF_MAPPED) && (bp->b_page_count > 1))
@@ -323,18 +323,11 @@
for (i = 0; i < bp->b_page_count; i++) {
struct page *page = bp->b_pages[i];
- ASSERT(!PagePrivate(page));
+ if (bp->b_flags & _XBF_PAGE_CACHE)
+ ASSERT(!PagePrivate(page));
page_cache_release(page);
}
_xfs_buf_free_pages(bp);
- } else if (bp->b_flags & _XBF_KMEM_ALLOC) {
- /*
- * XXX(hch): bp->b_count_desired might be incorrect (see
- * xfs_buf_associate_memory for details), but fortunately
- * the Linux version of kmem_free ignores the len argument..
- */
- kmem_free(bp->b_addr, bp->b_count_desired);
- _xfs_buf_free_pages(bp);
}
xfs_buf_deallocate(bp);
@@ -764,41 +757,41 @@
size_t len,
xfs_buftarg_t *target)
{
- size_t malloc_len = len;
+ unsigned long page_count = PAGE_ALIGN(len) >> PAGE_SHIFT;
+ int error, i;
xfs_buf_t *bp;
- void *data;
- int error;
bp = xfs_buf_allocate(0);
if (unlikely(bp == NULL))
goto fail;
_xfs_buf_initialize(bp, target, 0, len, 0);
- try_again:
- data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL | KM_LARGE);
- if (unlikely(data == NULL))
+ error = _xfs_buf_get_pages(bp, page_count, 0);
+ if (error)
goto fail_free_buf;
- /* check whether alignment matches.. */
- if ((__psunsigned_t)data !=
- ((__psunsigned_t)data & ~target->bt_smask)) {
- /* .. else double the size and try again */
- kmem_free(data, malloc_len);
- malloc_len <<= 1;
- goto try_again;
- }
-
- error = xfs_buf_associate_memory(bp, data, len);
- if (error)
+ for (i = 0; i < page_count; i++) {
+ bp->b_pages[i] = alloc_page(GFP_KERNEL);
+ if (!bp->b_pages[i])
+ goto fail_free_mem;
+ }
+ bp->b_flags |= _XBF_PAGES;
+
+ error = _xfs_buf_map_pages(bp, XBF_MAPPED);
+ if (unlikely(error)) {
+ printk(KERN_WARNING "%s: failed to map pages\n",
+ __FUNCTION__);
goto fail_free_mem;
- bp->b_flags |= _XBF_KMEM_ALLOC;
+ }
xfs_buf_unlock(bp);
XB_TRACE(bp, "no_daddr", data);
return bp;
+
fail_free_mem:
- kmem_free(data, malloc_len);
+ for ( ; i >= 0; i--)
+ __free_page(bp->b_pages[i]);
fail_free_buf:
xfs_buf_free(bp);
fail:
Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.h
===================================================================
--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.h 2007-03-08 19:08:38.000000000 +0100
+++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.h 2007-03-09 08:58:50.000000000 +0100
@@ -63,7 +63,7 @@
/* flags used only internally */
_XBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */
- _XBF_KMEM_ALLOC = (1 << 18),/* backed by kmem_alloc() */
+ _XBF_PAGES = (1 << 18), /* backed by refcounted pages */
_XBF_RUN_QUEUES = (1 << 19),/* run block device task queue */
_XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */
} xfs_buf_flags_t;
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr
2007-03-09 11:55 ` Christoph Hellwig
@ 2007-03-13 0:08 ` Timothy Shimmin
2007-03-17 0:36 ` Christoph Hellwig
0 siblings, 1 reply; 8+ messages in thread
From: Timothy Shimmin @ 2007-03-13 0:08 UTC (permalink / raw)
To: Christoph Hellwig, xfs, ecashin, akpm; +Cc: linux-kernel
Hi,
--On 9 March 2007 12:55:11 PM +0100 Christoph Hellwig <hch@lst.de> wrote:
> Ed Cashin found a bug in the error handling code for the case where
> a page allocation fails. Here's the updated version:
>
> Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.c
> ===================================================================
> --- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.c 2007-03-08 19:08:38.000000000 +0100
> +++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.c 2007-03-09 08:59:15.000000000 +0100
....
> + for (i = 0; i < page_count; i++) {
> + bp->b_pages[i] = alloc_page(GFP_KERNEL);
> + if (!bp->b_pages[i])
> + goto fail_free_mem;
> + }
> + bp->b_flags |= _XBF_PAGES;
> +
> + error = _xfs_buf_map_pages(bp, XBF_MAPPED);
> + if (unlikely(error)) {
> + printk(KERN_WARNING "%s: failed to map pages\n",
> + __FUNCTION__);
> goto fail_free_mem;
> - bp->b_flags |= _XBF_KMEM_ALLOC;
> + }
>
> xfs_buf_unlock(bp);
>
> XB_TRACE(bp, "no_daddr", data);
> return bp;
> +
> fail_free_mem:
> - kmem_free(data, malloc_len);
> + for ( ; i >= 0; i--)
> + __free_page(bp->b_pages[i]);
> fail_free_buf:
> xfs_buf_free(bp);
> fail:
It looks like you might need: for (i--; i >= 0; i--)
(or: for (j = 0; j < i; j++) etc.)
Because if the initial alloc_page loop goes to completion then:
i == pagecount
and if alloc_page loop terminates early then
bp->b_pages[i] == NULL
So we have gone 1 too far in both cases and need to
start free'ing back one.
Unless I missed something.
--Tim
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr
2007-03-13 0:08 ` Timothy Shimmin
@ 2007-03-17 0:36 ` Christoph Hellwig
0 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2007-03-17 0:36 UTC (permalink / raw)
To: Timothy Shimmin; +Cc: Christoph Hellwig, xfs, ecashin, akpm, linux-kernel
> It looks like you might need: for (i--; i >= 0; i--)
> (or: for (j = 0; j < i; j++) etc.)
>
> Because if the initial alloc_page loop goes to completion then:
> i == pagecount
> and if alloc_page loop terminates early then
> bp->b_pages[i] == NULL
> So we have gone 1 too far in both cases and need to
> start free'ing back one.
> Unless I missed something.
No, I was missing something :)
Here's the updated version:
Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.c
===================================================================
--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.c 2007-03-16 15:32:20.000000000 +0100
+++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.c 2007-03-16 15:35:10.000000000 +0100
@@ -314,7 +314,7 @@ xfs_buf_free(
ASSERT(list_empty(&bp->b_hash_list));
- if (bp->b_flags & _XBF_PAGE_CACHE) {
+ if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) {
uint i;
if ((bp->b_flags & XBF_MAPPED) && (bp->b_page_count > 1))
@@ -323,18 +323,11 @@ xfs_buf_free(
for (i = 0; i < bp->b_page_count; i++) {
struct page *page = bp->b_pages[i];
- ASSERT(!PagePrivate(page));
+ if (bp->b_flags & _XBF_PAGE_CACHE)
+ ASSERT(!PagePrivate(page));
page_cache_release(page);
}
_xfs_buf_free_pages(bp);
- } else if (bp->b_flags & _XBF_KMEM_ALLOC) {
- /*
- * XXX(hch): bp->b_count_desired might be incorrect (see
- * xfs_buf_associate_memory for details), but fortunately
- * the Linux version of kmem_free ignores the len argument..
- */
- kmem_free(bp->b_addr, bp->b_count_desired);
- _xfs_buf_free_pages(bp);
}
xfs_buf_deallocate(bp);
@@ -764,41 +757,41 @@ xfs_buf_get_noaddr(
size_t len,
xfs_buftarg_t *target)
{
- size_t malloc_len = len;
+ unsigned long page_count = PAGE_ALIGN(len) >> PAGE_SHIFT;
+ int error, i;
xfs_buf_t *bp;
- void *data;
- int error;
bp = xfs_buf_allocate(0);
if (unlikely(bp == NULL))
goto fail;
_xfs_buf_initialize(bp, target, 0, len, 0);
- try_again:
- data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL | KM_LARGE);
- if (unlikely(data == NULL))
+ error = _xfs_buf_get_pages(bp, page_count, 0);
+ if (error)
goto fail_free_buf;
- /* check whether alignment matches.. */
- if ((__psunsigned_t)data !=
- ((__psunsigned_t)data & ~target->bt_smask)) {
- /* .. else double the size and try again */
- kmem_free(data, malloc_len);
- malloc_len <<= 1;
- goto try_again;
- }
-
- error = xfs_buf_associate_memory(bp, data, len);
- if (error)
+ for (i = 0; i < page_count; i++) {
+ bp->b_pages[i] = alloc_page(GFP_KERNEL);
+ if (!bp->b_pages[i])
+ goto fail_free_mem;
+ }
+ bp->b_flags |= _XBF_PAGES;
+
+ error = _xfs_buf_map_pages(bp, XBF_MAPPED);
+ if (unlikely(error)) {
+ printk(KERN_WARNING "%s: failed to map pages\n",
+ __FUNCTION__);
goto fail_free_mem;
- bp->b_flags |= _XBF_KMEM_ALLOC;
+ }
xfs_buf_unlock(bp);
XB_TRACE(bp, "no_daddr", data);
return bp;
+
fail_free_mem:
- kmem_free(data, malloc_len);
+ while (--i >= 0)
+ __free_page(bp->b_pages[i]);
fail_free_buf:
xfs_buf_free(bp);
fail:
Index: linux-2.6/fs/xfs/linux-2.6/xfs_buf.h
===================================================================
--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_buf.h 2007-03-13 18:18:05.000000000 +0100
+++ linux-2.6/fs/xfs/linux-2.6/xfs_buf.h 2007-03-16 15:34:20.000000000 +0100
@@ -63,7 +63,7 @@ typedef enum {
/* flags used only internally */
_XBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */
- _XBF_KMEM_ALLOC = (1 << 18),/* backed by kmem_alloc() */
+ _XBF_PAGES = (1 << 18), /* backed by refcounted pages */
_XBF_RUN_QUEUES = (1 << 19),/* run block device task queue */
_XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */
} xfs_buf_flags_t;
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2007-03-17 0:36 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-03-07 10:13 [PATCH 2/2] xfs: stop using kmalloc in xfs_buf_get_noaddr Christoph Hellwig
2007-03-07 12:14 ` Shailendra Tripathi
2007-03-07 12:38 ` Christoph Hellwig
2007-03-07 17:04 ` Michael Nishimoto
2007-03-07 17:15 ` Christoph Hellwig
2007-03-09 11:55 ` Christoph Hellwig
2007-03-13 0:08 ` Timothy Shimmin
2007-03-17 0:36 ` Christoph Hellwig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox