> +static int do_make_shared(struct vfsmount *mnt) > +{ > + int err=0; > + struct vfspnode *old_pnode = NULL; > + /* > + * if the mount is already a slave mount, > + * allocate a new pnode and make it > + * a slave pnode of the original pnode. > + */ > + if (IS_MNT_SLAVE(mnt)) { > + old_pnode = mnt->mnt_pnode; > + pnode_del_slave_mnt(mnt); > + } > + if(!IS_MNT_SHARED(mnt)) { > + mnt->mnt_pnode = pnode_alloc(); > + if(!mnt->mnt_pnode) { > + pnode_add_slave_mnt(old_pnode, mnt); > + err = -ENOMEM; > + goto out; > + } > + pnode_add_member_mnt(mnt->mnt_pnode, mnt); > + } > + if(old_pnode) > + pnode_add_slave_pnode(old_pnode, mnt->mnt_pnode); > + set_mnt_shared(mnt); > +out: > + return err; > +}
This is an example, where having struct pnode just complicates things. If there was no struct pnode, this function would be just one line: setting the shared flag. > +static kmem_cache_t * pnode_cachep; > + > +/* spinlock for pnode related operations */ > + __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfspnode_lock); > + > +enum pnode_vfs_type { > + PNODE_MEMBER_VFS = 0x01, > + PNODE_SLAVE_VFS = 0x02 > +}; > + > +void __init pnode_init(unsigned long mempages) > +{ > + pnode_cachep = kmem_cache_create("pnode_cache", > + sizeof(struct vfspnode), 0, > + SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); > +} > + > +struct vfspnode * pnode_alloc(void) > +{ > + struct vfspnode *pnode = kmem_cache_alloc(pnode_cachep, GFP_KERNEL); > + INIT_LIST_HEAD(&pnode->pnode_vfs); > + INIT_LIST_HEAD(&pnode->pnode_slavevfs); > + INIT_LIST_HEAD(&pnode->pnode_slavepnode); > + INIT_LIST_HEAD(&pnode->pnode_peer_slave); > + pnode->pnode_master = NULL; > + pnode->pnode_flags = 0; > + atomic_set(&pnode->pnode_count,0); > + return pnode; > +} > + > +void inline pnode_free(struct vfspnode *pnode) > +{ > + kmem_cache_free(pnode_cachep, pnode); > +} > + > +/* > + * __put_pnode() should be called with vfspnode_lock held > + */ > +void __put_pnode(struct vfspnode *pnode) > +{ > + struct vfspnode *tmp_pnode; > + do { > + tmp_pnode = pnode->pnode_master; > + list_del_init(&pnode->pnode_peer_slave); > + BUG_ON(!list_empty(&pnode->pnode_vfs)); > + BUG_ON(!list_empty(&pnode->pnode_slavevfs)); > + BUG_ON(!list_empty(&pnode->pnode_slavepnode)); > + pnode_free(pnode); > + pnode = tmp_pnode; > + if (!pnode || !atomic_dec_and_test(&pnode->pnode_count)) > + break; > + } while(pnode); > +} > + All these are really unnecessary IMO. > +/* > + * merge 'pnode' into 'peer_pnode' and get rid of pnode > + * @pnode: pnode the contents of which have to be merged > + * @peer_pnode: pnode into which the contents are merged > + */ > +int pnode_merge_pnode(struct vfspnode *pnode, struct vfspnode *peer_pnode) > +{ > + struct vfspnode *slave_pnode, *pnext; > + struct vfsmount *mnt, *slave_mnt, *next; > + > + list_for_each_entry_safe(slave_pnode, pnext, > + &pnode->pnode_slavepnode, pnode_peer_slave) { > + slave_pnode->pnode_master = peer_pnode; > + list_move(&slave_pnode->pnode_peer_slave, > + &peer_pnode->pnode_slavepnode); > + put_pnode_locked(pnode); > + get_pnode(peer_pnode); > + } > + > + list_for_each_entry_safe(slave_mnt, next, > + &pnode->pnode_slavevfs, mnt_pnode_mntlist) { > + slave_mnt->mnt_pnode = peer_pnode; > + list_move(&slave_mnt->mnt_pnode_mntlist, > + &peer_pnode->pnode_slavevfs); > + put_pnode_locked(pnode); > + get_pnode(peer_pnode); > + } > + > + list_for_each_entry_safe(mnt, next, > + &pnode->pnode_vfs, mnt_pnode_mntlist) { > + mnt->mnt_pnode = peer_pnode; > + list_move(&mnt->mnt_pnode_mntlist, > + &peer_pnode->pnode_vfs); > + put_pnode_locked(pnode); > + get_pnode(peer_pnode); > + } > + return 0; > +} Much overcomplication. It would just be a list_splice(), if there was no struct pnode. > +static void empty_pnode(struct vfspnode *pnode) { struct vfsmount *slave_mnt, > + *next; struct vfspnode *master_pnode, *slave_pnode, *pnext; > + > + if ((master_pnode = pnode->pnode_master)) { > + pnode->pnode_master = NULL; > + list_del_init(&pnode->pnode_peer_slave); > + pnode_merge_pnode(pnode, master_pnode); > + put_pnode_locked(master_pnode); > + } else { > + list_for_each_entry_safe(slave_mnt, next, > + &pnode->pnode_slavevfs, mnt_pnode_mntlist) { > + list_del_init(&slave_mnt->mnt_pnode_mntlist); > + set_mnt_private(slave_mnt); > + put_pnode_locked(pnode); > + } > + list_for_each_entry_safe(slave_pnode, pnext, > + &pnode->pnode_slavepnode, pnode_peer_slave) { > + slave_pnode->pnode_master = NULL; > + list_del_init(&slave_pnode->pnode_peer_slave); > + put_pnode_locked(pnode); > + } > + } > +} > + Unnecessary. > +static int pnode_next(struct pcontext *context) > +{ > + struct vfspnode *pnode = context->pnode; > + struct vfspnode *master_pnode=context->master_pnode; > + struct list_head *next; > + > + if (!pnode) { > + BUG_ON(!context->start); > + get_pnode(context->start); > + context->pnode = context->start; > + context->master_pnode = NULL; > + context->level = 0; > + return 1; > + } > + > + spin_lock(&vfspnode_lock); > + next = pnode->pnode_slavepnode.next; > + if (next == &pnode->pnode_slavepnode) { > + while (1) { > + int flag; > + > + if (pnode == context->start) { > + put_pnode_locked(pnode); > + spin_unlock(&vfspnode_lock); > + BUG_ON(context->level != 0); > + return 0; > + } > + > + next = pnode->pnode_peer_slave.next; > + flag = (next != &pnode->pnode_master->pnode_slavepnode); > + put_pnode_locked(pnode); > + > + if (flag) > + break; > + > + pnode = master_pnode; > + master_pnode = pnode->pnode_master; > + context->level--; > + } > + } else { > + master_pnode = pnode; > + context->level++; > + } > + > + pnode = list_entry(next, struct vfspnode, pnode_peer_slave); > + get_pnode(pnode); > + > + context->pnode = pnode; > + context->master_pnode = master_pnode; > + spin_unlock(&vfspnode_lock); > + return 1; > +} > + > +/* > + * skip the rest of the tree, cleaning up > + * reference to pnodes held in pnode_next(). > + */ > +static void pnode_end(struct pcontext *context) > +{ > + struct vfspnode *p = context->pnode; > + struct vfspnode *start = context->start; > + > + do { > + put_pnode(p); > + } while (p != start && (p = p->pnode_master)); > + return; > +} > + > +/* > + * traverse the pnode tree and at each pnode encountered, execute the > + * pnode_fnc(). For each vfsmount encountered call the vfs_fnc(). > + * > + * @pnode: pnode tree to be traversed > + * @in_data: input data > + * @out_data: output data > + * @pnode_func: function to be called when a new pnode is encountered. > + * @vfs_func: function to be called on each slave and member vfs belonging > + * to the pnode. > + */ > +static int pnode_traverse(struct vfspnode *pnode, > + void *in_data, > + void **out_data, > + int (*pnode_pre_func)(struct vfspnode *, > + void *, void **, va_list), > + int (*pnode_post_func)(struct vfspnode *, > + void *, va_list), > + int (*vfs_func)(struct vfsmount *, > + enum pnode_vfs_type, void *, va_list), > + ...) > +{ > + va_list args; > + int ret = 0, level; > + void *my_data, *data_from_master; > + struct vfspnode *master_pnode; > + struct vfsmount *slave_mnt, *member_mnt, *t_m; > + struct pcontext context; > + static void *p_array[PNODE_MAX_SLAVE_LEVEL]; > + > + context.start = pnode; > + context.pnode = NULL; > + /* > + * determine whether to process vfs first or the > + * slave pnode first > + */ > + while (pnode_next(&context)) { > + level = context.level; > + > + if (level >= PNODE_MAX_SLAVE_LEVEL) > + goto error; > + > + pnode = context.pnode; > + master_pnode = context.master_pnode; > + > + if (master_pnode) { > + data_from_master = p_array[level-1]; > + my_data = NULL; > + } else { > + data_from_master = NULL; > + my_data = in_data; > + } > + > + if (pnode_pre_func) { > + va_start(args, vfs_func); > + if((ret = pnode_pre_func(pnode, > + data_from_master, &my_data, args))) > + goto error; > + va_end(args); > + } > + > + // traverse member vfsmounts > + spin_lock(&vfspnode_lock); > + list_for_each_entry_safe(member_mnt, > + t_m, &pnode->pnode_vfs, mnt_pnode_mntlist) { > + > + spin_unlock(&vfspnode_lock); > + va_start(args, vfs_func); > + if ((ret = vfs_func(member_mnt, > + PNODE_MEMBER_VFS, my_data, args))) > + goto error; > + va_end(args); > + spin_lock(&vfspnode_lock); > + } > + list_for_each_entry_safe(slave_mnt, t_m, > + &pnode->pnode_slavevfs, mnt_pnode_mntlist) { > + > + spin_unlock(&vfspnode_lock); > + va_start(args, vfs_func); > + if ((ret = vfs_func(slave_mnt, PNODE_SLAVE_VFS, > + my_data, args))) > + goto error; > + va_end(args); > + spin_lock(&vfspnode_lock); > + } > + spin_unlock(&vfspnode_lock); > + > + if (pnode_post_func) { > + va_start(args, vfs_func); > + if((ret = pnode_post_func(pnode, > + my_data, args))) > + goto error; > + va_end(args); > + } > + > + p_array[level] = my_data; > + } > +out: > + if (out_data) > + *out_data = p_array[0]; > + return ret; > +error: > + va_end(args); > + pnode_end(&context); > + goto out; > +} > And this is the worst part. As I said earlier, void pointers and variable argument functions have no place in this kind of code. I think you could get rid of all these if you'd implement a simple iterator function which returns the traversed vfsmounts. That's another big argument for getting rid of struct pnode: you could do iteration simply by holding onto a vfsmount pointer, instead of having to do a two level iteration, once over pnodes, then over vfsmounts. > +extern spinlock_t vfspnode_lock; > +extern void __put_pnode(struct vfspnode *); > + > +static inline struct vfspnode * > +get_pnode(struct vfspnode *pnode) > +{ > + if (!pnode) > + return NULL; > + atomic_inc(&pnode->pnode_count); > + return pnode; > +} > + > +static inline void > +put_pnode(struct vfspnode *pnode) > +{ > + if (!pnode) > + return; > + if (atomic_dec_and_lock(&pnode->pnode_count, &vfspnode_lock)) { > + __put_pnode(pnode); > + spin_unlock(&vfspnode_lock); > + } > +} Unnecessary. > +#define MNT_PRIVATE 0x10 /* if the vfsmount is private, by default it is > private*/ If by default it's private, why is this flag needed? Miklos - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/