Skip to content
Snippets Groups Projects
buffer.c 81.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * Buffer-head allocation
     */
    static kmem_cache_t *bh_cachep;
    
    /*
     * Once the number of bh's in the machine exceeds this level, we start
     * stripping them in writeback.
     */
    static int max_buffer_heads;
    
    int buffer_heads_over_limit;
    
    struct bh_accounting {
    	int nr;			/* Number of live bh's */
    	int ratelimit;		/* Limit cacheline bouncing */
    };
    
    static DEFINE_PER_CPU(struct bh_accounting, bh_accounting) = {0, 0};
    
    static void recalc_bh_state(void)
    {
    	int i;
    	int tot = 0;
    
    	if (__get_cpu_var(bh_accounting).ratelimit++ < 4096)
    		return;
    	__get_cpu_var(bh_accounting).ratelimit = 0;
    	for_each_cpu(i)
    		tot += per_cpu(bh_accounting, i).nr;
    	buffer_heads_over_limit = (tot > max_buffer_heads);
    }
    	
    struct buffer_head *alloc_buffer_head(unsigned int __nocast gfp_flags)
    {
    	struct buffer_head *ret = kmem_cache_alloc(bh_cachep, gfp_flags);
    	if (ret) {
    		preempt_disable();
    		__get_cpu_var(bh_accounting).nr++;
    		recalc_bh_state();
    		preempt_enable();
    	}
    	return ret;
    }
    EXPORT_SYMBOL(alloc_buffer_head);
    
    void free_buffer_head(struct buffer_head *bh)
    {
    	BUG_ON(!list_empty(&bh->b_assoc_buffers));
    	kmem_cache_free(bh_cachep, bh);
    	preempt_disable();
    	__get_cpu_var(bh_accounting).nr--;
    	recalc_bh_state();
    	preempt_enable();
    }
    EXPORT_SYMBOL(free_buffer_head);
    
    static void
    init_buffer_head(void *data, kmem_cache_t *cachep, unsigned long flags)
    {
    	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
    			    SLAB_CTOR_CONSTRUCTOR) {
    		struct buffer_head * bh = (struct buffer_head *)data;
    
    		memset(bh, 0, sizeof(*bh));
    		INIT_LIST_HEAD(&bh->b_assoc_buffers);
    	}
    }
    
    #ifdef CONFIG_HOTPLUG_CPU
    static void buffer_exit_cpu(int cpu)
    {
    	int i;
    	struct bh_lru *b = &per_cpu(bh_lrus, cpu);
    
    	for (i = 0; i < BH_LRU_SIZE; i++) {
    		brelse(b->bhs[i]);
    		b->bhs[i] = NULL;
    	}
    }
    
    static int buffer_cpu_notify(struct notifier_block *self,
    			      unsigned long action, void *hcpu)
    {
    	if (action == CPU_DEAD)
    		buffer_exit_cpu((unsigned long)hcpu);
    	return NOTIFY_OK;
    }
    #endif /* CONFIG_HOTPLUG_CPU */
    
    void __init buffer_init(void)
    {
    	int nrpages;
    
    	bh_cachep = kmem_cache_create("buffer_head",
    			sizeof(struct buffer_head), 0,
    
    			SLAB_RECLAIM_ACCOUNT|SLAB_PANIC, init_buffer_head, NULL);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 * Limit the bh occupancy to 10% of ZONE_NORMAL
    	 */
    	nrpages = (nr_free_buffer_pages() * 10) / 100;
    	max_buffer_heads = nrpages * (PAGE_SIZE / sizeof(struct buffer_head));
    	hotcpu_notifier(buffer_cpu_notify, 0);
    }
    
    EXPORT_SYMBOL(__bforget);
    EXPORT_SYMBOL(__brelse);
    EXPORT_SYMBOL(__wait_on_buffer);
    EXPORT_SYMBOL(block_commit_write);
    EXPORT_SYMBOL(block_prepare_write);
    EXPORT_SYMBOL(block_read_full_page);
    EXPORT_SYMBOL(block_sync_page);
    EXPORT_SYMBOL(block_truncate_page);
    EXPORT_SYMBOL(block_write_full_page);
    EXPORT_SYMBOL(cont_prepare_write);
    EXPORT_SYMBOL(end_buffer_async_write);
    EXPORT_SYMBOL(end_buffer_read_sync);
    EXPORT_SYMBOL(end_buffer_write_sync);
    EXPORT_SYMBOL(file_fsync);
    EXPORT_SYMBOL(fsync_bdev);
    EXPORT_SYMBOL(generic_block_bmap);
    EXPORT_SYMBOL(generic_commit_write);
    EXPORT_SYMBOL(generic_cont_expand);
    EXPORT_SYMBOL(init_buffer);
    EXPORT_SYMBOL(invalidate_bdev);
    EXPORT_SYMBOL(ll_rw_block);
    EXPORT_SYMBOL(mark_buffer_dirty);
    EXPORT_SYMBOL(submit_bh);
    EXPORT_SYMBOL(sync_dirty_buffer);
    EXPORT_SYMBOL(unlock_buffer);