cregit-Linux how code gets into the kernel

Release 4.7 fs/buffer.c

Directory: fs
/*
 *  linux/fs/buffer.c
 *
 *  Copyright (C) 1991, 1992, 2002  Linus Torvalds
 */

/*
 * Start bdflush() with kernel_thread not syscall - Paul Gortmaker, 12/95
 *
 * Removed a lot of unnecessary code and simplified things now that
 * the buffer cache isn't our primary cache - Andrew Tridgell 12/96
 *
 * Speed up hash, lru, and free list operations.  Use gfp() for allocating
 * hash table, use SLAB cache for buffer heads. SMP threading.  -DaveM
 *
 * Added 32k buffer block sizes - these are required older ARM systems. - RMK
 *
 * async buffer flushing, 1999 Andrea Arcangeli <andrea@suse.de>
 */

#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/percpu.h>
#include <linux/slab.h>
#include <linux/capability.h>
#include <linux/blkdev.h>
#include <linux/file.h>
#include <linux/quotaops.h>
#include <linux/highmem.h>
#include <linux/export.h>
#include <linux/backing-dev.h>
#include <linux/writeback.h>
#include <linux/hash.h>
#include <linux/suspend.h>
#include <linux/buffer_head.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/bio.h>
#include <linux/notifier.h>
#include <linux/cpu.h>
#include <linux/bitops.h>
#include <linux/mpage.h>
#include <linux/bit_spinlock.h>
#include <trace/events/block.h>

static int fsync_buffers_list(spinlock_t *lock, struct list_head *list);
static int submit_bh_wbc(int rw, struct buffer_head *bh,
			 unsigned long bio_flags,
			 struct writeback_control *wbc);


#define BH_ENTRY(list) list_entry((list), struct buffer_head, b_assoc_buffers)


void init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *private) { bh->b_end_io = handler; bh->b_private = private; }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2996.67%150.00%
yan hongyan hong13.33%150.00%
Total30100.00%2100.00%

EXPORT_SYMBOL(init_buffer);
inline void touch_buffer(struct buffer_head *bh) { trace_block_touch_buffer(bh); mark_page_accessed(bh->b_page); }

Contributors

PersonTokensPropCommitsCommitProp
tejun heotejun heo23100.00%2100.00%
Total23100.00%2100.00%

EXPORT_SYMBOL(touch_buffer);
void __lock_buffer(struct buffer_head *bh) { wait_on_bit_lock_io(&bh->b_state, BH_Lock, TASK_UNINTERRUPTIBLE); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton1568.18%125.00%
william lee irwin iiiwilliam lee irwin iii627.27%250.00%
neil brownneil brown14.55%125.00%
Total22100.00%4100.00%

EXPORT_SYMBOL(__lock_buffer);
void unlock_buffer(struct buffer_head *bh) { clear_bit_unlock(BH_Lock, &bh->b_state); smp_mb__after_atomic(); wake_up_bit(&bh->b_state, BH_Lock); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2266.67%350.00%
nick pigginnick piggin618.18%116.67%
linus torvaldslinus torvalds412.12%116.67%
peter zijlstrapeter zijlstra13.03%116.67%
Total33100.00%6100.00%

EXPORT_SYMBOL(unlock_buffer); /* * Returns if the page has dirty or writeback buffers. If all the buffers * are unlocked and clean then the PageDirty information is stale. If * any of the pages are locked, it is assumed they are locked for IO. */
void buffer_check_dirty_writeback(struct page *page, bool *dirty, bool *writeback) { struct buffer_head *head, *bh; *dirty = false; *writeback = false; BUG_ON(!PageLocked(page)); if (!page_has_buffers(page)) return; if (PageWriteback(page)) *writeback = true; head = page_buffers(page); bh = head; do { if (buffer_locked(bh)) *writeback = true; if (buffer_dirty(bh)) *dirty = true; bh = bh->b_this_page; } while (bh != head); }

Contributors

PersonTokensPropCommitsCommitProp
mel gormanmel gorman117100.00%1100.00%
Total117100.00%1100.00%

EXPORT_SYMBOL(buffer_check_dirty_writeback); /* * Block until a buffer comes unlocked. This doesn't stop it * from becoming locked again - you have to lock it yourself * if you want to preserve its state. */
void __wait_on_buffer(struct buffer_head * bh) { wait_on_bit_io(&bh->b_state, BH_Lock, TASK_UNINTERRUPTIBLE); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton1463.64%233.33%
william lee irwin iiiwilliam lee irwin iii627.27%233.33%
neil brownneil brown14.55%116.67%
linus torvaldslinus torvalds14.55%116.67%
Total22100.00%6100.00%

EXPORT_SYMBOL(__wait_on_buffer);
static void __clear_page_buffers(struct page *page) { ClearPagePrivate(page); set_page_private(page, 0); put_page(page); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2382.14%250.00%
hugh dickinshugh dickins414.29%125.00%
kirill a. shutemovkirill a. shutemov13.57%125.00%
Total28100.00%4100.00%


static void buffer_io_error(struct buffer_head *bh, char *msg) { if (!test_bit(BH_Quiet, &bh->b_state)) printk_ratelimited(KERN_ERR "Buffer I/O error on dev %pg, logical block %llu%s\n", bh->b_bdev, (unsigned long long)bh->b_blocknr, msg); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2857.14%457.14%
robert elliottrobert elliott2040.82%228.57%
dmitriy monakhovdmitriy monakhov12.04%114.29%
Total49100.00%7100.00%

/* * End-of-IO handler helper function which does not touch the bh after * unlocking it. * Note: unlock_buffer() sort-of does touch the bh after unlocking it, but * a race there is benign: unlock_buffer() only use the bh's address for * hashing after unlocking the buffer, so it doesn't actually touch the bh * itself. */
static void __end_buffer_read_notouch(struct buffer_head *bh, int uptodate) { if (uptodate) { set_buffer_uptodate(bh); } else { /* This happens, due to failed READA attempts. */ clear_buffer_uptodate(bh); } unlock_buffer(bh); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2564.10%466.67%
linus torvaldslinus torvalds1128.21%116.67%
dmitriy monakhovdmitriy monakhov37.69%116.67%
Total39100.00%6100.00%

/* * Default synchronous end-of-IO handler.. Just mark it up-to-date and * unlock the buffer. This is what ll_rw_block uses too. */
void end_buffer_read_sync(struct buffer_head *bh, int uptodate) { __end_buffer_read_notouch(bh, uptodate); put_bh(bh); }

Contributors

PersonTokensPropCommitsCommitProp
dmitriy monakhovdmitriy monakhov1976.00%150.00%
andrew mortonandrew morton624.00%150.00%
Total25100.00%2100.00%

EXPORT_SYMBOL(end_buffer_read_sync);
void end_buffer_write_sync(struct buffer_head *bh, int uptodate) { if (uptodate) { set_buffer_uptodate(bh); } else { buffer_io_error(bh, ", lost sync page write"); set_buffer_write_io_error(bh); clear_buffer_uptodate(bh); } unlock_buffer(bh); put_bh(bh); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton3870.37%342.86%
linus torvaldslinus torvalds1527.78%342.86%
robert elliottrobert elliott11.85%114.29%
Total54100.00%7100.00%

EXPORT_SYMBOL(end_buffer_write_sync); /* * Various filesystems appear to want __find_get_block to be non-blocking. * But it's the page lock which protects the buffers. To get around this, * we get exclusion from try_to_free_buffers with the blockdev mapping's * private_lock. * * Hack idea: for the blockdev mapping, i_bufferlist_lock contention * may be quite high. This code could TryLock the page, and if that * succeeds, there is no need to take private_lock. (But if * private_lock is contended then so is mapping->tree_lock). */
static struct buffer_head * __find_get_block_slow(struct block_device *bdev, sector_t block) { struct inode *bd_inode = bdev->bd_inode; struct address_space *bd_mapping = bd_inode->i_mapping; struct buffer_head *ret = NULL; pgoff_t index; struct buffer_head *bh; struct buffer_head *head; struct page *page; int all_mapped = 1; index = block >> (PAGE_SHIFT - bd_inode->i_blkbits); page = find_get_page_flags(bd_mapping, index, FGP_ACCESSED); if (!page) goto out; spin_lock(&bd_mapping->private_lock); if (!page_has_buffers(page)) goto out_unlock; head = page_buffers(page); bh = head; do { if (!buffer_mapped(bh)) all_mapped = 0; else if (bh->b_blocknr == block) { ret = bh; get_bh(bh); goto out_unlock; } bh = bh->b_this_page; } while (bh != head); /* we might be here because some of the buffers on this page are * not mapped. This is due to various races between * file io on the block device and getblk. It gets dealt with * elsewhere, don't buffer_error if we had some unmapped buffers */ if (all_mapped) { printk("__find_get_block_slow() failed. " "block=%llu, b_blocknr=%llu\n", (unsigned long long)block, (unsigned long long)bh->b_blocknr); printk("b_state=0x%08lx, b_size=%zu\n", bh->b_state, bh->b_size); printk("device %pg blocksize: %d\n", bdev, 1 << bd_inode->i_blkbits); } out_unlock: spin_unlock(&bd_mapping->private_lock); put_page(page); out: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton16565.74%731.82%
pre-gitpre-git3513.94%522.73%
nikanth karthikesannikanth karthikesan135.18%14.55%
chris masonchris mason124.78%14.55%
al viroal viro114.38%29.09%
linus torvaldslinus torvalds51.99%14.55%
tao matao ma31.20%14.55%
mel gormanmel gorman31.20%14.55%
kirill a. shutemovkirill a. shutemov20.80%14.55%
badari pulavartybadari pulavarty10.40%14.55%
dmitriy monakhovdmitriy monakhov10.40%14.55%
Total251100.00%22100.00%

/* * Kick the writeback threads then try to free up some ZONE_NORMAL memory. */
static void free_more_memory(void) { struct zoneref *z; int nid; wakeup_flusher_threads(1024, WB_REASON_FREE_MORE_MEM); yield(); for_each_online_node(nid) { z = first_zones_zonelist(node_zonelist(nid, GFP_NOFS), gfp_zone(GFP_NOFS), NULL); if (z->zone) try_to_free_pages(node_zonelist(nid, GFP_NOFS), 0, GFP_NOFS, NULL); } }

Contributors

PersonTokensPropCommitsCommitProp
mel gormanmel gorman3142.47%531.25%
andrew mortonandrew morton2331.51%318.75%
linus torvaldslinus torvalds56.85%16.25%
pre-gitpre-git45.48%16.25%
al viroal viro34.11%212.50%
kamezawa hiroyukikamezawa hiroyuki22.74%16.25%
andy whitcroftandy whitcroft22.74%16.25%
curt wohlgemuthcurt wohlgemuth22.74%16.25%
jens axboejens axboe11.37%16.25%
Total73100.00%16100.00%

/* * I/O completion handler for block_read_full_page() - pages * which come unlocked at the end of I/O. */
static void end_buffer_async_read(struct buffer_head *bh, int uptodate) { unsigned long flags; struct buffer_head *first; struct buffer_head *tmp; struct page *page; int page_uptodate = 1; BUG_ON(!buffer_async_read(bh)); page = bh->b_page; if (uptodate) { set_buffer_uptodate(bh); } else { clear_buffer_uptodate(bh); buffer_io_error(bh, ", async page read"); SetPageError(page); } /* * Be _very_ careful from here on. Bad things can happen if * two buffer heads end IO at almost the same time and both * decide that the page is now completely done. */ first = page_buffers(page); local_irq_save(flags); bit_spin_lock(BH_Uptodate_Lock, &first->b_state); clear_buffer_async_read(bh); unlock_buffer(bh); tmp = bh; do { if (!buffer_uptodate(tmp)) page_uptodate = 0; if (buffer_async_read(tmp)) { BUG_ON(!buffer_locked(tmp)); goto still_busy; } tmp = tmp->b_this_page; } while (tmp != bh); bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); local_irq_restore(flags); /* * If none of the buffers had errors and they are all * uptodate then we can set the page uptodate. */ if (page_uptodate && !PageError(page)) SetPageUptodate(page); unlock_page(page); return; still_busy: bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); local_irq_restore(flags); return; }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton13760.89%538.46%
nick pigginnick piggin4319.11%17.69%
pre-gitpre-git2812.44%430.77%
linus torvaldslinus torvalds156.67%215.38%
robert elliottrobert elliott20.89%17.69%
Total225100.00%13100.00%

/* * Completion handler for block_write_full_page() - pages which are unlocked * during I/O, and which have PageWriteback cleared upon I/O completion. */
void end_buffer_async_write(struct buffer_head *bh, int uptodate) { unsigned long flags; struct buffer_head *first; struct buffer_head *tmp; struct page *page; BUG_ON(!buffer_async_write(bh)); page = bh->b_page; if (uptodate) { set_buffer_uptodate(bh); } else { buffer_io_error(bh, ", lost async page write"); set_bit(AS_EIO, &page->mapping->flags); set_buffer_write_io_error(bh); clear_buffer_uptodate(bh); SetPageError(page); } first = page_buffers(page); local_irq_save(flags); bit_spin_lock(BH_Uptodate_Lock, &first->b_state); clear_buffer_async_write(bh); unlock_buffer(bh); tmp = bh->b_this_page; while (tmp != bh) { if (buffer_async_write(tmp)) { BUG_ON(!buffer_locked(tmp)); goto still_busy; } tmp = tmp->b_this_page; } bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); local_irq_restore(flags); end_page_writeback(page); return; still_busy: bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); local_irq_restore(flags); return; }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton15574.88%758.33%
nick pigginnick piggin4320.77%18.33%
jan karajan kara52.42%18.33%
linus torvaldslinus torvalds20.97%18.33%
pre-gitpre-git10.48%18.33%
robert elliottrobert elliott10.48%18.33%
Total207100.00%12100.00%

EXPORT_SYMBOL(end_buffer_async_write); /* * If a page's buffers are under async readin (end_buffer_async_read * completion) then there is a possibility that another thread of * control could lock one of the buffers after it has completed * but while some of the other buffers have not completed. This * locked buffer would confuse end_buffer_async_read() into not unlocking * the page. So the absence of BH_Async_Read tells end_buffer_async_read() * that this buffer is not under async I/O. * * The page comes unlocked when it has no locked buffer_async buffers * left. * * PageLocked prevents anyone starting new async I/O reads any of * the buffers. * * PageWriteback is used to prevent simultaneous writeout of the same * page. * * PageLocked prevents anyone from starting writeback of a page which is * under read I/O (PageWriteback is only ever set against a locked page). */
static void mark_buffer_async_read(struct buffer_head *bh) { bh->b_end_io = end_buffer_async_read; set_buffer_async_read(bh); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton2195.45%150.00%
christoph hellwigchristoph hellwig14.55%150.00%
Total22100.00%2100.00%


static void mark_buffer_async_write_endio(struct buffer_head *bh, bh_end_io_t *handler) { bh->b_end_io = handler; set_buffer_async_write(bh); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git1453.85%342.86%
chris masonchris mason623.08%114.29%
andrew mortonandrew morton519.23%228.57%
h hartley sweetenh hartley sweeten13.85%114.29%
Total26100.00%7100.00%


void mark_buffer_async_write(struct buffer_head *bh) { mark_buffer_async_write_endio(bh, end_buffer_async_write); }

Contributors

PersonTokensPropCommitsCommitProp
chris masonchris mason17100.00%1100.00%
Total17100.00%1100.00%

EXPORT_SYMBOL(mark_buffer_async_write); /* * fs/buffer.c contains helper functions for buffer-backed address space's * fsync functions. A common requirement for buffer-based filesystems is * that certain data from the backing blockdev needs to be written out for * a successful fsync(). For example, ext2 indirect blocks need to be * written back and waited upon before fsync() returns. * * The functions mark_buffer_inode_dirty(), fsync_inode_buffers(), * inode_has_buffers() and invalidate_inode_buffers() are provided for the * management of a list of dependent buffers at ->i_mapping->private_list. * * Locking is a little subtle: try_to_free_buffers() will remove buffers * from their controlling inode's queue when they are being freed. But * try_to_free_buffers() will be operating against the *blockdev* mapping * at the time, not against the S_ISREG file which depends on those buffers. * So the locking for private_list is via the private_lock in the address_space * which backs the buffers. Which is different from the address_space * against which the buffers are listed. So for a particular address_space, * mapping->private_lock does *not* protect mapping->private_list! In fact, * mapping->private_list will always be protected by the backing blockdev's * ->private_lock. * * Which introduces a requirement: all buffers on an address_space's * ->private_list must be from the same address_space: the blockdev's. * * address_spaces which do not place buffers at ->private_list via these * utility functions are free to use private_lock and private_list for * whatever they want. The only requirement is that list_empty(private_list) * be true at clear_inode() time. * * FIXME: clear_inode should not call invalidate_inode_buffers(). The * filesystems should do that. invalidate_inode_buffers() should just go * BUG_ON(!list_empty). * * FIXME: mark_buffer_dirty_inode() is a data-plane operation. It should * take an address_space, not an inode. And it should be called * mark_buffer_dirty_fsync() to clearly define why those buffers are being * queued up. * * FIXME: mark_buffer_dirty_inode() doesn't need to add the buffer to the * list if it is already on a list. Because if the buffer is on a list, * it *must* already be on the right one. If not, the filesystem is being * silly. This will save a ton of locking. But first we have to ensure * that buffers are taken *off* the old inode's list when they are freed * (presumably in truncate). That requires careful auditing of all * filesystems (do it inside bforget()). It could also be done by bringing * b_inode back. */ /* * The buffer's backing address_space's private_lock must be held */
static void __remove_assoc_queue(struct buffer_head *bh) { list_del_init(&bh->b_assoc_buffers); WARN_ON(!bh->b_assoc_map); if (buffer_write_io_error(bh)) set_bit(AS_EIO, &bh->b_assoc_map->flags); bh->b_assoc_map = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
jan karajan kara3363.46%150.00%
andrew mortonandrew morton1936.54%150.00%
Total52100.00%2100.00%


int inode_has_buffers(struct inode *inode) { return !list_empty(&inode->i_data.private_list); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton22100.00%2100.00%
Total22100.00%2100.00%

/* * osync is designed to support O_SYNC io. It waits synchronously for * all already-submitted IO to complete, but does not queue any new * writes to the disk. * * To do O_SYNC writes, just queue the buffer writes with ll_rw_block as * you dirty the buffers, and then use osync_inode_buffers to wait for * completion. Any other dirty buffers which are not yet queued for * write will not be flushed to disk by the osync. */
static int osync_buffers_list(spinlock_t *lock, struct list_head *list) { struct buffer_head *bh; struct list_head *p; int err = 0; spin_lock(lock); repeat: list_for_each_prev(p, list) { bh = BH_ENTRY(p); if (buffer_locked(bh)) { get_bh(bh); spin_unlock(lock); wait_on_buffer(bh); if (!buffer_uptodate(bh)) err = -EIO; brelse(bh); spin_lock(lock); goto repeat; } } spin_unlock(lock); return err; }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton6256.36%110.00%
pre-gitpre-git2825.45%550.00%
linus torvaldslinus torvalds1210.91%330.00%
christoph hellwigchristoph hellwig87.27%110.00%
Total110100.00%10100.00%


static void do_thaw_one(struct super_block *sb, void *unused) { while (sb->s_bdev && !thaw_bdev(sb->s_bdev, sb)) printk(KERN_WARNING "Emergency Thaw on %pg\n", sb->s_bdev); }

Contributors

PersonTokensPropCommitsCommitProp
eric sandeeneric sandeen3175.61%116.67%
al viroal viro717.07%233.33%
jens axboejens axboe12.44%116.67%
h hartley sweetenh hartley sweeten12.44%116.67%
dmitriy monakhovdmitriy monakhov12.44%116.67%
Total41100.00%6100.00%


static void do_thaw_all(struct work_struct *work) { iterate_supers(do_thaw_one, NULL); kfree(work); printk(KERN_WARNING "Emergency Thaw complete\n"); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro1448.28%133.33%
eric sandeeneric sandeen1034.48%133.33%
jens axboejens axboe517.24%133.33%
Total29100.00%3100.00%

/** * emergency_thaw_all -- forcibly thaw every frozen filesystem * * Used for emergency unfreeze of all filesystems via SysRq */
void emergency_thaw_all(void) { struct work_struct *work; work = kmalloc(sizeof(*work), GFP_ATOMIC); if (work) { INIT_WORK(work, do_thaw_all); schedule_work(work); } }

Contributors

PersonTokensPropCommitsCommitProp
jens axboejens axboe3274.42%150.00%
eric sandeeneric sandeen1125.58%150.00%
Total43100.00%2100.00%

/** * sync_mapping_buffers - write out & wait upon a mapping's "associated" buffers * @mapping: the mapping which wants those buffers written * * Starts I/O against the buffers at mapping->private_list, and waits upon * that I/O. * * Basically, this is a convenience function for fsync(). * @mapping is a file or directory which needs those buffers to be written for * a successful fsync(). */
int sync_mapping_buffers(struct address_space *mapping) { struct address_space *buffer_mapping = mapping->private_data; if (buffer_mapping == NULL || list_empty(&mapping->private_list)) return 0; return fsync_buffers_list(&buffer_mapping->private_lock, &mapping->private_list); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton4998.00%150.00%
rafael aquinirafael aquini12.00%150.00%
Total50100.00%2100.00%

EXPORT_SYMBOL(sync_mapping_buffers); /* * Called when we've recently written block `bblock', and it is known that * `bblock' was for a buffer_boundary() buffer. This means that the block at * `bblock + 1' is probably a dirty indirect block. Hunt it down and, if it's * dirty, schedule it for IO. So that indirects merge nicely with their data. */
void write_boundary_block(struct block_device *bdev, sector_t bblock, unsigned blocksize) { struct buffer_head *bh = __find_get_block(bdev, bblock + 1, blocksize); if (bh) { if (buffer_dirty(bh)) ll_rw_block(WRITE, 1, &bh); put_bh(bh); } }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton60100.00%1100.00%
Total60100.00%1100.00%


void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode) { struct address_space *mapping = inode->i_mapping; struct address_space *buffer_mapping = bh->b_page->mapping; mark_buffer_dirty(bh); if (!mapping->private_data) { mapping->private_data = buffer_mapping; } else { BUG_ON(mapping->private_data != buffer_mapping); } if (!bh->b_assoc_map) { spin_lock(&buffer_mapping->private_lock); list_move_tail(&bh->b_assoc_buffers, &mapping->private_list); bh->b_assoc_map = mapping; spin_unlock(&buffer_mapping->private_lock); } }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton7971.17%225.00%
christoph hellwigchristoph hellwig1715.32%112.50%
jan karajan kara87.21%225.00%
rafael aquinirafael aquini32.70%112.50%
eric sesterhenneric sesterhenn32.70%112.50%
anton blanchardanton blanchard10.90%112.50%
Total111100.00%8100.00%

EXPORT_SYMBOL(mark_buffer_dirty_inode); /* * Mark the page dirty, and set it dirty in the radix tree, and mark the inode * dirty. * * If warn is true, then emit a warning if the page is not uptodate and has * not been truncated. * * The caller must hold lock_page_memcg(). */
static void __set_page_dirty(struct page *page, struct address_space *mapping, int warn) { unsigned long flags; spin_lock_irqsave(&mapping->tree_lock, flags); if (page->mapping) { /* Race with truncate? */ WARN_ON_ONCE(warn && !PageUptodate(page)); account_page_dirtied(page, mapping); radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } spin_unlock_irqrestore(&mapping->tree_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton4957.65%436.36%
nick pigginnick piggin2124.71%218.18%
kosaki motohirokosaki motohiro1011.76%19.09%
christoph lameterchristoph lameter22.35%19.09%
linus torvaldslinus torvalds11.18%19.09%
edward shishkinedward shishkin11.18%19.09%
peter zijlstrapeter zijlstra11.18%19.09%
Total85100.00%11100.00%

/* * Add a page to the dirty page list. * * It is a sad fact of life that this function is called from several places * deeply under spinlocking. It may not sleep. * * If the page has buffers, the uptodate buffers are set dirty, to preserve * dirty-state coherency between the page and the buffers. It the page does * not have buffers then when they are later attached they will all be set * dirty. * * The buffers are dirtied before the page is dirtied. There's a small race * window in which a writepage caller may see the page cleanness but not the * buffer dirtiness. That's fine. If this code were to set the page dirty * before the buffers, a concurrent writepage caller could clear the page dirty * bit, see a bunch of clean buffers and we'd end up with dirty buffers/clean * page on the dirty page list. * * We use private_lock to lock against try_to_free_buffers while using the * page's buffer list. Also use this to protect against clean buffers being * added to the page after it was set dirty. * * FIXME: may need to call ->reservepage here as well. That's rather up to the * address_space though. */
int __set_page_dirty_buffers(struct page *page) { int newly_dirty; struct address_space *mapping = page_mapping(page); if (unlikely(!mapping)) return !TestSetPageDirty(page); spin_lock(&mapping->private_lock); if (page_has_buffers(page)) { struct buffer_head *head = page_buffers(page); struct buffer_head *bh = head; do { set_buffer_dirty(bh); bh = bh->b_this_page; } while (bh != head); } /* * Lock out page->mem_cgroup migration to keep PageDirty * synchronized with per-memcg dirty page counters. */ lock_page_memcg(page); newly_dirty = !TestSetPageDirty(page); spin_unlock(&mapping->private_lock); if (newly_dirty) __set_page_dirty(page, mapping, 1); unlock_page_memcg(page); if (newly_dirty) __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); return newly_dirty; }

Contributors

PersonTokensPropCommitsCommitProp
nick pigginnick piggin10771.81%120.00%
greg thelengreg thelen2013.42%120.00%
linus torvaldslinus torvalds1812.08%120.00%
johannes weinerjohannes weiner42.68%240.00%
Total149100.00%5100.00%

EXPORT_SYMBOL(__set_page_dirty_buffers); /* * Write out and wait upon a list of buffers. * * We have conflicting pressures: we want to make sure that all * initially dirty buffers get waited on, but that any subsequently * dirtied buffers don't. After all, we don't want fsync to last * forever if somebody is actively writing to the file. * * Do this in two main stages: first we copy dirty buffers to a * temporary inode list, queueing the writes as we go. Then we clean * up, waiting for those writes to complete. * * During this second stage, any subsequent updates to the file may end * up refiling the buffer on the original inode's dirty list again, so * there is a chance we will end up with a buffer queued for write but * not yet completed on that list. So, as a final cleanup we go through * the osync code to catch these locked, dirty buffers without requeuing * any newly dirty buffers for write. */
static int fsync_buffers_list(spinlock_t *lock, struct list_head *list) { struct buffer_head *bh; struct list_head tmp; struct address_space *mapping; int err = 0, err2; struct blk_plug plug; INIT_LIST_HEAD(&tmp); blk_start_plug(&plug); spin_lock(lock); while (!list_empty(list)) { bh = BH_ENTRY(list->next); mapping = bh->b_assoc_map; __remove_assoc_queue(bh); /* Avoid race with mark_buffer_dirty_inode() which does * a lockless check and we rely on seeing the dirty bit */ smp_mb(); if (buffer_dirty(bh) || buffer_locked(bh)) { list_add(&bh->b_assoc_buffers, &tmp); bh->b_assoc_map = mapping; if (buffer_dirty(bh)) { get_bh(bh); spin_unlock(lock); /* * Ensure any pending I/O completes so that * write_dirty_buffer() actually writes the * current contents - it is a noop if I/O is * still in flight on potentially older * contents. */ write_dirty_buffer(bh, WRITE_SYNC); /* * Kick off IO for the previous mapping. Note * that we will not run the very last mapping, * wait_on_buffer() will do that for us * through sync_buffer(). */ brelse(bh); spin_lock(lock); } } } spin_unlock(lock); blk_finish_plug(&plug); spin_lock(lock); while (!list_empty(&tmp)) { bh = BH_ENTRY(tmp.prev); get_bh(bh); mapping = bh->b_assoc_map; __remove_assoc_queue(bh); /* Avoid race with mark_buffer_dirty_inode() which does * a lockless check and we rely on seeing the dirty bit */ smp_mb(); if (buffer_dirty(bh)) { list_add(&bh->b_assoc_buffers, &mapping->private_list); bh->b_assoc_map = mapping; } spin_unlock(lock); wait_on_buffer(bh); if (!buffer_uptodate(bh)) err = -EIO; brelse(bh); spin_lock(lock); } spin_unlock(lock); err2 = osync_buffers_list(lock, list); if (err) return err; else return err2; }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton15751.99%321.43%
jan karajan kara6220.53%321.43%
jens axboejens axboe289.27%321.43%
linus torvaldslinus torvalds289.27%214.29%
pre-gitpre-git237.62%17.14%
christoph hellwigchristoph hellwig41.32%214.29%
Total302100.00%14100.00%

/* * Invalidate any and all dirty buffers on a given inode. We are * probably unmounting the fs, but that doesn't mean we have already * done a sync(). Just drop the buffers from the inode list. * * NOTE: we take the inode's blockdev's mapping's private_lock. Which * assumes that all the buffers are against the blockdev. Not true * for reiserfs. */
void invalidate_inode_buffers(struct inode *inode) { if (inode_has_buffers(inode)) { struct address_space *mapping = &inode->i_data; struct list_head *list = &mapping->private_list; struct address_space *buffer_mapping = mapping->private_data; spin_lock(&buffer_mapping->private_lock); while (!list_empty(list)) __remove_assoc_queue(BH_ENTRY(list->next)); spin_unlock(&buffer_mapping->private_lock); } }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton6174.39%350.00%
pre-gitpre-git1923.17%116.67%
rafael aquinirafael aquini11.22%116.67%
linus torvaldslinus torvalds11.22%116.67%
Total82100.00%6100.00%

EXPORT_SYMBOL(invalidate_inode_buffers); /* * Remove any clean buffers from the inode's buffer list. This is called * when we're trying to free the inode itself. Those buffers can pin it. * * Returns true if all buffers were removed. */
int remove_inode_buffers(struct inode *inode) { int ret = 1; if (inode_has_buffers(inode)) { struct address_space *mapping = &inode->i_data; struct list_head *list = &mapping->private_list; struct address_space *buffer_mapping = mapping->private_data; spin_lock(&buffer_mapping->private_lock); while (!list_empty(list)) { struct buffer_head *bh = BH_ENTRY(list->next); if (buffer_dirty(bh)) { ret = 0; break; } __remove_assoc_queue(bh); } spin_unlock(&buffer_mapping->private_lock); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
andrew mortonandrew morton11299.12%150.00%
rafael aquinirafael aquini10.88%150.00%
Total113100.00%2100.00%

/* * Create the appropriate buffers when given a page for data area and * the size of each buffer.. Use the bh->b_this_page linked list to * follow the buffers created. Return NULL if unable to create more * buffers. * * The retry flag is used to differentiate async IO (paging, swapping) * which may not fail from ordinary buffer allocations. */
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size, int retry) { struct buffer_head *bh, *head; long offset; try_again: head = NULL; offset = PAGE_SIZE; while ((offset -= size) >= 0) { bh = alloc_buffer_head(GFP_NOFS); if (!bh) goto no_grow; bh->b_this_page = head; bh->b_blocknr = -1; head = bh; bh->b_size = size; /* Link the buffer to its page */ set_bh_page(bh, page, offset); } return head; /* * In case anything failed, we just free everything we got. */ no_grow: if (head) { do