/* * Copyright (C) 2005-2016 Junjiro R. Okajima */ /* * dentry private data */ #include "aufs.h" void au_di_init_once(void *_dinfo) { struct au_dinfo *dinfo = _dinfo; static struct lock_class_key aufs_di; au_rw_init(&dinfo->di_rwsem); au_rw_class(&dinfo->di_rwsem, &aufs_di); } struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) { struct au_dinfo *dinfo; int nbr, i; dinfo = au_cache_alloc_dinfo(); if (unlikely(!dinfo)) goto out; nbr = au_sbend(sb) + 1; if (nbr <= 0) nbr = 1; dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); if (dinfo->di_hdentry) { au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); dinfo->di_bstart = -1; dinfo->di_bend = -1; dinfo->di_bwh = -1; dinfo->di_bdiropq = -1; dinfo->di_tmpfile = 0; for (i = 0; i < nbr; i++) dinfo->di_hdentry[i].hd_id = -1; goto out; } au_cache_free_dinfo(dinfo); dinfo = NULL; out: return dinfo; } void au_di_free(struct au_dinfo *dinfo) { struct au_hdentry *p; aufs_bindex_t bend, bindex; /* dentry may not be revalidated */ bindex = dinfo->di_bstart; if (bindex >= 0) { bend = dinfo->di_bend; p = dinfo->di_hdentry + bindex; while (bindex++ <= bend) au_hdput(p++); } kfree(dinfo->di_hdentry); au_cache_free_dinfo(dinfo); } void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) { struct au_hdentry *p; aufs_bindex_t bi; AuRwMustWriteLock(&a->di_rwsem); AuRwMustWriteLock(&b->di_rwsem); #define DiSwap(v, name) \ do { \ v = a->di_##name; \ a->di_##name = b->di_##name; \ b->di_##name = v; \ } while (0) DiSwap(p, hdentry); DiSwap(bi, bstart); DiSwap(bi, bend); DiSwap(bi, bwh); DiSwap(bi, bdiropq); /* smp_mb(); */ #undef DiSwap } void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) { AuRwMustWriteLock(&dst->di_rwsem); AuRwMustWriteLock(&src->di_rwsem); dst->di_bstart = src->di_bstart; dst->di_bend = src->di_bend; dst->di_bwh = src->di_bwh; dst->di_bdiropq = src->di_bdiropq; /* smp_mb(); */ } int au_di_init(struct dentry *dentry) { int err; struct super_block *sb; struct au_dinfo *dinfo; err = 0; sb = dentry->d_sb; dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); if (dinfo) { atomic_set(&dinfo->di_generation, au_sigen(sb)); /* smp_mb(); */ /* atomic_set */ dentry->d_fsdata = dinfo; } else err = -ENOMEM; return err; } void au_di_fin(struct dentry *dentry) { struct au_dinfo *dinfo; dinfo = au_di(dentry); AuRwDestroy(&dinfo->di_rwsem); au_di_free(dinfo); } int au_di_realloc(struct au_dinfo *dinfo, int nbr) { int err, sz; struct au_hdentry *hdp; AuRwMustWriteLock(&dinfo->di_rwsem); err = -ENOMEM; sz = sizeof(*hdp) * (dinfo->di_bend + 1); if (!sz) sz = sizeof(*hdp); hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); if (hdp) { dinfo->di_hdentry = hdp; err = 0; } return err; } /* ---------------------------------------------------------------------- */ static void do_ii_write_lock(struct inode *inode, unsigned int lsc) { switch (lsc) { case AuLsc_DI_CHILD: ii_write_lock_child(inode); break; case AuLsc_DI_CHILD2: ii_write_lock_child2(inode); break; case AuLsc_DI_CHILD3: ii_write_lock_child3(inode); break; case AuLsc_DI_PARENT: ii_write_lock_parent(inode); break; case AuLsc_DI_PARENT2: ii_write_lock_parent2(inode); break; case AuLsc_DI_PARENT3: ii_write_lock_parent3(inode); break; default: BUG(); } } static void do_ii_read_lock(struct inode *inode, unsigned int lsc) { switch (lsc) { case AuLsc_DI_CHILD: ii_read_lock_child(inode); break; case AuLsc_DI_CHILD2: ii_read_lock_child2(inode); break; case AuLsc_DI_CHILD3: ii_read_lock_child3(inode); break; case AuLsc_DI_PARENT: ii_read_lock_parent(inode); break; case AuLsc_DI_PARENT2: ii_read_lock_parent2(inode); break; case AuLsc_DI_PARENT3: ii_read_lock_parent3(inode); break; default: BUG(); } } void di_read_lock(struct dentry *d, int flags, unsigned int lsc) { struct inode *inode; au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); if (d_really_is_positive(d)) { inode = d_inode(d); if (au_ftest_lock(flags, IW)) do_ii_write_lock(inode, lsc); else if (au_ftest_lock(flags, IR)) do_ii_read_lock(inode, lsc); } } void di_read_unlock(struct dentry *d, int flags) { struct inode *inode; if (d_really_is_positive(d)) { inode = d_inode(d); if (au_ftest_lock(flags, IW)) { au_dbg_verify_dinode(d); ii_write_unlock(inode); } else if (au_ftest_lock(flags, IR)) { au_dbg_verify_dinode(d); ii_read_unlock(inode); } } au_rw_read_unlock(&au_di(d)->di_rwsem); } void di_downgrade_lock(struct dentry *d, int flags) { if (d_really_is_positive(d) && au_ftest_lock(flags, IR)) ii_downgrade_lock(d_inode(d)); au_rw_dgrade_lock(&au_di(d)->di_rwsem); } void di_write_lock(struct dentry *d, unsigned int lsc) { au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); if (d_really_is_positive(d)) do_ii_write_lock(d_inode(d), lsc); } void di_write_unlock(struct dentry *d) { au_dbg_verify_dinode(d); if (d_really_is_positive(d)) ii_write_unlock(d_inode(d)); au_rw_write_unlock(&au_di(d)->di_rwsem); } void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) { AuDebugOn(d1 == d2 || d_inode(d1) == d_inode(d2) || d1->d_sb != d2->d_sb); if (isdir && au_test_subdir(d1, d2)) { di_write_lock_child(d1); di_write_lock_child2(d2); } else { /* there should be no races */ di_write_lock_child(d2); di_write_lock_child2(d1); } } void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) { AuDebugOn(d1 == d2 || d_inode(d1) == d_inode(d2) || d1->d_sb != d2->d_sb); if (isdir && au_test_subdir(d1, d2)) { di_write_lock_parent(d1); di_write_lock_parent2(d2); } else { /* there should be no races */ di_write_lock_parent(d2); di_write_lock_parent2(d1); } } void di_write_unlock2(struct dentry *d1, struct dentry *d2) { di_write_unlock(d1); if (d_inode(d1) == d_inode(d2)) au_rw_write_unlock(&au_di(d2)->di_rwsem); else di_write_unlock(d2); } /* ---------------------------------------------------------------------- */ struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) { struct dentry *d; DiMustAnyLock(dentry); if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) return NULL; AuDebugOn(bindex < 0); d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; AuDebugOn(d && au_dcount(d) <= 0); return d; } /* * extended version of au_h_dptr(). * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or * error. */ struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) { struct dentry *h_dentry; struct inode *inode, *h_inode; AuDebugOn(d_really_is_negative(dentry)); h_dentry = NULL; if (au_dbstart(dentry) <= bindex && bindex <= au_dbend(dentry)) h_dentry = au_h_dptr(dentry, bindex); if (h_dentry && !au_d_linkable(h_dentry)) { dget(h_dentry); goto out; /* success */ } inode = d_inode(dentry); AuDebugOn(bindex < au_ibstart(inode)); AuDebugOn(au_ibend(inode) < bindex); h_inode = au_h_iptr(inode, bindex); h_dentry = d_find_alias(h_inode); if (h_dentry) { if (!IS_ERR(h_dentry)) { if (!au_d_linkable(h_dentry)) goto out; /* success */ dput(h_dentry); } else goto out; } if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { h_dentry = au_plink_lkup(inode, bindex); AuDebugOn(!h_dentry); if (!IS_ERR(h_dentry)) { if (!au_d_hashed_positive(h_dentry)) goto out; /* success */ dput(h_dentry); h_dentry = NULL; } } out: AuDbgDentry(h_dentry); return h_dentry; } aufs_bindex_t au_dbtail(struct dentry *dentry) { aufs_bindex_t bend, bwh; bend = au_dbend(dentry); if (0 <= bend) { bwh = au_dbwh(dentry); if (!bwh) return bwh; if (0 < bwh && bwh < bend) return bwh - 1; } return bend; } aufs_bindex_t au_dbtaildir(struct dentry *dentry) { aufs_bindex_t bend, bopq; bend = au_dbtail(dentry); if (0 <= bend) { bopq = au_dbdiropq(dentry); if (0 <= bopq && bopq < bend) bend = bopq; } return bend; } /* ---------------------------------------------------------------------- */ void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_dentry) { struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; struct au_branch *br; DiMustWriteLock(dentry); au_hdput(hd); hd->hd_dentry = h_dentry; if (h_dentry) { br = au_sbr(dentry->d_sb, bindex); hd->hd_id = br->br_id; } } int au_dbrange_test(struct dentry *dentry) { int err; aufs_bindex_t bstart, bend; err = 0; bstart = au_dbstart(dentry); bend = au_dbend(dentry); if (bstart >= 0) AuDebugOn(bend < 0 && bstart > bend); else { err = -EIO; AuDebugOn(bend >= 0); } return err; } int au_digen_test(struct dentry *dentry, unsigned int sigen) { int err; err = 0; if (unlikely(au_digen(dentry) != sigen || au_iigen_test(d_inode(dentry), sigen))) err = -EIO; return err; } void au_update_digen(struct dentry *dentry) { atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); /* smp_mb(); */ /* atomic_set */ } void au_update_dbrange(struct dentry *dentry, int do_put_zero) { struct au_dinfo *dinfo; struct dentry *h_d; struct au_hdentry *hdp; DiMustWriteLock(dentry); dinfo = au_di(dentry); if (!dinfo || dinfo->di_bstart < 0) return; hdp = dinfo->di_hdentry; if (do_put_zero) { aufs_bindex_t bindex, bend; bend = dinfo->di_bend; for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { h_d = hdp[0 + bindex].hd_dentry; if (h_d && d_is_negative(h_d)) au_set_h_dptr(dentry, bindex, NULL); } } dinfo->di_bstart = -1; while (++dinfo->di_bstart <= dinfo->di_bend) if (hdp[0 + dinfo->di_bstart].hd_dentry) break; if (dinfo->di_bstart > dinfo->di_bend) { dinfo->di_bstart = -1; dinfo->di_bend = -1; return; } dinfo->di_bend++; while (0 <= --dinfo->di_bend) if (hdp[0 + dinfo->di_bend].hd_dentry) break; AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); } void au_update_dbstart(struct dentry *dentry) { aufs_bindex_t bindex, bend; struct dentry *h_dentry; bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; if (d_is_positive(h_dentry)) { au_set_dbstart(dentry, bindex); return; } au_set_h_dptr(dentry, bindex, NULL); } } void au_update_dbend(struct dentry *dentry) { aufs_bindex_t bindex, bstart; struct dentry *h_dentry; bstart = au_dbstart(dentry); for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; if (d_is_positive(h_dentry)) { au_set_dbend(dentry, bindex); return; } au_set_h_dptr(dentry, bindex, NULL); } } int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) { aufs_bindex_t bindex, bend; bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) if (au_h_dptr(dentry, bindex) == h_dentry) return bindex; return -1; }