/* * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/module.h> #include <linux/slab.h> #include <linux/mempool.h> #include "netfs.h" static struct kmem_cache *pohmelfs_mcache_cache; static mempool_t *pohmelfs_mcache_pool; static inline int pohmelfs_mcache_cmp(u64 gen, u64 new) { if (gen < new) return 1; if (gen > new) return -1; return 0; } struct pohmelfs_mcache *pohmelfs_mcache_search(struct pohmelfs_sb *psb, u64 gen) { struct rb_root *root = &psb->mcache_root; struct rb_node *n = root->rb_node; struct pohmelfs_mcache *tmp, *ret = NULL; int cmp; while (n) { tmp = rb_entry(n, struct pohmelfs_mcache, mcache_entry); cmp = pohmelfs_mcache_cmp(tmp->gen, gen); if (cmp < 0) n = n->rb_left; else if (cmp > 0) n = n->rb_right; else { ret = tmp; pohmelfs_mcache_get(ret); break; } } return ret; } static int pohmelfs_mcache_insert(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) { struct rb_root *root = &psb->mcache_root; struct rb_node **n = &root->rb_node, *parent = NULL; struct pohmelfs_mcache *ret = NULL, *tmp; int cmp; while (*n) { parent = *n; tmp = rb_entry(parent, struct pohmelfs_mcache, mcache_entry); cmp = pohmelfs_mcache_cmp(tmp->gen, m->gen); if (cmp < 0) n = &parent->rb_left; else if (cmp > 0) n = &parent->rb_right; else { ret = tmp; break; } } if (ret) return -EEXIST; rb_link_node(&m->mcache_entry, parent, n); rb_insert_color(&m->mcache_entry, root); return 0; } static int pohmelfs_mcache_remove(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) { if (m && m->mcache_entry.rb_parent_color) { rb_erase(&m->mcache_entry, &psb->mcache_root); m->mcache_entry.rb_parent_color = 0; return 1; } return 0; } void pohmelfs_mcache_remove_locked(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) { mutex_lock(&psb->mcache_lock); pohmelfs_mcache_remove(psb, m); mutex_unlock(&psb->mcache_lock); } struct pohmelfs_mcache *pohmelfs_mcache_alloc(struct pohmelfs_sb *psb, u64 start, unsigned int size, void *data) { struct pohmelfs_mcache *m; int err = -ENOMEM; m = mempool_alloc(pohmelfs_mcache_pool, GFP_KERNEL); if (!m) goto err_out_exit; init_completion(&m->complete); m->err = 0; atomic_set(&m->refcnt, 1); m->data = data; m->start = start; m->size = size; m->gen = atomic_long_inc_return(&psb->mcache_gen); mutex_lock(&psb->mcache_lock); err = pohmelfs_mcache_insert(psb, m); mutex_unlock(&psb->mcache_lock); if (err) goto err_out_free; return m; err_out_free: mempool_free(m, pohmelfs_mcache_pool); err_out_exit: return ERR_PTR(err); } void pohmelfs_mcache_free(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) { pohmelfs_mcache_remove_locked(psb, m); mempool_free(m, pohmelfs_mcache_pool); } int __init pohmelfs_mcache_init(void) { pohmelfs_mcache_cache = kmem_cache_create("pohmelfs_mcache_cache", sizeof(struct pohmelfs_mcache), 0, (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), NULL); if (!pohmelfs_mcache_cache) goto err_out_exit; pohmelfs_mcache_pool = mempool_create_slab_pool(256, pohmelfs_mcache_cache); if (!pohmelfs_mcache_pool) goto err_out_free; return 0; err_out_free: kmem_cache_destroy(pohmelfs_mcache_cache); err_out_exit: return -ENOMEM; } void pohmelfs_mcache_exit(void) { mempool_destroy(pohmelfs_mcache_pool); kmem_cache_destroy(pohmelfs_mcache_cache); }