📄 ref.c
字号:
/* * Fusion Kernel Module * * (c) Copyright 2002-2003 Convergence GmbH * * Written by Denis Oliver Kropp <dok@directfb.org> * * * 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. */#include <linux/config.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/sched.h>#include <linux/fusion.h>#include "fusiondev.h"#include "list.h"#include "call.h"#include "ref.h"typedef struct __Fusion_FusionRef FusionRef;typedef struct { FusionLink link; int fusion_id; int refs;} LocalRef;typedef struct { FusionLink link; FusionRef *ref;} Inheritor;struct __Fusion_FusionRef { FusionLink link; struct semaphore lock; int id; int pid; int global; int local; int locked; /* non-zero fusion id of lock owner */ bool watched; /* true if watch has been installed */ int call_id; /* id of call registered with a watch */ int call_arg; /* optional call parameter */ FusionRef *inherited; FusionLink *inheritors; FusionLink *local_refs; wait_queue_head_t wait;};/******************************************************************************/static int lookup_ref (FusionDev *dev, bool locked, int id, FusionRef **ret_ref);static int lock_ref (FusionDev *dev, bool locked, int id, FusionRef **ret_ref);static void unlock_ref (FusionRef *ref);static int add_local (FusionRef *ref, int fusion_id, int add);static void clear_local (FusionDev *dev, FusionRef *ref, int fusion_id);static void free_all_local (FusionRef *ref);static int propagate_local (FusionDev *dev, FusionRef *ref, int diff);static void notify_ref (FusionDev *dev, FusionRef *ref);static int add_inheritor (FusionRef *ref, FusionRef *from);static void remove_inheritor(FusionRef *ref, FusionRef *from);static void drop_inheritors (FusionDev *dev, FusionRef *ref);/******************************************************************************/static intrefs_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *private){ FusionLink *l; FusionDev *dev = private; int written = 0; if (down_interruptible (&dev->ref.lock)) return -EINTR; fusion_list_foreach (l, dev->ref.list) { FusionRef *ref = (FusionRef*) l; if (ref->locked) written += sprintf(buf+written, "(%5d) 0x%08x %2d %2d (locked by %d)\n", ref->pid, ref->id, ref->global, ref->local, ref->locked); else written += sprintf(buf+written, "(%5d) 0x%08x %2d %2d\n", ref->pid, ref->id, ref->global, ref->local); if (written < offset) { offset -= written; written = 0; } if (written >= len) break; } up (&dev->ref.lock); *start = buf + offset; written -= offset; if (written > len) { *eof = 0; return len; } *eof = 1; return(written<0) ? 0 : written;}intfusion_ref_init (FusionDev *dev){ init_MUTEX (&dev->ref.lock); create_proc_read_entry("refs", 0, dev->proc_dir, refs_read_proc, dev); return 0;}voidfusion_ref_deinit (FusionDev *dev){ FusionLink *l; down (&dev->ref.lock); remove_proc_entry ("refs", dev->proc_dir); l = dev->ref.list; while (l) { FusionLink *next = l->next; FusionRef *ref = (FusionRef *) l; free_all_local (ref); kfree (ref); l = next; } up (&dev->ref.lock);}/******************************************************************************/intfusion_ref_new (FusionDev *dev, int *id){ FusionRef *ref; ref = kmalloc (sizeof(FusionRef), GFP_KERNEL); if (!ref) return -ENOMEM; memset (ref, 0, sizeof(FusionRef)); if (down_interruptible (&dev->ref.lock)) { kfree (ref); return -EINTR; } ref->id = dev->ref.ids++; ref->pid = current->pid; init_MUTEX (&ref->lock); init_waitqueue_head (&ref->wait); fusion_list_prepend (&dev->ref.list, &ref->link); up (&dev->ref.lock); *id = ref->id; return 0;}intfusion_ref_up (FusionDev *dev, int id, int fusion_id){ int ret; FusionRef *ref; ret = lookup_ref (dev, false, id, &ref); if (ret) return ret; if (down_interruptible (&ref->lock)) { up (&dev->ref.lock); return -EINTR; } dev->stat.ref_up++; if (ref->locked) { ret = -EAGAIN; goto out; } if (fusion_id) { ret = add_local (ref, fusion_id, 1); if (ret) goto out; ret = propagate_local( dev, ref, 1 ); } else ref->global++;out: up (&dev->ref.lock); unlock_ref (ref); return ret;}intfusion_ref_down (FusionDev *dev, int id, int fusion_id){ int ret; FusionRef *ref; ret = lookup_ref (dev, false, id, &ref); if (ret) return ret; if (down_interruptible (&ref->lock)) { up (&dev->ref.lock); return -EINTR; } dev->stat.ref_down++; if (ref->locked) { ret = -EAGAIN; goto out; } if (fusion_id) { ret = -EIO; if (!ref->local) goto out; ret = add_local (ref, fusion_id, -1); if (ret) goto out; ret = propagate_local( dev, ref, -1 ); } else { if (!ref->global) { ret = -EIO; goto out; } ref->global--; if (ref->local + ref->global == 0) notify_ref (dev, ref); }out: up (&dev->ref.lock); unlock_ref (ref); return ret;}intfusion_ref_zero_lock (FusionDev *dev, int id, int fusion_id){ int ret; FusionRef *ref; while (true) { ret = lock_ref (dev, false, id, &ref); if (ret) return ret; if (ref->watched) { unlock_ref (ref); return -EACCES; } if (ref->locked) { unlock_ref (ref); return ref->locked == fusion_id ? -EIO : -EAGAIN; } if (ref->global || ref->local) { fusion_sleep_on (&ref->wait, &ref->lock, 0); if (signal_pending(current)) return -EINTR; } else break; } ref->locked = fusion_id; unlock_ref (ref); return 0;}intfusion_ref_zero_trylock (FusionDev *dev, int id, int fusion_id){ int ret; FusionRef *ref; ret = lock_ref (dev, false, id, &ref); if (ret) return ret; if (ref->locked) { unlock_ref (ref); return ref->locked == fusion_id ? -EIO : -EAGAIN; } if (ref->global || ref->local) ret = -ETOOMANYREFS; else ref->locked = fusion_id; unlock_ref (ref); return ret;}intfusion_ref_unlock (FusionDev *dev, int id, int fusion_id){ int ret; FusionRef *ref; ret = lock_ref (dev, false, id, &ref); if (ret) return ret; if (ref->locked != fusion_id) { unlock_ref (ref); return -EIO; } ref->locked = 0; unlock_ref (ref); return 0;}intfusion_ref_stat (FusionDev *dev, int id, int *refs){ int ret; FusionRef *ref; ret = lock_ref (dev, false, id, &ref); if (ret) return ret; *refs = ref->global + ref->local; unlock_ref (ref);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -