Skip to content

Commit

Permalink
afs: convert to new aops
Browse files Browse the repository at this point in the history
Cannot assume writes will fully complete, so this conversion goes the easy
way and always brings the page uptodate before the write.

[dhowells@redhat.com: style tweaks]
Signed-off-by: Nick Piggin <npiggin@suse.de>
Acked-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Nick Piggin authored and torvalds committed Oct 16, 2008
1 parent 8360e81 commit 15b4650
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 92 deletions.
4 changes: 2 additions & 2 deletions fs/afs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ const struct address_space_operations afs_fs_aops = {
.launder_page = afs_launder_page,
.releasepage = afs_releasepage,
.invalidatepage = afs_invalidatepage,
.prepare_write = afs_prepare_write,
.commit_write = afs_commit_write,
.write_begin = afs_write_begin,
.write_end = afs_write_end,
.writepage = afs_writepage,
.writepages = afs_writepages,
};
Expand Down
8 changes: 6 additions & 2 deletions fs/afs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,12 @@ extern int afs_volume_release_fileserver(struct afs_vnode *,
*/
extern int afs_set_page_dirty(struct page *);
extern void afs_put_writeback(struct afs_writeback *);
extern int afs_prepare_write(struct file *, struct page *, unsigned, unsigned);
extern int afs_commit_write(struct file *, struct page *, unsigned, unsigned);
extern int afs_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata);
extern int afs_write_end(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata);
extern int afs_writepage(struct page *, struct writeback_control *);
extern int afs_writepages(struct address_space *, struct writeback_control *);
extern int afs_write_inode(struct inode *, int);
Expand Down
131 changes: 43 additions & 88 deletions fs/afs/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,23 @@ void afs_put_writeback(struct afs_writeback *wb)
* partly or wholly fill a page that's under preparation for writing
*/
static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
unsigned start, unsigned len, struct page *page)
loff_t pos, unsigned len, struct page *page)
{
loff_t i_size;
unsigned eof;
int ret;

_enter(",,%u,%u", start, len);
_enter(",,%llu,%u", (unsigned long long)pos, len);

ASSERTCMP(start + len, <=, PAGE_SIZE);
ASSERTCMP(len, <=, PAGE_CACHE_SIZE);

ret = afs_vnode_fetch_data(vnode, key, start, len, page);
i_size = i_size_read(&vnode->vfs_inode);
if (pos + len > i_size)
eof = i_size;
else
eof = PAGE_CACHE_SIZE;

ret = afs_vnode_fetch_data(vnode, key, 0, eof, page);
if (ret < 0) {
if (ret == -ENOENT) {
_debug("got NOENT from server"
Expand All @@ -106,110 +114,56 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
return ret;
}

/*
* prepare a page for being written to
*/
static int afs_prepare_page(struct afs_vnode *vnode, struct page *page,
struct key *key, unsigned offset, unsigned to)
{
unsigned eof, tail, start, stop, len;
loff_t i_size, pos;
void *p;
int ret;

_enter("");

if (offset == 0 && to == PAGE_SIZE)
return 0;

p = kmap_atomic(page, KM_USER0);

i_size = i_size_read(&vnode->vfs_inode);
pos = (loff_t) page->index << PAGE_SHIFT;
if (pos >= i_size) {
/* partial write, page beyond EOF */
_debug("beyond");
if (offset > 0)
memset(p, 0, offset);
if (to < PAGE_SIZE)
memset(p + to, 0, PAGE_SIZE - to);
kunmap_atomic(p, KM_USER0);
return 0;
}

if (i_size - pos >= PAGE_SIZE) {
/* partial write, page entirely before EOF */
_debug("before");
tail = eof = PAGE_SIZE;
} else {
/* partial write, page overlaps EOF */
eof = i_size - pos;
_debug("overlap %u", eof);
tail = max(eof, to);
if (tail < PAGE_SIZE)
memset(p + tail, 0, PAGE_SIZE - tail);
if (offset > eof)
memset(p + eof, 0, PAGE_SIZE - eof);
}

kunmap_atomic(p, KM_USER0);

ret = 0;
if (offset > 0 || eof > to) {
/* need to fill one or two bits that aren't going to be written
* (cover both fillers in one read if there are two) */
start = (offset > 0) ? 0 : to;
stop = (eof > to) ? eof : offset;
len = stop - start;
_debug("wr=%u-%u av=0-%u rd=%u@%u",
offset, to, eof, start, len);
ret = afs_fill_page(vnode, key, start, len, page);
}

_leave(" = %d", ret);
return ret;
}

/*
* prepare to perform part of a write to a page
* - the caller holds the page locked, preventing it from being written out or
* modified by anyone else
*/
int afs_prepare_write(struct file *file, struct page *page,
unsigned offset, unsigned to)
int afs_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
struct afs_writeback *candidate, *wb;
struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode);
struct page *page;
struct key *key = file->private_data;
pgoff_t index;
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
unsigned to = from + len;
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
int ret;

_enter("{%x:%u},{%lx},%u,%u",
vnode->fid.vid, vnode->fid.vnode, page->index, offset, to);
vnode->fid.vid, vnode->fid.vnode, index, from, to);

candidate = kzalloc(sizeof(*candidate), GFP_KERNEL);
if (!candidate)
return -ENOMEM;
candidate->vnode = vnode;
candidate->first = candidate->last = page->index;
candidate->offset_first = offset;
candidate->first = candidate->last = index;
candidate->offset_first = from;
candidate->to_last = to;
candidate->usage = 1;
candidate->state = AFS_WBACK_PENDING;
init_waitqueue_head(&candidate->waitq);

page = __grab_cache_page(mapping, index);
if (!page) {
kfree(candidate);
return -ENOMEM;
}
*pagep = page;
/* page won't leak in error case: it eventually gets cleaned off LRU */

if (!PageUptodate(page)) {
_debug("not up to date");
ret = afs_prepare_page(vnode, page, key, offset, to);
ret = afs_fill_page(vnode, key, pos, len, page);
if (ret < 0) {
kfree(candidate);
_leave(" = %d [prep]", ret);
return ret;
}
SetPageUptodate(page);
}

try_again:
index = page->index;
spin_lock(&vnode->writeback_lock);

/* see if this page is already pending a writeback under a suitable key
Expand Down Expand Up @@ -242,8 +196,8 @@ int afs_prepare_write(struct file *file, struct page *page,
subsume_in_current_wb:
_debug("subsume");
ASSERTRANGE(wb->first, <=, index, <=, wb->last);
if (index == wb->first && offset < wb->offset_first)
wb->offset_first = offset;
if (index == wb->first && from < wb->offset_first)
wb->offset_first = from;
if (index == wb->last && to > wb->to_last)
wb->to_last = to;
spin_unlock(&vnode->writeback_lock);
Expand Down Expand Up @@ -289,17 +243,17 @@ int afs_prepare_write(struct file *file, struct page *page,
/*
* finalise part of a write to a page
*/
int afs_commit_write(struct file *file, struct page *page,
unsigned offset, unsigned to)
int afs_write_end(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode);
loff_t i_size, maybe_i_size;

_enter("{%x:%u},{%lx},%u,%u",
vnode->fid.vid, vnode->fid.vnode, page->index, offset, to);
_enter("{%x:%u},{%lx}",
vnode->fid.vid, vnode->fid.vnode, page->index);

maybe_i_size = (loff_t) page->index << PAGE_SHIFT;
maybe_i_size += to;
maybe_i_size = pos + copied;

i_size = i_size_read(&vnode->vfs_inode);
if (maybe_i_size > i_size) {
Expand All @@ -310,12 +264,13 @@ int afs_commit_write(struct file *file, struct page *page,
spin_unlock(&vnode->writeback_lock);
}

SetPageUptodate(page);
set_page_dirty(page);
if (PageDirty(page))
_debug("dirtied");
unlock_page(page);
page_cache_release(page);

return 0;
return copied;
}

/*
Expand Down

0 comments on commit 15b4650

Please sign in to comment.