/* * Compatibility functions for older Linux versions */ #include // for old sles10 kernels, which forgot to include it in backing-dev.h #include #include #include #include #include #include #include #include #include #ifndef KERNEL_HAS_MEMDUP_USER /** * memdup_user - duplicate memory region from user space * * @src: source address in user space * @len: number of bytes to copy * * Returns an ERR_PTR() on failure. */ void *memdup_user(const void __user *src, size_t len) { void *p; /* * Always use GFP_KERNEL, since copy_from_user() can sleep and * cause pagefault, which makes it pointless to use GFP_NOFS * or GFP_ATOMIC. */ p = kmalloc(len, GFP_KERNEL); if (!p) return ERR_PTR(-ENOMEM); if (copy_from_user(p, src, len)) { kfree(p); return ERR_PTR(-EFAULT); } return p; } #endif // memdup_user, LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) #if defined(KERNEL_HAS_SB_BDI) && !defined(KERNEL_HAS_BDI_SETUP_AND_REGISTER) && \ !defined(KERNEL_HAS_SUPER_SETUP_BDI_NAME) /* * For use from filesystems to quickly init and register a bdi associated * with dirty writeback */ int bdi_setup_and_register(struct backing_dev_info *bdi, char *name, unsigned int cap) { static atomic_long_t fhgfs_bdiSeq = ATOMIC_LONG_INIT(0); char tmp[32]; int err; bdi->name = name; bdi->capabilities = cap; err = bdi_init(bdi); if (err) return err; sprintf(tmp, "%.28s%s", name, "-%d"); err = bdi_register(bdi, NULL, tmp, atomic_long_inc_return(&fhgfs_bdiSeq)); if (err) { bdi_destroy(bdi); return err; } return 0; } #endif /* NOTE: We can't do a feature detection for find_get_pages_tag(), as * this function is in all headers of all supported kernel versions. * However, it is only _exported_ since 2.6.22 and also only * exported in RHEL >=5.10. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) /** * find_get_pages_tag - find and return pages that match @tag * @mapping: the address_space to search * @index: the starting page index * @tag: the tag index * @nr_pages: the maximum number of pages * @pages: where the resulting pages are placed * * Like find_get_pages, except we only return pages which are tagged with * @tag. We update @index to index the next page for the traversal. */ unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index, int tag, unsigned int nr_pages, struct page **pages) { unsigned int i; unsigned int ret; read_lock_irq(&mapping->tree_lock); ret = radix_tree_gang_lookup_tag(&mapping->page_tree, (void **)pages, *index, nr_pages, tag); for (i = 0; i < ret; i++) page_cache_get(pages[i]); if (ret) *index = pages[ret - 1]->index + 1; read_unlock_irq(&mapping->tree_lock); return ret; } #endif // find_get_pages_tag() for <2.6.22 #ifndef KERNEL_HAS_D_MAKE_ROOT /** * This is the former d_alloc_root with an additional iput on error. */ struct dentry *d_make_root(struct inode *root_inode) { struct dentry* allocRes = d_alloc_root(root_inode); if(!allocRes) iput(root_inode); return allocRes; } #endif #ifndef KERNEL_HAS_D_MATERIALISE_UNIQUE /** * d_materialise_unique() was merged into d_splice_alias() in linux-3.19 */ struct dentry* d_materialise_unique(struct dentry *dentry, struct inode *inode) { return d_splice_alias(inode, dentry); } #endif // KERNEL_HAS_D_MATERIALISE_UNIQUE /** * Note: Call this once during module init (and remember to call kmem_cache_destroy() ) */ #if defined(KERNEL_HAS_KMEMCACHE_CACHE_FLAGS_CTOR) struct kmem_cache* OsCompat_initKmemCache(const char* cacheName, size_t cacheSize, void initFuncPtr(void* initObj, struct kmem_cache* cache, unsigned long flags) ) #elif defined(KERNEL_HAS_KMEMCACHE_CACHE_CTOR) struct kmem_cache* OsCompat_initKmemCache(const char* cacheName, size_t cacheSize, void initFuncPtr(struct kmem_cache* cache, void* initObj) ) #else struct kmem_cache* OsCompat_initKmemCache(const char* cacheName, size_t cacheSize, void initFuncPtr(void* initObj) ) #endif // LINUX_VERSION_CODE { struct kmem_cache* cache; #if defined(KERNEL_HAS_SLAB_MEM_SPREAD) unsigned long cacheFlags = SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD; #else unsigned long cacheFlags = SLAB_RECLAIM_ACCOUNT; #endif #if defined(KERNEL_HAS_KMEMCACHE_DTOR) cache = kmem_cache_create(cacheName, cacheSize, 0, cacheFlags, initFuncPtr, NULL); #else cache = kmem_cache_create(cacheName, cacheSize, 0, cacheFlags, initFuncPtr); #endif // LINUX_VERSION_CODE return cache; } #ifndef rbtree_postorder_for_each_entry_safe static struct rb_node* rb_left_deepest_node(const struct rb_node* node) { for (;;) { if (node->rb_left) node = node->rb_left; else if (node->rb_right) node = node->rb_right; else return (struct rb_node*) node; } } struct rb_node* rb_next_postorder(const struct rb_node* node) { const struct rb_node *parent; if (!node) return NULL; parent = rb_parent(node); /* If we're sitting on node, we've already seen our children */ if (parent && node == parent->rb_left && parent->rb_right) { /* If we are the parent's left node, go to the parent's right * node then all the way down to the left */ return rb_left_deepest_node(parent->rb_right); } else /* Otherwise we are the parent's right node, and the parent * should be next */ return (struct rb_node*) parent; } struct rb_node* rb_first_postorder(const struct rb_root* root) { if (!root->rb_node) return NULL; return rb_left_deepest_node(root->rb_node); } #endif #ifdef KERNEL_HAS_GENERIC_WRITE_CHECKS_ITER int os_generic_write_checks(struct file* filp, loff_t* offset, size_t* size, int isblk) { struct iovec iov = { 0, *size }; struct iov_iter iter; ssize_t checkRes; struct kiocb iocb; iov_iter_init(&iter, WRITE, &iov, 1, *size); init_sync_kiocb(&iocb, filp); iocb.ki_pos = *offset; checkRes = generic_write_checks(&iocb, &iter); if(checkRes < 0) return checkRes; *offset = iocb.ki_pos; *size = iter.count; return 0; } #endif #ifndef KERNEL_HAS_HAVE_SUBMOUNTS /** * enum d_walk_ret - action to talke during tree walk * @D_WALK_CONTINUE: contrinue walk * @D_WALK_QUIT: quit walk * @D_WALK_NORETRY: quit when retry is needed * @D_WALK_SKIP: skip this dentry and its children */ enum d_walk_ret { D_WALK_CONTINUE, D_WALK_QUIT, D_WALK_NORETRY, D_WALK_SKIP, }; /* * Search for at least 1 mount point in the dentry's subdirs. * We descend to the next level whenever the d_subdirs * list is non-empty and continue searching. */ static enum d_walk_ret check_mount(void *data, struct dentry *dentry) { int *ret = data; if (d_mountpoint(dentry)) { *ret = 1; return D_WALK_QUIT; } return D_WALK_CONTINUE; } #if defined(KERNEL_HAS_DENTRY_SUBDIRS) /** * d_walk - walk the dentry tree * @parent: start of walk * @data: data passed to @enter() and @finish() * @enter: callback when first entering the dentry * @finish: callback when successfully finished the walk * * The @enter() and @finish() callbacks are called with d_lock held. */ static void d_walk(struct dentry *parent, void *data, enum d_walk_ret (*enter)(void *, struct dentry *), void (*finish)(void *)) { struct dentry *this_parent; struct list_head *next; unsigned seq = 0; enum d_walk_ret ret; bool retry = true; again: read_seqbegin_or_lock(&rename_lock, &seq); this_parent = parent; spin_lock(&this_parent->d_lock); ret = enter(data, this_parent); switch (ret) { case D_WALK_CONTINUE: break; case D_WALK_QUIT: case D_WALK_SKIP: goto out_unlock; case D_WALK_NORETRY: retry = false; break; } repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; struct dentry *dentry = list_entry(tmp, struct dentry, d_child); next = tmp->next; if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR)) continue; spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); ret = enter(data, dentry); switch (ret) { case D_WALK_CONTINUE: break; case D_WALK_QUIT: spin_unlock(&dentry->d_lock); goto out_unlock; case D_WALK_NORETRY: retry = false; break; case D_WALK_SKIP: spin_unlock(&dentry->d_lock); continue; } if (!list_empty(&dentry->d_subdirs)) { spin_unlock(&this_parent->d_lock); #if defined(KERNEL_SPIN_RELEASE_HAS_3_ARGUMENTS) spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_); #else spin_release(&dentry->d_lock.dep_map, _RET_IP_); #endif this_parent = dentry; spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_); goto repeat; } spin_unlock(&dentry->d_lock); } /* * All done at this level ... ascend and resume the search. */ rcu_read_lock(); ascend: if (this_parent != parent) { struct dentry *child = this_parent; this_parent = child->d_parent; spin_unlock(&child->d_lock); spin_lock(&this_parent->d_lock); /* might go back up the wrong parent if we have had a rename. */ if (need_seqretry(&rename_lock, seq)) goto rename_retry; /* go into the first sibling still alive */ do { next = child->d_child.next; if (next == &this_parent->d_subdirs) goto ascend; child = list_entry(next, struct dentry, d_child); } while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)); rcu_read_unlock(); goto resume; } if (need_seqretry(&rename_lock, seq)) goto rename_retry; rcu_read_unlock(); if (finish) finish(data); out_unlock: spin_unlock(&this_parent->d_lock); done_seqretry(&rename_lock, seq); return; rename_retry: spin_unlock(&this_parent->d_lock); rcu_read_unlock(); BUG_ON(seq & 1); if (!retry) return; seq = 1; goto again; } #else /** * d_walk - walk the dentry tree * @parent: start of walk * @data: data passed to @enter() and @finish() * @enter: callback when first entering the dentry * * The @enter() callbacks are called with d_lock held. */ static void d_walk(struct dentry *parent, void *data, enum d_walk_ret (*enter)(void *, struct dentry *)) { struct dentry *this_parent, *dentry; unsigned seq = 0; enum d_walk_ret ret; bool retry = true; again: read_seqbegin_or_lock(&rename_lock, &seq); this_parent = parent; spin_lock(&this_parent->d_lock); ret = enter(data, this_parent); switch (ret) { case D_WALK_CONTINUE: break; case D_WALK_QUIT: case D_WALK_SKIP: goto out_unlock; case D_WALK_NORETRY: retry = false; break; } repeat: dentry = d_first_child(this_parent); resume: hlist_for_each_entry_from(dentry, d_sib) { if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR)) continue; spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); ret = enter(data, dentry); switch (ret) { case D_WALK_CONTINUE: break; case D_WALK_QUIT: spin_unlock(&dentry->d_lock); goto out_unlock; case D_WALK_NORETRY: retry = false; break; case D_WALK_SKIP: spin_unlock(&dentry->d_lock); continue; } if (!hlist_empty(&dentry->d_children)) { spin_unlock(&this_parent->d_lock); spin_release(&dentry->d_lock.dep_map, _RET_IP_); this_parent = dentry; spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_); goto repeat; } spin_unlock(&dentry->d_lock); } /* * All done at this level ... ascend and resume the search. */ rcu_read_lock(); ascend: if (this_parent != parent) { dentry = this_parent; this_parent = dentry->d_parent; spin_unlock(&dentry->d_lock); spin_lock(&this_parent->d_lock); /* might go back up the wrong parent if we have had a rename. */ if (need_seqretry(&rename_lock, seq)) goto rename_retry; /* go into the first sibling still alive */ hlist_for_each_entry_continue(dentry, d_sib) { if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) { rcu_read_unlock(); goto resume; } } goto ascend; } if (need_seqretry(&rename_lock, seq)) goto rename_retry; rcu_read_unlock(); out_unlock: spin_unlock(&this_parent->d_lock); done_seqretry(&rename_lock, seq); return; rename_retry: spin_unlock(&this_parent->d_lock); rcu_read_unlock(); BUG_ON(seq & 1); if (!retry) return; seq = 1; goto again; } #endif /** * have_submounts - check for mounts over a dentry * @parent: dentry to check. * * Return true if the parent or its subdirectories contain * a mount point */ int have_submounts(struct dentry *parent) { int ret = 0; #if defined(KERNEL_HAS_DENTRY_SUBDIRS) d_walk(parent, &ret, check_mount, NULL); #else d_walk(parent, &ret, check_mount); #endif return ret; } #endif