It's very common for file systems to need to zero part or all of a page,
the simplist way is just to use kmap_atomic() and memset(). There's
actually a library function in include/linux/highmem.h that does exactly
that, but it's confusingly named memclear_highpage_flush(), which is
descriptive of *how* it does the work rather than what the *purpose* is.
So this patchset renames the function to zero_user_page(), and calls it
from the various places that currently open code it.
This first patch introduces the new function call, and converts all the
core kernel callsites, both the open-coded ones and the old
memclear_highpage_flush() ones. Following this patch is a series of
conversions for each file system individually, per AKPM, and finally a
patch deprecating the old call. The diffstat below shows the entire
patchset.
[akpm@linux-foundation.org: fix a few things]
Signed-off-by: Nate Diller <nate.diller@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
transfer_result = lo_do_transfer(lo, WRITE, page, offset,
bvec->bv_page, bv_offs, size, IV);
if (unlikely(transfer_result)) {
transfer_result = lo_do_transfer(lo, WRITE, page, offset,
bvec->bv_page, bv_offs, size, IV);
if (unlikely(transfer_result)) {
/*
* The transfer failed, but we still write the data to
* keep prepare/commit calls balanced.
*/
printk(KERN_ERR "loop: transfer error block %llu\n",
(unsigned long long)index);
/*
* The transfer failed, but we still write the data to
* keep prepare/commit calls balanced.
*/
printk(KERN_ERR "loop: transfer error block %llu\n",
(unsigned long long)index);
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + offset, 0, size);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, offset, size, KM_USER0);
}
flush_dcache_page(page);
ret = aops->commit_write(file, page, offset,
}
flush_dcache_page(page);
ret = aops->commit_write(file, page, offset,
if (block_start >= to)
break;
if (buffer_new(bh)) {
if (block_start >= to)
break;
if (buffer_new(bh)) {
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr+block_start, 0, bh->b_size);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, block_start, bh->b_size, KM_USER0);
set_buffer_uptodate(bh);
mark_buffer_dirty(bh);
}
set_buffer_uptodate(bh);
mark_buffer_dirty(bh);
}
SetPageError(page);
}
if (!buffer_mapped(bh)) {
SetPageError(page);
}
if (!buffer_mapped(bh)) {
- void *kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + i * blocksize, 0, blocksize);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, i * blocksize, blocksize,
+ KM_USER0);
if (!err)
set_buffer_uptodate(bh);
continue;
if (!err)
set_buffer_uptodate(bh);
continue;
long status;
unsigned zerofrom;
unsigned blocksize = 1 << inode->i_blkbits;
long status;
unsigned zerofrom;
unsigned blocksize = 1 << inode->i_blkbits;
while(page->index > (pgpos = *bytes>>PAGE_CACHE_SHIFT)) {
status = -ENOMEM;
while(page->index > (pgpos = *bytes>>PAGE_CACHE_SHIFT)) {
status = -ENOMEM;
PAGE_CACHE_SIZE, get_block);
if (status)
goto out_unmap;
PAGE_CACHE_SIZE, get_block);
if (status)
goto out_unmap;
- kaddr = kmap_atomic(new_page, KM_USER0);
- memset(kaddr+zerofrom, 0, PAGE_CACHE_SIZE-zerofrom);
- flush_dcache_page(new_page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, zerofrom, PAGE_CACHE_SIZE - zerofrom,
+ KM_USER0);
generic_commit_write(NULL, new_page, zerofrom, PAGE_CACHE_SIZE);
unlock_page(new_page);
page_cache_release(new_page);
generic_commit_write(NULL, new_page, zerofrom, PAGE_CACHE_SIZE);
unlock_page(new_page);
page_cache_release(new_page);
if (status)
goto out1;
if (zerofrom < offset) {
if (status)
goto out1;
if (zerofrom < offset) {
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr+zerofrom, 0, offset-zerofrom);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, zerofrom, offset - zerofrom, KM_USER0);
__block_commit_write(inode, page, zerofrom, offset);
}
return 0;
__block_commit_write(inode, page, zerofrom, offset);
}
return 0;
* Error recovery is pretty slack. Clear the page and mark it dirty
* so we'll later zero out any blocks which _were_ allocated.
*/
* Error recovery is pretty slack. Clear the page and mark it dirty
* so we'll later zero out any blocks which _were_ allocated.
*/
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr, 0, PAGE_CACHE_SIZE);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0);
SetPageUptodate(page);
set_page_dirty(page);
return ret;
SetPageUptodate(page);
set_page_dirty(page);
return ret;
loff_t i_size = i_size_read(inode);
const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
unsigned offset;
loff_t i_size = i_size_read(inode);
const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
unsigned offset;
int ret;
/* Is the page fully inside i_size? */
int ret;
/* Is the page fully inside i_size? */
* the page size, the remaining memory is zeroed when mapped, and
* writes to that region are not written out to the file."
*/
* the page size, the remaining memory is zeroed when mapped, and
* writes to that region are not written out to the file."
*/
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, offset, PAGE_CACHE_SIZE - offset, KM_USER0);
out:
ret = mpage_writepage(page, get_block, wbc);
if (ret == -EAGAIN)
out:
ret = mpage_writepage(page, get_block, wbc);
if (ret == -EAGAIN)
unsigned to;
struct page *page;
const struct address_space_operations *a_ops = mapping->a_ops;
unsigned to;
struct page *page;
const struct address_space_operations *a_ops = mapping->a_ops;
int ret = 0;
if ((offset & (blocksize - 1)) == 0)
int ret = 0;
if ((offset & (blocksize - 1)) == 0)
to = (offset + blocksize) & ~(blocksize - 1);
ret = a_ops->prepare_write(NULL, page, offset, to);
if (ret == 0) {
to = (offset + blocksize) & ~(blocksize - 1);
ret = a_ops->prepare_write(NULL, page, offset, to);
if (ret == 0) {
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, offset, PAGE_CACHE_SIZE - offset,
+ KM_USER0);
/*
* It would be more correct to call aops->commit_write()
* here, but this is more efficient.
/*
* It would be more correct to call aops->commit_write()
* here, but this is more efficient.
struct inode *inode = mapping->host;
struct page *page;
struct buffer_head *bh;
struct inode *inode = mapping->host;
struct page *page;
struct buffer_head *bh;
int err;
blocksize = 1 << inode->i_blkbits;
int err;
blocksize = 1 << inode->i_blkbits;
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + offset, 0, length);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
-
+ zero_user_page(page, offset, length, KM_USER0);
mark_buffer_dirty(bh);
err = 0;
mark_buffer_dirty(bh);
err = 0;
loff_t i_size = i_size_read(inode);
const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
unsigned offset;
loff_t i_size = i_size_read(inode);
const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
unsigned offset;
/* Is the page fully inside i_size? */
if (page->index < end_index)
/* Is the page fully inside i_size? */
if (page->index < end_index)
* the page size, the remaining memory is zeroed when mapped, and
* writes to that region are not written out to the file."
*/
* the page size, the remaining memory is zeroed when mapped, and
* writes to that region are not written out to the file."
*/
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, offset, PAGE_CACHE_SIZE - offset, KM_USER0);
return __block_write_full_page(inode, page, get_block, wbc);
}
return __block_write_full_page(inode, page, get_block, wbc);
}
do_holes:
/* Handle holes */
if (!buffer_mapped(map_bh)) {
do_holes:
/* Handle holes */
if (!buffer_mapped(map_bh)) {
loff_t i_size_aligned;
/* AKPM: eargh, -ENOTBLK is a hack */
loff_t i_size_aligned;
/* AKPM: eargh, -ENOTBLK is a hack */
page_cache_release(page);
goto out;
}
page_cache_release(page);
goto out;
}
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + (block_in_page << blkbits),
- 0, 1 << blkbits);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, block_in_page << blkbits,
+ 1 << blkbits, KM_USER0);
dio->block_in_file++;
block_in_page++;
goto next_block;
dio->block_in_file++;
block_in_page++;
goto next_block;
}
if (first_hole != blocks_per_page) {
}
if (first_hole != blocks_per_page) {
- char *kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + (first_hole << blkbits), 0,
- PAGE_CACHE_SIZE - (first_hole << blkbits));
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, first_hole << blkbits,
+ PAGE_CACHE_SIZE - (first_hole << blkbits),
+ KM_USER0);
if (first_hole == 0) {
SetPageUptodate(page);
unlock_page(page);
if (first_hole == 0) {
SetPageUptodate(page);
unlock_page(page);
* written out to the file."
*/
unsigned offset = i_size & (PAGE_CACHE_SIZE - 1);
* written out to the file."
*/
unsigned offset = i_size & (PAGE_CACHE_SIZE - 1);
if (page->index > end_index || !offset)
goto confused;
if (page->index > end_index || !offset)
goto confused;
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, offset, PAGE_CACHE_SIZE - offset,
+ KM_USER0);
/*
* Same but also flushes aliased cache contents to RAM.
/*
* Same but also flushes aliased cache contents to RAM.
+ *
+ * This must be a macro because KM_USER0 and friends aren't defined if
+ * !CONFIG_HIGHMEM
-static inline void memclear_highpage_flush(struct page *page, unsigned int offset, unsigned int size)
+#define zero_user_page(page, offset, size, km_type) \
+ do { \
+ void *kaddr; \
+ \
+ BUG_ON((offset) + (size) > PAGE_SIZE); \
+ \
+ kaddr = kmap_atomic(page, km_type); \
+ memset((char *)kaddr + (offset), 0, (size)); \
+ flush_dcache_page(page); \
+ kunmap_atomic(kaddr, (km_type)); \
+ } while (0)
+
+
+static inline void memclear_highpage_flush(struct page *page,
+ unsigned int offset, unsigned int size)
- void *kaddr;
-
- BUG_ON(offset + size > PAGE_SIZE);
-
- kaddr = kmap_atomic(page, KM_USER0);
- memset((char *)kaddr + offset, 0, size);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
+ zero_user_page(page, offset, size, KM_USER0);
}
#ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE
}
#ifndef __HAVE_ARCH_COPY_USER_HIGHPAGE
unsigned blocksize;
unsigned length;
struct page *page;
unsigned blocksize;
unsigned length;
struct page *page;
BUG_ON(!mapping->a_ops->get_xip_page);
BUG_ON(!mapping->a_ops->get_xip_page);
else
return PTR_ERR(page);
}
else
return PTR_ERR(page);
}
- kaddr = kmap_atomic(page, KM_USER0);
- memset(kaddr + offset, 0, length);
- kunmap_atomic(kaddr, KM_USER0);
-
- flush_dcache_page(page);
+ zero_user_page(page, offset, length, KM_USER0);
return 0;
}
EXPORT_SYMBOL_GPL(xip_truncate_page);
return 0;
}
EXPORT_SYMBOL_GPL(xip_truncate_page);
#include <linux/swap.h>
#include <linux/module.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/module.h>
#include <linux/pagemap.h>
+#include <linux/highmem.h>
#include <linux/pagevec.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/buffer_head.h> /* grr. try_to_release_page,
#include <linux/pagevec.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/buffer_head.h> /* grr. try_to_release_page,
static inline void truncate_partial_page(struct page *page, unsigned partial)
{
static inline void truncate_partial_page(struct page *page, unsigned partial)
{
- memclear_highpage_flush(page, partial, PAGE_CACHE_SIZE-partial);
+ zero_user_page(page, partial, PAGE_CACHE_SIZE - partial, KM_USER0);
if (PagePrivate(page))
do_invalidatepage(page, partial);
}
if (PagePrivate(page))
do_invalidatepage(page, partial);
}