Skip to content
Snippets Groups Projects
check-integrity.c 103 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		       (unsigned long long)dev_bytenr);
    		for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) {
    
    			ret = btrfsic_map_block(state, bytenr,
    						state->metablock_size,
    
    						&block_ctx, mirror_num);
    			if (ret)
    				continue;
    
    			printk(KERN_INFO "Read logical bytenr @%llu maps to"
    			       " (%s/%llu/%d)\n",
    			       (unsigned long long)bytenr,
    			       block_ctx.dev->name,
    			       (unsigned long long)block_ctx.dev_bytenr,
    			       mirror_num);
    		}
    		WARN_ON(1);
    	}
    }
    
    static struct btrfsic_dev_state *btrfsic_dev_state_lookup(
    		struct block_device *bdev)
    {
    	struct btrfsic_dev_state *ds;
    
    	ds = btrfsic_dev_state_hashtable_lookup(bdev,
    						&btrfsic_dev_state_hashtable);
    	return ds;
    }
    
    int btrfsic_submit_bh(int rw, struct buffer_head *bh)
    {
    	struct btrfsic_dev_state *dev_state;
    
    	if (!btrfsic_is_initialized)
    		return submit_bh(rw, bh);
    
    	mutex_lock(&btrfsic_mutex);
    	/* since btrfsic_submit_bh() might also be called before
    	 * btrfsic_mount(), this might return NULL */
    	dev_state = btrfsic_dev_state_lookup(bh->b_bdev);
    
    	/* Only called to write the superblock (incl. FLUSH/FUA) */
    	if (NULL != dev_state &&
    	    (rw & WRITE) && bh->b_size > 0) {
    		u64 dev_bytenr;
    
    		dev_bytenr = 4096 * bh->b_blocknr;
    		if (dev_state->state->print_mask &
    		    BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH)
    			printk(KERN_INFO
    			       "submit_bh(rw=0x%x, blocknr=%lu (bytenr %llu),"
    			       " size=%lu, data=%p, bdev=%p)\n",
    
    			       rw, (unsigned long)bh->b_blocknr,
    			       (unsigned long long)dev_bytenr,
    			       (unsigned long)bh->b_size, bh->b_data,
    			       bh->b_bdev);
    
    		btrfsic_process_written_block(dev_state, dev_bytenr,
    
    					      NULL, bh, rw);
    	} else if (NULL != dev_state && (rw & REQ_FLUSH)) {
    		if (dev_state->state->print_mask &
    		    BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH)
    			printk(KERN_INFO
    
    			       "submit_bh(rw=0x%x FLUSH, bdev=%p)\n",
    
    			       rw, bh->b_bdev);
    		if (!dev_state->dummy_block_for_bio_bh_flush.is_iodone) {
    			if ((dev_state->state->print_mask &
    			     (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH |
    			      BTRFSIC_PRINT_MASK_VERBOSE)))
    				printk(KERN_INFO
    				       "btrfsic_submit_bh(%s) with FLUSH"
    				       " but dummy block already in use"
    				       " (ignored)!\n",
    				       dev_state->name);
    		} else {
    			struct btrfsic_block *const block =
    				&dev_state->dummy_block_for_bio_bh_flush;
    
    			block->is_iodone = 0;
    			block->never_written = 0;
    			block->iodone_w_error = 0;
    			block->flush_gen = dev_state->last_flush_gen + 1;
    			block->submit_bio_bh_rw = rw;
    			block->orig_bio_bh_private = bh->b_private;
    			block->orig_bio_bh_end_io.bh = bh->b_end_io;
    			block->next_in_same_bio = NULL;
    			bh->b_private = block;
    			bh->b_end_io = btrfsic_bh_end_io;
    		}
    	}
    	mutex_unlock(&btrfsic_mutex);
    	return submit_bh(rw, bh);
    }
    
    void btrfsic_submit_bio(int rw, struct bio *bio)
    {
    	struct btrfsic_dev_state *dev_state;
    
    	if (!btrfsic_is_initialized) {
    		submit_bio(rw, bio);
    		return;
    	}
    
    	mutex_lock(&btrfsic_mutex);
    	/* since btrfsic_submit_bio() is also called before
    	 * btrfsic_mount(), this might return NULL */
    	dev_state = btrfsic_dev_state_lookup(bio->bi_bdev);
    	if (NULL != dev_state &&
    	    (rw & WRITE) && NULL != bio->bi_io_vec) {
    		unsigned int i;
    		u64 dev_bytenr;
    		int bio_is_patched;
    
    
    		dev_bytenr = 512 * bio->bi_sector;
    		bio_is_patched = 0;
    		if (dev_state->state->print_mask &
    		    BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH)
    			printk(KERN_INFO
    			       "submit_bio(rw=0x%x, bi_vcnt=%u,"
    			       " bi_sector=%lu (bytenr %llu), bi_bdev=%p)\n",
    
    			       rw, bio->bi_vcnt, (unsigned long)bio->bi_sector,
    
    			       (unsigned long long)dev_bytenr,
    			       bio->bi_bdev);
    
    
    		mapped_datav = kmalloc(sizeof(*mapped_datav) * bio->bi_vcnt,
    				       GFP_NOFS);
    		if (!mapped_datav)
    			goto leave;
    
    		for (i = 0; i < bio->bi_vcnt; i++) {
    
    			BUG_ON(bio->bi_io_vec[i].bv_len != PAGE_CACHE_SIZE);
    			mapped_datav[i] = kmap(bio->bi_io_vec[i].bv_page);
    			if (!mapped_datav[i]) {
    				while (i > 0) {
    					i--;
    					kunmap(bio->bi_io_vec[i].bv_page);
    				}
    				kfree(mapped_datav);
    				goto leave;
    			}
    
    			if ((BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH |
    			     BTRFSIC_PRINT_MASK_VERBOSE) ==
    			    (dev_state->state->print_mask &
    			     (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH |
    			      BTRFSIC_PRINT_MASK_VERBOSE)))
    				printk(KERN_INFO
    
    				       "#%u: page=%p, len=%u, offset=%u\n",
    
    				       i, bio->bi_io_vec[i].bv_page,
    				       bio->bi_io_vec[i].bv_len,
    				       bio->bi_io_vec[i].bv_offset);
    
    		}
    		btrfsic_process_written_block(dev_state, dev_bytenr,
    					      mapped_datav, bio->bi_vcnt,
    					      bio, &bio_is_patched,
    					      NULL, rw);
    		while (i > 0) {
    			i--;
    
    			kunmap(bio->bi_io_vec[i].bv_page);
    		}
    
    	} else if (NULL != dev_state && (rw & REQ_FLUSH)) {
    		if (dev_state->state->print_mask &
    		    BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH)
    			printk(KERN_INFO
    
    			       "submit_bio(rw=0x%x FLUSH, bdev=%p)\n",
    
    			       rw, bio->bi_bdev);
    		if (!dev_state->dummy_block_for_bio_bh_flush.is_iodone) {
    			if ((dev_state->state->print_mask &
    			     (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH |
    			      BTRFSIC_PRINT_MASK_VERBOSE)))
    				printk(KERN_INFO
    				       "btrfsic_submit_bio(%s) with FLUSH"
    				       " but dummy block already in use"
    				       " (ignored)!\n",
    				       dev_state->name);
    		} else {
    			struct btrfsic_block *const block =
    				&dev_state->dummy_block_for_bio_bh_flush;
    
    			block->is_iodone = 0;
    			block->never_written = 0;
    			block->iodone_w_error = 0;
    			block->flush_gen = dev_state->last_flush_gen + 1;
    			block->submit_bio_bh_rw = rw;
    			block->orig_bio_bh_private = bio->bi_private;
    			block->orig_bio_bh_end_io.bio = bio->bi_end_io;
    			block->next_in_same_bio = NULL;
    			bio->bi_private = block;
    			bio->bi_end_io = btrfsic_bio_end_io;
    		}
    	}
    
    	mutex_unlock(&btrfsic_mutex);
    
    	submit_bio(rw, bio);
    }
    
    int btrfsic_mount(struct btrfs_root *root,
    		  struct btrfs_fs_devices *fs_devices,
    		  int including_extent_data, u32 print_mask)
    {
    	int ret;
    	struct btrfsic_state *state;
    	struct list_head *dev_head = &fs_devices->devices;
    	struct btrfs_device *device;
    
    
    	if (root->nodesize != root->leafsize) {
    		printk(KERN_INFO
    		       "btrfsic: cannot handle nodesize %d != leafsize %d!\n",
    		       root->nodesize, root->leafsize);
    		return -1;
    	}
    	if (root->nodesize & ((u64)PAGE_CACHE_SIZE - 1)) {
    		printk(KERN_INFO
    		       "btrfsic: cannot handle nodesize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
    		       root->nodesize, (unsigned long)PAGE_CACHE_SIZE);
    		return -1;
    	}
    	if (root->leafsize & ((u64)PAGE_CACHE_SIZE - 1)) {
    		printk(KERN_INFO
    		       "btrfsic: cannot handle leafsize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
    		       root->leafsize, (unsigned long)PAGE_CACHE_SIZE);
    		return -1;
    	}
    	if (root->sectorsize & ((u64)PAGE_CACHE_SIZE - 1)) {
    		printk(KERN_INFO
    		       "btrfsic: cannot handle sectorsize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
    		       root->sectorsize, (unsigned long)PAGE_CACHE_SIZE);
    		return -1;
    	}
    
    	state = kzalloc(sizeof(*state), GFP_NOFS);
    	if (NULL == state) {
    		printk(KERN_INFO "btrfs check-integrity: kmalloc() failed!\n");
    		return -1;
    	}
    
    	if (!btrfsic_is_initialized) {
    		mutex_init(&btrfsic_mutex);
    		btrfsic_dev_state_hashtable_init(&btrfsic_dev_state_hashtable);
    		btrfsic_is_initialized = 1;
    	}
    	mutex_lock(&btrfsic_mutex);
    	state->root = root;
    	state->print_mask = print_mask;
    	state->include_extent_data = including_extent_data;
    	state->csum_size = 0;
    
    	state->metablock_size = root->nodesize;
    	state->datablock_size = root->sectorsize;
    
    	INIT_LIST_HEAD(&state->all_blocks_list);
    	btrfsic_block_hashtable_init(&state->block_hashtable);
    	btrfsic_block_link_hashtable_init(&state->block_link_hashtable);
    	state->max_superblock_generation = 0;
    	state->latest_superblock = NULL;
    
    	list_for_each_entry(device, dev_head, dev_list) {
    		struct btrfsic_dev_state *ds;
    		char *p;
    
    		if (!device->bdev || !device->name)
    			continue;
    
    		ds = btrfsic_dev_state_alloc();
    		if (NULL == ds) {
    			printk(KERN_INFO
    			       "btrfs check-integrity: kmalloc() failed!\n");
    			mutex_unlock(&btrfsic_mutex);
    			return -1;
    		}
    		ds->bdev = device->bdev;
    		ds->state = state;
    		bdevname(ds->bdev, ds->name);
    		ds->name[BDEVNAME_SIZE - 1] = '\0';
    		for (p = ds->name; *p != '\0'; p++);
    		while (p > ds->name && *p != '/')
    			p--;
    		if (*p == '/')
    			p++;
    		strlcpy(ds->name, p, sizeof(ds->name));
    		btrfsic_dev_state_hashtable_add(ds,
    						&btrfsic_dev_state_hashtable);
    	}
    
    	ret = btrfsic_process_superblock(state, fs_devices);
    	if (0 != ret) {
    		mutex_unlock(&btrfsic_mutex);
    		btrfsic_unmount(root, fs_devices);
    		return ret;
    	}
    
    	if (state->print_mask & BTRFSIC_PRINT_MASK_INITIAL_DATABASE)
    		btrfsic_dump_database(state);
    	if (state->print_mask & BTRFSIC_PRINT_MASK_INITIAL_TREE)
    		btrfsic_dump_tree(state);
    
    	mutex_unlock(&btrfsic_mutex);
    	return 0;
    }
    
    void btrfsic_unmount(struct btrfs_root *root,
    		     struct btrfs_fs_devices *fs_devices)
    {
    	struct list_head *elem_all;
    	struct list_head *tmp_all;
    	struct btrfsic_state *state;
    	struct list_head *dev_head = &fs_devices->devices;
    	struct btrfs_device *device;
    
    	if (!btrfsic_is_initialized)
    		return;
    
    	mutex_lock(&btrfsic_mutex);
    
    	state = NULL;
    	list_for_each_entry(device, dev_head, dev_list) {
    		struct btrfsic_dev_state *ds;
    
    		if (!device->bdev || !device->name)
    			continue;
    
    		ds = btrfsic_dev_state_hashtable_lookup(
    				device->bdev,
    				&btrfsic_dev_state_hashtable);
    		if (NULL != ds) {
    			state = ds->state;
    			btrfsic_dev_state_hashtable_remove(ds);
    			btrfsic_dev_state_free(ds);
    		}
    	}
    
    	if (NULL == state) {
    		printk(KERN_INFO
    		       "btrfsic: error, cannot find state information"
    		       " on umount!\n");
    		mutex_unlock(&btrfsic_mutex);
    		return;
    	}
    
    	/*
    	 * Don't care about keeping the lists' state up to date,
    	 * just free all memory that was allocated dynamically.
    	 * Free the blocks and the block_links.
    	 */
    	list_for_each_safe(elem_all, tmp_all, &state->all_blocks_list) {
    		struct btrfsic_block *const b_all =
    		    list_entry(elem_all, struct btrfsic_block,
    			       all_blocks_node);
    		struct list_head *elem_ref_to;
    		struct list_head *tmp_ref_to;
    
    		list_for_each_safe(elem_ref_to, tmp_ref_to,
    				   &b_all->ref_to_list) {
    			struct btrfsic_block_link *const l =
    			    list_entry(elem_ref_to,
    				       struct btrfsic_block_link,
    				       node_ref_to);
    
    			if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE)
    				btrfsic_print_rem_link(state, l);
    
    			l->ref_cnt--;
    			if (0 == l->ref_cnt)
    				btrfsic_block_link_free(l);
    		}
    
    
    		if (b_all->is_iodone || b_all->never_written)
    
    			btrfsic_block_free(b_all);
    		else
    			printk(KERN_INFO "btrfs: attempt to free %c-block"
    			       " @%llu (%s/%llu/%d) on umount which is"
    			       " not yet iodone!\n",
    			       btrfsic_get_block_type(state, b_all),
    			       (unsigned long long)b_all->logical_bytenr,
    			       b_all->dev_state->name,
    			       (unsigned long long)b_all->dev_bytenr,
    			       b_all->mirror_num);
    	}
    
    	mutex_unlock(&btrfsic_mutex);
    
    	kfree(state);
    }