[XFS] Only use refcounted pages for I/O
Many block drivers (aoe, iscsi) really want refcountable pages in bios, which is what almost everyone send down. XFS unfortunately has a few places where it sends down buffers that may come from kmalloc, which breaks them. Fix the places that use kmalloc()d buffers. SGI-PV: 964546 SGI-Modid: xfs-linux-melb:xfs-kern:28562a Signed-Off-By: Christoph Hellwig <hch@infradead.org> Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Tim Shimmin <tes@sgi.com>
This commit is contained in:
committed by
Tim Shimmin
parent
4eb6bf6bfb
commit
1fa40b01ae
@@ -314,7 +314,7 @@ xfs_buf_free(
|
|||||||
|
|
||||||
ASSERT(list_empty(&bp->b_hash_list));
|
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;
|
uint i;
|
||||||
|
|
||||||
if ((bp->b_flags & XBF_MAPPED) && (bp->b_page_count > 1))
|
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++) {
|
for (i = 0; i < bp->b_page_count; i++) {
|
||||||
struct page *page = bp->b_pages[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);
|
page_cache_release(page);
|
||||||
}
|
}
|
||||||
_xfs_buf_free_pages(bp);
|
_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);
|
xfs_buf_deallocate(bp);
|
||||||
@@ -764,41 +757,41 @@ xfs_buf_get_noaddr(
|
|||||||
size_t len,
|
size_t len,
|
||||||
xfs_buftarg_t *target)
|
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;
|
xfs_buf_t *bp;
|
||||||
void *data;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
bp = xfs_buf_allocate(0);
|
bp = xfs_buf_allocate(0);
|
||||||
if (unlikely(bp == NULL))
|
if (unlikely(bp == NULL))
|
||||||
goto fail;
|
goto fail;
|
||||||
_xfs_buf_initialize(bp, target, 0, len, 0);
|
_xfs_buf_initialize(bp, target, 0, len, 0);
|
||||||
|
|
||||||
try_again:
|
error = _xfs_buf_get_pages(bp, page_count, 0);
|
||||||
data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL | KM_LARGE);
|
if (error)
|
||||||
if (unlikely(data == NULL))
|
|
||||||
goto fail_free_buf;
|
goto fail_free_buf;
|
||||||
|
|
||||||
/* check whether alignment matches.. */
|
for (i = 0; i < page_count; i++) {
|
||||||
if ((__psunsigned_t)data !=
|
bp->b_pages[i] = alloc_page(GFP_KERNEL);
|
||||||
((__psunsigned_t)data & ~target->bt_smask)) {
|
if (!bp->b_pages[i])
|
||||||
/* .. else double the size and try again */
|
goto fail_free_mem;
|
||||||
kmem_free(data, malloc_len);
|
|
||||||
malloc_len <<= 1;
|
|
||||||
goto try_again;
|
|
||||||
}
|
}
|
||||||
|
bp->b_flags |= _XBF_PAGES;
|
||||||
|
|
||||||
error = xfs_buf_associate_memory(bp, data, len);
|
error = _xfs_buf_map_pages(bp, XBF_MAPPED);
|
||||||
if (error)
|
if (unlikely(error)) {
|
||||||
|
printk(KERN_WARNING "%s: failed to map pages\n",
|
||||||
|
__FUNCTION__);
|
||||||
goto fail_free_mem;
|
goto fail_free_mem;
|
||||||
bp->b_flags |= _XBF_KMEM_ALLOC;
|
}
|
||||||
|
|
||||||
xfs_buf_unlock(bp);
|
xfs_buf_unlock(bp);
|
||||||
|
|
||||||
XB_TRACE(bp, "no_daddr", data);
|
XB_TRACE(bp, "no_daddr", len);
|
||||||
return bp;
|
return bp;
|
||||||
|
|
||||||
fail_free_mem:
|
fail_free_mem:
|
||||||
kmem_free(data, malloc_len);
|
while (--i >= 0)
|
||||||
|
__free_page(bp->b_pages[i]);
|
||||||
fail_free_buf:
|
fail_free_buf:
|
||||||
xfs_buf_free(bp);
|
xfs_buf_free(bp);
|
||||||
fail:
|
fail:
|
||||||
|
@@ -63,7 +63,7 @@ typedef enum {
|
|||||||
|
|
||||||
/* flags used only internally */
|
/* flags used only internally */
|
||||||
_XBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */
|
_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_RUN_QUEUES = (1 << 19),/* run block device task queue */
|
||||||
_XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */
|
_XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */
|
||||||
} xfs_buf_flags_t;
|
} xfs_buf_flags_t;
|
||||||
|
@@ -1199,11 +1199,18 @@ xlog_alloc_log(xfs_mount_t *mp,
|
|||||||
*iclogp = (xlog_in_core_t *)
|
*iclogp = (xlog_in_core_t *)
|
||||||
kmem_zalloc(sizeof(xlog_in_core_t), KM_SLEEP);
|
kmem_zalloc(sizeof(xlog_in_core_t), KM_SLEEP);
|
||||||
iclog = *iclogp;
|
iclog = *iclogp;
|
||||||
iclog->hic_data = (xlog_in_core_2_t *)
|
|
||||||
kmem_zalloc(iclogsize, KM_SLEEP | KM_LARGE);
|
|
||||||
|
|
||||||
iclog->ic_prev = prev_iclog;
|
iclog->ic_prev = prev_iclog;
|
||||||
prev_iclog = iclog;
|
prev_iclog = iclog;
|
||||||
|
|
||||||
|
bp = xfs_buf_get_noaddr(log->l_iclog_size, mp->m_logdev_targp);
|
||||||
|
if (!XFS_BUF_CPSEMA(bp))
|
||||||
|
ASSERT(0);
|
||||||
|
XFS_BUF_SET_IODONE_FUNC(bp, xlog_iodone);
|
||||||
|
XFS_BUF_SET_BDSTRAT_FUNC(bp, xlog_bdstrat_cb);
|
||||||
|
XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1);
|
||||||
|
iclog->ic_bp = bp;
|
||||||
|
iclog->hic_data = bp->b_addr;
|
||||||
|
|
||||||
log->l_iclog_bak[i] = (xfs_caddr_t)&(iclog->ic_header);
|
log->l_iclog_bak[i] = (xfs_caddr_t)&(iclog->ic_header);
|
||||||
|
|
||||||
head = &iclog->ic_header;
|
head = &iclog->ic_header;
|
||||||
@@ -1216,11 +1223,6 @@ xlog_alloc_log(xfs_mount_t *mp,
|
|||||||
INT_SET(head->h_fmt, ARCH_CONVERT, XLOG_FMT);
|
INT_SET(head->h_fmt, ARCH_CONVERT, XLOG_FMT);
|
||||||
memcpy(&head->h_fs_uuid, &mp->m_sb.sb_uuid, sizeof(uuid_t));
|
memcpy(&head->h_fs_uuid, &mp->m_sb.sb_uuid, sizeof(uuid_t));
|
||||||
|
|
||||||
bp = xfs_buf_get_empty(log->l_iclog_size, mp->m_logdev_targp);
|
|
||||||
XFS_BUF_SET_IODONE_FUNC(bp, xlog_iodone);
|
|
||||||
XFS_BUF_SET_BDSTRAT_FUNC(bp, xlog_bdstrat_cb);
|
|
||||||
XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1);
|
|
||||||
iclog->ic_bp = bp;
|
|
||||||
|
|
||||||
iclog->ic_size = XFS_BUF_SIZE(bp) - log->l_iclog_hsize;
|
iclog->ic_size = XFS_BUF_SIZE(bp) - log->l_iclog_hsize;
|
||||||
iclog->ic_state = XLOG_STATE_ACTIVE;
|
iclog->ic_state = XLOG_STATE_ACTIVE;
|
||||||
@@ -1528,7 +1530,6 @@ xlog_dealloc_log(xlog_t *log)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
next_iclog = iclog->ic_next;
|
next_iclog = iclog->ic_next;
|
||||||
kmem_free(iclog->hic_data, log->l_iclog_size);
|
|
||||||
kmem_free(iclog, sizeof(xlog_in_core_t));
|
kmem_free(iclog, sizeof(xlog_in_core_t));
|
||||||
iclog = next_iclog;
|
iclog = next_iclog;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user