Merge branch 'for-linus' of git://git.open-osd.org/linux-open-osd
[pandora-kernel.git] / fs / exofs / inode.c
index f39a38f..3e5f3a6 100644 (file)
 
 #define EXOFS_DBGMSG2(M...) do {} while (0)
 
-enum { BIO_MAX_PAGES_KMALLOC =
-               (PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec),
-       MAX_PAGES_KMALLOC =
-               PAGE_SIZE / sizeof(struct page *),
-};
+enum {MAX_PAGES_KMALLOC = PAGE_SIZE / sizeof(struct page *), };
 
 unsigned exofs_max_io_pages(struct ore_layout *layout,
                            unsigned expected_pages)
@@ -49,8 +45,7 @@ unsigned exofs_max_io_pages(struct ore_layout *layout,
        unsigned pages = min_t(unsigned, expected_pages, MAX_PAGES_KMALLOC);
 
        /* TODO: easily support bio chaining */
-       pages =  min_t(unsigned, pages,
-                      layout->group_width * BIO_MAX_PAGES_KMALLOC);
+       pages =  min_t(unsigned, pages, layout->max_io_length / PAGE_SIZE);
        return pages;
 }
 
@@ -68,6 +63,7 @@ struct page_collect {
        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,
@@ -86,6 +82,7 @@ static void _pcol_init(struct page_collect *pcol, unsigned expected_pages,
        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)
@@ -98,6 +95,7 @@ static void _pcol_reset(struct page_collect *pcol)
        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
@@ -149,14 +147,17 @@ static int pcol_add_page(struct page_collect *pcol, struct page *page,
        return 0;
 }
 
+enum {PAGE_WAS_NOT_IN_IO = 17};
 static int update_read_page(struct page *page, int ret)
 {
-       if (ret == 0) {
+       switch (ret) {
+       case 0:
                /* Everything is OK */
                SetPageUptodate(page);
                if (PageError(page))
                        ClearPageError(page);
-       } else if (ret == -EFAULT) {
+               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
@@ -167,16 +168,22 @@ static int update_read_page(struct page *page, int ret)
                SetPageUptodate(page);
                if (PageError(page))
                        ClearPageError(page);
-               ret = 0; /* recovered error */
                EXOFS_DBGMSG("recovered read error\n");
-       } else /* Error */
+               /* 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);
@@ -190,15 +197,16 @@ static void update_write_page(struct page *page, int ret)
 static int __readpages_done(struct page_collect *pcol)
 {
        int i;
-       u64 resid;
        u64 good_bytes;
        u64 length = 0;
-       int ret = ore_check_io(pcol->ios, &resid);
+       int ret = ore_check_io(pcol->ios, NULL);
 
-       if (likely(!ret))
+       if (likely(!ret)) {
                good_bytes = pcol->length;
-       else
-               good_bytes = pcol->length - resid;
+               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",
@@ -259,6 +267,46 @@ static void _unlock_pcol_pages(struct page_collect *pcol, int ret, int rw)
        }
 }
 
+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 page_collect *pcol)
 {
        struct exofs_i_info *oi = exofs_i(pcol->inode);
@@ -270,7 +318,7 @@ static int read_exec(struct page_collect *pcol)
                return 0;
 
        if (!pcol->ios) {
-               int ret = ore_get_rw_state(&pcol->sbi->layout, &oi->comps, true,
+               int ret = ore_get_rw_state(&pcol->sbi->layout, &oi->oc, true,
                                             pcol->pg_first << PAGE_CACHE_SHIFT,
                                             pcol->length, &pcol->ios);
 
@@ -280,7 +328,6 @@ static int read_exec(struct page_collect *pcol)
 
        ios = pcol->ios;
        ios->pages = pcol->pages;
-       ios->nr_pages = pcol->nr_pages;
 
        if (pcol->read_4_write) {
                ore_read(pcol->ios);
@@ -296,17 +343,23 @@ static int read_exec(struct page_collect *pcol)
        *pcol_copy = *pcol;
        ios->done = readpages_done;
        ios->private = pcol_copy;
+
+       /* pages ownership was passed to pcol_copy */
+       _pcol_reset(pcol);
+
+       ret = _maybe_not_all_in_one_io(ios, pcol_copy, pcol);
+       if (unlikely(ret))
+               goto err;
+
+       EXOFS_DBGMSG2("read_exec(0x%lx) offset=0x%llx length=0x%llx\n",
+               pcol->inode->i_ino, _LLU(ios->offset), _LLU(ios->length));
+
        ret = ore_read(ios);
        if (unlikely(ret))
                goto err;
 
        atomic_inc(&pcol->sbi->s_curr_pending);
 
-       EXOFS_DBGMSG2("read_exec obj=0x%llx start=0x%llx length=0x%lx\n",
-                 oi->one_comp.obj.id, _LLU(ios->offset), pcol->length);
-
-       /* pages ownership was passed to pcol_copy */
-       _pcol_reset(pcol);
        return 0;
 
 err:
@@ -341,6 +394,8 @@ static int readpage_strip(void *data, struct page *page)
                EXOFS_ERR("PageUptodate(0x%lx, 0x%lx)\n", pcol->inode->i_ino,
                          page->index);
 
+       pcol->that_locked_page = page;
+
        if (page->index < end_index)
                len = PAGE_CACHE_SIZE;
        else if (page->index == end_index)
@@ -429,6 +484,10 @@ static int exofs_readpages(struct file *file, struct address_space *mapping,
                return ret;
        }
 
+       ret = read_exec(&pcol);
+       if (unlikely(ret))
+               return ret;
+
        return read_exec(&pcol);
 }
 
@@ -462,17 +521,18 @@ static void writepages_done(struct ore_io_state *ios, void *p)
 {
        struct page_collect *pcol = p;
        int i;
-       u64 resid;
        u64  good_bytes;
        u64  length = 0;
-       int ret = ore_check_io(ios, &resid);
+       int ret = ore_check_io(ios, NULL);
 
        atomic_dec(&pcol->sbi->s_curr_pending);
 
-       if (likely(!ret))
+       if (likely(!ret)) {
                good_bytes = pcol->length;
-       else
-               good_bytes = pcol->length - resid;
+               ret = PAGE_WAS_NOT_IN_IO;
+       } else {
+               good_bytes = 0;
+       }
 
        EXOFS_DBGMSG2("writepages_done(0x%lx) good_bytes=0x%llx"
                     " length=0x%lx nr_pages=%u\n",
@@ -505,6 +565,56 @@ static void writepages_done(struct ore_io_state *ios, void *p)
        EXOFS_DBGMSG2("writepages_done END\n");
 }
 
+static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate)
+{
+       struct page_collect *pcol = priv;
+       pgoff_t index = offset / PAGE_SIZE;
+
+       if (!pcol->that_locked_page ||
+           (pcol->that_locked_page->index != index)) {
+               struct page *page = find_get_page(pcol->inode->i_mapping, index);
+
+               if (!page) {
+                       page = find_or_create_page(pcol->inode->i_mapping,
+                                                  index, GFP_NOFS);
+                       if (unlikely(!page)) {
+                               EXOFS_DBGMSG("grab_cache_page Failed "
+                                       "index=0x%llx\n", _LLU(index));
+                               return NULL;
+                       }
+                       unlock_page(page);
+               }
+               if (PageDirty(page) || PageWriteback(page))
+                       *uptodate = true;
+               else
+                       *uptodate = PageUptodate(page);
+               EXOFS_DBGMSG("index=0x%lx uptodate=%d\n", index, *uptodate);
+               return page;
+       } else {
+               EXOFS_DBGMSG("YES that_locked_page index=0x%lx\n",
+                            pcol->that_locked_page->index);
+               *uptodate = true;
+               return pcol->that_locked_page;
+       }
+}
+
+static void __r4w_put_page(void *priv, struct page *page)
+{
+       struct page_collect *pcol = priv;
+
+       if (pcol->that_locked_page != page) {
+               EXOFS_DBGMSG("index=0x%lx\n", page->index);
+               page_cache_release(page);
+               return;
+       }
+       EXOFS_DBGMSG("that_locked_page index=0x%lx\n", page->index);
+}
+
+static const struct _ore_r4w_op _r4w_op = {
+       .get_page = &__r4w_get_page,
+       .put_page = &__r4w_put_page,
+};
+
 static int write_exec(struct page_collect *pcol)
 {
        struct exofs_i_info *oi = exofs_i(pcol->inode);
@@ -516,10 +626,9 @@ static int write_exec(struct page_collect *pcol)
                return 0;
 
        BUG_ON(pcol->ios);
-       ret = ore_get_rw_state(&pcol->sbi->layout, &oi->comps, false,
+       ret = ore_get_rw_state(&pcol->sbi->layout, &oi->oc, false,
                                 pcol->pg_first << PAGE_CACHE_SHIFT,
                                 pcol->length, &pcol->ios);
-
        if (unlikely(ret))
                goto err;
 
@@ -534,10 +643,20 @@ static int write_exec(struct page_collect *pcol)
 
        ios = pcol->ios;
        ios->pages = pcol_copy->pages;
-       ios->nr_pages = pcol_copy->nr_pages;
        ios->done = writepages_done;
+       ios->r4w = &_r4w_op;
        ios->private = pcol_copy;
 
+       /* pages ownership was passed to pcol_copy */
+       _pcol_reset(pcol);
+
+       ret = _maybe_not_all_in_one_io(ios, pcol_copy, pcol);
+       if (unlikely(ret))
+               goto err;
+
+       EXOFS_DBGMSG2("write_exec(0x%lx) offset=0x%llx length=0x%llx\n",
+               pcol->inode->i_ino, _LLU(ios->offset), _LLU(ios->length));
+
        ret = ore_write(ios);
        if (unlikely(ret)) {
                EXOFS_ERR("write_exec: ore_write() Failed\n");
@@ -545,11 +664,6 @@ static int write_exec(struct page_collect *pcol)
        }
 
        atomic_inc(&pcol->sbi->s_curr_pending);
-       EXOFS_DBGMSG2("write_exec(0x%lx, 0x%llx) start=0x%llx length=0x%lx\n",
-                 pcol->inode->i_ino, pcol->pg_first, _LLU(ios->offset),
-                 pcol->length);
-       /* pages ownership was passed to pcol_copy */
-       _pcol_reset(pcol);
        return 0;
 
 err:
@@ -689,14 +803,33 @@ static int exofs_writepages(struct address_space *mapping,
        _pcol_init(&pcol, expected_pages, mapping->host);
 
        ret = write_cache_pages(mapping, wbc, writepage_strip, &pcol);
-       if (ret) {
+       if (unlikely(ret)) {
                EXOFS_ERR("write_cache_pages => %d\n", ret);
                return ret;
        }
 
-       return write_exec(&pcol);
+       ret = write_exec(&pcol);
+       if (unlikely(ret))
+               return ret;
+
+       if (wbc->sync_mode == WB_SYNC_ALL) {
+               return write_exec(&pcol); /* pump the last reminder */
+       } else if (pcol.nr_pages) {
+               /* not SYNC let the reminder join the next writeout */
+               unsigned i;
+
+               for (i = 0; i < pcol.nr_pages; i++) {
+                       struct page *page = pcol.pages[i];
+
+                       end_page_writeback(page);
+                       set_page_dirty(page);
+                       unlock_page(page);
+               }
+       }
+       return 0;
 }
 
+/*
 static int exofs_writepage(struct page *page, struct writeback_control *wbc)
 {
        struct page_collect pcol;
@@ -712,7 +845,7 @@ static int exofs_writepage(struct page *page, struct writeback_control *wbc)
 
        return write_exec(&pcol);
 }
-
+*/
 /* i_mutex held using inode->i_size directly */
 static void _write_failed(struct inode *inode, loff_t to)
 {
@@ -818,7 +951,7 @@ static void exofs_invalidatepage(struct page *page, unsigned long offset)
 const struct address_space_operations exofs_aops = {
        .readpage       = exofs_readpage,
        .readpages      = exofs_readpages,
-       .writepage      = exofs_writepage,
+       .writepage      = NULL,
        .writepages     = exofs_writepages,
        .write_begin    = exofs_write_begin_export,
        .write_end      = exofs_write_end,
@@ -860,7 +993,7 @@ static int _do_truncate(struct inode *inode, loff_t newsize)
 
        inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 
-       ret = ore_truncate(&sbi->layout, &oi->comps, (u64)newsize);
+       ret = ore_truncate(&sbi->layout, &oi->oc, (u64)newsize);
        if (likely(!ret))
                truncate_setsize(inode, newsize);
 
@@ -927,14 +1060,14 @@ static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi,
        struct exofs_on_disk_inode_layout *layout;
        int ret;
 
-       ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios);
+       ret = ore_get_io_state(&sbi->layout, &oi->oc, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__);
                return ret;
        }
 
-       attrs[1].len = exofs_on_disk_inode_layout_size(sbi->comps.numdevs);
-       attrs[2].len = exofs_on_disk_inode_layout_size(sbi->comps.numdevs);
+       attrs[1].len = exofs_on_disk_inode_layout_size(sbi->oc.numdevs);
+       attrs[2].len = exofs_on_disk_inode_layout_size(sbi->oc.numdevs);
 
        ios->in_attr = attrs;
        ios->in_attr_len = ARRAY_SIZE(attrs);
@@ -1018,7 +1151,7 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
                return inode;
        oi = exofs_i(inode);
        __oi_init(oi);
-       exofs_init_comps(&oi->comps, &oi->one_comp, sb->s_fs_info,
+       exofs_init_comps(&oi->oc, &oi->one_comp, sb->s_fs_info,
                         exofs_oi_objno(oi));
 
        /* read the inode from the osd */
@@ -1172,13 +1305,13 @@ struct inode *exofs_new_inode(struct inode *dir, int mode)
        spin_unlock(&sbi->s_next_gen_lock);
        insert_inode_hash(inode);
 
-       exofs_init_comps(&oi->comps, &oi->one_comp, sb->s_fs_info,
+       exofs_init_comps(&oi->oc, &oi->one_comp, sb->s_fs_info,
                         exofs_oi_objno(oi));
        exofs_sbi_write_stats(sbi); /* Make sure new sbi->s_nextid is on disk */
 
        mark_inode_dirty(inode);
 
-       ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios);
+       ret = ore_get_io_state(&sbi->layout, &oi->oc, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("exofs_new_inode: ore_get_io_state failed\n");
                return ERR_PTR(ret);
@@ -1267,7 +1400,7 @@ static int exofs_update_inode(struct inode *inode, int do_sync)
        } else
                memcpy(fcb->i_data, oi->i_data, sizeof(fcb->i_data));
 
-       ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios);
+       ret = ore_get_io_state(&sbi->layout, &oi->oc, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__);
                goto free_args;
@@ -1350,7 +1483,7 @@ void exofs_evict_inode(struct inode *inode)
        /* ignore the error, attempt a remove anyway */
 
        /* Now Remove the OSD objects */
-       ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios);
+       ret = ore_get_io_state(&sbi->layout, &oi->oc, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("%s: ore_get_io_state failed\n", __func__);
                return;