Chris Mason wrote:
> Daniel Phillips wrote:
> >
> > Simply stated, the new cache design divides filesystem blocks into two
> > classes: those that can be memory-mapped and those that can't.  There
> > is no defined way to move a given block from one class to the other.
> >
> This is the sequence of events for a tail unmerge:
> 
>   - Fix up various inode fields
>   - bread the tail block
>   - Allocate new block from ext2, map it into a buffer with getblk
>   - Copy the tail fragment to the buffer of the newly allocated block
>   - Mark the buffer dirty and go away
> 
> It isn't as bad as you think ;-)  They key is to only unmerge into the page
> cache.  If you can't unmerge only into the page cache, you need to do a
> getblk in your get_block call, and deal with the aliases.
> 
> If you unmerge inside ext2_get_block, you get passed bh_result.  Just
> change bh_result->b_blocknr to your new block, set b_dev, and set it
> mapped, and copy the tail into bh_result.  Then block_write_full_page or
> block_commit_write are the ones that dirty bh_result for you, no alias
> created.

The fly in the ointment is that the file size may have changed by this
time.  I rely on the file size to tell me which block is the tail
block and when the file size changes I unmerge the tail - nice simple
idea, right?  I could keep my own "blocks" field to tell me where the
tail is, but that road descends to hell.

After thinking about it last night I saw the correct approach.  I need
a new page cache primitive:

  struct page *getpage (struct address_space *mapping, unsigned long
index)

This is getblk, except for pages.  It finds or sets up a page in a
mapping.  It puts buffers on the page if necessary but doesn't cause
any I/O action.

I also need another function, and I'll write it right now:

        struct buffer_head *page_buffer (struct page *page, int i)
        {
                struct buffer_head *bh = page->buffers;
                while (i--)
                        bh = bh->b_this_page;
                return bh;
        }

Getpage isn't as easy as this, but it's not that hard either.  A
gutted version of do_generic_read_page serves the purpose; the result
is fairly pleasing to the eye (but is for my eyes only until I've
tested it).

Now I can unmerge this way:

  - Fix up various inode fields
 - getpage the tail page from the mapping
  - bread the shared tail block
  - get the appropriate page buffer using page_buffer
  - copy the tail fragment to that buffer and dirty it

I can do this at a high level - the place where the unmerge conditions
are most easily and accurately detected - as opposed to deep in the
guts of the page I/O.  The page is left in a state that already makes
sense to read_page, write_page and friends.

The only question is: should I do it the correct way or should I do a
messy hack "just for now" using the existing page primitives.  Fix the
symptom or go after the deep problem?  Considering that for about one
nanosecond I decide to do the right thing, and lo! I find myself in a
new cave.

--
Daniel
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to [EMAIL PROTECTED]

Reply via email to