Commit 6701334c authored by Jean Tourrilhes's avatar Jean Tourrilhes Committed by Jeff Garzik

irda update 3/6:

o [FEATURE] New hashbin locking scheme for irqueue
o [FEATURE] Get rid of old broken hashbin locking schemes
o [FEATURE] Lock hasbins while enumerating it in various places
o [CORRECT] Remove all remaining "save_flags(flags);cli();"
o [CORRECT] Fix two "return with spinlock" found by Stanford checker
parent 3ff00907
......@@ -183,7 +183,6 @@ struct irlmp_cb {
hashbin_t *services;
hashbin_t *cachelog; /* Current discovery log */
spinlock_t log_lock; /* discovery log spinlock */
int running;
......@@ -221,7 +220,7 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
struct sk_buff *userdata);
int irlmp_disconnect_request(struct lsap_cb *, struct sk_buff *userdata);
void irlmp_discovery_confirm(hashbin_t *discovery_log, DISCOVERY_MODE);
void irlmp_discovery_confirm(hashbin_t *discovery_log, DISCOVERY_MODE mode);
void irlmp_discovery_request(int nslots);
struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots);
void irlmp_do_expiry(void);
......@@ -258,8 +257,6 @@ extern int sysctl_discovery;
extern int sysctl_lap_keepalive_time; /* in ms, default is LM_IDLE_TIMEOUT */
extern struct irlmp_cb *irlmp;
static inline hashbin_t *irlmp_get_cachelog(void) { return irlmp->cachelog; }
/* Check if LAP queue is full.
* Used by IrTTP for low control, see comments in irlap.h - Jean II */
static inline int irlmp_lap_tx_queue_full(struct lsap_cb *self)
......
......@@ -36,12 +36,12 @@
#define NAME_SIZE 32
/*
* Hash types
* Hash types (some flags can be xored)
* See comments in irqueue.c for which one to use...
*/
#define HB_NOLOCK 0
#define HB_GLOBAL 1
#define HB_LOCAL 2
#define HB_SORTED 4
#define HB_NOLOCK 0 /* No concurent access prevention */
#define HB_LOCK 1 /* Prevent concurent write with global lock */
#define HB_SORTED 4 /* Not yet supported */
/*
* Hash defines
......@@ -57,11 +57,6 @@
typedef void (*FREE_FUNC)(void *arg);
/*
* Hashbin
*/
#define GET_HASHBIN(x) ( x & HASHBIN_MASK )
struct irda_queue {
struct irda_queue *q_next;
struct irda_queue *q_prev;
......@@ -75,8 +70,9 @@ typedef struct hashbin_t {
__u32 magic;
int hb_type;
int hb_size;
spinlock_t hb_mutex[HASHBIN_SIZE] IRDA_ALIGN;
irda_queue_t *hb_queue[HASHBIN_SIZE] IRDA_ALIGN;
spinlock_t hb_spinlock; /* HB_LOCK - Can be used by the user */
irda_queue_t* hb_queue[HASHBIN_SIZE] IRDA_ALIGN;
irda_queue_t* hb_current;
} hashbin_t;
......@@ -86,17 +82,16 @@ int hashbin_delete(hashbin_t* hashbin, FREE_FUNC func);
int hashbin_clear(hashbin_t* hashbin, FREE_FUNC free_func);
void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
char* name);
void* hashbin_find(hashbin_t* hashbin, long hashv, char* name);
void* hashbin_remove(hashbin_t* hashbin, long hashv, char* name);
void* hashbin_remove_first(hashbin_t *hashbin);
void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry);
void* hashbin_find(hashbin_t* hashbin, long hashv, char* name);
void* hashbin_lock_find(hashbin_t* hashbin, long hashv, char* name);
void* hashbin_find_next(hashbin_t* hashbin, long hashv, char* name,
void ** pnext);
irda_queue_t *hashbin_get_first(hashbin_t *hashbin);
irda_queue_t *hashbin_get_next(hashbin_t *hashbin);
void enqueue_last(irda_queue_t **queue, irda_queue_t* element);
void enqueue_first(irda_queue_t **queue, irda_queue_t* element);
irda_queue_t *dequeue_first(irda_queue_t **queue);
#define HASHBIN_GET_SIZE(hashbin) hashbin->hb_size
#endif
......@@ -61,7 +61,7 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
/* Set time of first discovery if node is new (see below) */
new->first_timestamp = new->timestamp;
spin_lock_irqsave(&irlmp->log_lock, flags);
spin_lock_irqsave(&cachelog->hb_spinlock, flags);
/*
* Remove all discoveries of devices that has previously been
......@@ -95,13 +95,13 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
/* Insert the new and updated version */
hashbin_insert(cachelog, (irda_queue_t *) new, new->daddr, NULL);
spin_unlock_irqrestore(&irlmp->log_lock, flags);
spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
}
/*
* Function irlmp_add_discovery_log (cachelog, log)
*
* Merge a disovery log into the cachlog.
* Merge a disovery log into the cachelog.
*
*/
void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
......@@ -115,11 +115,17 @@ void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
* discovery, so restart discovery again with just the half timeout
* of the normal one.
*/
/* Well... It means that there was nobody out there - Jean II */
if (log == NULL) {
/* irlmp_start_discovery_timer(irlmp, 150); */
return;
}
/*
* Locking : we are the only owner of this discovery log, so
* no need to lock it.
* We just need to lock the global log in irlmp_add_discovery().
*/
discovery = (discovery_t *) hashbin_remove_first(log);
while (discovery != NULL) {
irlmp_add_discovery(cachelog, discovery);
......@@ -146,7 +152,7 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
IRDA_DEBUG(4, __FUNCTION__ "()\n");
spin_lock_irqsave(&irlmp->log_lock, flags);
spin_lock_irqsave(&log->hb_spinlock, flags);
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
......@@ -169,7 +175,7 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
}
}
spin_unlock_irqrestore(&irlmp->log_lock, flags);
spin_unlock_irqrestore(&log->hb_spinlock, flags);
}
/*
......@@ -230,13 +236,13 @@ struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 m
return NULL;
/* Save spin lock - spinlock should be discovery specific */
spin_lock_irqsave(&irlmp->log_lock, flags);
spin_lock_irqsave(&log->hb_spinlock, flags);
/* Create the client specific buffer */
n = HASHBIN_GET_SIZE(log);
buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
if (buffer == NULL) {
spin_unlock_irqrestore(&irlmp->log_lock, flags);
spin_unlock_irqrestore(&log->hb_spinlock, flags);
return NULL;
}
......@@ -257,7 +263,7 @@ struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 m
discovery = (discovery_t *) hashbin_get_next(log);
}
spin_unlock_irqrestore(&irlmp->log_lock, flags);
spin_unlock_irqrestore(&log->hb_spinlock, flags);
/* Get the actual number of device in the buffer and return */
*pn = i;
......@@ -276,7 +282,7 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr)
unsigned long flags;
discovery_t *d;
spin_lock_irqsave(&irlmp->log_lock, flags);
spin_lock_irqsave(&cachelog->hb_spinlock, flags);
/* Look at all discoveries for that link */
d = (discovery_t *) hashbin_get_first(cachelog);
......@@ -288,13 +294,13 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr)
if (strcmp(name, d->nickname) == 0) {
*saddr = d->saddr;
spin_unlock_irqrestore(&irlmp->log_lock, flags);
spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
return d->daddr;
}
d = (discovery_t *) hashbin_get_next(cachelog);
}
spin_unlock_irqrestore(&irlmp->log_lock, flags);
spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
return 0;
}
......@@ -310,7 +316,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int length,
{
discovery_t *discovery;
unsigned long flags;
hashbin_t *cachelog = irlmp_get_cachelog();
hashbin_t *cachelog = irlmp->cachelog;
int len = 0;
if (!irlmp)
......@@ -318,7 +324,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int length,
len = sprintf(buf, "IrLMP: Discovery log:\n\n");
spin_lock_irqsave(&irlmp->log_lock, flags);
spin_lock_irqsave(&cachelog->hb_spinlock, flags);
discovery = (discovery_t *) hashbin_get_first(cachelog);
while (( discovery != NULL) && (len < length)) {
......@@ -362,7 +368,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int length,
discovery = (discovery_t *) hashbin_get_next(cachelog);
}
spin_unlock_irqrestore(&irlmp->log_lock, flags);
spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
return len;
}
......@@ -91,13 +91,13 @@ int irda_device_proc_read(char *buf, char **start, off_t offset, int len,
int __init irda_device_init( void)
{
dongles = hashbin_new(HB_GLOBAL);
dongles = hashbin_new(HB_LOCK);
if (dongles == NULL) {
printk(KERN_WARNING "IrDA: Can't allocate dongles hashbin!\n");
return -ENOMEM;
}
tasks = hashbin_new(HB_GLOBAL);
tasks = hashbin_new(HB_LOCK);
if (tasks == NULL) {
printk(KERN_WARNING "IrDA: Can't allocate tasks hashbin!\n");
return -ENOMEM;
......@@ -438,7 +438,7 @@ dongle_t *irda_device_dongle_init(struct net_device *dev, int type)
}
#endif
if (!(reg = hashbin_find(dongles, type, NULL))) {
if (!(reg = hashbin_lock_find(dongles, type, NULL))) {
ERROR("IrDA: Unable to find requested dongle\n");
return NULL;
}
......@@ -477,7 +477,7 @@ int irda_device_dongle_cleanup(dongle_t *dongle)
int irda_device_register_dongle(struct dongle_reg *new)
{
/* Check if this dongle has been registred before */
if (hashbin_find(dongles, new->type, NULL)) {
if (hashbin_lock_find(dongles, new->type, NULL)) {
MESSAGE("%s: Dongle already registered\n", __FUNCTION__);
return 0;
}
......
......@@ -91,11 +91,12 @@ int __init iriap_init(void)
__u16 hints;
/* Allocate master array */
iriap = hashbin_new(HB_LOCAL);
iriap = hashbin_new(HB_LOCK);
if (!iriap)
return -ENOMEM;
objects = hashbin_new(HB_LOCAL);
/* Object repository - defined in irias_object.c */
objects = hashbin_new(HB_LOCK);
if (!objects) {
WARNING("%s: Can't allocate objects hashbin!\n", __FUNCTION__);
return -ENOMEM;
......@@ -973,13 +974,12 @@ int irias_proc_read(char *buf, char **start, off_t offset, int len)
ASSERT( objects != NULL, return 0;);
save_flags( flags);
cli();
len = 0;
len += sprintf(buf+len, "LM-IAS Objects:\n");
spin_lock_irqsave(&objects->hb_spinlock, flags);
/* List all objects */
obj = (struct ias_object *) hashbin_get_first(objects);
while ( obj != NULL) {
......@@ -989,6 +989,11 @@ int irias_proc_read(char *buf, char **start, off_t offset, int len)
len += sprintf(buf+len, "id=%d", obj->id);
len += sprintf(buf+len, "\n");
/* Careful for priority inversions here !
* All other uses of attrib spinlock are independant of
* the object spinlock, so we are safe. Jean II */
spin_lock(&obj->attribs->hb_spinlock);
/* List all attributes for this object */
attrib = (struct ias_attrib *)
hashbin_get_first(obj->attribs);
......@@ -1025,9 +1030,11 @@ int irias_proc_read(char *buf, char **start, off_t offset, int len)
attrib = (struct ias_attrib *)
hashbin_get_next(obj->attribs);
}
spin_unlock(&obj->attribs->hb_spinlock);
obj = (struct ias_object *) hashbin_get_next(objects);
}
restore_flags(flags);
spin_unlock_irqrestore(&objects->hb_spinlock, flags);
return len;
}
......
......@@ -93,7 +93,10 @@ struct ias_object *irias_new_object( char *name, int id)
obj->name = strndup(name, IAS_MAX_CLASSNAME);
obj->id = id;
obj->attribs = hashbin_new(HB_LOCAL);
/* Locking notes : the attrib spinlock has lower precendence
* than the objects spinlock. Never grap the objects spinlock
* while holding any attrib spinlock (risk of deadlock). Jean II */
obj->attribs = hashbin_new(HB_LOCK);
return obj;
}
......@@ -211,7 +214,8 @@ struct ias_object *irias_find_object(char *name)
{
ASSERT(name != NULL, return NULL;);
return hashbin_find(objects, 0, name);
/* Unsafe (locking), object might change */
return hashbin_lock_find(objects, 0, name);
}
/*
......@@ -228,10 +232,11 @@ struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name)
ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;);
ASSERT(name != NULL, return NULL;);
attrib = hashbin_find(obj->attribs, 0, name);
attrib = hashbin_lock_find(obj->attribs, 0, name);
if (attrib == NULL)
return NULL;
/* Unsafe (locking), attrib might change */
return attrib;
}
......@@ -267,26 +272,32 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name,
{
struct ias_object *obj;
struct ias_attrib *attrib;
unsigned long flags;
/* Find object */
obj = hashbin_find(objects, 0, obj_name);
obj = hashbin_lock_find(objects, 0, obj_name);
if (obj == NULL) {
WARNING("%s: Unable to find object: %s\n", __FUNCTION__,
obj_name);
return -1;
}
/* Slightly unsafe (obj might get removed under us) */
spin_lock_irqsave(&obj->attribs->hb_spinlock, flags);
/* Find attribute */
attrib = hashbin_find(obj->attribs, 0, attrib_name);
if (attrib == NULL) {
WARNING("%s: Unable to find attribute: %s\n", __FUNCTION__,
attrib_name);
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return -1;
}
if ( attrib->value->type != new_value->type) {
IRDA_DEBUG( 0, __FUNCTION__
"(), changing value type not allowed!\n");
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return -1;
}
......@@ -297,6 +308,7 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name,
attrib->value = new_value;
/* Success */
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return 0;
}
......
......@@ -80,7 +80,7 @@ int irlap_proc_read(char *, char **, off_t, int);
int __init irlap_init(void)
{
/* Allocate master array */
irlap = hashbin_new(HB_LOCAL);
irlap = hashbin_new(HB_LOCK);
if (irlap == NULL) {
ERROR("%s: can't allocate irlap hashbin!\n", __FUNCTION__);
return -ENOMEM;
......@@ -146,7 +146,7 @@ struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
do {
get_random_bytes(&self->saddr, sizeof(self->saddr));
} while ((self->saddr == 0x0) || (self->saddr == BROADCAST) ||
(hashbin_find(irlap, self->saddr, NULL)) );
(hashbin_lock_find(irlap, self->saddr, NULL)) );
/* Copy to the driver */
memcpy(dev->dev_addr, &self->saddr, 4);
......@@ -530,7 +530,8 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
self->discovery_log = NULL;
}
self->discovery_log= hashbin_new(HB_LOCAL);
/* All operations will occur at predictable time, no need to lock */
self->discovery_log= hashbin_new(HB_NOLOCK);
info.S = discovery->nslots; /* Number of slots */
info.s = 0; /* Current slot */
......@@ -1092,15 +1093,14 @@ int irlap_proc_read(char *buf, char **start, off_t offset, int len)
unsigned long flags;
int i = 0;
save_flags(flags);
cli();
spin_lock_irqsave(&irlap->hb_spinlock, flags);
len = 0;
self = (struct irlap_cb *) hashbin_get_first(irlap);
while (self != NULL) {
ASSERT(self != NULL, return -ENODEV;);
ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
ASSERT(self != NULL, break;);
ASSERT(self->magic == LAP_MAGIC, break;);
len += sprintf(buf+len, "irlap%d ", i++);
len += sprintf(buf+len, "state: %s\n",
......@@ -1172,7 +1172,7 @@ int irlap_proc_read(char *buf, char **start, off_t offset, int len)
self = (struct irlap_cb *) hashbin_get_next(irlap);
}
restore_flags(flags);
spin_unlock_irqrestore(&irlap->hb_spinlock, flags);
return len;
}
......
......@@ -83,13 +83,13 @@ int __init irlmp_init(void)
memset(irlmp, 0, sizeof(struct irlmp_cb));
irlmp->magic = LMP_MAGIC;
spin_lock_init(&irlmp->log_lock);
irlmp->clients = hashbin_new(HB_GLOBAL);
irlmp->services = hashbin_new(HB_GLOBAL);
irlmp->links = hashbin_new(HB_GLOBAL);
irlmp->unconnected_lsaps = hashbin_new(HB_GLOBAL);
irlmp->cachelog = hashbin_new(HB_GLOBAL);
irlmp->clients = hashbin_new(HB_LOCK);
irlmp->services = hashbin_new(HB_LOCK);
irlmp->links = hashbin_new(HB_LOCK);
irlmp->unconnected_lsaps = hashbin_new(HB_LOCK);
irlmp->cachelog = hashbin_new(HB_NOLOCK);
spin_lock_init(&irlmp->cachelog->hb_spinlock);
irlmp->free_lsap_sel = 0x10; /* Reserved 0x00-0x0f */
strcpy(sysctl_devname, "Linux");
......@@ -286,7 +286,7 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify)
lap->magic = LMP_LAP_MAGIC;
lap->saddr = saddr;
lap->daddr = DEV_ADDR_ANY;
lap->lsaps = hashbin_new(HB_GLOBAL);
lap->lsaps = hashbin_new(HB_LOCK);
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
lap->cache.valid = FALSE;
#endif
......@@ -347,7 +347,6 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
struct sk_buff *skb = NULL;
struct lap_cb *lap;
struct lsap_cb *lsap;
discovery_t *discovery;
ASSERT(self != NULL, return -EBADR;);
ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;);
......@@ -388,6 +387,10 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
* device with the given daddr
*/
if ((!saddr) || (saddr == DEV_ADDR_ANY)) {
discovery_t *discovery;
unsigned long flags;
spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags);
if (daddr != DEV_ADDR_ANY)
discovery = hashbin_find(irlmp->cachelog, daddr, NULL);
else {
......@@ -400,8 +403,9 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
saddr = discovery->saddr;
daddr = discovery->daddr;
}
spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags);
}
lap = hashbin_find(irlmp->links, saddr, NULL);
lap = hashbin_lock_find(irlmp->links, saddr, NULL);
if (lap == NULL) {
IRDA_DEBUG(1, __FUNCTION__ "(), Unable to find a usable link!\n");
return -EHOSTUNREACH;
......@@ -411,11 +415,8 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
if (lap->daddr == DEV_ADDR_ANY)
lap->daddr = daddr;
else if (lap->daddr != daddr) {
struct lsap_cb *any_lsap;
/* Check if some LSAPs are active on this LAP */
any_lsap = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
if (any_lsap == NULL) {
if (HASHBIN_GET_SIZE(lap->lsaps) == 0) {
/* No active connection, but LAP hasn't been
* disconnected yet (waiting for timeout in LAP).
* Maybe we could give LAP a bit of help in this case.
......@@ -575,25 +576,37 @@ void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb)
struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
{
struct lsap_cb *new;
unsigned long flags;
IRDA_DEBUG(1, __FUNCTION__ "()\n");
spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
/* Only allowed to duplicate unconnected LSAP's */
if (!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) {
IRDA_DEBUG(0, __FUNCTION__ "(), unable to find LSAP\n");
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
flags);
return NULL;
}
/* Allocate a new instance */
new = kmalloc(sizeof(struct lsap_cb), GFP_ATOMIC);
if (!new) {
IRDA_DEBUG(0, __FUNCTION__ "(), unable to kmalloc\n");
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
flags);
return NULL;
}
/* Dup */
memcpy(new, orig, sizeof(struct lsap_cb));
new->notify.instance = instance;
/* new->lap = orig->lap; => done in the memcpy() */
/* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
/* Not everything is the same */
new->notify.instance = instance;
init_timer(&new->watchdog_timer);
hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new,
......@@ -887,7 +900,7 @@ void irlmp_check_services(discovery_t *discovery)
*/
while ((service = service_log[i++]) != S_END) {
IRDA_DEBUG( 4, "service=%02x\n", service);
client = hashbin_find(irlmp->registry, service, NULL);
client = hashbin_lock_find(irlmp->registry, service, NULL);
if (entry && entry->discovery_callback) {
IRDA_DEBUG( 4, "discovery_callback!\n");
......@@ -904,6 +917,7 @@ void irlmp_check_services(discovery_t *discovery)
kfree(service_log);
}
#endif
/*
* Function irlmp_notify_client (log)
*
......@@ -931,6 +945,12 @@ irlmp_notify_client(irlmp_client_t *client,
/*
* Now, check all discovered devices (if any), and notify client
* only about the services that the client is interested in
* Note : most often, we will get called immediately following
* a discovery, so the log is not going to expire.
* On the other hand, comming here through irlmp_discovery_request()
* is *very* problematic - Jean II
* Can't use hashbin_find_next(), key is not unique. I'm running
* out of options :-( - Jean II
*/
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
......@@ -957,6 +977,7 @@ irlmp_notify_client(irlmp_client_t *client,
void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
{
irlmp_client_t *client;
irlmp_client_t *client_next;
IRDA_DEBUG(3, __FUNCTION__ "()\n");
......@@ -966,11 +987,12 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
return;
client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
while (client != NULL) {
while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
(void *) &client_next) ) {
/* Check if we should notify client */
irlmp_notify_client(client, log, mode);
client = (irlmp_client_t *) hashbin_get_next(irlmp->clients);
client = client_next;
}
}
......@@ -988,13 +1010,15 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
void irlmp_discovery_expiry(discovery_t *expiry)
{
irlmp_client_t *client;
irlmp_client_t *client_next;
IRDA_DEBUG(3, __FUNCTION__ "()\n");
ASSERT(expiry != NULL, return;);
client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
while (client != NULL) {
while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
(void *) &client_next) ) {
/* Check if we should notify client */
if ((client->expir_callback) &&
(client->hint_mask & expiry->hints.word & 0x7f7f))
......@@ -1002,7 +1026,7 @@ void irlmp_discovery_expiry(discovery_t *expiry)
client->priv);
/* Next client */
client = (irlmp_client_t *) hashbin_get_next(irlmp->clients);
client = client_next;
}
}
......@@ -1197,11 +1221,9 @@ void irlmp_status_indication(struct lap_cb *self,
struct lsap_cb *curr;
/* Send status_indication to all LSAPs using this link */
next = (struct lsap_cb *) hashbin_get_first( self->lsaps);
while (next != NULL ) {
curr = next;
next = (struct lsap_cb *) hashbin_get_next(self->lsaps);
curr = (struct lsap_cb *) hashbin_get_first( self->lsaps);
while (NULL != hashbin_find_next(self->lsaps, (long) curr, NULL,
(void *) &next) ) {
ASSERT(curr->magic == LMP_LSAP_MAGIC, return;);
/*
* Inform service user if he has requested it
......@@ -1211,6 +1233,8 @@ void irlmp_status_indication(struct lap_cb *self,
link, lock);
else
IRDA_DEBUG(2, __FUNCTION__ "(), no handler\n");
curr = next;
}
}
......@@ -1246,29 +1270,15 @@ void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow)
(IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) {
/* Try to find the next lsap we should poll. */
next = self->flow_next;
if(next != NULL) {
/* Note that if there is only one LSAP on the LAP
* (most common case), self->flow_next is always NULL,
* so we always avoid this loop. - Jean II */
IRDA_DEBUG(4, __FUNCTION__ "() : searching my LSAP\n");
/* We look again in hashbins, because the lsap
* might have gone away... - Jean II */
curr = (struct lsap_cb *) hashbin_get_first(self->lsaps);
while((curr != NULL ) && (curr != next))
curr = (struct lsap_cb *) hashbin_get_next(self->lsaps);
} else
curr = NULL;
/* If we have no lsap, restart from first one */
if(curr == NULL)
curr = (struct lsap_cb *) hashbin_get_first(self->lsaps);
if(next == NULL)
next = (struct lsap_cb *) hashbin_get_first(self->lsaps);
/* Verify current one and find the next one */
curr = hashbin_find_next(self->lsaps, (long) next, NULL,
(void *) &self->flow_next);
/* Uh-oh... Paranoia */
if(curr == NULL)
break;
/* Next time, we will get the next one (or the first one) */
self->flow_next = (struct lsap_cb *) hashbin_get_next(self->lsaps);
IRDA_DEBUG(4, __FUNCTION__ "() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n", curr, next, self->flow_next, lsap_todo, IRLAP_GET_TX_QUEUE_LEN(self->irlap));
/* Inform lsap user that it can send one more packet. */
......@@ -1446,6 +1456,7 @@ void *irlmp_register_service(__u16 hints)
int irlmp_unregister_service(void *handle)
{
irlmp_service_t *service;
unsigned long flags;
IRDA_DEBUG(4, __FUNCTION__ "()\n");
......@@ -1453,7 +1464,7 @@ int irlmp_unregister_service(void *handle)
return -1;
/* Caller may call with invalid handle (it's legal) - Jean II */
service = hashbin_find(irlmp->services, (long) handle, NULL);
service = hashbin_lock_find(irlmp->services, (long) handle, NULL);
if (!service) {
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown service!\n");
return -1;
......@@ -1466,12 +1477,14 @@ int irlmp_unregister_service(void *handle)
irlmp->hints.word = 0;
/* Refresh current hint bits */
spin_lock_irqsave(&irlmp->services->hb_spinlock, flags);
service = (irlmp_service_t *) hashbin_get_first(irlmp->services);
while (service) {
irlmp->hints.word |= service->hints;
service = (irlmp_service_t *)hashbin_get_next(irlmp->services);
}
spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags);
return 0;
}
......@@ -1528,7 +1541,7 @@ int irlmp_update_client(void *handle, __u16 hint_mask,
if (!handle)
return -1;
client = hashbin_find(irlmp->clients, (long) handle, NULL);
client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
if (!client) {
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown client!\n");
return -1;
......@@ -1558,7 +1571,7 @@ int irlmp_unregister_client(void *handle)
return -1;
/* Caller may call with invalid handle (it's legal) - Jean II */
client = hashbin_find(irlmp->clients, (long) handle, NULL);
client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
if (!client) {
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown client!\n");
return -1;
......@@ -1580,6 +1593,7 @@ int irlmp_slsap_inuse(__u8 slsap_sel)
{
struct lsap_cb *self;
struct lap_cb *lap;
unsigned long flags;
ASSERT(irlmp != NULL, return TRUE;);
ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;);
......@@ -1602,10 +1616,16 @@ int irlmp_slsap_inuse(__u8 slsap_sel)
* every IrLAP connection and check every LSAP assosiated with each
* the connection.
*/
spin_lock_irqsave(&irlmp->links->hb_spinlock, flags);
lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
while (lap != NULL) {
ASSERT(lap->magic == LMP_LAP_MAGIC, return TRUE;);
/* Careful for priority inversions here !
* All other uses of attrib spinlock are independant of
* the object spinlock, so we are safe. Jean II */
spin_lock(&lap->lsaps->hb_spinlock);
self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
while (self != NULL) {
ASSERT(self->magic == LMP_LSAP_MAGIC, return TRUE;);
......@@ -1617,8 +1637,11 @@ int irlmp_slsap_inuse(__u8 slsap_sel)
}
self = (struct lsap_cb*) hashbin_get_next(lap->lsaps);
}
spin_unlock(&lap->lsaps->hb_spinlock);
/* Next LAP */
lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
}
spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
return FALSE;
}
......@@ -1727,15 +1750,13 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int len)
ASSERT(irlmp != NULL, return 0;);
save_flags( flags);
cli();
len = 0;
len += sprintf( buf+len, "Unconnected LSAPs:\n");
spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
self = (struct lsap_cb *) hashbin_get_first( irlmp->unconnected_lsaps);
while (self != NULL) {
ASSERT(self->magic == LMP_LSAP_MAGIC, return 0;);
ASSERT(self->magic == LMP_LSAP_MAGIC, break;);
len += sprintf(buf+len, "lsap state: %s, ",
irlsap_state[ self->lsap_state]);
len += sprintf(buf+len,
......@@ -1747,9 +1768,10 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int len)
self = (struct lsap_cb *) hashbin_get_next(
irlmp->unconnected_lsaps);
}
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
len += sprintf(buf+len, "\nRegistred Link Layers:\n");
spin_lock_irqsave(&irlmp->links->hb_spinlock, flags);
lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
while (lap != NULL) {
len += sprintf(buf+len, "lap state: %s, ",
......@@ -1761,10 +1783,15 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int len)
HASHBIN_GET_SIZE(lap->lsaps));
len += sprintf(buf+len, "\n");
/* Careful for priority inversions here !
* All other uses of attrib spinlock are independant of
* the object spinlock, so we are safe. Jean II */
spin_lock(&lap->lsaps->hb_spinlock);
len += sprintf(buf+len, "\n Connected LSAPs:\n");
self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
while (self != NULL) {
ASSERT(self->magic == LMP_LSAP_MAGIC, return 0;);
ASSERT(self->magic == LMP_LSAP_MAGIC, break;);
len += sprintf(buf+len, " lsap state: %s, ",
irlsap_state[ self->lsap_state]);
len += sprintf(buf+len,
......@@ -1776,11 +1803,12 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int len)
self = (struct lsap_cb *) hashbin_get_next(
lap->lsaps);
}
spin_unlock(&lap->lsaps->hb_spinlock);
len += sprintf(buf+len, "\n");
lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
}
restore_flags(flags);
spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
return len;
}
......
......@@ -207,6 +207,43 @@ void irlmp_idle_timer_expired(void *data)
irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL);
}
/*
* Send an event on all LSAPs attached to this LAP.
*/
static inline void
irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin,
IRLMP_EVENT event)
{
struct lsap_cb *lsap;
struct lsap_cb *lsap_next;
/* Note : this function use the new hashbin_find_next()
* function, instead of the old hashbin_get_next().
* This make sure that we are always pointing one lsap
* ahead, so that if the current lsap is removed as the
* result of sending the event, we don't care.
* Also, as we store the context ourselves, if an enumeration
* of the same lsap hashbin happens as the result of sending the
* event, we don't care.
* The only problem is if the next lsap is removed. In that case,
* hashbin_find_next() will return NULL and we will abort the
* enumeration. - Jean II */
/* Also : we don't accept any skb in input. We can *NOT* pass
* the same skb to multiple clients safely, we would need to
* skb_clone() it. - Jean II */
lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin);
while (NULL != hashbin_find_next(lsap_hashbin,
(long) lsap,
NULL,
(void *) &lsap_next) ) {
irlmp_do_lsap_event(lsap, event, NULL);
lsap = lsap_next;
}
}
/*********************************************************************
*
* LAP connection control states
......@@ -274,9 +311,6 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
struct lsap_cb *lsap;
struct lsap_cb *lsap_current;
IRDA_DEBUG(2, __FUNCTION__ "(), event=%s\n", irlmp_event[event]);
switch (event) {
......@@ -290,11 +324,9 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
/* Just accept connection TODO, this should be fixed */
irlap_connect_response(self->irlap, skb);
lsap = (struct lsap_cb *) hashbin_get_first(self->lsaps);
while (lsap != NULL) {
irlmp_do_lsap_event(lsap, LM_LAP_CONNECT_CONFIRM, NULL);
lsap = (struct lsap_cb*) hashbin_get_next(self->lsaps);
}
/* Tell LSAPs that they can start sending data */
irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
/* Note : by the time we get there (LAP retries and co),
* the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */
......@@ -310,11 +342,9 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
/* For all lsap_ce E Associated do LS_Connect_confirm */
irlmp_next_lap_state(self, LAP_ACTIVE);
lsap = (struct lsap_cb *) hashbin_get_first(self->lsaps);
while (lsap != NULL) {
irlmp_do_lsap_event(lsap, LM_LAP_CONNECT_CONFIRM, NULL);
lsap = (struct lsap_cb*) hashbin_get_next(self->lsaps);
}
/* Tell LSAPs that they can start sending data */
irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
/* Note : by the time we get there (LAP retries and co),
* the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */
......@@ -328,18 +358,8 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
irlmp_next_lap_state(self, LAP_STANDBY);
/* Send disconnect event to all LSAPs using this link */
lsap = (struct lsap_cb *) hashbin_get_first( self->lsaps);
while (lsap != NULL ) {
ASSERT(lsap->magic == LMP_LSAP_MAGIC, return;);
lsap_current = lsap;
/* Be sure to stay one item ahead */
lsap = (struct lsap_cb *) hashbin_get_next(self->lsaps);
irlmp_do_lsap_event(lsap_current,
LM_LAP_DISCONNECT_INDICATION,
NULL);
}
irlmp_do_all_lsap_event(self->lsaps,
LM_LAP_DISCONNECT_INDICATION);
break;
case LM_LAP_DISCONNECT_REQUEST:
IRDA_DEBUG(4, __FUNCTION__ "(), LM_LAP_DISCONNECT_REQUEST\n");
......@@ -368,9 +388,6 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
struct lsap_cb *lsap;
struct lsap_cb *lsap_current;
IRDA_DEBUG(4, __FUNCTION__ "()\n");
switch (event) {
......@@ -383,22 +400,11 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
* notify all LSAPs using this LAP, but that should be safe to
* do anyway.
*/
lsap = (struct lsap_cb *) hashbin_get_first(self->lsaps);
while (lsap != NULL) {
irlmp_do_lsap_event(lsap, LM_LAP_CONNECT_CONFIRM, NULL);
lsap = (struct lsap_cb*) hashbin_get_next(self->lsaps);
}
irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
/* Needed by connect indication */
lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
while (lsap != NULL) {
lsap_current = lsap;
/* Be sure to stay one item ahead */
lsap = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps);
irlmp_do_lsap_event(lsap_current,
LM_LAP_CONNECT_CONFIRM, NULL);
}
irlmp_do_all_lsap_event(irlmp->unconnected_lsaps,
LM_LAP_CONNECT_CONFIRM);
/* Keep state */
break;
case LM_LAP_DISCONNECT_REQUEST:
......@@ -447,18 +453,8 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
/*
* Inform all connected LSAP's using this link
*/
lsap = (struct lsap_cb *) hashbin_get_first(self->lsaps);
while (lsap != NULL ) {
ASSERT(lsap->magic == LMP_LSAP_MAGIC, return;);
lsap_current = lsap;
/* Be sure to stay one item ahead */
lsap = (struct lsap_cb *) hashbin_get_next(self->lsaps);
irlmp_do_lsap_event(lsap_current,
LM_LAP_DISCONNECT_INDICATION,
NULL);
}
irlmp_do_all_lsap_event(self->lsaps,
LM_LAP_DISCONNECT_INDICATION);
/* Force an expiry of the discovery log.
* Now that the LAP is free, the system may attempt to
......
......@@ -210,6 +210,7 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
__u8 dlsap_sel; /* Destination LSAP address */
__u8 pid; /* Protocol identifier */
__u8 *fp;
unsigned long flags;
IRDA_DEBUG(4, __FUNCTION__ "()\n");
......@@ -242,6 +243,8 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
return;
}
/* Search the connectionless LSAP */
spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
while (lsap != NULL) {
/*
......@@ -255,6 +258,8 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
}
lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
}
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
if (lsap)
irlmp_connless_data_indication(lsap, skb);
else {
......@@ -374,6 +379,7 @@ void irlmp_link_discovery_indication(struct lap_cb *self,
ASSERT(self != NULL, return;);
ASSERT(self->magic == LMP_LAP_MAGIC, return;);
/* Add to main log, cleanup */
irlmp_add_discovery(irlmp->cachelog, discovery);
/* Just handle it the same way as a discovery confirm,
......@@ -396,6 +402,7 @@ void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
ASSERT(self != NULL, return;);
ASSERT(self->magic == LMP_LAP_MAGIC, return;);
/* Add to main log, cleanup */
irlmp_add_discovery_log(irlmp->cachelog, log);
/* Propagate event to various LSAPs registered for it.
......@@ -411,6 +418,8 @@ void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
static inline void irlmp_update_cache(struct lap_cb *lap,
struct lsap_cb *lsap)
{
/* Prevent concurent read to get garbage */
lap->cache.valid = FALSE;
/* Update cache entry */
lap->cache.dlsap_sel = lsap->dlsap_sel;
lap->cache.slsap_sel = lsap->slsap_sel;
......@@ -441,6 +450,7 @@ static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
hashbin_t *queue)
{
struct lsap_cb *lsap;
unsigned long flags;
/*
* Optimize for the common case. We assume that the last frame
......@@ -455,6 +465,9 @@ static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
return (self->cache.lsap);
}
#endif
spin_lock_irqsave(&queue->hb_spinlock, flags);
lsap = (struct lsap_cb *) hashbin_get_first(queue);
while (lsap != NULL) {
/*
......@@ -465,29 +478,27 @@ static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
*/
if ((status == CONNECT_CMD) &&
(lsap->slsap_sel == slsap_sel) &&
(lsap->dlsap_sel == LSAP_ANY))
{
(lsap->dlsap_sel == LSAP_ANY)) {
/* This is where the dest lsap sel is set on incomming
* lsaps */
lsap->dlsap_sel = dlsap_sel;
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
irlmp_update_cache(self, lsap);
#endif
return lsap;
break;
}
/*
* Check if source LSAP and dest LSAP selectors match.
*/
if ((lsap->slsap_sel == slsap_sel) &&
(lsap->dlsap_sel == dlsap_sel))
{
break;
lsap = (struct lsap_cb *) hashbin_get_next(queue);
}
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
if(lsap)
irlmp_update_cache(self, lsap);
#endif
return lsap;
}
lsap = (struct lsap_cb *) hashbin_get_next(queue);
}
spin_unlock_irqrestore(&queue->hb_spinlock, flags);
/* Sorry not found! */
return NULL;
/* Return what we've found or NULL */
return lsap;
}
......@@ -49,11 +49,397 @@
* Jean II
*/
/*
* Notes on the concurent access to hashbin and other SMP issues
* -------------------------------------------------------------
* Hashbins are very often in the IrDA stack a global repository of
* information, and therefore used in a very asynchronous manner following
* various events (driver calls, timers, user calls...).
* Therefore, very often it is highly important to consider the
* management of concurent access to the hashbin and how to guarantee the
* consistency of the operations on it.
*
* First, we need to define the objective of locking :
* 1) Protect user data (content pointed by the hashbin)
* 2) Protect hashbin structure itself (linked list in each bin)
*
* OLD LOCKING
* -----------
*
* The previous locking strategy, either HB_LOCAL or HB_GLOBAL were
* both inadequate in *both* aspect.
* o HB_GLOBAL was using a spinlock for each bin (local locking).
* o HB_LOCAL was disabling irq on *all* CPUs, so use a single
* global semaphore.
* The problems were :
* A) Global irq disabling is no longer supported by the kernel
* B) No protection for the hashbin struct global data
* o hashbin_delete()
* o hb_current
* C) No protection for user data in some cases
*
* A) HB_LOCAL use global irq disabling, so doesn't work on kernel
* 2.5.X. Even when it is supported (kernel 2.4.X and earlier), its
* performance is not satisfactory on SMP setups. Most hashbins were
* HB_LOCAL, so (A) definitely need fixing.
* B) HB_LOCAL could be modified to fix (B). However, because HB_GLOBAL
* lock only the individual bins, it will never be able to lock the
* global data, so can't do (B).
* C) Some functions return pointer to data that is still in the
* hashbin :
* o hashbin_find()
* o hashbin_get_first()
* o hashbin_get_next()
* As the data is still in the hashbin, it may be changed or free'd
* while the caller is examinimg the data. In those case, locking can't
* be done within the hashbin, but must include use of the data within
* the caller.
* The caller can easily do this with HB_LOCAL (just disable irqs).
* However, this is impossible with HB_GLOBAL because the caller has no
* way to know the proper bin, so don't know which spinlock to use.
*
* Quick summary : can no longer use HB_LOCAL, and HB_GLOBAL is
* fundamentally broken and will never work.
*
* NEW LOCKING
* -----------
*
* To fix those problems, I've introduce a few changes in the
* hashbin locking :
* 1) New HB_LOCK scheme
* 2) hashbin->hb_spinlock
* 3) New hashbin usage policy
*
* HB_LOCK :
* -------
* HB_LOCK is a locking scheme intermediate between the old HB_LOCAL
* and HB_GLOBAL. It uses a single spinlock to protect the whole content
* of the hashbin. As it is a single spinlock, it can protect the global
* data of the hashbin and not only the bins themselves.
* HB_LOCK can only protect some of the hashbin calls, so it only lock
* call that can be made 100% safe and leave other call unprotected.
* HB_LOCK in theory is slower than HB_GLOBAL, but as the hashbin
* content is always small contention is not high, so it doesn't matter
* much. HB_LOCK is probably faster than HB_LOCAL.
*
* hashbin->hb_spinlock :
* --------------------
* The spinlock that HB_LOCK uses is available for caller, so that
* the caller can protect unprotected calls (see below).
* If the caller want to do entirely its own locking (HB_NOLOCK), he
* can do so and may use safely this spinlock.
* Locking is done like this :
* spin_lock_irqsave(&hashbin->hb_spinlock, flags);
* Releasing the lock :
* spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
*
* Safe & Protected calls :
* ----------------------
* The following calls are safe or protected via HB_LOCK :
* o hashbin_new() -> safe
* o hashbin_delete()
* o hashbin_insert()
* o hashbin_remove_first()
* o hashbin_remove()
* o hashbin_remove_this()
* o HASHBIN_GET_SIZE() -> atomic
*
* The following calls only protect the hashbin itself :
* o hashbin_lock_find()
* o hashbin_find_next()
*
* Unprotected calls :
* -----------------
* The following calls need to be protected by the caller :
* o hashbin_find()
* o hashbin_get_first()
* o hashbin_get_next()
*
* Locking Policy :
* --------------
* If the hashbin is used only in a single thread of execution
* (explicitely or implicitely), you can use HB_NOLOCK
* If the calling module already provide concurent access protection,
* you may use HB_NOLOCK.
*
* In all other cases, you need to use HB_LOCK and lock the hashbin
* everytime before calling one of the unprotected calls. You also must
* use the pointer returned by the unprotected call within the locked
* region.
*
* Extra care for enumeration :
* --------------------------
* hashbin_get_first() and hashbin_get_next() use the hashbin to
* store the current position, in hb_current.
* As long as the hashbin remains locked, this is safe. If you unlock
* the hashbin, the current position may change if anybody else modify
* or enumerate the hashbin.
* Summary : do the full enumeration while locked.
*
* Alternatively, you may use hashbin_find_next(). But, this will
* be slower, is more complex to use and doesn't protect the hashbin
* content. So, care is needed here as well.
*
* Other issues :
* ------------
* I believe that we are overdoing it by using spin_lock_irqsave()
* and we should use only spin_lock_bh() or similar. But, I don't have
* the balls to try it out.
* Don't believe that because hashbin are now (somewhat) SMP safe
* that the rest of the code is. Higher layers tend to be safest,
* but LAP and LMP would need some serious dedicated love.
*
* Jean II
*/
#include <net/irda/irda.h>
#include <net/irda/irqueue.h>
static irda_queue_t *dequeue_general( irda_queue_t **queue, irda_queue_t* element);
static __u32 hash( char* name);
/************************ QUEUE SUBROUTINES ************************/
/*
* Hashbin
*/
#define GET_HASHBIN(x) ( x & HASHBIN_MASK )
/*
* Function hash (name)
*
* This function hash the input string 'name' using the ELF hash
* function for strings.
*/
static __u32 hash( char* name)
{
__u32 h = 0;
__u32 g;
while(*name) {
h = (h<<4) + *name++;
if ((g = (h & 0xf0000000)))
h ^=g>>24;
h &=~g;
}
return h;
}
/*
* Function enqueue_first (queue, proc)
*
* Insert item first in queue.
*
*/
static void enqueue_first(irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 4, __FUNCTION__ "()\n");
/*
* Check if queue is empty.
*/
if ( *queue == NULL ) {
/*
* Queue is empty. Insert one element into the queue.
*/
element->q_next = element->q_prev = *queue = element;
} else {
/*
* Queue is not empty. Insert element into front of queue.
*/
element->q_next = (*queue);
(*queue)->q_prev->q_next = element;
element->q_prev = (*queue)->q_prev;
(*queue)->q_prev = element;
(*queue) = element;
}
}
#ifdef HASHBIN_UNUSED
/*
* Function enqueue_last (queue, proc)
*
* Insert item into end of queue.
*
*/
static void __enqueue_last( irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 4, __FUNCTION__ "()\n");
/*
* Check if queue is empty.
*/
if ( *queue == NULL ) {
/*
* Queue is empty. Insert one element into the queue.
*/
element->q_next = element->q_prev = *queue = element;
} else {
/*
* Queue is not empty. Insert element into end of queue.
*/
element->q_prev = (*queue)->q_prev;
element->q_prev->q_next = element;
(*queue)->q_prev = element;
element->q_next = *queue;
}
}
static inline void enqueue_last( irda_queue_t **queue, irda_queue_t* element)
{
unsigned long flags;
save_flags(flags);
cli();
__enqueue_last( queue, element);
restore_flags(flags);
}
/*
* Function enqueue_queue (queue, list)
*
* Insert a queue (list) into the start of the first queue
*
*/
static void enqueue_queue( irda_queue_t** queue, irda_queue_t** list )
{
irda_queue_t* tmp;
/*
* Check if queue is empty
*/
if ( *queue ) {
(*list)->q_prev->q_next = (*queue);
(*queue)->q_prev->q_next = (*list);
tmp = (*list)->q_prev;
(*list)->q_prev = (*queue)->q_prev;
(*queue)->q_prev = tmp;
} else {
*queue = (*list);
}
(*list) = NULL;
}
/*
* Function enqueue_second (queue, proc)
*
* Insert item behind head of queue.
*
*/
static void enqueue_second(irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 0, "enqueue_second()\n");
/*
* Check if queue is empty.
*/
if ( *queue == NULL ) {
/*
* Queue is empty. Insert one element into the queue.
*/
element->q_next = element->q_prev = *queue = element;
} else {
/*
* Queue is not empty. Insert element into ..
*/
element->q_prev = (*queue);
(*queue)->q_next->q_prev = element;
element->q_next = (*queue)->q_next;
(*queue)->q_next = element;
}
}
#endif /* HASHBIN_UNUSED */
/*
* Function dequeue (queue)
*
* Remove first entry in queue
*
*/
static irda_queue_t *dequeue_first(irda_queue_t **queue)
{
irda_queue_t *ret;
IRDA_DEBUG( 4, "dequeue_first()\n");
/*
* Set return value
*/
ret = *queue;
if ( *queue == NULL ) {
/*
* Queue was empty.
*/
} else if ( (*queue)->q_next == *queue ) {
/*
* Queue only contained a single element. It will now be
* empty.
*/
*queue = NULL;
} else {
/*
* Queue contained several element. Remove the first one.
*/
(*queue)->q_prev->q_next = (*queue)->q_next;
(*queue)->q_next->q_prev = (*queue)->q_prev;
*queue = (*queue)->q_next;
}
/*
* Return the removed entry (or NULL of queue was empty).
*/
return ret;
}
/*
* Function dequeue_general (queue, element)
*
*
*/
static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element)
{
irda_queue_t *ret;
IRDA_DEBUG( 4, "dequeue_general()\n");
/*
* Set return value
*/
ret = *queue;
if ( *queue == NULL ) {
/*
* Queue was empty.
*/
} else if ( (*queue)->q_next == *queue ) {
/*
* Queue only contained a single element. It will now be
* empty.
*/
*queue = NULL;
} else {
/*
* Remove specific element.
*/
element->q_prev->q_next = element->q_next;
element->q_next->q_prev = element->q_prev;
if ( (*queue) == element)
(*queue) = element->q_next;
}
/*
* Return the removed entry (or NULL of queue was empty).
*/
return ret;
}
/************************ HASHBIN MANAGEMENT ************************/
/*
* Function hashbin_create ( type, name )
......@@ -64,7 +450,6 @@ static __u32 hash( char* name);
hashbin_t *hashbin_new(int type)
{
hashbin_t* hashbin;
int i;
/*
* Allocate new hashbin
......@@ -79,14 +464,17 @@ hashbin_t *hashbin_new(int type)
memset(hashbin, 0, sizeof(hashbin_t));
hashbin->hb_type = type;
hashbin->magic = HB_MAGIC;
//hashbin->hb_current = NULL;
/* Make sure all spinlock's are unlocked */
for (i=0;i<HASHBIN_SIZE;i++)
hashbin->hb_mutex[i] = SPIN_LOCK_UNLOCKED;
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_init(&hashbin->hb_spinlock);
}
return hashbin;
}
#ifdef HASHBIN_UNUSED
/*
* Function hashbin_clear (hashbin, free_func)
*
......@@ -117,7 +505,7 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func)
return 0;
}
#endif /* HASHBIN_UNUSED */
/*
* Function hashbin_delete (hashbin, free_func)
......@@ -129,11 +517,17 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func)
int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
{
irda_queue_t* queue;
unsigned long flags = 0;
int i;
ASSERT(hashbin != NULL, return -1;);
ASSERT(hashbin->magic == HB_MAGIC, return -1;);
/* Synchronize */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
}
/*
* Free the entries in the hashbin, TODO: use hashbin_clear when
* it has been shown to work
......@@ -148,15 +542,25 @@ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
}
}
/* Cleanup local data */
hashbin->hb_current = NULL;
hashbin->magic = ~HB_MAGIC;
/* Release lock */
if ( hashbin->hb_type & HB_LOCK) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
}
/*
* Free the hashbin structure
*/
hashbin->magic = ~HB_MAGIC;
kfree(hashbin);
return 0;
}
/********************* HASHBIN LIST OPERATIONS *********************/
/*
* Function hashbin_insert (hashbin, entry, name)
*
......@@ -181,12 +585,8 @@ void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, char* n
bin = GET_HASHBIN( hashv );
/* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) {
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
} else if ( hashbin->hb_type & HB_LOCAL ) {
save_flags(flags);
cli();
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
/*
......@@ -209,102 +609,61 @@ void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, char* n
hashbin->hb_size++;
/* Release lock */
if ( hashbin->hb_type & HB_GLOBAL) {
spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags);
} else if ( hashbin->hb_type & HB_LOCAL) {
restore_flags( flags);
}
if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
}
/*
* Function hashbin_find (hashbin, hashv, name)
* Function hashbin_remove_first (hashbin)
*
* Find item with the given hashv or name
* Remove first entry of the hashbin
*
* Note : this function no longer use hashbin_remove(), but does things
* similar to hashbin_remove_this(), so can be considered safe.
* Jean II
*/
void* hashbin_find( hashbin_t* hashbin, long hashv, char* name )
void *hashbin_remove_first( hashbin_t *hashbin)
{
int bin, found = FALSE;
unsigned long flags = 0;
irda_queue_t* entry;
IRDA_DEBUG( 4, "hashbin_find()\n");
irda_queue_t *entry = NULL;
ASSERT( hashbin != NULL, return NULL;);
ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
/* Synchronize */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
entry = hashbin_get_first( hashbin);
if ( entry != NULL) {
int bin;
long hashv;
/*
* Locate hashbin
*/
if ( name )
hashv = hash( name );
hashv = entry->q_hash;
bin = GET_HASHBIN( hashv );
/* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) {
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
} else if ( hashbin->hb_type & HB_LOCAL ) {
save_flags(flags);
cli();
} /* Default is no-lock */
/*
* Search for entry
*/
entry = hashbin->hb_queue[ bin];
if ( entry ) {
do {
/*
* Check for key
* Dequeue the entry...
*/
if ( entry->q_hash == hashv ) {
dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
(irda_queue_t*) entry );
hashbin->hb_size--;
entry->q_next = NULL;
entry->q_prev = NULL;
/*
* Name compare too?
* Check if this item is the currently selected item, and in
* that case we must reset hb_current
*/
if ( name ) {
if ( strcmp( entry->q_name, name ) == 0 ) {
found = TRUE;
break;
}
} else {
found = TRUE;
break;
}
}
entry = entry->q_next;
} while ( entry != hashbin->hb_queue[ bin ] );
if ( entry == hashbin->hb_current)
hashbin->hb_current = NULL;
}
/* Release lock */
if ( hashbin->hb_type & HB_GLOBAL) {
spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags);
} else if ( hashbin->hb_type & HB_LOCAL) {
restore_flags( flags);
}
if ( found )
return entry;
else
return NULL;
}
void *hashbin_remove_first( hashbin_t *hashbin)
{
unsigned long flags;
irda_queue_t *entry = NULL;
save_flags(flags);
cli();
entry = hashbin_get_first( hashbin);
if ( entry != NULL)
hashbin_remove( hashbin, entry->q_hash, NULL);
restore_flags( flags);
if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
return entry;
}
......@@ -343,12 +702,8 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, char* name)
bin = GET_HASHBIN( hashv );
/* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) {
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
} else if ( hashbin->hb_type & HB_LOCAL ) {
save_flags(flags);
cli();
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
/*
......@@ -396,12 +751,9 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, char* name)
}
/* Release lock */
if ( hashbin->hb_type & HB_GLOBAL) {
spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags);
} else if ( hashbin->hb_type & HB_LOCAL) {
restore_flags( flags);
}
if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
/* Return */
......@@ -413,7 +765,7 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, char* name)
}
/*
* Function hashbin_remove (hashbin, hashv, name)
* Function hashbin_remove_this (hashbin, entry)
*
* Remove entry with the given name
*
......@@ -435,6 +787,11 @@ void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
ASSERT( entry != NULL, return NULL;);
/* Synchronize */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
/* Check if valid and not already removed... */
if((entry->q_next == NULL) || (entry->q_prev == NULL))
return NULL;
......@@ -442,41 +799,151 @@ void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
/*
* Locate hashbin
*/
hashv = entry->q_hash;
bin = GET_HASHBIN( hashv );
hashv = entry->q_hash;
bin = GET_HASHBIN( hashv );
/*
* Dequeue the entry...
*/
dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
(irda_queue_t*) entry );
hashbin->hb_size--;
entry->q_next = NULL;
entry->q_prev = NULL;
/*
* Check if this item is the currently selected item, and in
* that case we must reset hb_current
*/
if ( entry == hashbin->hb_current)
hashbin->hb_current = NULL;
/* Release lock */
if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
return entry;
}
/*********************** HASHBIN ENUMERATION ***********************/
/*
* Function hashbin_common_find (hashbin, hashv, name)
*
* Find item with the given hashv or name
*
*/
void* hashbin_find( hashbin_t* hashbin, long hashv, char* name )
{
int bin;
irda_queue_t* entry;
IRDA_DEBUG( 4, "hashbin_find()\n");
ASSERT( hashbin != NULL, return NULL;);
ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
/*
* Locate hashbin
*/
if ( name )
hashv = hash( name );
bin = GET_HASHBIN( hashv );
/*
* Search for entry
*/
entry = hashbin->hb_queue[ bin];
if ( entry ) {
do {
/*
* Check for key
*/
if ( entry->q_hash == hashv ) {
/*
* Name compare too?
*/
if ( name ) {
if ( strcmp( entry->q_name, name ) == 0 ) {
return entry;
}
} else {
return entry;
}
}
entry = entry->q_next;
} while ( entry != hashbin->hb_queue[ bin ] );
}
return NULL;
}
/*
* Function hashbin_lock_find (hashbin, hashv, name)
*
* Find item with the given hashv or name
*
* Same, but with spinlock protection...
* I call it safe, but it's only safe with respect to the hashbin, not its
* content. - Jean II
*/
void* hashbin_lock_find( hashbin_t* hashbin, long hashv, char* name )
{
unsigned long flags = 0;
irda_queue_t* entry;
/* Synchronize */
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
/*
* Search for entry
*/
entry = (irda_queue_t* ) hashbin_find( hashbin, hashv, name );
/* Release lock */
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
return entry;
}
/*
* Function hashbin_find (hashbin, hashv, name, pnext)
*
* Find an item with the given hashv or name, and its successor
*
* This function allow to do concurent enumerations without the
* need to lock over the whole session, because the caller keep the
* context of the search. On the other hand, it might fail and return
* NULL if the entry is removed. - Jean II
*/
void* hashbin_find_next( hashbin_t* hashbin, long hashv, char* name,
void ** pnext)
{
unsigned long flags = 0;
irda_queue_t* entry;
/* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) {
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
} else if ( hashbin->hb_type & HB_LOCAL ) {
save_flags(flags);
cli();
} /* Default is no-lock */
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
/*
* Dequeue the entry...
* Search for current entry
* This allow to check if the current item is still in the
* hashbin or has been removed.
*/
dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
(irda_queue_t*) entry );
hashbin->hb_size--;
entry->q_next = NULL;
entry->q_prev = NULL;
entry = (irda_queue_t* ) hashbin_find( hashbin, hashv, name );
/*
* Check if this item is the currently selected item, and in
* that case we must reset hb_current
* Trick hashbin_get_next() to return what we want
*/
if ( entry == hashbin->hb_current)
hashbin->hb_current = NULL;
if(entry) {
hashbin->hb_current = entry;
*pnext = hashbin_get_next( hashbin );
} else
*pnext = NULL;
/* Release lock */
if ( hashbin->hb_type & HB_GLOBAL) {
spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags);
} else if ( hashbin->hb_type & HB_LOCAL) {
restore_flags( flags);
}
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
return entry;
}
......@@ -519,6 +986,8 @@ irda_queue_t *hashbin_get_first( hashbin_t* hashbin)
* be started by a call to hashbin_get_first(). The function returns
* NULL when all items have been traversed
*
* The context of the search is stored within the hashbin, so you must
* protect yourself from concurent enumerations. - Jean II
*/
irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
{
......@@ -566,240 +1035,3 @@ irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
}
return NULL;
}
/*
* Function enqueue_last (queue, proc)
*
* Insert item into end of queue.
*
*/
static void __enqueue_last( irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 4, __FUNCTION__ "()\n");
/*
* Check if queue is empty.
*/
if ( *queue == NULL ) {
/*
* Queue is empty. Insert one element into the queue.
*/
element->q_next = element->q_prev = *queue = element;
} else {
/*
* Queue is not empty. Insert element into end of queue.
*/
element->q_prev = (*queue)->q_prev;
element->q_prev->q_next = element;
(*queue)->q_prev = element;
element->q_next = *queue;
}
}
inline void enqueue_last( irda_queue_t **queue, irda_queue_t* element)
{
unsigned long flags;
save_flags(flags);
cli();
__enqueue_last( queue, element);
restore_flags(flags);
}
/*
* Function enqueue_first (queue, proc)
*
* Insert item first in queue.
*
*/
void enqueue_first(irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 4, __FUNCTION__ "()\n");
/*
* Check if queue is empty.
*/
if ( *queue == NULL ) {
/*
* Queue is empty. Insert one element into the queue.
*/
element->q_next = element->q_prev = *queue = element;
} else {
/*
* Queue is not empty. Insert element into front of queue.
*/
element->q_next = (*queue);
(*queue)->q_prev->q_next = element;
element->q_prev = (*queue)->q_prev;
(*queue)->q_prev = element;
(*queue) = element;
}
}
/*
* Function enqueue_queue (queue, list)
*
* Insert a queue (list) into the start of the first queue
*
*/
void enqueue_queue( irda_queue_t** queue, irda_queue_t** list )
{
irda_queue_t* tmp;
/*
* Check if queue is empty
*/
if ( *queue ) {
(*list)->q_prev->q_next = (*queue);
(*queue)->q_prev->q_next = (*list);
tmp = (*list)->q_prev;
(*list)->q_prev = (*queue)->q_prev;
(*queue)->q_prev = tmp;
} else {
*queue = (*list);
}
(*list) = NULL;
}
/*
* Function enqueue_second (queue, proc)
*
* Insert item behind head of queue.
*
*/
#if 0
static void enqueue_second(irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 0, "enqueue_second()\n");
/*
* Check if queue is empty.
*/
if ( *queue == NULL ) {
/*
* Queue is empty. Insert one element into the queue.
*/
element->q_next = element->q_prev = *queue = element;
} else {
/*
* Queue is not empty. Insert element into ..
*/
element->q_prev = (*queue);
(*queue)->q_next->q_prev = element;
element->q_next = (*queue)->q_next;
(*queue)->q_next = element;
}
}
#endif
/*
* Function dequeue (queue)
*
* Remove first entry in queue
*
*/
irda_queue_t *dequeue_first(irda_queue_t **queue)
{
irda_queue_t *ret;
IRDA_DEBUG( 4, "dequeue_first()\n");
/*
* Set return value
*/
ret = *queue;
if ( *queue == NULL ) {
/*
* Queue was empty.
*/
} else if ( (*queue)->q_next == *queue ) {
/*
* Queue only contained a single element. It will now be
* empty.
*/
*queue = NULL;
} else {
/*
* Queue contained several element. Remove the first one.
*/
(*queue)->q_prev->q_next = (*queue)->q_next;
(*queue)->q_next->q_prev = (*queue)->q_prev;
*queue = (*queue)->q_next;
}
/*
* Return the removed entry (or NULL of queue was empty).
*/
return ret;
}
/*
* Function dequeue_general (queue, element)
*
*
*/
static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element)
{
irda_queue_t *ret;
IRDA_DEBUG( 4, "dequeue_general()\n");
/*
* Set return value
*/
ret = *queue;
if ( *queue == NULL ) {
/*
* Queue was empty.
*/
} else if ( (*queue)->q_next == *queue ) {
/*
* Queue only contained a single element. It will now be
* empty.
*/
*queue = NULL;
} else {
/*
* Remove specific element.
*/
element->q_prev->q_next = element->q_next;
element->q_next->q_prev = element->q_prev;
if ( (*queue) == element)
(*queue) = element->q_next;
}
/*
* Return the removed entry (or NULL of queue was empty).
*/
return ret;
}
/*
* Function hash (name)
*
* This function hash the input string 'name' using the ELF hash
* function for strings.
*/
static __u32 hash( char* name)
{
__u32 h = 0;
__u32 g;
while(*name) {
h = (h<<4) + *name++;
if ((g = (h & 0xf0000000)))
h ^=g>>24;
h &=~g;
}
return h;
}
......@@ -132,12 +132,14 @@ EXPORT_SYMBOL(irlmp_dup);
EXPORT_SYMBOL(lmp_reasons);
/* Queue */
EXPORT_SYMBOL(hashbin_find);
EXPORT_SYMBOL(hashbin_new);
EXPORT_SYMBOL(hashbin_insert);
EXPORT_SYMBOL(hashbin_delete);
EXPORT_SYMBOL(hashbin_remove);
EXPORT_SYMBOL(hashbin_remove_this);
EXPORT_SYMBOL(hashbin_find);
EXPORT_SYMBOL(hashbin_lock_find);
EXPORT_SYMBOL(hashbin_find_next);
EXPORT_SYMBOL(hashbin_get_next);
EXPORT_SYMBOL(hashbin_get_first);
......
......@@ -91,7 +91,7 @@ int __init irttp_init(void)
irttp->magic = TTP_MAGIC;
irttp->tsaps = hashbin_new(HB_LOCAL);
irttp->tsaps = hashbin_new(HB_LOCK);
if (!irttp->tsaps) {
ERROR("%s: can't allocate IrTTP hashbin!\n", __FUNCTION__);
return -ENOMEM;
......@@ -1365,30 +1365,43 @@ int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
{
struct tsap_cb *new;
unsigned long flags;
IRDA_DEBUG(1, __FUNCTION__ "()\n");
if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) {
/* Protect our access to the old tsap instance */
spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags);
/* Find the old instance */
if (!hashbin_find(irttp->tsaps, (int) orig, NULL)) {
IRDA_DEBUG(0, __FUNCTION__ "(), unable to find TSAP\n");
spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
return NULL;
}
/* Allocate a new instance */
new = kmalloc(sizeof(struct tsap_cb), GFP_ATOMIC);
if (!new) {
IRDA_DEBUG(0, __FUNCTION__ "(), unable to kmalloc\n");
spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
return NULL;
}
/* Dup */
memcpy(new, orig, sizeof(struct tsap_cb));
new->notify.instance = instance;
new->lsap = irlmp_dup(orig->lsap, new);
/* We don't need the old instance any more */
spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
/* Not everything should be copied */
new->notify.instance = instance;
new->lsap = irlmp_dup(orig->lsap, new);
init_timer(&new->todo_timer);
skb_queue_head_init(&new->rx_queue);
skb_queue_head_init(&new->tx_queue);
skb_queue_head_init(&new->rx_fragments);
/* This is locked */
hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL);
return new;
......@@ -1723,8 +1736,8 @@ int irttp_proc_read(char *buf, char **start, off_t offset, int len)
len = 0;
save_flags(flags);
cli();
/* Protect our access to the tsap list */
spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags);
self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps);
while (self != NULL) {
......@@ -1770,7 +1783,7 @@ int irttp_proc_read(char *buf, char **start, off_t offset, int len)
self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps);
}
restore_flags(flags);
spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
return len;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment