mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
OK, everything builds now. My initial intent was to place all of
the directories at the top level but that proved troublesome. The kernel buildsystem and autoconf were conflicting too much. To resolve the issue I moved the kernel bits in to a modules directory which can then only use the kernel build system. We just pass along the likely make targets to the kernel build system. git-svn-id: https://outreach.scidac.gov/svn/spl/trunk@11 7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# Makefile.in for spl kernel module
|
||||
|
||||
MODULES := spl
|
||||
DISTFILES = Makefile.in \
|
||||
linux-kmem.c linux-rwlock.c linux-taskq.c \
|
||||
linux-thread.c linux-generic.c
|
||||
CPPFLAGS += @KERNELCPPFLAGS@
|
||||
|
||||
# Solaris porting layer module
|
||||
obj-m := spl.o
|
||||
|
||||
spl-objs += linux-kmem.o
|
||||
spl-objs += linux-thread.o
|
||||
spl-objs += linux-taskq.o
|
||||
spl-objs += linux-rwlock.o
|
||||
spl-objs += linux-generic.o
|
||||
|
||||
splmodule := spl.ko
|
||||
splmoduledir := @kmoduledir@/kernel/lib/
|
||||
|
||||
install:
|
||||
mkdir -p $(DESTDIR)$(splmoduledir)
|
||||
$(INSTALL) -m 644 $(splmodule) $(DESTDIR)$(splmoduledir)/$(splmodule)
|
||||
-/sbin/depmod -a
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(splmoduledir)/$(splmodule)
|
||||
-/sbin/depmod -a
|
||||
|
||||
clean:
|
||||
-rm -f $(splmodule) *.o .*.cmd *.mod.c *.ko *.s */*.o
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
rm -rf .tmp_versions
|
||||
|
||||
maintainer-clean: distclean
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
cp -p $(DISTFILES) $(distdir)
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "linux-generic.h"
|
||||
|
||||
/*
|
||||
* Generic support
|
||||
*/
|
||||
|
||||
int p0 = 0;
|
||||
EXPORT_SYMBOL(p0);
|
||||
@@ -0,0 +1,251 @@
|
||||
#include "linux-kmem.h"
|
||||
|
||||
/*
|
||||
* Memory allocation interfaces
|
||||
*/
|
||||
#ifdef DEBUG_KMEM
|
||||
/* Shim layer memory accounting */
|
||||
atomic_t kmem_alloc_used;
|
||||
unsigned int kmem_alloc_max;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Slab allocation interfaces
|
||||
*
|
||||
* While the linux slab implementation was inspired by solaris they
|
||||
* have made some changes to the API which complicates this shim
|
||||
* layer. For one thing the same symbol names are used with different
|
||||
* arguments for the prototypes. To deal with this we must use the
|
||||
* preprocessor to re-order arguments. Happily for us standard C says,
|
||||
* "Macro's appearing in their own expansion are not reexpanded" so
|
||||
* this does not result in an infinite recursion. Additionally the
|
||||
* function pointers registered by solarias differ from those used
|
||||
* by linux so a lookup and mapping from linux style callback to a
|
||||
* solaris style callback is needed. There is some overhead in this
|
||||
* operation which isn't horibile but it needs to be kept in mind.
|
||||
*/
|
||||
typedef struct kmem_cache_cb {
|
||||
struct list_head kcc_list;
|
||||
kmem_cache_t * kcc_cache;
|
||||
kmem_constructor_t kcc_constructor;
|
||||
kmem_destructor_t kcc_destructor;
|
||||
kmem_reclaim_t kcc_reclaim;
|
||||
void * kcc_private;
|
||||
void * kcc_vmp;
|
||||
} kmem_cache_cb_t;
|
||||
|
||||
|
||||
static spinlock_t kmem_cache_cb_lock = SPIN_LOCK_UNLOCKED;
|
||||
//static spinlock_t kmem_cache_cb_lock = (spinlock_t) { 1 SPINLOCK_MAGIC_INIT };
|
||||
static LIST_HEAD(kmem_cache_cb_list);
|
||||
static struct shrinker *kmem_cache_shrinker;
|
||||
|
||||
/* Function must be called while holding the kmem_cache_cb_lock
|
||||
* Because kmem_cache_t is an opaque datatype we're forced to
|
||||
* match pointers to identify specific cache entires.
|
||||
*/
|
||||
static kmem_cache_cb_t *
|
||||
kmem_cache_find_cache_cb(kmem_cache_t *cache)
|
||||
{
|
||||
kmem_cache_cb_t *kcc;
|
||||
|
||||
list_for_each_entry(kcc, &kmem_cache_cb_list, kcc_list)
|
||||
if (cache == kcc->kcc_cache)
|
||||
return kcc;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static kmem_cache_cb_t *
|
||||
kmem_cache_add_cache_cb(kmem_cache_t *cache,
|
||||
kmem_constructor_t constructor,
|
||||
kmem_destructor_t destructor,
|
||||
kmem_reclaim_t reclaim,
|
||||
void *priv, void *vmp)
|
||||
{
|
||||
kmem_cache_cb_t *kcc;
|
||||
|
||||
kcc = (kmem_cache_cb_t *)kmalloc(sizeof(*kcc), GFP_KERNEL);
|
||||
if (kcc) {
|
||||
kcc->kcc_cache = cache;
|
||||
kcc->kcc_constructor = constructor;
|
||||
kcc->kcc_destructor = destructor;
|
||||
kcc->kcc_reclaim = reclaim;
|
||||
kcc->kcc_private = priv;
|
||||
kcc->kcc_vmp = vmp;
|
||||
spin_lock(&kmem_cache_cb_lock);
|
||||
list_add(&kcc->kcc_list, &kmem_cache_cb_list);
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
}
|
||||
|
||||
return kcc;
|
||||
}
|
||||
|
||||
static void
|
||||
kmem_cache_remove_cache_cb(kmem_cache_cb_t *kcc)
|
||||
{
|
||||
spin_lock(&kmem_cache_cb_lock);
|
||||
list_del(&kcc->kcc_list);
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
|
||||
if (kcc)
|
||||
kfree(kcc);
|
||||
}
|
||||
|
||||
static void
|
||||
kmem_cache_generic_constructor(void *ptr, kmem_cache_t *cache, unsigned long flags)
|
||||
{
|
||||
kmem_cache_cb_t *kcc;
|
||||
|
||||
spin_lock(&kmem_cache_cb_lock);
|
||||
|
||||
/* Callback list must be in sync with linux slab caches */
|
||||
kcc = kmem_cache_find_cache_cb(cache);
|
||||
BUG_ON(!kcc);
|
||||
|
||||
kcc->kcc_constructor(ptr, kcc->kcc_private, (int)flags);
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
/* Linux constructor has no return code, silently eat it */
|
||||
}
|
||||
|
||||
static void
|
||||
kmem_cache_generic_destructor(void *ptr, kmem_cache_t *cache, unsigned long flags)
|
||||
{
|
||||
kmem_cache_cb_t *kcc;
|
||||
|
||||
spin_lock(&kmem_cache_cb_lock);
|
||||
|
||||
/* Callback list must be in sync with linux slab caches */
|
||||
kcc = kmem_cache_find_cache_cb(cache);
|
||||
BUG_ON(!kcc);
|
||||
|
||||
/* Solaris destructor takes no flags, silently eat them */
|
||||
kcc->kcc_destructor(ptr, kcc->kcc_private);
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
}
|
||||
|
||||
/* XXX - Arguments are ignored */
|
||||
static int
|
||||
kmem_cache_generic_shrinker(int nr_to_scan, unsigned int gfp_mask)
|
||||
{
|
||||
kmem_cache_cb_t *kcc;
|
||||
int total = 0;
|
||||
|
||||
/* Under linux a shrinker is not tightly coupled with a slab
|
||||
* cache. In fact linux always systematically trys calling all
|
||||
* registered shrinker callbacks until its target reclamation level
|
||||
* is reached. Because of this we only register one shrinker
|
||||
* function in the shim layer for all slab caches. And we always
|
||||
* attempt to shrink all caches when this generic shrinker is called.
|
||||
*/
|
||||
spin_lock(&kmem_cache_cb_lock);
|
||||
|
||||
list_for_each_entry(kcc, &kmem_cache_cb_list, kcc_list) {
|
||||
/* Under linux the desired number and gfp type of objects
|
||||
* is passed to the reclaiming function as a sugested reclaim
|
||||
* target. I do not pass these args on because reclaim
|
||||
* policy is entirely up to the owner under solaris. We only
|
||||
* pass on the pre-registered private data.
|
||||
*/
|
||||
if (kcc->kcc_reclaim)
|
||||
kcc->kcc_reclaim(kcc->kcc_private);
|
||||
|
||||
total += 1;
|
||||
}
|
||||
|
||||
/* Under linux we should return the remaining number of entires in
|
||||
* the cache. Unfortunately, I don't see an easy way to safely
|
||||
* emulate this behavior so I'm returning one entry per cache which
|
||||
* was registered with the generic shrinker. This should fake out
|
||||
* the linux VM when it attempts to shrink caches.
|
||||
*/
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
return total;
|
||||
}
|
||||
|
||||
/* Ensure the __kmem_cache_create/__kmem_cache_destroy macros are
|
||||
* removed here to prevent a recursive substitution, we want to call
|
||||
* the native linux version.
|
||||
*/
|
||||
#undef kmem_cache_create
|
||||
#undef kmem_cache_destroy
|
||||
|
||||
kmem_cache_t *
|
||||
__kmem_cache_create(char *name, size_t size, size_t align,
|
||||
kmem_constructor_t constructor,
|
||||
kmem_destructor_t destructor,
|
||||
kmem_reclaim_t reclaim,
|
||||
void *priv, void *vmp, int flags)
|
||||
{
|
||||
kmem_cache_t *cache;
|
||||
kmem_cache_cb_t *kcc;
|
||||
int shrinker_flag = 0;
|
||||
|
||||
/* FIXME: - Option currently unsupported by shim layer */
|
||||
BUG_ON(vmp);
|
||||
|
||||
cache = kmem_cache_create(name, size, align, flags,
|
||||
kmem_cache_generic_constructor,
|
||||
kmem_cache_generic_destructor);
|
||||
if (cache == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Register shared shrinker function on initial cache create */
|
||||
spin_lock(&kmem_cache_cb_lock);
|
||||
if (list_empty(&kmem_cache_cb_list)) {
|
||||
kmem_cache_shrinker = set_shrinker(KMC_DEFAULT_SEEKS,
|
||||
kmem_cache_generic_shrinker);
|
||||
if (kmem_cache_shrinker == NULL) {
|
||||
kmem_cache_destroy(cache);
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
|
||||
kcc = kmem_cache_add_cache_cb(cache, constructor, destructor,
|
||||
reclaim, priv, vmp);
|
||||
if (kcc == NULL) {
|
||||
if (shrinker_flag) /* New shrinker registered must be removed */
|
||||
remove_shrinker(kmem_cache_shrinker);
|
||||
|
||||
kmem_cache_destroy(cache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
EXPORT_SYMBOL(__kmem_cache_create);
|
||||
|
||||
/* Return codes discarded because Solaris implementation has void return */
|
||||
void
|
||||
__kmem_cache_destroy(kmem_cache_t *cache)
|
||||
{
|
||||
kmem_cache_cb_t *kcc;
|
||||
|
||||
spin_lock(&kmem_cache_cb_lock);
|
||||
kcc = kmem_cache_find_cache_cb(cache);
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
if (kcc == NULL)
|
||||
return;
|
||||
|
||||
kmem_cache_destroy(cache);
|
||||
kmem_cache_remove_cache_cb(kcc);
|
||||
|
||||
/* Unregister generic shrinker on removal of all caches */
|
||||
spin_lock(&kmem_cache_cb_lock);
|
||||
if (list_empty(&kmem_cache_cb_list))
|
||||
remove_shrinker(kmem_cache_shrinker);
|
||||
|
||||
spin_unlock(&kmem_cache_cb_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(__kmem_cache_destroy);
|
||||
|
||||
void
|
||||
__kmem_reap(void) {
|
||||
/* Since there's no easy hook in to linux to force all the registered
|
||||
* shrinkers to run we just run the ones registered for this shim */
|
||||
kmem_cache_generic_shrinker(KMC_REAP_CHUNK, GFP_KERNEL);
|
||||
}
|
||||
EXPORT_SYMBOL(__kmem_reap);
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <linux-rwlock.h>
|
||||
|
||||
int
|
||||
rw_lock_held(krwlock_t *rwlp)
|
||||
{
|
||||
BUG_ON(rwlp->rw_magic != RW_MAGIC);
|
||||
|
||||
#ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
|
||||
if (rwlp->rw_sem.activity != 0) {
|
||||
#else
|
||||
if (rwlp->rw_sem.count != 0) {
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rw_read_held(krwlock_t *rwlp)
|
||||
{
|
||||
BUG_ON(rwlp->rw_magic != RW_MAGIC);
|
||||
|
||||
if (rw_lock_held(rwlp) && rwlp->rw_owner == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rw_write_held(krwlock_t *rwlp)
|
||||
{
|
||||
BUG_ON(rwlp->rw_magic != RW_MAGIC);
|
||||
|
||||
if (rwlp->rw_owner == current) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <linux-taskq.h>
|
||||
|
||||
/*
|
||||
* Task queue interface
|
||||
*
|
||||
* The taskq_work_wrapper functions are used to manage the work_structs
|
||||
* which must be submitted to linux. The shim layer allocates a wrapper
|
||||
* structure for all items which contains a pointer to itself as well as
|
||||
* the real work to be performed. When the work item run the generic
|
||||
* handle is called which calls the real work function and then using
|
||||
* the self pointer frees the work_struct.
|
||||
*/
|
||||
typedef struct taskq_work_wrapper {
|
||||
struct work_struct tww_work;
|
||||
task_func_t tww_func;
|
||||
void * tww_priv;
|
||||
} taskq_work_wrapper_t;
|
||||
|
||||
static void
|
||||
taskq_work_handler(void *priv)
|
||||
{
|
||||
taskq_work_wrapper_t *tww = priv;
|
||||
|
||||
BUG_ON(tww == NULL);
|
||||
BUG_ON(tww->tww_func == NULL);
|
||||
|
||||
/* Call the real function and free the wrapper */
|
||||
tww->tww_func(tww->tww_priv);
|
||||
kfree(tww);
|
||||
}
|
||||
|
||||
/* XXX - All flags currently ignored */
|
||||
taskqid_t
|
||||
__taskq_dispatch(taskq_t *tq, task_func_t func, void *priv, uint_t flags)
|
||||
{
|
||||
struct workqueue_struct *wq = tq;
|
||||
taskq_work_wrapper_t *tww;
|
||||
int rc;
|
||||
|
||||
|
||||
BUG_ON(in_interrupt());
|
||||
BUG_ON(tq == NULL);
|
||||
BUG_ON(func == NULL);
|
||||
|
||||
tww = (taskq_work_wrapper_t *)kmalloc(sizeof(*tww), GFP_KERNEL);
|
||||
if (!tww)
|
||||
return (taskqid_t)0;
|
||||
|
||||
INIT_WORK(&(tww->tww_work), taskq_work_handler, tww);
|
||||
tww->tww_func = func;
|
||||
tww->tww_priv = priv;
|
||||
|
||||
rc = queue_work(wq, &(tww->tww_work));
|
||||
if (!rc) {
|
||||
kfree(tww);
|
||||
return (taskqid_t)0;
|
||||
}
|
||||
|
||||
return (taskqid_t)wq;
|
||||
}
|
||||
EXPORT_SYMBOL(__taskq_dispatch);
|
||||
|
||||
/* XXX - Most args ignored until we decide if it's worth the effort
|
||||
* to emulate the solaris notion of dynamic thread pools. For
|
||||
* now we simply serialize everything through one thread which
|
||||
* may come back to bite us as a performance issue.
|
||||
* pri - Ignore priority
|
||||
* min - Ignored until this is a dynamic thread pool
|
||||
* max - Ignored until this is a dynamic thread pool
|
||||
* flags - Ignored until this is a dynamic thread_pool
|
||||
*/
|
||||
taskq_t *
|
||||
__taskq_create(const char *name, int nthreads, pri_t pri,
|
||||
int minalloc, int maxalloc, uint_t flags)
|
||||
{
|
||||
/* NOTE: Linux workqueue names are limited to 10 chars */
|
||||
|
||||
return create_singlethread_workqueue(name);
|
||||
}
|
||||
EXPORT_SYMBOL(__taskq_create);
|
||||
@@ -0,0 +1,114 @@
|
||||
#include <linux-thread.h>
|
||||
|
||||
/*
|
||||
* Thread interfaces
|
||||
*/
|
||||
typedef struct thread_priv_s {
|
||||
unsigned long tp_magic; /* Magic */
|
||||
void (*tp_func)(void *); /* Registered function */
|
||||
void *tp_args; /* Args to be passed to function */
|
||||
size_t tp_len; /* Len to be passed to function */
|
||||
int tp_state; /* State to start thread at */
|
||||
pri_t tp_pri; /* Priority to start threat at */
|
||||
volatile kthread_t *tp_task; /* Task pointer for new thread */
|
||||
spinlock_t tp_lock; /* Syncronization lock */
|
||||
wait_queue_head_t tp_waitq; /* Syncronization wait queue */
|
||||
} thread_priv_t;
|
||||
|
||||
static int
|
||||
thread_generic_wrapper(void *arg)
|
||||
{
|
||||
thread_priv_t *tp = (thread_priv_t *)arg;
|
||||
void (*func)(void *);
|
||||
void *args;
|
||||
char name[16];
|
||||
|
||||
/* Use the truncated function name as thread name */
|
||||
snprintf(name, sizeof(name), "%s", "kthread");
|
||||
daemonize(name);
|
||||
|
||||
spin_lock(&tp->tp_lock);
|
||||
BUG_ON(tp->tp_magic != TP_MAGIC);
|
||||
func = tp->tp_func;
|
||||
args = tp->tp_args;
|
||||
tp->tp_task = get_current();
|
||||
set_current_state(tp->tp_state);
|
||||
set_user_nice((kthread_t *)tp->tp_task, PRIO_TO_NICE(tp->tp_pri));
|
||||
|
||||
spin_unlock(&tp->tp_lock);
|
||||
wake_up(&tp->tp_waitq);
|
||||
|
||||
/* DO NOT USE 'ARG' AFTER THIS POINT, EVER, EVER, EVER!
|
||||
* Local variables are used here because after the calling thread
|
||||
* has been woken up it will exit and this memory will no longer
|
||||
* be safe to access since it was declared on the callers stack. */
|
||||
if (func)
|
||||
func(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
__thread_exit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(__thread_exit);
|
||||
|
||||
/* thread_create() may block forever if it cannot create a thread or
|
||||
* allocate memory. This is preferable to returning a NULL which Solaris
|
||||
* style callers likely never check for... since it can't fail. */
|
||||
kthread_t *
|
||||
__thread_create(caddr_t stk, size_t stksize, void (*proc)(void *),
|
||||
void *args, size_t len, proc_t *pp, int state, pri_t pri)
|
||||
{
|
||||
thread_priv_t tp;
|
||||
DEFINE_WAIT(wait);
|
||||
long pid;
|
||||
|
||||
/* Option pp is simply ignored */
|
||||
/* Variable stack size unsupported */
|
||||
BUG_ON(stk != NULL);
|
||||
BUG_ON(stk != 0);
|
||||
|
||||
/* Variable tp is located on the stack and not the heap because I want
|
||||
* to minimize any chance of a failure, since the Solaris code is designed
|
||||
* such that this function cannot fail. This is a little dangerous since
|
||||
* we're passing a stack address to a new thread but correct locking was
|
||||
* added to ensure the callee can use the data safely until wake_up(). */
|
||||
tp.tp_magic = TP_MAGIC;
|
||||
tp.tp_func = proc;
|
||||
tp.tp_args = args;
|
||||
tp.tp_len = len;
|
||||
tp.tp_state = state;
|
||||
tp.tp_pri = pri;
|
||||
tp.tp_task = NULL;
|
||||
spin_lock_init(&tp.tp_lock);
|
||||
init_waitqueue_head(&tp.tp_waitq);
|
||||
|
||||
spin_lock(&tp.tp_lock);
|
||||
|
||||
/* Solaris says this must never fail so we try forever */
|
||||
while ((pid = kernel_thread(thread_generic_wrapper, (void *)&tp, 0)) < 0)
|
||||
printk(KERN_ERR "linux-thread: Unable to create thread; "
|
||||
"pid = %ld\n", pid);
|
||||
|
||||
/* All signals are ignored due to sleeping TASK_UNINTERRUPTIBLE */
|
||||
for (;;) {
|
||||
prepare_to_wait(&tp.tp_waitq, &wait, TASK_UNINTERRUPTIBLE);
|
||||
if (tp.tp_task != NULL)
|
||||
break;
|
||||
|
||||
spin_unlock(&tp.tp_lock);
|
||||
schedule();
|
||||
spin_lock(&tp.tp_lock);
|
||||
}
|
||||
|
||||
/* Verify the pid retunred matches the pid in the task struct */
|
||||
BUG_ON(pid != (tp.tp_task)->pid);
|
||||
|
||||
spin_unlock(&tp.tp_lock);
|
||||
|
||||
return (kthread_t *)tp.tp_task;
|
||||
}
|
||||
EXPORT_SYMBOL(__thread_create);
|
||||
Reference in New Issue
Block a user