/*
* Copyright (C) 2005, 2006
* Avishay Traeger (avishay@gmail.com)
* Copyright (C) 2008, 2009
* Boaz Harrosh <ooo@electrozaur.com>
*
* Copyrights for code taken from ext2:
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
* from
* linux/fs/minix/inode.c
* Copyright (C) 1991, 1992 Linus Torvalds
*
* This file is part of exofs.
*
* exofs is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation. Since it is based on ext2, and the only
* valid version of GPL for the Linux kernel is version 2, the only valid
* version of GPL for exofs is version 2.
*
* exofs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with exofs; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/slab.h>
#include "exofs.h"
#define EXOFS_DBGMSG2(M...) do {} while (0)
unsigned exofs_max_io_pages(struct ore_layout *layout,
unsigned expected_pages)
{
unsigned pages = min_t(unsigned, expected_pages,
layout->max_io_length / PAGE_SIZE);
return pages;
}
struct page_collect {
struct exofs_sb_info *sbi;
struct inode *inode;
unsigned expected_pages;
struct ore_io_state *ios;
struct page **pages;
unsigned alloc_pages;
unsigned nr_pages;
unsigned long length;
loff_t pg_first; /* keep 64bit also in 32-arches */
bool read_4_write; /* This means two things: that the read is sync
* And the pages should not be unlocked.
*/
struct page *that_locked_page;
};
static void _pcol_init(struct page_collect *pcol, unsigned expected_pages,
struct inode *inode)
{
struct exofs_sb_info *sbi = inode->i_sb->s_fs_info;
pcol->sbi = sbi;
pcol->inode = inode;
pcol->expected_pages = expected_pages;
pcol->ios = NULL;
pcol->pages = NULL;
pcol->alloc_pages = 0;
pcol->nr_pages = 0;
pcol->length = 0;
pcol->pg_first = -1;
pcol->read_4_write = false;
pcol->that_locked_page = NULL;
}
static void _pcol_reset(struct page_collect *pcol)
{
pcol->expected_pages -= min(pcol->nr_pages, pcol->expected_pages);
pcol->pages = NULL;
pcol->alloc_pages = 0;
pcol->nr_pages = 0;
pcol->length = 0;
pcol->pg_first = -1;
pcol->ios = NULL;
pcol->that_locked_page = NULL;
/* this is probably the end of the loop but in writes
* it might not end here. don't be left with nothing
*/
if (!pcol->expected_pages)
pcol->expected_pages =
exofs_max_io_pages(&pcol->sbi->layout, ~0);
}
static int pcol_try_alloc(struct page_collect *pcol)
{
unsigned pages;
/* TODO: easily support bio chaining */
pages = exofs_max_io_pages(&pcol->sbi->layout, pcol->expected_pages);
for (; pages; pages >>= 1) {
pcol->pages = kmalloc(pages * sizeof(struct page *),
GFP_KERNEL);
if (likely(pcol->pages)) {
pcol->alloc_pages = pages;
return 0;
}
}
EXOFS_ERR("Failed to kmalloc expected_pages=%u\n",
pcol->expected_pages);
return -ENOMEM;
}
static void pcol_free(struct page_collect *pcol)
{
kfree(pcol->pages);
pcol->pages = NULL;
if (pcol->ios) {
ore_put_io_state(pcol->ios);
pcol->ios = NULL;
}
}
static int pcol_add_page(struct page_collect *pcol, struct page *page,
unsigned len)
{
if (unlikely(pcol->nr_pages >= pcol->alloc_pages))
return -ENOMEM;
pcol->pages[pcol->nr_pages++] = page;
pcol->length += len;
return 0;
}
enum {PAGE_WAS_NOT_IN_IO = 17};
static int update_read_page(struct page *page, int ret)
{
switch (ret) {
case 0:
/* Everything is OK */
SetPageUptodate(page);
if (PageError(page))
ClearPageError(page);
break;
case -EFAULT:
/* In this case we were trying to read something that wasn't on
* disk yet - return a page full of zeroes. This should be OK,
* because the object should be empty (if there was a write
* before this read, the read would be waiting with the page
* locked */
clear_highpage(page);
SetPageUptodate(page);
if (PageError(page))
ClearPageError(page);
EXOFS_DBGMSG("recovered read error\n");
/* fall through */
case PAGE_WAS_NOT_IN_IO:
ret = 0; /* recovered error */
break;
default:
SetPageError(page);
}
return ret;
}
static void update_write_page(struct page *page, int ret)
{
if (unlikely(ret == PAGE_WAS_NOT_IN_IO))
return; /* don't pass start don't collect $200 */
if (ret) {
mapping_set_error(page->mapping, ret);
SetPageError(page);
}
end_page_writeback(page);
}
/* Called at the end of reads, to optionally unlock pages and update their
* status.
*/
static int __readpages_done(struct page_collect *pcol)
{
int i;
u64 good_bytes;
u64 length = 0;
int ret = ore_check_io(pcol->ios, NULL);
if (likely(!ret)) {
good_bytes = pcol->length;
ret = PAGE_WAS_NOT_IN_IO;
} else {
good_bytes = 0;
}
EXOFS_DBGMSG2("readpages_done(0x%lx) good_bytes=0x%llx"
" length=0x%lx nr_pages=%u\n",
pcol->inode->i_ino, _LLU(good_bytes), pcol->length,
pcol->nr_pages);
for (i = 0; i < pcol->nr_pages; i++) {
struct page *page = pcol->pages[i];
struct inode *inode = page->mapping->host;
int page_stat;
if (inode != pcol->inode)
continue; /* osd might add more pages at end */
if (likely(length < good_bytes))
page_stat = 0;
else
page_stat = ret;
EXOFS_DBGMSG2(" readpages_done(0x%lx, 0x%lx) %s\n",
inode->i_ino, page->index,
page_stat ? "bad_bytes" : "good_bytes");
ret = update_read_page(page, page_stat);
if (!pcol->read_4_write)
unlock_page(page);
length += PAGE_SIZE;
}
pcol_free(pcol);
EXOFS_DBGMSG2("readpages_done END\n");
return ret;
}
/* callback of async reads */
static void readpages_done(struct ore_io_state *ios, void *p)
{
struct page_collect *pcol = p;
__readpages_done(pcol);
atomic_dec(&pcol->sbi->s_curr_pending);
kfree(pcol);
}
static void _unlock_pcol_pages(struct page_collect *pcol, int ret, int rw)
{
int i;
for (i = 0; i < pcol->nr_pages; i++) {
struct page *page = pcol->pages[i];
if (rw == READ)
update_read_page(page, ret);
else
update_write_page(page, ret);
unlock_page(page);
}
}
static int _maybe_not_all_in_one_io(struct ore_io_state *ios,
struct page_collect *pcol_src, struct page_collect *pcol)
{
/* length was wrong or offset was not page aligned */
BUG_ON(pcol_src->nr_pages < ios->nr_pages);
if (pcol_src->nr_pages > ios->nr_pages) {
struct page **src_page;
unsigned pages_less = pcol_src->nr_pages - ios->nr_pages;
unsigned long len_less = pcol_src->length - ios->length;
unsigned i;
int ret;
/* This IO was trimmed */
pcol_src->nr_pages = ios->nr_pages;
pcol_src->length = ios->length;
/* Left over pages are passed to the next io */
pcol->expected_pages += pages_less;
pcol->nr_pages = pages_less;
pcol->length = len_less;
src_page = pcol_src->pages + pcol_src->nr_pages;
pcol->pg_first = (*src_page)->index;
ret = pcol_try_alloc(pcol);
if (unlikely(ret))
return ret;
for (i = 0; i < pages_less; ++i)
pcol->pages[i] = *src_page++;
EXOFS_DBGMSG("Length was adjusted nr_pages=0x%x "
"pages_less=0x%x expected_pages=0x%x "
"next_offset=0x%llx next_len=0x%lx\n",
pcol_src->nr_pages, pages_less, pcol->expected_pages,
pcol->pg_first * PAGE_SIZE, pcol->length);
}
return 0;
}
static int read_exec(struct pa
|