2005-06-22 07:17:14 +07:00
|
|
|
/*
|
|
|
|
* bitmap.c two-level bitmap (C) Peter T. Breuer (ptb@ot.uc3m.es) 2003
|
|
|
|
*
|
|
|
|
* bitmap_create - sets up the bitmap structure
|
|
|
|
* bitmap_destroy - destroys the bitmap structure
|
|
|
|
*
|
|
|
|
* additions, Copyright (C) 2003-2004, Paul Clements, SteelEye Technology, Inc.:
|
|
|
|
* - added disk storage for bitmap
|
|
|
|
* - changes to allow various bitmap chunk sizes
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Still to do:
|
|
|
|
*
|
|
|
|
* flush after percent set rather than just time based. (maybe both).
|
|
|
|
* wait if count gets too high, wake when it drops to half.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/file.h>
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/buffer_head.h>
|
|
|
|
#include <linux/raid/md.h>
|
|
|
|
#include <linux/raid/bitmap.h>
|
|
|
|
|
|
|
|
/* debug macros */
|
|
|
|
|
|
|
|
#define DEBUG 0
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
/* these are for debugging purposes only! */
|
|
|
|
|
|
|
|
/* define one and only one of these */
|
|
|
|
#define INJECT_FAULTS_1 0 /* cause bitmap_alloc_page to fail always */
|
|
|
|
#define INJECT_FAULTS_2 0 /* cause bitmap file to be kicked when first bit set*/
|
|
|
|
#define INJECT_FAULTS_3 0 /* treat bitmap file as kicked at init time */
|
|
|
|
#define INJECT_FAULTS_4 0 /* undef */
|
|
|
|
#define INJECT_FAULTS_5 0 /* undef */
|
|
|
|
#define INJECT_FAULTS_6 0
|
|
|
|
|
|
|
|
/* if these are defined, the driver will fail! debug only */
|
|
|
|
#define INJECT_FATAL_FAULT_1 0 /* fail kmalloc, causing bitmap_create to fail */
|
|
|
|
#define INJECT_FATAL_FAULT_2 0 /* undef */
|
|
|
|
#define INJECT_FATAL_FAULT_3 0 /* undef */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//#define DPRINTK PRINTK /* set this NULL to avoid verbose debug output */
|
|
|
|
#define DPRINTK(x...) do { } while(0)
|
|
|
|
|
|
|
|
#ifndef PRINTK
|
|
|
|
# if DEBUG > 0
|
|
|
|
# define PRINTK(x...) printk(KERN_DEBUG x)
|
|
|
|
# else
|
|
|
|
# define PRINTK(x...)
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static inline char * bmname(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
return bitmap->mddev ? mdname(bitmap->mddev) : "mdX";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* just a placeholder - calls kmalloc for bitmap pages
|
|
|
|
*/
|
|
|
|
static unsigned char *bitmap_alloc_page(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
unsigned char *page;
|
|
|
|
|
2005-07-28 01:45:17 +07:00
|
|
|
#ifdef INJECT_FAULTS_1
|
2005-06-22 07:17:14 +07:00
|
|
|
page = NULL;
|
|
|
|
#else
|
|
|
|
page = kmalloc(PAGE_SIZE, GFP_NOIO);
|
|
|
|
#endif
|
|
|
|
if (!page)
|
|
|
|
printk("%s: bitmap_alloc_page FAILED\n", bmname(bitmap));
|
|
|
|
else
|
2005-06-22 07:17:27 +07:00
|
|
|
PRINTK("%s: bitmap_alloc_page: allocated page at %p\n",
|
2005-06-22 07:17:14 +07:00
|
|
|
bmname(bitmap), page);
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* for now just a placeholder -- just calls kfree for bitmap pages
|
|
|
|
*/
|
|
|
|
static void bitmap_free_page(struct bitmap *bitmap, unsigned char *page)
|
|
|
|
{
|
|
|
|
PRINTK("%s: bitmap_free_page: free page %p\n", bmname(bitmap), page);
|
|
|
|
kfree(page);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check a page and, if necessary, allocate it (or hijack it if the alloc fails)
|
|
|
|
*
|
|
|
|
* 1) check to see if this page is allocated, if it's not then try to alloc
|
|
|
|
* 2) if the alloc fails, set the page's hijacked flag so we'll use the
|
|
|
|
* page pointer directly as a counter
|
|
|
|
*
|
|
|
|
* if we find our page, we increment the page's refcount so that it stays
|
|
|
|
* allocated while we're using it
|
|
|
|
*/
|
|
|
|
static int bitmap_checkpage(struct bitmap *bitmap, unsigned long page, int create)
|
|
|
|
{
|
|
|
|
unsigned char *mappage;
|
|
|
|
|
|
|
|
if (page >= bitmap->pages) {
|
|
|
|
printk(KERN_ALERT
|
|
|
|
"%s: invalid bitmap page request: %lu (> %lu)\n",
|
|
|
|
bmname(bitmap), page, bitmap->pages-1);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bitmap->bp[page].hijacked) /* it's hijacked, don't try to alloc */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (bitmap->bp[page].map) /* page is already allocated, just return */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!create)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
spin_unlock_irq(&bitmap->lock);
|
|
|
|
|
|
|
|
/* this page has not been allocated yet */
|
|
|
|
|
|
|
|
if ((mappage = bitmap_alloc_page(bitmap)) == NULL) {
|
|
|
|
PRINTK("%s: bitmap map page allocation failed, hijacking\n",
|
|
|
|
bmname(bitmap));
|
|
|
|
/* failed - set the hijacked flag so that we can use the
|
|
|
|
* pointer as a counter */
|
|
|
|
spin_lock_irq(&bitmap->lock);
|
|
|
|
if (!bitmap->bp[page].map)
|
|
|
|
bitmap->bp[page].hijacked = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* got a page */
|
|
|
|
|
|
|
|
spin_lock_irq(&bitmap->lock);
|
|
|
|
|
|
|
|
/* recheck the page */
|
|
|
|
|
|
|
|
if (bitmap->bp[page].map || bitmap->bp[page].hijacked) {
|
|
|
|
/* somebody beat us to getting the page */
|
|
|
|
bitmap_free_page(bitmap, mappage);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no page was in place and we have one, so install it */
|
|
|
|
|
|
|
|
memset(mappage, 0, PAGE_SIZE);
|
|
|
|
bitmap->bp[page].map = mappage;
|
|
|
|
bitmap->missing_pages--;
|
|
|
|
out:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* if page is completely empty, put it back on the free list, or dealloc it */
|
|
|
|
/* if page was hijacked, unmark the flag so it might get alloced next time */
|
|
|
|
/* Note: lock should be held when calling this */
|
2006-01-15 04:20:43 +07:00
|
|
|
static void bitmap_checkfree(struct bitmap *bitmap, unsigned long page)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
if (bitmap->bp[page].count) /* page is still busy */
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* page is no longer in use, it can be released */
|
|
|
|
|
|
|
|
if (bitmap->bp[page].hijacked) { /* page was hijacked, undo this now */
|
|
|
|
bitmap->bp[page].hijacked = 0;
|
|
|
|
bitmap->bp[page].map = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* normal case, free the page */
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* actually ... let's not. We will probably need the page again exactly when
|
|
|
|
* memory is tight and we are flusing to disk
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
ptr = bitmap->bp[page].map;
|
|
|
|
bitmap->bp[page].map = NULL;
|
|
|
|
bitmap->missing_pages++;
|
|
|
|
bitmap_free_page(bitmap, ptr);
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* bitmap file handling - read and write the bitmap file and its superblock
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* copy the pathname of a file to a buffer */
|
|
|
|
char *file_path(struct file *file, char *buf, int count)
|
|
|
|
{
|
|
|
|
struct dentry *d;
|
|
|
|
struct vfsmount *v;
|
|
|
|
|
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
|
|
|
|
2006-12-08 17:37:19 +07:00
|
|
|
d = file->f_path.dentry;
|
|
|
|
v = file->f_path.mnt;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
buf = d_path(d, v, buf, count);
|
|
|
|
|
|
|
|
return IS_ERR(buf) ? NULL : buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* basic page I/O operations
|
|
|
|
*/
|
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
/* IO operations when bitmap is stored near all superblocks */
|
|
|
|
static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long index)
|
|
|
|
{
|
|
|
|
/* choose a good rdev and read the page from there */
|
|
|
|
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
struct list_head *tmp;
|
|
|
|
struct page *page = alloc_page(GFP_KERNEL);
|
|
|
|
sector_t target;
|
|
|
|
|
|
|
|
if (!page)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2005-09-10 06:23:52 +07:00
|
|
|
ITERATE_RDEV(mddev, rdev, tmp) {
|
2005-11-09 12:39:31 +07:00
|
|
|
if (! test_bit(In_sync, &rdev->flags)
|
|
|
|
|| test_bit(Faulty, &rdev->flags))
|
2005-09-10 06:23:52 +07:00
|
|
|
continue;
|
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
target = (rdev->sb_offset << 1) + offset + index * (PAGE_SIZE/512);
|
|
|
|
|
2005-09-10 06:23:52 +07:00
|
|
|
if (sync_page_io(rdev->bdev, target, PAGE_SIZE, page, READ)) {
|
|
|
|
page->index = index;
|
2006-06-26 14:27:49 +07:00
|
|
|
attach_page_buffers(page, NULL); /* so that free_buffer will
|
|
|
|
* quietly no-op */
|
2005-09-10 06:23:52 +07:00
|
|
|
return page;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ERR_PTR(-EIO);
|
2005-06-22 07:17:27 +07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-05-24 03:58:10 +07:00
|
|
|
static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait)
|
2005-06-22 07:17:27 +07:00
|
|
|
{
|
|
|
|
mdk_rdev_t *rdev;
|
|
|
|
struct list_head *tmp;
|
2007-05-24 03:58:10 +07:00
|
|
|
mddev_t *mddev = bitmap->mddev;
|
2005-06-22 07:17:27 +07:00
|
|
|
|
|
|
|
ITERATE_RDEV(mddev, rdev, tmp)
|
2005-11-09 12:39:31 +07:00
|
|
|
if (test_bit(In_sync, &rdev->flags)
|
2007-05-24 03:58:10 +07:00
|
|
|
&& !test_bit(Faulty, &rdev->flags)) {
|
|
|
|
int size = PAGE_SIZE;
|
|
|
|
if (page->index == bitmap->file_pages-1)
|
|
|
|
size = roundup(bitmap->last_page_size,
|
|
|
|
bdev_hardsect_size(rdev->bdev));
|
2007-07-17 18:06:12 +07:00
|
|
|
/* Just make sure we aren't corrupting data or
|
|
|
|
* metadata
|
|
|
|
*/
|
|
|
|
if (bitmap->offset < 0) {
|
|
|
|
/* DATA BITMAP METADATA */
|
|
|
|
if (bitmap->offset
|
2007-10-23 10:45:11 +07:00
|
|
|
+ (long)(page->index * (PAGE_SIZE/512))
|
2007-07-17 18:06:12 +07:00
|
|
|
+ size/512 > 0)
|
|
|
|
/* bitmap runs in to metadata */
|
|
|
|
return -EINVAL;
|
|
|
|
if (rdev->data_offset + mddev->size*2
|
|
|
|
> rdev->sb_offset*2 + bitmap->offset)
|
|
|
|
/* data runs in to bitmap */
|
|
|
|
return -EINVAL;
|
|
|
|
} else if (rdev->sb_offset*2 < rdev->data_offset) {
|
|
|
|
/* METADATA BITMAP DATA */
|
|
|
|
if (rdev->sb_offset*2
|
|
|
|
+ bitmap->offset
|
|
|
|
+ page->index*(PAGE_SIZE/512) + size/512
|
|
|
|
> rdev->data_offset)
|
|
|
|
/* bitmap runs in to data */
|
|
|
|
return -EINVAL;
|
|
|
|
} else {
|
|
|
|
/* DATA METADATA BITMAP - no problems */
|
|
|
|
}
|
2005-06-22 07:17:27 +07:00
|
|
|
md_super_write(mddev, rdev,
|
2007-05-24 03:58:10 +07:00
|
|
|
(rdev->sb_offset<<1) + bitmap->offset
|
2005-06-22 07:17:27 +07:00
|
|
|
+ page->index * (PAGE_SIZE/512),
|
2007-05-24 03:58:10 +07:00
|
|
|
size,
|
2005-06-22 07:17:27 +07:00
|
|
|
page);
|
2007-05-24 03:58:10 +07:00
|
|
|
}
|
2005-06-22 07:17:27 +07:00
|
|
|
|
|
|
|
if (wait)
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
md_super_wait(mddev);
|
2005-06-22 07:17:27 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
static void bitmap_file_kick(struct bitmap *bitmap);
|
2005-06-22 07:17:14 +07:00
|
|
|
/*
|
2005-06-22 07:17:27 +07:00
|
|
|
* write out a page to a file
|
2005-06-22 07:17:14 +07:00
|
|
|
*/
|
2007-07-17 18:06:13 +07:00
|
|
|
static void write_page(struct bitmap *bitmap, struct page *page, int wait)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
2006-06-26 14:27:48 +07:00
|
|
|
struct buffer_head *bh;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2007-07-17 18:06:12 +07:00
|
|
|
if (bitmap->file == NULL) {
|
|
|
|
switch (write_sb_page(bitmap, page, wait)) {
|
|
|
|
case -EINVAL:
|
|
|
|
bitmap->flags |= BITMAP_WRITE_ERROR;
|
|
|
|
}
|
2007-07-17 18:06:13 +07:00
|
|
|
} else {
|
2005-06-22 07:17:27 +07:00
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
bh = page_buffers(page);
|
2006-01-06 15:20:45 +07:00
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
while (bh && bh->b_blocknr) {
|
|
|
|
atomic_inc(&bitmap->pending_writes);
|
|
|
|
set_buffer_locked(bh);
|
|
|
|
set_buffer_mapped(bh);
|
|
|
|
submit_bh(WRITE, bh);
|
|
|
|
bh = bh->b_this_page;
|
|
|
|
}
|
2006-06-26 14:27:48 +07:00
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
if (wait) {
|
|
|
|
wait_event(bitmap->write_wait,
|
|
|
|
atomic_read(&bitmap->pending_writes)==0);
|
|
|
|
}
|
2005-06-22 07:17:29 +07:00
|
|
|
}
|
2007-07-17 18:06:13 +07:00
|
|
|
if (bitmap->flags & BITMAP_WRITE_ERROR)
|
|
|
|
bitmap_file_kick(bitmap);
|
2006-06-26 14:27:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void end_bitmap_write(struct buffer_head *bh, int uptodate)
|
|
|
|
{
|
|
|
|
struct bitmap *bitmap = bh->b_private;
|
|
|
|
unsigned long flags;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
if (!uptodate) {
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
|
|
|
bitmap->flags |= BITMAP_WRITE_ERROR;
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
2006-06-26 14:27:48 +07:00
|
|
|
if (atomic_dec_and_test(&bitmap->pending_writes))
|
|
|
|
wake_up(&bitmap->write_wait);
|
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
/* copied from buffer.c */
|
|
|
|
static void
|
|
|
|
__clear_page_buffers(struct page *page)
|
|
|
|
{
|
|
|
|
ClearPagePrivate(page);
|
|
|
|
set_page_private(page, 0);
|
|
|
|
page_cache_release(page);
|
|
|
|
}
|
|
|
|
static void free_buffers(struct page *page)
|
|
|
|
{
|
|
|
|
struct buffer_head *bh = page_buffers(page);
|
2005-06-22 07:17:21 +07:00
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
while (bh) {
|
|
|
|
struct buffer_head *next = bh->b_this_page;
|
|
|
|
free_buffer_head(bh);
|
|
|
|
bh = next;
|
2005-06-22 07:17:21 +07:00
|
|
|
}
|
2006-06-26 14:27:48 +07:00
|
|
|
__clear_page_buffers(page);
|
|
|
|
put_page(page);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
/* read a page from a file.
|
|
|
|
* We both read the page, and attach buffers to the page to record the
|
|
|
|
* address of each block (using bmap). These addresses will be used
|
|
|
|
* to write the block later, completely bypassing the filesystem.
|
|
|
|
* This usage is similar to how swap files are handled, and allows us
|
|
|
|
* to write to a file with no concerns of memory allocation failing.
|
|
|
|
*/
|
2005-06-22 07:17:14 +07:00
|
|
|
static struct page *read_page(struct file *file, unsigned long index,
|
2006-06-26 14:27:48 +07:00
|
|
|
struct bitmap *bitmap,
|
|
|
|
unsigned long count)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
struct page *page = NULL;
|
2006-12-08 17:37:19 +07:00
|
|
|
struct inode *inode = file->f_path.dentry->d_inode;
|
2006-06-26 14:27:48 +07:00
|
|
|
struct buffer_head *bh;
|
|
|
|
sector_t block;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2006-01-06 15:20:31 +07:00
|
|
|
PRINTK("read bitmap file (%dB @ %Lu)\n", (int)PAGE_SIZE,
|
|
|
|
(unsigned long long)index << PAGE_SHIFT);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
page = alloc_page(GFP_KERNEL);
|
|
|
|
if (!page)
|
|
|
|
page = ERR_PTR(-ENOMEM);
|
2005-06-22 07:17:14 +07:00
|
|
|
if (IS_ERR(page))
|
|
|
|
goto out;
|
2006-06-26 14:27:48 +07:00
|
|
|
|
|
|
|
bh = alloc_page_buffers(page, 1<<inode->i_blkbits, 0);
|
|
|
|
if (!bh) {
|
2006-01-06 15:20:31 +07:00
|
|
|
put_page(page);
|
2006-06-26 14:27:48 +07:00
|
|
|
page = ERR_PTR(-ENOMEM);
|
2005-06-22 07:17:14 +07:00
|
|
|
goto out;
|
|
|
|
}
|
2006-06-26 14:27:48 +07:00
|
|
|
attach_page_buffers(page, bh);
|
|
|
|
block = index << (PAGE_SHIFT - inode->i_blkbits);
|
|
|
|
while (bh) {
|
|
|
|
if (count == 0)
|
|
|
|
bh->b_blocknr = 0;
|
|
|
|
else {
|
|
|
|
bh->b_blocknr = bmap(inode, block);
|
|
|
|
if (bh->b_blocknr == 0) {
|
|
|
|
/* Cannot use this file! */
|
|
|
|
free_buffers(page);
|
|
|
|
page = ERR_PTR(-EINVAL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
bh->b_bdev = inode->i_sb->s_bdev;
|
|
|
|
if (count < (1<<inode->i_blkbits))
|
|
|
|
count = 0;
|
|
|
|
else
|
|
|
|
count -= (1<<inode->i_blkbits);
|
|
|
|
|
|
|
|
bh->b_end_io = end_bitmap_write;
|
|
|
|
bh->b_private = bitmap;
|
2006-06-26 14:27:49 +07:00
|
|
|
atomic_inc(&bitmap->pending_writes);
|
|
|
|
set_buffer_locked(bh);
|
|
|
|
set_buffer_mapped(bh);
|
|
|
|
submit_bh(READ, bh);
|
2006-06-26 14:27:48 +07:00
|
|
|
}
|
|
|
|
block++;
|
|
|
|
bh = bh->b_this_page;
|
|
|
|
}
|
|
|
|
page->index = index;
|
2006-06-26 14:27:49 +07:00
|
|
|
|
|
|
|
wait_event(bitmap->write_wait,
|
|
|
|
atomic_read(&bitmap->pending_writes)==0);
|
|
|
|
if (bitmap->flags & BITMAP_WRITE_ERROR) {
|
|
|
|
free_buffers(page);
|
|
|
|
page = ERR_PTR(-EIO);
|
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
out:
|
|
|
|
if (IS_ERR(page))
|
|
|
|
printk(KERN_ALERT "md: bitmap read error: (%dB @ %Lu): %ld\n",
|
2006-01-06 15:20:31 +07:00
|
|
|
(int)PAGE_SIZE,
|
|
|
|
(unsigned long long)index << PAGE_SHIFT,
|
2005-06-22 07:17:14 +07:00
|
|
|
PTR_ERR(page));
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* bitmap file superblock operations
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* update the event counter and sync the superblock to disk */
|
2007-07-17 18:06:13 +07:00
|
|
|
void bitmap_update_sb(struct bitmap *bitmap)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
bitmap_super_t *sb;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (!bitmap || !bitmap->mddev) /* no bitmap for this array */
|
2007-07-17 18:06:13 +07:00
|
|
|
return;
|
2005-06-22 07:17:14 +07:00
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
|
|
|
if (!bitmap->sb_page) { /* no superblock */
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2007-07-17 18:06:13 +07:00
|
|
|
return;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2006-01-06 15:20:34 +07:00
|
|
|
sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0);
|
2005-06-22 07:17:14 +07:00
|
|
|
sb->events = cpu_to_le64(bitmap->mddev->events);
|
|
|
|
if (!bitmap->mddev->degraded)
|
|
|
|
sb->events_cleared = cpu_to_le64(bitmap->mddev->events);
|
2006-01-06 15:20:34 +07:00
|
|
|
kunmap_atomic(sb, KM_USER0);
|
2007-07-17 18:06:13 +07:00
|
|
|
write_page(bitmap, bitmap->sb_page, 1);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* print out the bitmap file superblock */
|
|
|
|
void bitmap_print_sb(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
bitmap_super_t *sb;
|
|
|
|
|
|
|
|
if (!bitmap || !bitmap->sb_page)
|
|
|
|
return;
|
2006-01-06 15:20:34 +07:00
|
|
|
sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0);
|
2005-06-22 07:17:14 +07:00
|
|
|
printk(KERN_DEBUG "%s: bitmap file superblock:\n", bmname(bitmap));
|
2005-06-22 07:17:20 +07:00
|
|
|
printk(KERN_DEBUG " magic: %08x\n", le32_to_cpu(sb->magic));
|
|
|
|
printk(KERN_DEBUG " version: %d\n", le32_to_cpu(sb->version));
|
|
|
|
printk(KERN_DEBUG " uuid: %08x.%08x.%08x.%08x\n",
|
2005-06-22 07:17:14 +07:00
|
|
|
*(__u32 *)(sb->uuid+0),
|
|
|
|
*(__u32 *)(sb->uuid+4),
|
|
|
|
*(__u32 *)(sb->uuid+8),
|
|
|
|
*(__u32 *)(sb->uuid+12));
|
2005-06-22 07:17:20 +07:00
|
|
|
printk(KERN_DEBUG " events: %llu\n",
|
2005-06-22 07:17:14 +07:00
|
|
|
(unsigned long long) le64_to_cpu(sb->events));
|
2005-06-22 07:17:20 +07:00
|
|
|
printk(KERN_DEBUG "events cleared: %llu\n",
|
2005-06-22 07:17:14 +07:00
|
|
|
(unsigned long long) le64_to_cpu(sb->events_cleared));
|
2005-06-22 07:17:20 +07:00
|
|
|
printk(KERN_DEBUG " state: %08x\n", le32_to_cpu(sb->state));
|
|
|
|
printk(KERN_DEBUG " chunksize: %d B\n", le32_to_cpu(sb->chunksize));
|
|
|
|
printk(KERN_DEBUG " daemon sleep: %ds\n", le32_to_cpu(sb->daemon_sleep));
|
|
|
|
printk(KERN_DEBUG " sync size: %llu KB\n",
|
|
|
|
(unsigned long long)le64_to_cpu(sb->sync_size)/2);
|
2005-09-10 06:23:47 +07:00
|
|
|
printk(KERN_DEBUG "max write behind: %d\n", le32_to_cpu(sb->write_behind));
|
2006-01-06 15:20:34 +07:00
|
|
|
kunmap_atomic(sb, KM_USER0);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* read the superblock from the bitmap file and initialize some bitmap fields */
|
|
|
|
static int bitmap_read_sb(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
char *reason = NULL;
|
|
|
|
bitmap_super_t *sb;
|
2005-09-10 06:23:47 +07:00
|
|
|
unsigned long chunksize, daemon_sleep, write_behind;
|
2005-06-22 07:17:14 +07:00
|
|
|
unsigned long long events;
|
|
|
|
int err = -EINVAL;
|
|
|
|
|
|
|
|
/* page 0 is the superblock, read it... */
|
2007-01-26 15:57:03 +07:00
|
|
|
if (bitmap->file) {
|
|
|
|
loff_t isize = i_size_read(bitmap->file->f_mapping->host);
|
|
|
|
int bytes = isize > PAGE_SIZE ? PAGE_SIZE : isize;
|
|
|
|
|
|
|
|
bitmap->sb_page = read_page(bitmap->file, 0, bitmap, bytes);
|
|
|
|
} else {
|
2005-06-22 07:17:27 +07:00
|
|
|
bitmap->sb_page = read_sb_page(bitmap->mddev, bitmap->offset, 0);
|
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
if (IS_ERR(bitmap->sb_page)) {
|
|
|
|
err = PTR_ERR(bitmap->sb_page);
|
|
|
|
bitmap->sb_page = NULL;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:20:34 +07:00
|
|
|
sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
chunksize = le32_to_cpu(sb->chunksize);
|
|
|
|
daemon_sleep = le32_to_cpu(sb->daemon_sleep);
|
2005-09-10 06:23:47 +07:00
|
|
|
write_behind = le32_to_cpu(sb->write_behind);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
/* verify that the bitmap-specific fields are valid */
|
|
|
|
if (sb->magic != cpu_to_le32(BITMAP_MAGIC))
|
|
|
|
reason = "bad magic";
|
2005-11-09 12:39:32 +07:00
|
|
|
else if (le32_to_cpu(sb->version) < BITMAP_MAJOR_LO ||
|
|
|
|
le32_to_cpu(sb->version) > BITMAP_MAJOR_HI)
|
2005-06-22 07:17:14 +07:00
|
|
|
reason = "unrecognized superblock version";
|
2006-01-06 15:20:39 +07:00
|
|
|
else if (chunksize < PAGE_SIZE)
|
|
|
|
reason = "bitmap chunksize too small";
|
2005-06-22 07:17:14 +07:00
|
|
|
else if ((1 << ffz(~chunksize)) != chunksize)
|
|
|
|
reason = "bitmap chunksize not a power of 2";
|
2006-01-06 15:20:39 +07:00
|
|
|
else if (daemon_sleep < 1 || daemon_sleep > MAX_SCHEDULE_TIMEOUT / HZ)
|
|
|
|
reason = "daemon sleep period out of range";
|
2005-09-10 06:23:47 +07:00
|
|
|
else if (write_behind > COUNTER_MAX)
|
|
|
|
reason = "write-behind limit out of range (0 - 16383)";
|
2005-06-22 07:17:14 +07:00
|
|
|
if (reason) {
|
|
|
|
printk(KERN_INFO "%s: invalid bitmap file superblock: %s\n",
|
|
|
|
bmname(bitmap), reason);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* keep the array size field of the bitmap superblock up to date */
|
|
|
|
sb->sync_size = cpu_to_le64(bitmap->mddev->resync_max_sectors);
|
|
|
|
|
|
|
|
if (!bitmap->mddev->persistent)
|
|
|
|
goto success;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if we have a persistent array superblock, compare the
|
|
|
|
* bitmap's UUID and event counter to the mddev's
|
|
|
|
*/
|
|
|
|
if (memcmp(sb->uuid, bitmap->mddev->uuid, 16)) {
|
|
|
|
printk(KERN_INFO "%s: bitmap superblock UUID mismatch\n",
|
|
|
|
bmname(bitmap));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
events = le64_to_cpu(sb->events);
|
|
|
|
if (events < bitmap->mddev->events) {
|
|
|
|
printk(KERN_INFO "%s: bitmap file is out of date (%llu < %llu) "
|
|
|
|
"-- forcing full recovery\n", bmname(bitmap), events,
|
|
|
|
(unsigned long long) bitmap->mddev->events);
|
2006-10-22 00:24:09 +07:00
|
|
|
sb->state |= cpu_to_le32(BITMAP_STALE);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
success:
|
|
|
|
/* assign fields using values from superblock */
|
|
|
|
bitmap->chunksize = chunksize;
|
|
|
|
bitmap->daemon_sleep = daemon_sleep;
|
2005-09-10 06:23:49 +07:00
|
|
|
bitmap->daemon_lastrun = jiffies;
|
2005-09-10 06:23:47 +07:00
|
|
|
bitmap->max_write_behind = write_behind;
|
2006-10-22 00:24:09 +07:00
|
|
|
bitmap->flags |= le32_to_cpu(sb->state);
|
2005-11-09 12:39:32 +07:00
|
|
|
if (le32_to_cpu(sb->version) == BITMAP_MAJOR_HOSTENDIAN)
|
|
|
|
bitmap->flags |= BITMAP_HOSTENDIAN;
|
2005-06-22 07:17:14 +07:00
|
|
|
bitmap->events_cleared = le64_to_cpu(sb->events_cleared);
|
2006-10-22 00:24:09 +07:00
|
|
|
if (sb->state & cpu_to_le32(BITMAP_STALE))
|
2005-09-10 06:23:44 +07:00
|
|
|
bitmap->events_cleared = bitmap->mddev->events;
|
2005-06-22 07:17:14 +07:00
|
|
|
err = 0;
|
|
|
|
out:
|
2006-01-06 15:20:34 +07:00
|
|
|
kunmap_atomic(sb, KM_USER0);
|
2005-06-22 07:17:14 +07:00
|
|
|
if (err)
|
|
|
|
bitmap_print_sb(bitmap);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum bitmap_mask_op {
|
|
|
|
MASK_SET,
|
|
|
|
MASK_UNSET
|
|
|
|
};
|
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
/* record the state of the bitmap in the superblock. Return the old value */
|
|
|
|
static int bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits,
|
|
|
|
enum bitmap_mask_op op)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
bitmap_super_t *sb;
|
|
|
|
unsigned long flags;
|
2007-07-17 18:06:13 +07:00
|
|
|
int old;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
2006-03-25 18:07:51 +07:00
|
|
|
if (!bitmap->sb_page) { /* can't set the state */
|
2005-06-22 07:17:14 +07:00
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2007-07-17 18:06:13 +07:00
|
|
|
return 0;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2006-01-06 15:20:34 +07:00
|
|
|
sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0);
|
2007-07-17 18:06:13 +07:00
|
|
|
old = le32_to_cpu(sb->state) & bits;
|
2005-06-22 07:17:14 +07:00
|
|
|
switch (op) {
|
2006-10-22 00:24:09 +07:00
|
|
|
case MASK_SET: sb->state |= cpu_to_le32(bits);
|
2005-06-22 07:17:14 +07:00
|
|
|
break;
|
2006-10-22 00:24:09 +07:00
|
|
|
case MASK_UNSET: sb->state &= cpu_to_le32(~bits);
|
2005-06-22 07:17:14 +07:00
|
|
|
break;
|
|
|
|
default: BUG();
|
|
|
|
}
|
2006-01-06 15:20:34 +07:00
|
|
|
kunmap_atomic(sb, KM_USER0);
|
2007-07-17 18:06:13 +07:00
|
|
|
return old;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* general bitmap file operations
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* calculate the index of the page that contains this bit */
|
|
|
|
static inline unsigned long file_page_index(unsigned long chunk)
|
|
|
|
{
|
|
|
|
return CHUNK_BIT_OFFSET(chunk) >> PAGE_BIT_SHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate the (bit) offset of this bit within a page */
|
|
|
|
static inline unsigned long file_page_offset(unsigned long chunk)
|
|
|
|
{
|
|
|
|
return CHUNK_BIT_OFFSET(chunk) & (PAGE_BITS - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* return a pointer to the page in the filemap that contains the given bit
|
|
|
|
*
|
|
|
|
* this lookup is complicated by the fact that the bitmap sb might be exactly
|
|
|
|
* 1 page (e.g., x86) or less than 1 page -- so the bitmap might start on page
|
|
|
|
* 0 or page 1
|
|
|
|
*/
|
|
|
|
static inline struct page *filemap_get_page(struct bitmap *bitmap,
|
|
|
|
unsigned long chunk)
|
|
|
|
{
|
2006-10-03 15:15:49 +07:00
|
|
|
if (file_page_index(chunk) >= bitmap->file_pages) return NULL;
|
2005-06-22 07:17:14 +07:00
|
|
|
return bitmap->filemap[file_page_index(chunk) - file_page_index(0)];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void bitmap_file_unmap(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
struct page **map, *sb_page;
|
|
|
|
unsigned long *attr;
|
|
|
|
int pages;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
|
|
|
map = bitmap->filemap;
|
|
|
|
bitmap->filemap = NULL;
|
|
|
|
attr = bitmap->filemap_attr;
|
|
|
|
bitmap->filemap_attr = NULL;
|
|
|
|
pages = bitmap->file_pages;
|
|
|
|
bitmap->file_pages = 0;
|
|
|
|
sb_page = bitmap->sb_page;
|
|
|
|
bitmap->sb_page = NULL;
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
|
|
|
|
while (pages--)
|
|
|
|
if (map[pages]->index != 0) /* 0 is sb_page, release it below */
|
2006-06-26 14:27:48 +07:00
|
|
|
free_buffers(map[pages]);
|
2005-06-22 07:17:14 +07:00
|
|
|
kfree(map);
|
|
|
|
kfree(attr);
|
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
if (sb_page)
|
|
|
|
free_buffers(sb_page);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bitmap_file_put(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
struct file *file;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
|
|
|
file = bitmap->file;
|
|
|
|
bitmap->file = NULL;
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
if (file)
|
|
|
|
wait_event(bitmap->write_wait,
|
|
|
|
atomic_read(&bitmap->pending_writes)==0);
|
2005-06-22 07:17:14 +07:00
|
|
|
bitmap_file_unmap(bitmap);
|
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
if (file) {
|
2006-12-08 17:37:19 +07:00
|
|
|
struct inode *inode = file->f_path.dentry->d_inode;
|
2007-02-10 16:45:39 +07:00
|
|
|
invalidate_mapping_pages(inode->i_mapping, 0, -1);
|
2005-06-22 07:17:14 +07:00
|
|
|
fput(file);
|
2006-06-26 14:27:48 +07:00
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* bitmap_file_kick - if an error occurs while manipulating the bitmap file
|
|
|
|
* then it is no longer reliable, so we stop using it and we mark the file
|
|
|
|
* as failed in the superblock
|
|
|
|
*/
|
|
|
|
static void bitmap_file_kick(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
char *path, *ptr = NULL;
|
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
if (bitmap_mask_state(bitmap, BITMAP_STALE, MASK_SET) == 0) {
|
|
|
|
bitmap_update_sb(bitmap);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
if (bitmap->file) {
|
|
|
|
path = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
|
|
|
if (path)
|
|
|
|
ptr = file_path(bitmap->file, path, PAGE_SIZE);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
printk(KERN_ALERT
|
|
|
|
"%s: kicking failed bitmap file %s from array!\n",
|
|
|
|
bmname(bitmap), ptr ? ptr : "");
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
kfree(path);
|
|
|
|
} else
|
|
|
|
printk(KERN_ALERT
|
|
|
|
"%s: disabling internal bitmap due to errors\n",
|
|
|
|
bmname(bitmap));
|
2005-06-22 07:17:27 +07:00
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
bitmap_file_put(bitmap);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum bitmap_page_attr {
|
2006-06-26 14:27:45 +07:00
|
|
|
BITMAP_PAGE_DIRTY = 0, // there are set bits that need to be synced
|
|
|
|
BITMAP_PAGE_CLEAN = 1, // there are bits that might need to be cleared
|
|
|
|
BITMAP_PAGE_NEEDWRITE=2, // there are cleared bits that need to be synced
|
2005-06-22 07:17:14 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
|
|
|
|
enum bitmap_page_attr attr)
|
|
|
|
{
|
2006-06-26 14:27:45 +07:00
|
|
|
__set_bit((page->index<<2) + attr, bitmap->filemap_attr);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
|
|
|
|
enum bitmap_page_attr attr)
|
|
|
|
{
|
2006-06-26 14:27:45 +07:00
|
|
|
__clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
2006-06-26 14:27:45 +07:00
|
|
|
static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
|
|
|
|
enum bitmap_page_attr attr)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
2006-06-26 14:27:45 +07:00
|
|
|
return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* bitmap_file_set_bit -- called before performing a write to the md device
|
|
|
|
* to set (and eventually sync) a particular bit in the bitmap file
|
|
|
|
*
|
|
|
|
* we set the bit immediately, then we record the page number so that
|
|
|
|
* when an unplug occurs, we can flush the dirty pages out to disk
|
|
|
|
*/
|
|
|
|
static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
|
|
|
|
{
|
|
|
|
unsigned long bit;
|
|
|
|
struct page *page;
|
|
|
|
void *kaddr;
|
|
|
|
unsigned long chunk = block >> CHUNK_BLOCK_SHIFT(bitmap);
|
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
if (!bitmap->filemap) {
|
2005-06-22 07:17:14 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
page = filemap_get_page(bitmap, chunk);
|
2006-10-03 15:15:49 +07:00
|
|
|
if (!page) return;
|
2005-06-22 07:17:14 +07:00
|
|
|
bit = file_page_offset(chunk);
|
|
|
|
|
|
|
|
/* set the bit */
|
|
|
|
kaddr = kmap_atomic(page, KM_USER0);
|
2005-11-09 12:39:32 +07:00
|
|
|
if (bitmap->flags & BITMAP_HOSTENDIAN)
|
|
|
|
set_bit(bit, kaddr);
|
|
|
|
else
|
|
|
|
ext2_set_bit(bit, kaddr);
|
2005-06-22 07:17:14 +07:00
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
|
|
|
PRINTK("set file bit %lu page %lu\n", bit, page->index);
|
|
|
|
|
|
|
|
/* record page number so it gets flushed to disk when unplug occurs */
|
|
|
|
set_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this gets called when the md device is ready to unplug its underlying
|
|
|
|
* (slave) device queues -- before we let any writes go down, we need to
|
|
|
|
* sync the dirty pages of the bitmap file to disk */
|
2007-07-17 18:06:13 +07:00
|
|
|
void bitmap_unplug(struct bitmap *bitmap)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
2006-06-26 14:27:45 +07:00
|
|
|
unsigned long i, flags;
|
|
|
|
int dirty, need_write;
|
2005-06-22 07:17:14 +07:00
|
|
|
struct page *page;
|
|
|
|
int wait = 0;
|
|
|
|
|
|
|
|
if (!bitmap)
|
2007-07-17 18:06:13 +07:00
|
|
|
return;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
/* look at each page to see if there are any set bits that need to be
|
|
|
|
* flushed out to disk */
|
|
|
|
for (i = 0; i < bitmap->file_pages; i++) {
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
2005-06-22 07:17:27 +07:00
|
|
|
if (!bitmap->filemap) {
|
2005-06-22 07:17:14 +07:00
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2007-07-17 18:06:13 +07:00
|
|
|
return;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
page = bitmap->filemap[i];
|
2006-06-26 14:27:45 +07:00
|
|
|
dirty = test_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
|
|
|
|
need_write = test_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
|
2005-06-22 07:17:14 +07:00
|
|
|
clear_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
|
|
|
|
clear_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
|
2006-06-26 14:27:45 +07:00
|
|
|
if (dirty)
|
2005-06-22 07:17:14 +07:00
|
|
|
wait = 1;
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
|
2006-06-26 14:27:48 +07:00
|
|
|
if (dirty | need_write)
|
2007-07-17 18:06:13 +07:00
|
|
|
write_page(bitmap, page, 0);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
if (wait) { /* if any writes were performed, we need to wait on them */
|
2006-06-26 14:27:44 +07:00
|
|
|
if (bitmap->file)
|
2006-06-26 14:27:48 +07:00
|
|
|
wait_event(bitmap->write_wait,
|
|
|
|
atomic_read(&bitmap->pending_writes)==0);
|
2006-06-26 14:27:44 +07:00
|
|
|
else
|
[PATCH] md: support BIO_RW_BARRIER for md/raid1
We can only accept BARRIER requests if all slaves handle
barriers, and that can, of course, change with time....
So we keep track of whether the whole array seems safe for barriers,
and also whether each individual rdev handles barriers.
We initially assumes barriers are OK.
When writing the superblock we try a barrier, and if that fails, we flag
things for no-barriers. This will usually clear the flags fairly quickly.
If writing the superblock finds that BIO_RW_BARRIER is -ENOTSUPP, we need to
resubmit, so introduce function "md_super_wait" which waits for requests to
finish, and retries ENOTSUPP requests without the barrier flag.
When writing the real raid1, write requests which were BIO_RW_BARRIER but
which aresn't supported need to be retried. So raid1d is enhanced to do this,
and when any bio write completes (i.e. no retry needed) we remove it from the
r1bio, so that devices needing retry are easy to find.
We should hardly ever get -ENOTSUPP errors when writing data to the raid.
It should only happen if:
1/ the device used to support BARRIER, but now doesn't. Few devices
change like this, though raid1 can!
or
2/ the array has no persistent superblock, so there was no opportunity to
pre-test for barriers when writing the superblock.
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 12:39:34 +07:00
|
|
|
md_super_wait(bitmap->mddev);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
2006-06-26 14:27:48 +07:00
|
|
|
if (bitmap->flags & BITMAP_WRITE_ERROR)
|
|
|
|
bitmap_file_kick(bitmap);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
2005-09-10 06:23:44 +07:00
|
|
|
static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int needed);
|
2005-06-22 07:17:14 +07:00
|
|
|
/* * bitmap_init_from_disk -- called at bitmap_create time to initialize
|
|
|
|
* the in-memory bitmap from the on-disk bitmap -- also, sets up the
|
|
|
|
* memory mapping of the bitmap file
|
|
|
|
* Special cases:
|
|
|
|
* if there's no bitmap file, or if the bitmap file had been
|
|
|
|
* previously kicked from the array, we mark all the bits as
|
|
|
|
* 1's in order to cause a full resync.
|
2005-09-10 06:23:44 +07:00
|
|
|
*
|
|
|
|
* We ignore all bits for sectors that end earlier than 'start'.
|
|
|
|
* This is used when reading an out-of-date bitmap...
|
2005-06-22 07:17:14 +07:00
|
|
|
*/
|
2005-09-10 06:23:44 +07:00
|
|
|
static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
unsigned long i, chunks, index, oldindex, bit;
|
|
|
|
struct page *page = NULL, *oldpage = NULL;
|
|
|
|
unsigned long num_pages, bit_cnt = 0;
|
|
|
|
struct file *file;
|
2006-06-26 14:27:48 +07:00
|
|
|
unsigned long bytes, offset;
|
2005-06-22 07:17:14 +07:00
|
|
|
int outofdate;
|
|
|
|
int ret = -ENOSPC;
|
2006-01-06 15:20:34 +07:00
|
|
|
void *paddr;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
chunks = bitmap->chunks;
|
|
|
|
file = bitmap->file;
|
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
BUG_ON(!file && !bitmap->offset);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2005-07-28 01:45:17 +07:00
|
|
|
#ifdef INJECT_FAULTS_3
|
2005-06-22 07:17:14 +07:00
|
|
|
outofdate = 1;
|
|
|
|
#else
|
|
|
|
outofdate = bitmap->flags & BITMAP_STALE;
|
|
|
|
#endif
|
|
|
|
if (outofdate)
|
|
|
|
printk(KERN_INFO "%s: bitmap file is out of date, doing full "
|
|
|
|
"recovery\n", bmname(bitmap));
|
|
|
|
|
|
|
|
bytes = (chunks + 7) / 8;
|
2005-06-22 07:17:17 +07:00
|
|
|
|
2005-06-22 07:17:18 +07:00
|
|
|
num_pages = (bytes + sizeof(bitmap_super_t) + PAGE_SIZE - 1) / PAGE_SIZE;
|
2005-06-22 07:17:17 +07:00
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
if (file && i_size_read(file->f_mapping->host) < bytes + sizeof(bitmap_super_t)) {
|
2005-06-22 07:17:14 +07:00
|
|
|
printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n",
|
|
|
|
bmname(bitmap),
|
|
|
|
(unsigned long) i_size_read(file->f_mapping->host),
|
|
|
|
bytes + sizeof(bitmap_super_t));
|
2007-07-17 18:06:13 +07:00
|
|
|
goto err;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
2005-06-22 07:17:17 +07:00
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
bitmap->filemap = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL);
|
2005-06-22 07:17:17 +07:00
|
|
|
if (!bitmap->filemap)
|
2007-07-17 18:06:13 +07:00
|
|
|
goto err;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2006-06-26 14:27:45 +07:00
|
|
|
/* We need 4 bits per page, rounded up to a multiple of sizeof(unsigned long) */
|
|
|
|
bitmap->filemap_attr = kzalloc(
|
2007-04-12 13:28:44 +07:00
|
|
|
roundup( DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)),
|
2006-06-26 14:27:45 +07:00
|
|
|
GFP_KERNEL);
|
2005-06-22 07:17:17 +07:00
|
|
|
if (!bitmap->filemap_attr)
|
2007-07-17 18:06:13 +07:00
|
|
|
goto err;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
oldindex = ~0L;
|
|
|
|
|
|
|
|
for (i = 0; i < chunks; i++) {
|
2005-11-09 12:39:32 +07:00
|
|
|
int b;
|
2005-06-22 07:17:14 +07:00
|
|
|
index = file_page_index(i);
|
|
|
|
bit = file_page_offset(i);
|
|
|
|
if (index != oldindex) { /* this is a new page, read it in */
|
2006-06-26 14:27:48 +07:00
|
|
|
int count;
|
2005-06-22 07:17:14 +07:00
|
|
|
/* unmap the old page, we're done with it */
|
2006-06-26 14:27:48 +07:00
|
|
|
if (index == num_pages-1)
|
2007-01-26 15:57:03 +07:00
|
|
|
count = bytes + sizeof(bitmap_super_t)
|
|
|
|
- index * PAGE_SIZE;
|
2006-06-26 14:27:48 +07:00
|
|
|
else
|
|
|
|
count = PAGE_SIZE;
|
2005-06-22 07:17:14 +07:00
|
|
|
if (index == 0) {
|
|
|
|
/*
|
|
|
|
* if we're here then the superblock page
|
|
|
|
* contains some bits (PAGE_SIZE != sizeof sb)
|
|
|
|
* we've already read it in, so just use it
|
|
|
|
*/
|
|
|
|
page = bitmap->sb_page;
|
|
|
|
offset = sizeof(bitmap_super_t);
|
2005-06-22 07:17:27 +07:00
|
|
|
} else if (file) {
|
2006-06-26 14:27:48 +07:00
|
|
|
page = read_page(file, index, bitmap, count);
|
2005-06-22 07:17:27 +07:00
|
|
|
offset = 0;
|
|
|
|
} else {
|
|
|
|
page = read_sb_page(bitmap->mddev, bitmap->offset, index);
|
2005-06-22 07:17:14 +07:00
|
|
|
offset = 0;
|
|
|
|
}
|
2005-06-22 07:17:27 +07:00
|
|
|
if (IS_ERR(page)) { /* read error */
|
|
|
|
ret = PTR_ERR(page);
|
2007-07-17 18:06:13 +07:00
|
|
|
goto err;
|
2005-06-22 07:17:27 +07:00
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
oldindex = index;
|
|
|
|
oldpage = page;
|
|
|
|
|
|
|
|
if (outofdate) {
|
|
|
|
/*
|
|
|
|
* if bitmap is out of date, dirty the
|
|
|
|
* whole page and write it out
|
|
|
|
*/
|
2006-01-06 15:20:34 +07:00
|
|
|
paddr = kmap_atomic(page, KM_USER0);
|
|
|
|
memset(paddr + offset, 0xff,
|
2005-09-10 06:23:44 +07:00
|
|
|
PAGE_SIZE - offset);
|
2006-01-06 15:20:34 +07:00
|
|
|
kunmap_atomic(paddr, KM_USER0);
|
2007-07-17 18:06:13 +07:00
|
|
|
write_page(bitmap, page, 1);
|
|
|
|
|
|
|
|
ret = -EIO;
|
|
|
|
if (bitmap->flags & BITMAP_WRITE_ERROR) {
|
2005-06-22 07:17:14 +07:00
|
|
|
/* release, page not in filemap yet */
|
2006-01-06 15:20:31 +07:00
|
|
|
put_page(page);
|
2007-07-17 18:06:13 +07:00
|
|
|
goto err;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bitmap->filemap[bitmap->file_pages++] = page;
|
2007-05-24 03:58:10 +07:00
|
|
|
bitmap->last_page_size = count;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
2006-01-06 15:20:34 +07:00
|
|
|
paddr = kmap_atomic(page, KM_USER0);
|
2005-11-09 12:39:32 +07:00
|
|
|
if (bitmap->flags & BITMAP_HOSTENDIAN)
|
2006-01-06 15:20:34 +07:00
|
|
|
b = test_bit(bit, paddr);
|
2005-11-09 12:39:32 +07:00
|
|
|
else
|
2006-01-06 15:20:34 +07:00
|
|
|
b = ext2_test_bit(bit, paddr);
|
|
|
|
kunmap_atomic(paddr, KM_USER0);
|
2005-11-09 12:39:32 +07:00
|
|
|
if (b) {
|
2005-06-22 07:17:14 +07:00
|
|
|
/* if the disk bit is set, set the memory bit */
|
2005-09-10 06:23:44 +07:00
|
|
|
bitmap_set_memory_bits(bitmap, i << CHUNK_BLOCK_SHIFT(bitmap),
|
|
|
|
((i+1) << (CHUNK_BLOCK_SHIFT(bitmap)) >= start)
|
|
|
|
);
|
2005-06-22 07:17:14 +07:00
|
|
|
bit_cnt++;
|
2005-09-10 06:23:44 +07:00
|
|
|
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* everything went OK */
|
|
|
|
ret = 0;
|
|
|
|
bitmap_mask_state(bitmap, BITMAP_STALE, MASK_UNSET);
|
|
|
|
|
|
|
|
if (bit_cnt) { /* Kick recovery if any bits were set */
|
|
|
|
set_bit(MD_RECOVERY_NEEDED, &bitmap->mddev->recovery);
|
|
|
|
md_wakeup_thread(bitmap->mddev->thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(KERN_INFO "%s: bitmap initialized from disk: "
|
2007-07-17 18:06:13 +07:00
|
|
|
"read %lu/%lu pages, set %lu bits\n",
|
|
|
|
bmname(bitmap), bitmap->file_pages, num_pages, bit_cnt);
|
|
|
|
|
|
|
|
return 0;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
err:
|
|
|
|
printk(KERN_INFO "%s: bitmap initialisation failed: %d\n",
|
|
|
|
bmname(bitmap), ret);
|
2005-06-22 07:17:14 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
void bitmap_write_all(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
/* We don't actually write all bitmap blocks here,
|
|
|
|
* just flag them as needing to be written
|
|
|
|
*/
|
2006-06-26 14:27:45 +07:00
|
|
|
int i;
|
2005-06-22 07:17:27 +07:00
|
|
|
|
2006-06-26 14:27:45 +07:00
|
|
|
for (i=0; i < bitmap->file_pages; i++)
|
|
|
|
set_page_attr(bitmap, bitmap->filemap[i],
|
|
|
|
BITMAP_PAGE_NEEDWRITE);
|
2005-06-22 07:17:27 +07:00
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
static void bitmap_count_page(struct bitmap *bitmap, sector_t offset, int inc)
|
|
|
|
{
|
|
|
|
sector_t chunk = offset >> CHUNK_BLOCK_SHIFT(bitmap);
|
|
|
|
unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
|
|
|
|
bitmap->bp[page].count += inc;
|
|
|
|
/*
|
|
|
|
if (page == 0) printk("count page 0, offset %llu: %d gives %d\n",
|
|
|
|
(unsigned long long)offset, inc, bitmap->bp[page].count);
|
|
|
|
*/
|
|
|
|
bitmap_checkfree(bitmap, page);
|
|
|
|
}
|
|
|
|
static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
|
|
|
|
sector_t offset, int *blocks,
|
|
|
|
int create);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* bitmap daemon -- periodically wakes up to clean bits and flush pages
|
|
|
|
* out to disk
|
|
|
|
*/
|
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
void bitmap_daemon_work(struct bitmap *bitmap)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
2005-06-22 07:17:22 +07:00
|
|
|
unsigned long j;
|
2005-06-22 07:17:14 +07:00
|
|
|
unsigned long flags;
|
|
|
|
struct page *page = NULL, *lastpage = NULL;
|
|
|
|
int blocks;
|
2006-01-06 15:20:34 +07:00
|
|
|
void *paddr;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
if (bitmap == NULL)
|
2007-07-17 18:06:13 +07:00
|
|
|
return;
|
2005-06-22 07:17:14 +07:00
|
|
|
if (time_before(jiffies, bitmap->daemon_lastrun + bitmap->daemon_sleep*HZ))
|
2007-07-17 18:06:13 +07:00
|
|
|
return;
|
2005-06-22 07:17:14 +07:00
|
|
|
bitmap->daemon_lastrun = jiffies;
|
|
|
|
|
|
|
|
for (j = 0; j < bitmap->chunks; j++) {
|
|
|
|
bitmap_counter_t *bmc;
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
2005-06-22 07:17:27 +07:00
|
|
|
if (!bitmap->filemap) {
|
2005-06-22 07:17:14 +07:00
|
|
|
/* error or shutdown */
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
page = filemap_get_page(bitmap, j);
|
|
|
|
|
|
|
|
if (page != lastpage) {
|
2005-06-22 07:17:22 +07:00
|
|
|
/* skip this page unless it's marked as needing cleaning */
|
2006-06-26 14:27:45 +07:00
|
|
|
if (!test_page_attr(bitmap, page, BITMAP_PAGE_CLEAN)) {
|
|
|
|
int need_write = test_page_attr(bitmap, page,
|
|
|
|
BITMAP_PAGE_NEEDWRITE);
|
2006-06-26 14:27:46 +07:00
|
|
|
if (need_write)
|
2005-06-22 07:17:22 +07:00
|
|
|
clear_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
|
2006-06-26 14:27:46 +07:00
|
|
|
|
2005-06-22 07:17:22 +07:00
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2007-07-17 18:06:13 +07:00
|
|
|
if (need_write)
|
|
|
|
write_page(bitmap, page, 0);
|
2005-06-22 07:17:22 +07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
/* grab the new page, sync and release the old */
|
|
|
|
if (lastpage != NULL) {
|
2006-06-26 14:27:45 +07:00
|
|
|
if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
|
2005-06-22 07:17:14 +07:00
|
|
|
clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2007-07-17 18:06:13 +07:00
|
|
|
write_page(bitmap, lastpage, 0);
|
2005-06-22 07:17:14 +07:00
|
|
|
} else {
|
|
|
|
set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
lastpage = page;
|
|
|
|
/*
|
|
|
|
printk("bitmap clean at page %lu\n", j);
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
|
|
|
clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
|
|
|
|
}
|
|
|
|
bmc = bitmap_get_counter(bitmap, j << CHUNK_BLOCK_SHIFT(bitmap),
|
|
|
|
&blocks, 0);
|
|
|
|
if (bmc) {
|
|
|
|
/*
|
|
|
|
if (j < 100) printk("bitmap: j=%lu, *bmc = 0x%x\n", j, *bmc);
|
|
|
|
*/
|
|
|
|
if (*bmc == 2) {
|
|
|
|
*bmc=1; /* maybe clear the bit next time */
|
|
|
|
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
|
|
|
|
} else if (*bmc == 1) {
|
|
|
|
/* we can clear the bit */
|
|
|
|
*bmc = 0;
|
|
|
|
bitmap_count_page(bitmap, j << CHUNK_BLOCK_SHIFT(bitmap),
|
|
|
|
-1);
|
|
|
|
|
|
|
|
/* clear the bit */
|
2006-01-06 15:20:34 +07:00
|
|
|
paddr = kmap_atomic(page, KM_USER0);
|
2005-11-09 12:39:32 +07:00
|
|
|
if (bitmap->flags & BITMAP_HOSTENDIAN)
|
2006-01-06 15:20:34 +07:00
|
|
|
clear_bit(file_page_offset(j), paddr);
|
2005-11-09 12:39:32 +07:00
|
|
|
else
|
2006-01-06 15:20:34 +07:00
|
|
|
ext2_clear_bit(file_page_offset(j), paddr);
|
|
|
|
kunmap_atomic(paddr, KM_USER0);
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now sync the final page */
|
|
|
|
if (lastpage != NULL) {
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
2006-06-26 14:27:45 +07:00
|
|
|
if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
|
2005-06-22 07:17:14 +07:00
|
|
|
clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
2007-07-17 18:06:13 +07:00
|
|
|
write_page(bitmap, lastpage, 0);
|
2005-06-22 07:17:14 +07:00
|
|
|
} else {
|
|
|
|
set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
|
|
|
|
sector_t offset, int *blocks,
|
|
|
|
int create)
|
|
|
|
{
|
|
|
|
/* If 'create', we might release the lock and reclaim it.
|
|
|
|
* The lock must have been taken with interrupts enabled.
|
|
|
|
* If !create, we don't release the lock.
|
|
|
|
*/
|
|
|
|
sector_t chunk = offset >> CHUNK_BLOCK_SHIFT(bitmap);
|
|
|
|
unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
|
|
|
|
unsigned long pageoff = (chunk & PAGE_COUNTER_MASK) << COUNTER_BYTE_SHIFT;
|
|
|
|
sector_t csize;
|
|
|
|
|
|
|
|
if (bitmap_checkpage(bitmap, page, create) < 0) {
|
|
|
|
csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap));
|
|
|
|
*blocks = csize - (offset & (csize- 1));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* now locked ... */
|
|
|
|
|
|
|
|
if (bitmap->bp[page].hijacked) { /* hijacked pointer */
|
|
|
|
/* should we use the first or second counter field
|
|
|
|
* of the hijacked pointer? */
|
|
|
|
int hi = (pageoff > PAGE_COUNTER_MASK);
|
|
|
|
csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap) +
|
|
|
|
PAGE_COUNTER_SHIFT - 1);
|
|
|
|
*blocks = csize - (offset & (csize- 1));
|
|
|
|
return &((bitmap_counter_t *)
|
|
|
|
&bitmap->bp[page].map)[hi];
|
|
|
|
} else { /* page is allocated */
|
|
|
|
csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap));
|
|
|
|
*blocks = csize - (offset & (csize- 1));
|
|
|
|
return (bitmap_counter_t *)
|
|
|
|
&(bitmap->bp[page].map[pageoff]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-10 06:23:47 +07:00
|
|
|
int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sectors, int behind)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
if (!bitmap) return 0;
|
2005-09-10 06:23:47 +07:00
|
|
|
|
|
|
|
if (behind) {
|
|
|
|
atomic_inc(&bitmap->behind_writes);
|
|
|
|
PRINTK(KERN_DEBUG "inc write-behind count %d/%d\n",
|
|
|
|
atomic_read(&bitmap->behind_writes), bitmap->max_write_behind);
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
while (sectors) {
|
|
|
|
int blocks;
|
|
|
|
bitmap_counter_t *bmc;
|
|
|
|
|
|
|
|
spin_lock_irq(&bitmap->lock);
|
|
|
|
bmc = bitmap_get_counter(bitmap, offset, &blocks, 1);
|
|
|
|
if (!bmc) {
|
|
|
|
spin_unlock_irq(&bitmap->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-09 05:20:37 +07:00
|
|
|
if (unlikely((*bmc & COUNTER_MAX) == COUNTER_MAX)) {
|
|
|
|
DEFINE_WAIT(__wait);
|
|
|
|
/* note that it is safe to do the prepare_to_wait
|
|
|
|
* after the test as long as we do it before dropping
|
|
|
|
* the spinlock.
|
|
|
|
*/
|
|
|
|
prepare_to_wait(&bitmap->overflow_wait, &__wait,
|
|
|
|
TASK_UNINTERRUPTIBLE);
|
|
|
|
spin_unlock_irq(&bitmap->lock);
|
2007-11-08 02:26:56 +07:00
|
|
|
blk_unplug(bitmap->mddev->queue);
|
2007-02-09 05:20:37 +07:00
|
|
|
schedule();
|
|
|
|
finish_wait(&bitmap->overflow_wait, &__wait);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
switch(*bmc) {
|
|
|
|
case 0:
|
|
|
|
bitmap_file_set_bit(bitmap, offset);
|
|
|
|
bitmap_count_page(bitmap,offset, 1);
|
|
|
|
blk_plug_device(bitmap->mddev->queue);
|
|
|
|
/* fall through */
|
|
|
|
case 1:
|
|
|
|
*bmc = 2;
|
|
|
|
}
|
2007-02-09 05:20:37 +07:00
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
(*bmc)++;
|
|
|
|
|
|
|
|
spin_unlock_irq(&bitmap->lock);
|
|
|
|
|
|
|
|
offset += blocks;
|
|
|
|
if (sectors > blocks)
|
|
|
|
sectors -= blocks;
|
|
|
|
else sectors = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long sectors,
|
2005-09-10 06:23:47 +07:00
|
|
|
int success, int behind)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
if (!bitmap) return;
|
2005-09-10 06:23:47 +07:00
|
|
|
if (behind) {
|
|
|
|
atomic_dec(&bitmap->behind_writes);
|
|
|
|
PRINTK(KERN_DEBUG "dec write-behind count %d/%d\n",
|
|
|
|
atomic_read(&bitmap->behind_writes), bitmap->max_write_behind);
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
while (sectors) {
|
|
|
|
int blocks;
|
|
|
|
unsigned long flags;
|
|
|
|
bitmap_counter_t *bmc;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
|
|
|
bmc = bitmap_get_counter(bitmap, offset, &blocks, 0);
|
|
|
|
if (!bmc) {
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!success && ! (*bmc & NEEDED_MASK))
|
|
|
|
*bmc |= NEEDED_MASK;
|
|
|
|
|
2007-02-09 05:20:37 +07:00
|
|
|
if ((*bmc & COUNTER_MAX) == COUNTER_MAX)
|
|
|
|
wake_up(&bitmap->overflow_wait);
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
(*bmc)--;
|
|
|
|
if (*bmc <= 2) {
|
|
|
|
set_page_attr(bitmap,
|
|
|
|
filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
|
|
|
|
BITMAP_PAGE_CLEAN);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
offset += blocks;
|
|
|
|
if (sectors > blocks)
|
|
|
|
sectors -= blocks;
|
|
|
|
else sectors = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-15 17:56:35 +07:00
|
|
|
int bitmap_start_sync(struct bitmap *bitmap, sector_t offset, int *blocks,
|
|
|
|
int degraded)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
bitmap_counter_t *bmc;
|
|
|
|
int rv;
|
|
|
|
if (bitmap == NULL) {/* FIXME or bitmap set as 'failed' */
|
|
|
|
*blocks = 1024;
|
|
|
|
return 1; /* always resync if no bitmap */
|
|
|
|
}
|
|
|
|
spin_lock_irq(&bitmap->lock);
|
|
|
|
bmc = bitmap_get_counter(bitmap, offset, blocks, 0);
|
|
|
|
rv = 0;
|
|
|
|
if (bmc) {
|
|
|
|
/* locked */
|
|
|
|
if (RESYNC(*bmc))
|
|
|
|
rv = 1;
|
|
|
|
else if (NEEDED(*bmc)) {
|
|
|
|
rv = 1;
|
2005-07-15 17:56:35 +07:00
|
|
|
if (!degraded) { /* don't set/clear bits if degraded */
|
|
|
|
*bmc |= RESYNC_MASK;
|
|
|
|
*bmc &= ~NEEDED_MASK;
|
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&bitmap->lock);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bitmap_end_sync(struct bitmap *bitmap, sector_t offset, int *blocks, int aborted)
|
|
|
|
{
|
|
|
|
bitmap_counter_t *bmc;
|
|
|
|
unsigned long flags;
|
|
|
|
/*
|
|
|
|
if (offset == 0) printk("bitmap_end_sync 0 (%d)\n", aborted);
|
|
|
|
*/ if (bitmap == NULL) {
|
|
|
|
*blocks = 1024;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
spin_lock_irqsave(&bitmap->lock, flags);
|
|
|
|
bmc = bitmap_get_counter(bitmap, offset, blocks, 0);
|
|
|
|
if (bmc == NULL)
|
|
|
|
goto unlock;
|
|
|
|
/* locked */
|
|
|
|
/*
|
|
|
|
if (offset == 0) printk("bitmap_end sync found 0x%x, blocks %d\n", *bmc, *blocks);
|
|
|
|
*/
|
|
|
|
if (RESYNC(*bmc)) {
|
|
|
|
*bmc &= ~RESYNC_MASK;
|
|
|
|
|
|
|
|
if (!NEEDED(*bmc) && aborted)
|
|
|
|
*bmc |= NEEDED_MASK;
|
|
|
|
else {
|
|
|
|
if (*bmc <= 2) {
|
|
|
|
set_page_attr(bitmap,
|
|
|
|
filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
|
|
|
|
BITMAP_PAGE_CLEAN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unlock:
|
|
|
|
spin_unlock_irqrestore(&bitmap->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bitmap_close_sync(struct bitmap *bitmap)
|
|
|
|
{
|
|
|
|
/* Sync has finished, and any bitmap chunks that weren't synced
|
|
|
|
* properly have been aborted. It remains to us to clear the
|
|
|
|
* RESYNC bit wherever it is still on
|
|
|
|
*/
|
|
|
|
sector_t sector = 0;
|
|
|
|
int blocks;
|
|
|
|
if (!bitmap) return;
|
|
|
|
while (sector < bitmap->mddev->resync_max_sectors) {
|
|
|
|
bitmap_end_sync(bitmap, sector, &blocks, 0);
|
|
|
|
/*
|
|
|
|
if (sector < 500) printk("bitmap_close_sync: sec %llu blks %d\n",
|
|
|
|
(unsigned long long)sector, blocks);
|
|
|
|
*/ sector += blocks;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-10 06:23:44 +07:00
|
|
|
static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int needed)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
/* For each chunk covered by any of these sectors, set the
|
2005-08-05 02:53:33 +07:00
|
|
|
* counter to 1 and set resync_needed. They should all
|
2005-06-22 07:17:14 +07:00
|
|
|
* be 0 at this point
|
|
|
|
*/
|
2005-08-05 02:53:33 +07:00
|
|
|
|
|
|
|
int secs;
|
|
|
|
bitmap_counter_t *bmc;
|
|
|
|
spin_lock_irq(&bitmap->lock);
|
|
|
|
bmc = bitmap_get_counter(bitmap, offset, &secs, 1);
|
|
|
|
if (!bmc) {
|
2005-06-22 07:17:14 +07:00
|
|
|
spin_unlock_irq(&bitmap->lock);
|
2005-08-05 02:53:33 +07:00
|
|
|
return;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
2005-08-05 02:53:33 +07:00
|
|
|
if (! *bmc) {
|
|
|
|
struct page *page;
|
2005-09-10 06:23:44 +07:00
|
|
|
*bmc = 1 | (needed?NEEDED_MASK:0);
|
2005-08-05 02:53:33 +07:00
|
|
|
bitmap_count_page(bitmap, offset, 1);
|
|
|
|
page = filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap));
|
|
|
|
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&bitmap->lock);
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
2006-10-03 15:15:49 +07:00
|
|
|
/* dirty the memory and file bits for bitmap chunks "s" to "e" */
|
|
|
|
void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e)
|
|
|
|
{
|
|
|
|
unsigned long chunk;
|
|
|
|
|
|
|
|
for (chunk = s; chunk <= e; chunk++) {
|
|
|
|
sector_t sec = chunk << CHUNK_BLOCK_SHIFT(bitmap);
|
|
|
|
bitmap_set_memory_bits(bitmap, sec, 1);
|
|
|
|
bitmap_file_set_bit(bitmap, sec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-05 02:53:35 +07:00
|
|
|
/*
|
|
|
|
* flush out any pending updates
|
|
|
|
*/
|
|
|
|
void bitmap_flush(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
struct bitmap *bitmap = mddev->bitmap;
|
|
|
|
int sleep;
|
|
|
|
|
|
|
|
if (!bitmap) /* there was no bitmap */
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* run the daemon_work three time to ensure everything is flushed
|
|
|
|
* that can be
|
|
|
|
*/
|
|
|
|
sleep = bitmap->daemon_sleep;
|
|
|
|
bitmap->daemon_sleep = 0;
|
|
|
|
bitmap_daemon_work(bitmap);
|
|
|
|
bitmap_daemon_work(bitmap);
|
|
|
|
bitmap_daemon_work(bitmap);
|
|
|
|
bitmap->daemon_sleep = sleep;
|
|
|
|
bitmap_update_sb(bitmap);
|
|
|
|
}
|
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
/*
|
|
|
|
* free memory that was allocated
|
|
|
|
*/
|
2005-09-10 06:23:50 +07:00
|
|
|
static void bitmap_free(struct bitmap *bitmap)
|
2005-06-22 07:17:14 +07:00
|
|
|
{
|
|
|
|
unsigned long k, pages;
|
|
|
|
struct bitmap_page *bp;
|
|
|
|
|
|
|
|
if (!bitmap) /* there was no bitmap */
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* release the bitmap file and kill the daemon */
|
|
|
|
bitmap_file_put(bitmap);
|
|
|
|
|
|
|
|
bp = bitmap->bp;
|
|
|
|
pages = bitmap->pages;
|
|
|
|
|
|
|
|
/* free all allocated memory */
|
|
|
|
|
|
|
|
if (bp) /* deallocate the page memory */
|
|
|
|
for (k = 0; k < pages; k++)
|
|
|
|
if (bp[k].map && !bp[k].hijacked)
|
|
|
|
kfree(bp[k].map);
|
|
|
|
kfree(bp);
|
|
|
|
kfree(bitmap);
|
|
|
|
}
|
2005-09-10 06:23:50 +07:00
|
|
|
void bitmap_destroy(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
struct bitmap *bitmap = mddev->bitmap;
|
|
|
|
|
|
|
|
if (!bitmap) /* there was no bitmap */
|
|
|
|
return;
|
|
|
|
|
|
|
|
mddev->bitmap = NULL; /* disconnect from the md device */
|
2006-01-06 15:20:16 +07:00
|
|
|
if (mddev->thread)
|
|
|
|
mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
|
2005-09-10 06:23:50 +07:00
|
|
|
|
|
|
|
bitmap_free(bitmap);
|
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize the bitmap structure
|
|
|
|
* if this returns an error, bitmap_destroy must be called to do clean up
|
|
|
|
*/
|
|
|
|
int bitmap_create(mddev_t *mddev)
|
|
|
|
{
|
|
|
|
struct bitmap *bitmap;
|
|
|
|
unsigned long blocks = mddev->resync_max_sectors;
|
|
|
|
unsigned long chunks;
|
|
|
|
unsigned long pages;
|
|
|
|
struct file *file = mddev->bitmap_file;
|
|
|
|
int err;
|
2005-09-10 06:23:44 +07:00
|
|
|
sector_t start;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2006-10-11 15:22:26 +07:00
|
|
|
BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
if (!file && !mddev->bitmap_offset) /* bitmap disabled, nothing to do */
|
2005-06-22 07:17:14 +07:00
|
|
|
return 0;
|
|
|
|
|
2005-06-22 07:17:27 +07:00
|
|
|
BUG_ON(file && mddev->bitmap_offset);
|
|
|
|
|
2006-01-06 15:20:32 +07:00
|
|
|
bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
|
2005-06-22 07:17:14 +07:00
|
|
|
if (!bitmap)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock_init(&bitmap->lock);
|
2006-06-26 14:27:49 +07:00
|
|
|
atomic_set(&bitmap->pending_writes, 0);
|
|
|
|
init_waitqueue_head(&bitmap->write_wait);
|
2007-02-09 05:20:37 +07:00
|
|
|
init_waitqueue_head(&bitmap->overflow_wait);
|
2006-06-26 14:27:49 +07:00
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
bitmap->mddev = mddev;
|
|
|
|
|
|
|
|
bitmap->file = file;
|
2005-06-22 07:17:27 +07:00
|
|
|
bitmap->offset = mddev->bitmap_offset;
|
2006-06-26 14:27:49 +07:00
|
|
|
if (file) {
|
|
|
|
get_file(file);
|
2007-05-08 14:27:10 +07:00
|
|
|
do_sync_mapping_range(file->f_mapping, 0, LLONG_MAX,
|
|
|
|
SYNC_FILE_RANGE_WAIT_BEFORE |
|
|
|
|
SYNC_FILE_RANGE_WRITE |
|
|
|
|
SYNC_FILE_RANGE_WAIT_AFTER);
|
2006-06-26 14:27:49 +07:00
|
|
|
}
|
2005-06-22 07:17:14 +07:00
|
|
|
/* read superblock from bitmap file (this sets bitmap->chunksize) */
|
|
|
|
err = bitmap_read_sb(bitmap);
|
|
|
|
if (err)
|
2005-09-10 06:23:50 +07:00
|
|
|
goto error;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
2006-10-03 15:16:01 +07:00
|
|
|
bitmap->chunkshift = ffz(~bitmap->chunksize);
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
/* now that chunksize and chunkshift are set, we can use these macros */
|
|
|
|
chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) /
|
|
|
|
CHUNK_BLOCK_RATIO(bitmap);
|
|
|
|
pages = (chunks + PAGE_COUNTER_RATIO - 1) / PAGE_COUNTER_RATIO;
|
|
|
|
|
|
|
|
BUG_ON(!pages);
|
|
|
|
|
|
|
|
bitmap->chunks = chunks;
|
|
|
|
bitmap->pages = pages;
|
|
|
|
bitmap->missing_pages = pages;
|
|
|
|
bitmap->counter_bits = COUNTER_BITS;
|
|
|
|
|
|
|
|
bitmap->syncchunk = ~0UL;
|
|
|
|
|
2005-07-28 01:45:17 +07:00
|
|
|
#ifdef INJECT_FATAL_FAULT_1
|
2005-06-22 07:17:14 +07:00
|
|
|
bitmap->bp = NULL;
|
|
|
|
#else
|
2006-01-06 15:20:32 +07:00
|
|
|
bitmap->bp = kzalloc(pages * sizeof(*bitmap->bp), GFP_KERNEL);
|
2005-06-22 07:17:14 +07:00
|
|
|
#endif
|
2005-09-10 06:23:50 +07:00
|
|
|
err = -ENOMEM;
|
2005-06-22 07:17:14 +07:00
|
|
|
if (!bitmap->bp)
|
2005-09-10 06:23:50 +07:00
|
|
|
goto error;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
/* now that we have some pages available, initialize the in-memory
|
|
|
|
* bitmap from the on-disk bitmap */
|
2005-09-10 06:23:44 +07:00
|
|
|
start = 0;
|
|
|
|
if (mddev->degraded == 0
|
|
|
|
|| bitmap->events_cleared == mddev->events)
|
|
|
|
/* no need to keep dirty bits to optimise a re-add of a missing device */
|
|
|
|
start = mddev->recovery_cp;
|
|
|
|
err = bitmap_init_from_disk(bitmap, start);
|
2005-08-05 02:53:33 +07:00
|
|
|
|
2005-06-22 07:17:14 +07:00
|
|
|
if (err)
|
2005-09-10 06:23:50 +07:00
|
|
|
goto error;
|
2005-06-22 07:17:14 +07:00
|
|
|
|
|
|
|
printk(KERN_INFO "created bitmap (%lu pages) for device %s\n",
|
|
|
|
pages, bmname(bitmap));
|
|
|
|
|
2005-09-10 06:23:50 +07:00
|
|
|
mddev->bitmap = bitmap;
|
|
|
|
|
2006-01-06 15:20:16 +07:00
|
|
|
mddev->thread->timeout = bitmap->daemon_sleep * HZ;
|
|
|
|
|
2007-07-17 18:06:13 +07:00
|
|
|
bitmap_update_sb(bitmap);
|
|
|
|
|
|
|
|
return (bitmap->flags & BITMAP_WRITE_ERROR) ? -EIO : 0;
|
2005-09-10 06:23:50 +07:00
|
|
|
|
|
|
|
error:
|
|
|
|
bitmap_free(bitmap);
|
|
|
|
return err;
|
2005-06-22 07:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* the bitmap API -- for raid personalities */
|
|
|
|
EXPORT_SYMBOL(bitmap_startwrite);
|
|
|
|
EXPORT_SYMBOL(bitmap_endwrite);
|
|
|
|
EXPORT_SYMBOL(bitmap_start_sync);
|
|
|
|
EXPORT_SYMBOL(bitmap_end_sync);
|
|
|
|
EXPORT_SYMBOL(bitmap_unplug);
|
|
|
|
EXPORT_SYMBOL(bitmap_close_sync);
|