cregit-Linux how code gets into the kernel

Release 4.11 fs/sysv/itree.c

Directory: fs/sysv
/*
 *  linux/fs/sysv/itree.c
 *
 *  Handling of indirect blocks' trees.
 *  AV, Sep--Dec 2000
 */

#include <linux/buffer_head.h>
#include <linux/mount.h>
#include <linux/string.h>
#include "sysv.h"



enum {DIRECT = 10, DEPTH = 4};	

/* Have triple indirect */


static inline void dirty_indirect(struct buffer_head *bh, struct inode *inode) { mark_buffer_dirty_inode(bh, inode); if (IS_SYNC(inode)) sync_dirty_buffer(bh); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds3597.22%150.00%
Andrew Morton12.78%150.00%
Total36100.00%2100.00%


static int block_to_path(struct inode *inode, long block, int offsets[DEPTH]) { struct super_block *sb = inode->i_sb; struct sysv_sb_info *sbi = SYSV_SB(sb); int ptrs_bits = sbi->s_ind_per_block_bits; unsigned long indirect_blocks = sbi->s_ind_per_block, double_blocks = sbi->s_ind_per_block_2; int n = 0; if (block < 0) { printk("sysv_block_map: block < 0\n"); } else if (block < DIRECT) { offsets[n++] = block; } else if ( (block -= DIRECT) < indirect_blocks) { offsets[n++] = DIRECT; offsets[n++] = block; } else if ((block -= indirect_blocks) < double_blocks) { offsets[n++] = DIRECT+1; offsets[n++] = block >> ptrs_bits; offsets[n++] = block & (indirect_blocks - 1); } else if (((block -= double_blocks) >> (ptrs_bits * 2)) < indirect_blocks) { offsets[n++] = DIRECT+2; offsets[n++] = block >> (ptrs_bits * 2); offsets[n++] = (block >> ptrs_bits) & (indirect_blocks - 1); offsets[n++] = block & (indirect_blocks - 1); } else { /* nothing */; } return n; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds24093.75%150.00%
Christoph Hellwig166.25%150.00%
Total256100.00%2100.00%


static inline int block_to_cpu(struct sysv_sb_info *sbi, sysv_zone_t nr) { return sbi->s_block_base + fs32_to_cpu(sbi, nr); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds2177.78%133.33%
Christoph Hellwig518.52%133.33%
Al Viro13.70%133.33%
Total27100.00%3100.00%

typedef struct { sysv_zone_t *p; sysv_zone_t key; struct buffer_head *bh; } Indirect; static DEFINE_RWLOCK(pointers_lock);
static inline void add_chain(Indirect *p, struct buffer_head *bh, sysv_zone_t *v) { p->key = *(p->p = v); p->bh = bh; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds3897.44%150.00%
Al Viro12.56%150.00%
Total39100.00%2100.00%


static inline int verify_chain(Indirect *from, Indirect *to) { while (from <= to && from->key == *from->p) from++; return (from > to); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds40100.00%1100.00%
Total40100.00%1100.00%


static inline sysv_zone_t *block_end(struct buffer_head *bh) { return (sysv_zone_t*)((char*)bh->b_data + bh->b_size); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds3093.75%150.00%
Al Viro26.25%150.00%
Total32100.00%2100.00%

/* * Requires read_lock(&pointers_lock) or write_lock(&pointers_lock) */
static Indirect *get_branch(struct inode *inode, int depth, int offsets[], Indirect chain[], int *err) { struct super_block *sb = inode->i_sb; Indirect *p = chain; struct buffer_head *bh; *err = 0; add_chain(chain, NULL, SYSV_I(inode)->i_data + *offsets); if (!p->key) goto no_block; while (--depth) { int block = block_to_cpu(SYSV_SB(sb), p->key); bh = sb_bread(sb, block); if (!bh) goto failure; if (!verify_chain(chain, p)) goto changed; add_chain(++p, bh, (sysv_zone_t*)bh->b_data + *++offsets); if (!p->key) goto no_block; } return NULL; changed: brelse(bh); *err = -EAGAIN; goto no_block; failure: *err = -EIO; no_block: return p; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds18495.34%350.00%
Al Viro63.11%233.33%
Christoph Hellwig31.55%116.67%
Total193100.00%6100.00%


static int alloc_branch(struct inode *inode, int num, int *offsets, Indirect *branch) { int blocksize = inode->i_sb->s_blocksize; int n = 0; int i; branch[0].key = sysv_new_block(inode->i_sb); if (branch[0].key) for (n = 1; n < num; n++) { struct buffer_head *bh; int parent; /* Allocate the next block */ branch[n].key = sysv_new_block(inode->i_sb); if (!branch[n].key) break; /* * Get buffer_head for parent block, zero it out and set * the pointer to new one, then send parent to disk. */ parent = block_to_cpu(SYSV_SB(inode->i_sb), branch[n-1].key); bh = sb_getblk(inode->i_sb, parent); lock_buffer(bh); memset(bh->b_data, 0, blocksize); branch[n].bh = bh; branch[n].p = (sysv_zone_t*) bh->b_data + offsets[n]; *branch[n].p = branch[n].key; set_buffer_uptodate(bh); unlock_buffer(bh); dirty_indirect(bh, inode); } if (n == num) return 0; /* Allocation failed, free what we already allocated */ for (i = 1; i < n; i++) bforget(branch[i].bh); for (i = 0; i < n; i++) sysv_free_block(inode->i_sb, branch[i].key); return -ENOSPC; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds28098.25%240.00%
Christoph Hellwig31.05%120.00%
Al Viro10.35%120.00%
Andrew Morton10.35%120.00%
Total285100.00%5100.00%


static inline int splice_branch(struct inode *inode, Indirect chain[], Indirect *where, int num) { int i; /* Verify that place we are splicing to is still there and vacant */ write_lock(&pointers_lock); if (!verify_chain(chain, where-1) || *where->p) goto changed; *where->p = where->key; write_unlock(&pointers_lock); inode->i_ctime = current_time(inode); /* had we spliced it onto indirect block? */ if (where->bh) dirty_indirect(where->bh, inode); if (IS_SYNC(inode)) sysv_sync_inode(inode); else mark_inode_dirty(inode); return 0; changed: write_unlock(&pointers_lock); for (i = 1; i < num; i++) bforget(where[i].bh); for (i = 0; i < num; i++) sysv_free_block(inode->i_sb, where[i].key); return -EAGAIN; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds15487.50%133.33%
Christoph Hellwig1810.23%133.33%
Deepa Dinamani42.27%133.33%
Total176100.00%3100.00%


static int get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { int err = -EIO; int offsets[DEPTH]; Indirect chain[DEPTH]; struct super_block *sb = inode->i_sb; Indirect *partial; int left; int depth = block_to_path(inode, iblock, offsets); if (depth == 0) goto out; reread: read_lock(&pointers_lock); partial = get_branch(inode, depth, offsets, chain, &err); read_unlock(&pointers_lock); /* Simplest case - block found, no allocation needed */ if (!partial) { got_it: map_bh(bh_result, sb, block_to_cpu(SYSV_SB(sb), chain[depth-1].key)); /* Clean up and exit */ partial = chain+depth-1; /* the whole chain */ goto cleanup; } /* Next simple case - plain lookup or failed read of indirect block */ if (!create || err == -EIO) { cleanup: while (partial > chain) { brelse(partial->bh); partial--; } out: return err; } /* * Indirect block might be removed by truncate while we were * reading it. Handling of that case (forget what we've got and * reread) is taken out of the main path. */ if (err == -EAGAIN) goto changed; left = (chain + depth) - partial; err = alloc_branch(inode, left, offsets+(partial-chain), partial); if (err) goto cleanup; if (splice_branch(inode, chain, partial, left) < 0) goto changed; set_buffer_new(bh_result); goto got_it; changed: while (partial > chain) { brelse(partial->bh); partial--; } goto reread; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds27093.75%350.00%
Andrew Morton155.21%233.33%
Christoph Hellwig31.04%116.67%
Total288100.00%6100.00%


static inline int all_zeroes(sysv_zone_t *p, sysv_zone_t *q) { while (p < q) if (*p++) return 0; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds3193.94%150.00%
Al Viro26.06%150.00%
Total33100.00%2100.00%


static Indirect *find_shared(struct inode *inode, int depth, int offsets[], Indirect chain[], sysv_zone_t *top) { Indirect *partial, *p; int k, err; *top = 0; for (k = depth; k > 1 && !offsets[k-1]; k--) ; write_lock(&pointers_lock); partial = get_branch(inode, k, offsets, chain, &err); if (!partial) partial = chain + k-1; /* * If the branch acquired continuation since we've looked at it - * fine, it should all survive and (new) top doesn't belong to us. */ if (!partial->key && *partial->p) { write_unlock(&pointers_lock); goto no_top; } for (p=partial; p>chain && all_zeroes((sysv_zone_t*)p->bh->b_data,p->p); p--) ; /* * OK, we've found the last block that must survive. The rest of our * branch should be detached before unlocking. However, if that rest * of branch is all ours and does not grow immediately from the inode * it's easier to cheat and just decrement partial->p. */ if (p == chain + k - 1 && p > chain) { p->p--; } else { *top = *p->p; *p->p = 0; } write_unlock(&pointers_lock); while (partial > p) { brelse(partial->bh); partial--; } no_top: return partial; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds20390.22%133.33%
Christoph Hellwig208.89%133.33%
Al Viro20.89%133.33%
Total225100.00%3100.00%


static inline void free_data(struct inode *inode, sysv_zone_t *p, sysv_zone_t *q) { for ( ; p < q ; p++) { sysv_zone_t nr = *p; if (nr) { *p = 0; sysv_free_block(inode->i_sb, nr); mark_inode_dirty(inode); } } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds6095.24%150.00%
Al Viro34.76%150.00%
Total63100.00%2100.00%


static void free_branches(struct inode *inode, sysv_zone_t *p, sysv_zone_t *q, int depth) { struct buffer_head * bh; struct super_block *sb = inode->i_sb; if (depth--) { for ( ; p < q ; p++) { int block; sysv_zone_t nr = *p; if (!nr) continue; *p = 0; block = block_to_cpu(SYSV_SB(sb), nr); bh = sb_bread(sb, block); if (!bh) continue; free_branches(inode, (sysv_zone_t*)bh->b_data, block_end(bh), depth); bforget(bh); sysv_free_block(sb, nr); mark_inode_dirty(inode); } } else free_data(inode, p, q); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds14295.30%250.00%
Al Viro42.68%125.00%
Christoph Hellwig32.01%125.00%
Total149100.00%4100.00%


void sysv_truncate (struct inode * inode) { sysv_zone_t *i_data = SYSV_I(inode)->i_data; int offsets[DEPTH]; Indirect chain[DEPTH]; Indirect *partial; sysv_zone_t nr = 0; int n; long iblock; unsigned blocksize; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; blocksize = inode->i_sb->s_blocksize; iblock = (inode->i_size + blocksize-1) >> inode->i_sb->s_blocksize_bits; block_truncate_page(inode->i_mapping, inode->i_size, get_block); n = block_to_path(inode, iblock, offsets); if (n == 0) return; if (n == 1) { free_data(inode, i_data+offsets[0], i_data + DIRECT); goto do_indirects; } partial = find_shared(inode, n, offsets, chain, &nr); /* Kill the top of shared branch (already detached) */ if (nr) { if (partial == chain) mark_inode_dirty(inode); else dirty_indirect(partial->bh, inode); free_branches(inode, &nr, &nr+1, (chain+n-1) - partial); } /* Clear the ends of indirect blocks on the shared branch */ while (partial > chain) { free_branches(inode, partial->p + 1, block_end(partial->bh), (chain+n-1) - partial); dirty_indirect(partial->bh, inode); brelse (partial->bh); partial--; } do_indirects: /* Kill the remaining (whole) subtrees (== subtrees deeper than...) */ while (n < DEPTH) { nr = i_data[DIRECT + n - 1]; if (nr) { i_data[DIRECT + n - 1] = 0; mark_inode_dirty(inode); free_branches(inode, &nr, &nr+1, n); } n++; } inode->i_mtime = inode->i_ctime = current_time(inode); if (IS_SYNC(inode)) sysv_sync_inode (inode); else mark_inode_dirty(inode); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds37298.41%250.00%
Deepa Dinamani41.06%125.00%
Al Viro20.53%125.00%
Total378100.00%4100.00%


static unsigned sysv_nblocks(struct super_block *s, loff_t size) { struct sysv_sb_info *sbi = SYSV_SB(s); int ptrs_bits = sbi->s_ind_per_block_bits; unsigned blocks, res, direct = DIRECT, i = DEPTH; blocks = (size + s->s_blocksize - 1) >> s->s_blocksize_bits; res = blocks; while (--i && blocks > direct) { blocks = ((blocks - direct - 1) >> ptrs_bits) + 1; res += blocks; direct = 1; } return blocks; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro102100.00%1100.00%
Total102100.00%1100.00%


int sysv_getattr(const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) { struct super_block *s = path->dentry->d_sb; generic_fillattr(d_inode(path->dentry), stat); stat->blocks = (s->s_blocksize / 512) * sysv_nblocks(s, stat->size); stat->blksize = s->s_blocksize; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro6178.21%250.00%
David Howells1721.79%250.00%
Total78100.00%4100.00%


static int sysv_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page,get_block,wbc); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds1973.08%150.00%
Andrew Morton726.92%150.00%
Total26100.00%2100.00%


static int sysv_readpage(struct file *file, struct page *page) { return block_read_full_page(page,get_block); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds24100.00%1100.00%
Total24100.00%1100.00%


int sysv_prepare_chunk(struct page *page, loff_t pos, unsigned len) { return __block_write_begin(page, pos, len, get_block); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds1553.57%125.00%
Nicholas Piggin828.57%125.00%
Christoph Hellwig517.86%250.00%
Total28100.00%4100.00%


static void sysv_write_failed(struct address_space *mapping, loff_t to) { struct inode *inode = mapping->host; if (to > inode->i_size) { truncate_pagecache(inode, inode->i_size); sysv_truncate(inode); } }

Contributors

PersonTokensPropCommitsCommitProp
Marco Stornelli47100.00%1100.00%
Total47100.00%1100.00%


static int sysv_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { int ret; ret = block_write_begin(mapping, pos, len, flags, pagep, get_block); if (unlikely(ret)) sysv_write_failed(mapping, pos + len); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Nicholas Piggin5066.67%125.00%
Christoph Hellwig2229.33%250.00%
Marco Stornelli34.00%125.00%
Total75100.00%4100.00%


static sector_t sysv_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,get_block); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds2291.67%150.00%
Andrew Morton28.33%150.00%
Total24100.00%2100.00%

const struct address_space_operations sysv_aops = { .readpage = sysv_readpage, .writepage = sysv_writepage, .write_begin = sysv_write_begin, .write_end = generic_write_end, .bmap = sysv_bmap };

Overall Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds222882.34%413.33%
Al Viro1896.98%413.33%
Christoph Hellwig1114.10%1033.33%
Nicholas Piggin622.29%13.33%
Marco Stornelli501.85%13.33%
Andrew Morton271.00%516.67%
David Howells170.63%26.67%
Art Haas100.37%13.33%
Deepa Dinamani80.30%13.33%
Thomas Gleixner40.15%13.33%
Total2706100.00%30100.00%
Directory: fs/sysv
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.