/* * 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; if (bitmap_mask_state(bitmap, BITMAP_STALE, MASK_SET) == 0) { bitmap_update_sb(bitmap); if (bitmap->file) { path = kmalloc(PAGE_SIZE, GFP_KERNEL); if (path) ptr = file_path(bitmap->file, path, PAGE_SIZE); printk(KERN_ALERT "%s: kicking failed bitmap file %s from array!\n", bmname(bitmap), ptr ? ptr : ""); kfree(path); } else printk(KERN_ALERT "%s: disabling internal bitmap due to errors\n", bmname(bitmap)); } bitmap_file_put(bitmap); return; }
/* * 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; }
/* * just a placeholder - calls kmalloc for bitmap pages */ static unsigned char *bitmap_alloc_page(struct bitmap *bitmap) { unsigned char *page; #ifdef INJECT_FAULTS_1 page = NULL; #else page = kmalloc(PAGE_SIZE, GFP_NOIO); #endif if (!page) printk("%s: bitmap_alloc_page FAILED\n", bmname(bitmap)); else PRINTK("%s: bitmap_alloc_page: allocated page at %p\n", bmname(bitmap), page); return page; }
/* print out the bitmap file superblock */ void bitmap_print_sb(struct bitmap *bitmap) { bitmap_super_t *sb; if (!bitmap || !bitmap->sb_page) return; sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0); printk(KERN_DEBUG "%s: bitmap file superblock:\n", bmname(bitmap)); 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", *(__u32 *)(sb->uuid+0), *(__u32 *)(sb->uuid+4), *(__u32 *)(sb->uuid+8), *(__u32 *)(sb->uuid+12)); printk(KERN_DEBUG " events: %llu\n", (unsigned long long) le64_to_cpu(sb->events)); printk(KERN_DEBUG "events cleared: %llu\n", (unsigned long long) le64_to_cpu(sb->events_cleared)); 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); printk(KERN_DEBUG "max write behind: %d\n", le32_to_cpu(sb->write_behind)); kunmap_atomic(sb, KM_USER0); }
/* * 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); }
/* * 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. * * We ignore all bits for sectors that end earlier than 'start'. * This is used when reading an out-of-date bitmap... */ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start) { unsigned long i, chunks, index, oldindex, bit; struct page *page = NULL, *oldpage = NULL; unsigned long num_pages, bit_cnt = 0; struct file *file; unsigned long bytes, offset; int outofdate; int ret = -ENOSPC; void *paddr; chunks = bitmap->chunks; file = bitmap->file; BUG_ON(!file && !bitmap->offset); #ifdef INJECT_FAULTS_3 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; num_pages = (bytes + sizeof(bitmap_super_t) + PAGE_SIZE - 1) / PAGE_SIZE; if (file && i_size_read(file->f_mapping->host) < bytes + sizeof(bitmap_super_t)) { 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)); goto out; } ret = -ENOMEM; bitmap->filemap = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL); if (!bitmap->filemap) goto out; /* We need 4 bits per page, rounded up to a multiple of sizeof(unsigned long) */ bitmap->filemap_attr = kzalloc( roundup( DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)), GFP_KERNEL); if (!bitmap->filemap_attr) goto out; oldindex = ~0L; for (i = 0; i < chunks; i++) { int b; index = file_page_index(i); bit = file_page_offset(i); if (index != oldindex) { /* this is a new page, read it in */ int count; /* unmap the old page, we're done with it */ if (index == num_pages-1) count = bytes + sizeof(bitmap_super_t) - index * PAGE_SIZE; else count = PAGE_SIZE; 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); } else if (file) { page = read_page(file, index, bitmap, count); offset = 0; } else { page = read_sb_page(bitmap->mddev, bitmap->offset, index); offset = 0; } if (IS_ERR(page)) { /* read error */ ret = PTR_ERR(page); goto out; } oldindex = index; oldpage = page; if (outofdate) { /* * if bitmap is out of date, dirty the * whole page and write it out */ paddr = kmap_atomic(page, KM_USER0); memset(paddr + offset, 0xff, PAGE_SIZE - offset); kunmap_atomic(paddr, KM_USER0); ret = write_page(bitmap, page, 1); if (ret) { /* release, page not in filemap yet */ put_page(page); goto out; } } bitmap->filemap[bitmap->file_pages++] = page; bitmap->last_page_size = count; } paddr = kmap_atomic(page, KM_USER0); if (bitmap->flags & BITMAP_HOSTENDIAN) b = test_bit(bit, paddr); else b = ext2_test_bit(bit, paddr); kunmap_atomic(paddr, KM_USER0); if (b) { /* if the disk bit is set, set the memory bit */ bitmap_set_memory_bits(bitmap, i << CHUNK_BLOCK_SHIFT(bitmap), ((i+1) << (CHUNK_BLOCK_SHIFT(bitmap)) >= start) ); bit_cnt++; set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); } } /* 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); } out: printk(KERN_INFO "%s: bitmap initialized from disk: " "read %lu/%lu pages, set %lu bits, status: %d\n", bmname(bitmap), bitmap->file_pages, num_pages, bit_cnt, ret); return ret; }
/* 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; unsigned long chunksize, daemon_sleep, write_behind; unsigned long long events; int err = -EINVAL; /* page 0 is the superblock, read it... */ 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 { bitmap->sb_page = read_sb_page(bitmap->mddev, bitmap->offset, 0); } if (IS_ERR(bitmap->sb_page)) { err = PTR_ERR(bitmap->sb_page); bitmap->sb_page = NULL; return err; } sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0); chunksize = le32_to_cpu(sb->chunksize); daemon_sleep = le32_to_cpu(sb->daemon_sleep); write_behind = le32_to_cpu(sb->write_behind); /* verify that the bitmap-specific fields are valid */ if (sb->magic != cpu_to_le32(BITMAP_MAGIC)) reason = "bad magic"; else if (le32_to_cpu(sb->version) < BITMAP_MAJOR_LO || le32_to_cpu(sb->version) > BITMAP_MAJOR_HI) reason = "unrecognized superblock version"; else if (chunksize < PAGE_SIZE) reason = "bitmap chunksize too small"; else if ((1 << ffz(~chunksize)) != chunksize) reason = "bitmap chunksize not a power of 2"; else if (daemon_sleep < 1 || daemon_sleep > MAX_SCHEDULE_TIMEOUT / HZ) reason = "daemon sleep period out of range"; else if (write_behind > COUNTER_MAX) reason = "write-behind limit out of range (0 - 16383)"; 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); sb->state |= cpu_to_le32(BITMAP_STALE); } success: /* assign fields using values from superblock */ bitmap->chunksize = chunksize; bitmap->daemon_sleep = daemon_sleep; bitmap->daemon_lastrun = jiffies; bitmap->max_write_behind = write_behind; bitmap->flags |= le32_to_cpu(sb->state); if (le32_to_cpu(sb->version) == BITMAP_MAJOR_HOSTENDIAN) bitmap->flags |= BITMAP_HOSTENDIAN; bitmap->events_cleared = le64_to_cpu(sb->events_cleared); if (sb->state & cpu_to_le32(BITMAP_STALE)) bitmap->events_cleared = bitmap->mddev->events; err = 0; out: kunmap_atomic(sb, KM_USER0); if (err) bitmap_print_sb(bitmap); return err; }