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 { ...@@ -183,7 +183,6 @@ struct irlmp_cb {
hashbin_t *services; hashbin_t *services;
hashbin_t *cachelog; /* Current discovery log */ hashbin_t *cachelog; /* Current discovery log */
spinlock_t log_lock; /* discovery log spinlock */
int running; int running;
...@@ -221,7 +220,7 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, ...@@ -221,7 +220,7 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
struct sk_buff *userdata); struct sk_buff *userdata);
int irlmp_disconnect_request(struct lsap_cb *, 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); void irlmp_discovery_request(int nslots);
struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots); struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots);
void irlmp_do_expiry(void); void irlmp_do_expiry(void);
...@@ -258,8 +257,6 @@ extern int sysctl_discovery; ...@@ -258,8 +257,6 @@ extern int sysctl_discovery;
extern int sysctl_lap_keepalive_time; /* in ms, default is LM_IDLE_TIMEOUT */ extern int sysctl_lap_keepalive_time; /* in ms, default is LM_IDLE_TIMEOUT */
extern struct irlmp_cb *irlmp; extern struct irlmp_cb *irlmp;
static inline hashbin_t *irlmp_get_cachelog(void) { return irlmp->cachelog; }
/* Check if LAP queue is full. /* Check if LAP queue is full.
* Used by IrTTP for low control, see comments in irlap.h - Jean II */ * 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) static inline int irlmp_lap_tx_queue_full(struct lsap_cb *self)
......
...@@ -36,12 +36,12 @@ ...@@ -36,12 +36,12 @@
#define NAME_SIZE 32 #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_NOLOCK 0 /* No concurent access prevention */
#define HB_GLOBAL 1 #define HB_LOCK 1 /* Prevent concurent write with global lock */
#define HB_LOCAL 2 #define HB_SORTED 4 /* Not yet supported */
#define HB_SORTED 4
/* /*
* Hash defines * Hash defines
...@@ -57,11 +57,6 @@ ...@@ -57,11 +57,6 @@
typedef void (*FREE_FUNC)(void *arg); typedef void (*FREE_FUNC)(void *arg);
/*
* Hashbin
*/
#define GET_HASHBIN(x) ( x & HASHBIN_MASK )
struct irda_queue { struct irda_queue {
struct irda_queue *q_next; struct irda_queue *q_next;
struct irda_queue *q_prev; struct irda_queue *q_prev;
...@@ -75,8 +70,9 @@ typedef struct hashbin_t { ...@@ -75,8 +70,9 @@ typedef struct hashbin_t {
__u32 magic; __u32 magic;
int hb_type; int hb_type;
int hb_size; int hb_size;
spinlock_t hb_mutex[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_queue[HASHBIN_SIZE] IRDA_ALIGN;
irda_queue_t* hb_current; irda_queue_t* hb_current;
} hashbin_t; } hashbin_t;
...@@ -86,17 +82,16 @@ int hashbin_delete(hashbin_t* hashbin, FREE_FUNC func); ...@@ -86,17 +82,16 @@ int hashbin_delete(hashbin_t* hashbin, FREE_FUNC func);
int hashbin_clear(hashbin_t* hashbin, FREE_FUNC free_func); int hashbin_clear(hashbin_t* hashbin, FREE_FUNC free_func);
void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
char* name); 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(hashbin_t* hashbin, long hashv, char* name);
void* hashbin_remove_first(hashbin_t *hashbin); void* hashbin_remove_first(hashbin_t *hashbin);
void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry); 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_first(hashbin_t *hashbin);
irda_queue_t *hashbin_get_next(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 #define HASHBIN_GET_SIZE(hashbin) hashbin->hb_size
#endif #endif
...@@ -61,7 +61,7 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new) ...@@ -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) */ /* Set time of first discovery if node is new (see below) */
new->first_timestamp = new->timestamp; 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 * Remove all discoveries of devices that has previously been
...@@ -95,13 +95,13 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new) ...@@ -95,13 +95,13 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
/* Insert the new and updated version */ /* Insert the new and updated version */
hashbin_insert(cachelog, (irda_queue_t *) new, new->daddr, NULL); 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) * 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) 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) ...@@ -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 * discovery, so restart discovery again with just the half timeout
* of the normal one. * of the normal one.
*/ */
/* Well... It means that there was nobody out there - Jean II */
if (log == NULL) { if (log == NULL) {
/* irlmp_start_discovery_timer(irlmp, 150); */ /* irlmp_start_discovery_timer(irlmp, 150); */
return; 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); discovery = (discovery_t *) hashbin_remove_first(log);
while (discovery != NULL) { while (discovery != NULL) {
irlmp_add_discovery(cachelog, discovery); irlmp_add_discovery(cachelog, discovery);
...@@ -146,7 +152,7 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force) ...@@ -146,7 +152,7 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
IRDA_DEBUG(4, __FUNCTION__ "()\n"); 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); discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) { while (discovery != NULL) {
...@@ -169,7 +175,7 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force) ...@@ -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 ...@@ -230,13 +236,13 @@ struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 m
return NULL; return NULL;
/* Save spin lock - spinlock should be discovery specific */ /* 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 */ /* Create the client specific buffer */
n = HASHBIN_GET_SIZE(log); n = HASHBIN_GET_SIZE(log);
buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
if (buffer == NULL) { if (buffer == NULL) {
spin_unlock_irqrestore(&irlmp->log_lock, flags); spin_unlock_irqrestore(&log->hb_spinlock, flags);
return NULL; return NULL;
} }
...@@ -257,7 +263,7 @@ struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 m ...@@ -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); 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 */ /* Get the actual number of device in the buffer and return */
*pn = i; *pn = i;
...@@ -276,7 +282,7 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr) ...@@ -276,7 +282,7 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr)
unsigned long flags; unsigned long flags;
discovery_t *d; discovery_t *d;
spin_lock_irqsave(&irlmp->log_lock, flags); spin_lock_irqsave(&cachelog->hb_spinlock, flags);
/* Look at all discoveries for that link */ /* Look at all discoveries for that link */
d = (discovery_t *) hashbin_get_first(cachelog); d = (discovery_t *) hashbin_get_first(cachelog);
...@@ -288,13 +294,13 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr) ...@@ -288,13 +294,13 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr)
if (strcmp(name, d->nickname) == 0) { if (strcmp(name, d->nickname) == 0) {
*saddr = d->saddr; *saddr = d->saddr;
spin_unlock_irqrestore(&irlmp->log_lock, flags); spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
return d->daddr; return d->daddr;
} }
d = (discovery_t *) hashbin_get_next(cachelog); d = (discovery_t *) hashbin_get_next(cachelog);
} }
spin_unlock_irqrestore(&irlmp->log_lock, flags); spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
return 0; return 0;
} }
...@@ -310,7 +316,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int length, ...@@ -310,7 +316,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int length,
{ {
discovery_t *discovery; discovery_t *discovery;
unsigned long flags; unsigned long flags;
hashbin_t *cachelog = irlmp_get_cachelog(); hashbin_t *cachelog = irlmp->cachelog;
int len = 0; int len = 0;
if (!irlmp) if (!irlmp)
...@@ -318,7 +324,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int length, ...@@ -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"); 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); discovery = (discovery_t *) hashbin_get_first(cachelog);
while (( discovery != NULL) && (len < length)) { while (( discovery != NULL) && (len < length)) {
...@@ -362,7 +368,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int 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); discovery = (discovery_t *) hashbin_get_next(cachelog);
} }
spin_unlock_irqrestore(&irlmp->log_lock, flags); spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
return len; return len;
} }
...@@ -91,13 +91,13 @@ int irda_device_proc_read(char *buf, char **start, off_t offset, int 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) int __init irda_device_init( void)
{ {
dongles = hashbin_new(HB_GLOBAL); dongles = hashbin_new(HB_LOCK);
if (dongles == NULL) { if (dongles == NULL) {
printk(KERN_WARNING "IrDA: Can't allocate dongles hashbin!\n"); printk(KERN_WARNING "IrDA: Can't allocate dongles hashbin!\n");
return -ENOMEM; return -ENOMEM;
} }
tasks = hashbin_new(HB_GLOBAL); tasks = hashbin_new(HB_LOCK);
if (tasks == NULL) { if (tasks == NULL) {
printk(KERN_WARNING "IrDA: Can't allocate tasks hashbin!\n"); printk(KERN_WARNING "IrDA: Can't allocate tasks hashbin!\n");
return -ENOMEM; return -ENOMEM;
...@@ -438,7 +438,7 @@ dongle_t *irda_device_dongle_init(struct net_device *dev, int type) ...@@ -438,7 +438,7 @@ dongle_t *irda_device_dongle_init(struct net_device *dev, int type)
} }
#endif #endif
if (!(reg = hashbin_find(dongles, type, NULL))) { if (!(reg = hashbin_lock_find(dongles, type, NULL))) {
ERROR("IrDA: Unable to find requested dongle\n"); ERROR("IrDA: Unable to find requested dongle\n");
return NULL; return NULL;
} }
...@@ -477,7 +477,7 @@ int irda_device_dongle_cleanup(dongle_t *dongle) ...@@ -477,7 +477,7 @@ int irda_device_dongle_cleanup(dongle_t *dongle)
int irda_device_register_dongle(struct dongle_reg *new) int irda_device_register_dongle(struct dongle_reg *new)
{ {
/* Check if this dongle has been registred before */ /* 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__); MESSAGE("%s: Dongle already registered\n", __FUNCTION__);
return 0; return 0;
} }
......
...@@ -91,11 +91,12 @@ int __init iriap_init(void) ...@@ -91,11 +91,12 @@ int __init iriap_init(void)
__u16 hints; __u16 hints;
/* Allocate master array */ /* Allocate master array */
iriap = hashbin_new(HB_LOCAL); iriap = hashbin_new(HB_LOCK);
if (!iriap) if (!iriap)
return -ENOMEM; return -ENOMEM;
objects = hashbin_new(HB_LOCAL); /* Object repository - defined in irias_object.c */
objects = hashbin_new(HB_LOCK);
if (!objects) { if (!objects) {
WARNING("%s: Can't allocate objects hashbin!\n", __FUNCTION__); WARNING("%s: Can't allocate objects hashbin!\n", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
...@@ -973,13 +974,12 @@ int irias_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -973,13 +974,12 @@ int irias_proc_read(char *buf, char **start, off_t offset, int len)
ASSERT( objects != NULL, return 0;); ASSERT( objects != NULL, return 0;);
save_flags( flags);
cli();
len = 0; len = 0;
len += sprintf(buf+len, "LM-IAS Objects:\n"); len += sprintf(buf+len, "LM-IAS Objects:\n");
spin_lock_irqsave(&objects->hb_spinlock, flags);
/* List all objects */ /* List all objects */
obj = (struct ias_object *) hashbin_get_first(objects); obj = (struct ias_object *) hashbin_get_first(objects);
while ( obj != NULL) { while ( obj != NULL) {
...@@ -989,6 +989,11 @@ int irias_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -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, "id=%d", obj->id);
len += sprintf(buf+len, "\n"); 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 */ /* List all attributes for this object */
attrib = (struct ias_attrib *) attrib = (struct ias_attrib *)
hashbin_get_first(obj->attribs); hashbin_get_first(obj->attribs);
...@@ -1025,9 +1030,11 @@ int irias_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -1025,9 +1030,11 @@ int irias_proc_read(char *buf, char **start, off_t offset, int len)
attrib = (struct ias_attrib *) attrib = (struct ias_attrib *)
hashbin_get_next(obj->attribs); hashbin_get_next(obj->attribs);
} }
spin_unlock(&obj->attribs->hb_spinlock);
obj = (struct ias_object *) hashbin_get_next(objects); obj = (struct ias_object *) hashbin_get_next(objects);
} }
restore_flags(flags); spin_unlock_irqrestore(&objects->hb_spinlock, flags);
return len; return len;
} }
......
...@@ -93,7 +93,10 @@ struct ias_object *irias_new_object( char *name, int id) ...@@ -93,7 +93,10 @@ struct ias_object *irias_new_object( char *name, int id)
obj->name = strndup(name, IAS_MAX_CLASSNAME); obj->name = strndup(name, IAS_MAX_CLASSNAME);
obj->id = id; 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; return obj;
} }
...@@ -211,7 +214,8 @@ struct ias_object *irias_find_object(char *name) ...@@ -211,7 +214,8 @@ struct ias_object *irias_find_object(char *name)
{ {
ASSERT(name != NULL, return NULL;); 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) ...@@ -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(obj->magic == IAS_OBJECT_MAGIC, return NULL;);
ASSERT(name != NULL, 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) if (attrib == NULL)
return NULL; return NULL;
/* Unsafe (locking), attrib might change */
return attrib; return attrib;
} }
...@@ -267,26 +272,32 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name, ...@@ -267,26 +272,32 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name,
{ {
struct ias_object *obj; struct ias_object *obj;
struct ias_attrib *attrib; struct ias_attrib *attrib;
unsigned long flags;
/* Find object */ /* Find object */
obj = hashbin_find(objects, 0, obj_name); obj = hashbin_lock_find(objects, 0, obj_name);
if (obj == NULL) { if (obj == NULL) {
WARNING("%s: Unable to find object: %s\n", __FUNCTION__, WARNING("%s: Unable to find object: %s\n", __FUNCTION__,
obj_name); obj_name);
return -1; return -1;
} }
/* Slightly unsafe (obj might get removed under us) */
spin_lock_irqsave(&obj->attribs->hb_spinlock, flags);
/* Find attribute */ /* Find attribute */
attrib = hashbin_find(obj->attribs, 0, attrib_name); attrib = hashbin_find(obj->attribs, 0, attrib_name);
if (attrib == NULL) { if (attrib == NULL) {
WARNING("%s: Unable to find attribute: %s\n", __FUNCTION__, WARNING("%s: Unable to find attribute: %s\n", __FUNCTION__,
attrib_name); attrib_name);
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return -1; return -1;
} }
if ( attrib->value->type != new_value->type) { if ( attrib->value->type != new_value->type) {
IRDA_DEBUG( 0, __FUNCTION__ IRDA_DEBUG( 0, __FUNCTION__
"(), changing value type not allowed!\n"); "(), changing value type not allowed!\n");
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return -1; return -1;
} }
...@@ -297,6 +308,7 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name, ...@@ -297,6 +308,7 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name,
attrib->value = new_value; attrib->value = new_value;
/* Success */ /* Success */
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return 0; return 0;
} }
......
...@@ -80,7 +80,7 @@ int irlap_proc_read(char *, char **, off_t, int); ...@@ -80,7 +80,7 @@ int irlap_proc_read(char *, char **, off_t, int);
int __init irlap_init(void) int __init irlap_init(void)
{ {
/* Allocate master array */ /* Allocate master array */
irlap = hashbin_new(HB_LOCAL); irlap = hashbin_new(HB_LOCK);
if (irlap == NULL) { if (irlap == NULL) {
ERROR("%s: can't allocate irlap hashbin!\n", __FUNCTION__); ERROR("%s: can't allocate irlap hashbin!\n", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
...@@ -146,7 +146,7 @@ struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, ...@@ -146,7 +146,7 @@ struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
do { do {
get_random_bytes(&self->saddr, sizeof(self->saddr)); get_random_bytes(&self->saddr, sizeof(self->saddr));
} while ((self->saddr == 0x0) || (self->saddr == BROADCAST) || } while ((self->saddr == 0x0) || (self->saddr == BROADCAST) ||
(hashbin_find(irlap, self->saddr, NULL)) ); (hashbin_lock_find(irlap, self->saddr, NULL)) );
/* Copy to the driver */ /* Copy to the driver */
memcpy(dev->dev_addr, &self->saddr, 4); memcpy(dev->dev_addr, &self->saddr, 4);
...@@ -530,7 +530,8 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) ...@@ -530,7 +530,8 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
self->discovery_log = NULL; 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 = discovery->nslots; /* Number of slots */
info.s = 0; /* Current slot */ info.s = 0; /* Current slot */
...@@ -1092,15 +1093,14 @@ int irlap_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -1092,15 +1093,14 @@ int irlap_proc_read(char *buf, char **start, off_t offset, int len)
unsigned long flags; unsigned long flags;
int i = 0; int i = 0;
save_flags(flags); spin_lock_irqsave(&irlap->hb_spinlock, flags);
cli();
len = 0; len = 0;
self = (struct irlap_cb *) hashbin_get_first(irlap); self = (struct irlap_cb *) hashbin_get_first(irlap);
while (self != NULL) { while (self != NULL) {
ASSERT(self != NULL, return -ENODEV;); ASSERT(self != NULL, break;);
ASSERT(self->magic == LAP_MAGIC, return -EBADR;); ASSERT(self->magic == LAP_MAGIC, break;);
len += sprintf(buf+len, "irlap%d ", i++); len += sprintf(buf+len, "irlap%d ", i++);
len += sprintf(buf+len, "state: %s\n", len += sprintf(buf+len, "state: %s\n",
...@@ -1172,7 +1172,7 @@ int irlap_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -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); self = (struct irlap_cb *) hashbin_get_next(irlap);
} }
restore_flags(flags); spin_unlock_irqrestore(&irlap->hb_spinlock, flags);
return len; return len;
} }
......
...@@ -83,13 +83,13 @@ int __init irlmp_init(void) ...@@ -83,13 +83,13 @@ int __init irlmp_init(void)
memset(irlmp, 0, sizeof(struct irlmp_cb)); memset(irlmp, 0, sizeof(struct irlmp_cb));
irlmp->magic = LMP_MAGIC; irlmp->magic = LMP_MAGIC;
spin_lock_init(&irlmp->log_lock);
irlmp->clients = hashbin_new(HB_GLOBAL); irlmp->clients = hashbin_new(HB_LOCK);
irlmp->services = hashbin_new(HB_GLOBAL); irlmp->services = hashbin_new(HB_LOCK);
irlmp->links = hashbin_new(HB_GLOBAL); irlmp->links = hashbin_new(HB_LOCK);
irlmp->unconnected_lsaps = hashbin_new(HB_GLOBAL); irlmp->unconnected_lsaps = hashbin_new(HB_LOCK);
irlmp->cachelog = hashbin_new(HB_GLOBAL); irlmp->cachelog = hashbin_new(HB_NOLOCK);
spin_lock_init(&irlmp->cachelog->hb_spinlock);
irlmp->free_lsap_sel = 0x10; /* Reserved 0x00-0x0f */ irlmp->free_lsap_sel = 0x10; /* Reserved 0x00-0x0f */
strcpy(sysctl_devname, "Linux"); strcpy(sysctl_devname, "Linux");
...@@ -286,7 +286,7 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify) ...@@ -286,7 +286,7 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify)
lap->magic = LMP_LAP_MAGIC; lap->magic = LMP_LAP_MAGIC;
lap->saddr = saddr; lap->saddr = saddr;
lap->daddr = DEV_ADDR_ANY; lap->daddr = DEV_ADDR_ANY;
lap->lsaps = hashbin_new(HB_GLOBAL); lap->lsaps = hashbin_new(HB_LOCK);
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
lap->cache.valid = FALSE; lap->cache.valid = FALSE;
#endif #endif
...@@ -347,7 +347,6 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, ...@@ -347,7 +347,6 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
struct lap_cb *lap; struct lap_cb *lap;
struct lsap_cb *lsap; struct lsap_cb *lsap;
discovery_t *discovery;
ASSERT(self != NULL, return -EBADR;); ASSERT(self != NULL, return -EBADR;);
ASSERT(self->magic == LMP_LSAP_MAGIC, 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, ...@@ -388,6 +387,10 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
* device with the given daddr * device with the given daddr
*/ */
if ((!saddr) || (saddr == DEV_ADDR_ANY)) { 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) if (daddr != DEV_ADDR_ANY)
discovery = hashbin_find(irlmp->cachelog, daddr, NULL); discovery = hashbin_find(irlmp->cachelog, daddr, NULL);
else { else {
...@@ -400,8 +403,9 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, ...@@ -400,8 +403,9 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
saddr = discovery->saddr; saddr = discovery->saddr;
daddr = discovery->daddr; 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) { if (lap == NULL) {
IRDA_DEBUG(1, __FUNCTION__ "(), Unable to find a usable link!\n"); IRDA_DEBUG(1, __FUNCTION__ "(), Unable to find a usable link!\n");
return -EHOSTUNREACH; return -EHOSTUNREACH;
...@@ -411,11 +415,8 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, ...@@ -411,11 +415,8 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
if (lap->daddr == DEV_ADDR_ANY) if (lap->daddr == DEV_ADDR_ANY)
lap->daddr = daddr; lap->daddr = daddr;
else if (lap->daddr != daddr) { else if (lap->daddr != daddr) {
struct lsap_cb *any_lsap;
/* Check if some LSAPs are active on this LAP */ /* Check if some LSAPs are active on this LAP */
any_lsap = (struct lsap_cb *) hashbin_get_first(lap->lsaps); if (HASHBIN_GET_SIZE(lap->lsaps) == 0) {
if (any_lsap == NULL) {
/* No active connection, but LAP hasn't been /* No active connection, but LAP hasn't been
* disconnected yet (waiting for timeout in LAP). * disconnected yet (waiting for timeout in LAP).
* Maybe we could give LAP a bit of help in this case. * 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) ...@@ -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 *irlmp_dup(struct lsap_cb *orig, void *instance)
{ {
struct lsap_cb *new; struct lsap_cb *new;
unsigned long flags;
IRDA_DEBUG(1, __FUNCTION__ "()\n"); IRDA_DEBUG(1, __FUNCTION__ "()\n");
spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
/* Only allowed to duplicate unconnected LSAP's */ /* Only allowed to duplicate unconnected LSAP's */
if (!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) { if (!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) {
IRDA_DEBUG(0, __FUNCTION__ "(), unable to find LSAP\n"); IRDA_DEBUG(0, __FUNCTION__ "(), unable to find LSAP\n");
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
flags);
return NULL; return NULL;
} }
/* Allocate a new instance */
new = kmalloc(sizeof(struct lsap_cb), GFP_ATOMIC); new = kmalloc(sizeof(struct lsap_cb), GFP_ATOMIC);
if (!new) { if (!new) {
IRDA_DEBUG(0, __FUNCTION__ "(), unable to kmalloc\n"); IRDA_DEBUG(0, __FUNCTION__ "(), unable to kmalloc\n");
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
flags);
return NULL; return NULL;
} }
/* Dup */ /* Dup */
memcpy(new, orig, sizeof(struct lsap_cb)); memcpy(new, orig, sizeof(struct lsap_cb));
new->notify.instance = instance;
/* new->lap = orig->lap; => done in the memcpy() */ /* new->lap = orig->lap; => done in the memcpy() */
/* new->slsap_sel = orig->slsap_sel; => 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); init_timer(&new->watchdog_timer);
hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new,
...@@ -887,7 +900,7 @@ void irlmp_check_services(discovery_t *discovery) ...@@ -887,7 +900,7 @@ void irlmp_check_services(discovery_t *discovery)
*/ */
while ((service = service_log[i++]) != S_END) { while ((service = service_log[i++]) != S_END) {
IRDA_DEBUG( 4, "service=%02x\n", service); 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) { if (entry && entry->discovery_callback) {
IRDA_DEBUG( 4, "discovery_callback!\n"); IRDA_DEBUG( 4, "discovery_callback!\n");
...@@ -904,6 +917,7 @@ void irlmp_check_services(discovery_t *discovery) ...@@ -904,6 +917,7 @@ void irlmp_check_services(discovery_t *discovery)
kfree(service_log); kfree(service_log);
} }
#endif #endif
/* /*
* Function irlmp_notify_client (log) * Function irlmp_notify_client (log)
* *
...@@ -931,6 +945,12 @@ irlmp_notify_client(irlmp_client_t *client, ...@@ -931,6 +945,12 @@ irlmp_notify_client(irlmp_client_t *client,
/* /*
* Now, check all discovered devices (if any), and notify client * Now, check all discovered devices (if any), and notify client
* only about the services that the client is interested in * 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); discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) { while (discovery != NULL) {
...@@ -957,6 +977,7 @@ irlmp_notify_client(irlmp_client_t *client, ...@@ -957,6 +977,7 @@ irlmp_notify_client(irlmp_client_t *client,
void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode) void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
{ {
irlmp_client_t *client; irlmp_client_t *client;
irlmp_client_t *client_next;
IRDA_DEBUG(3, __FUNCTION__ "()\n"); IRDA_DEBUG(3, __FUNCTION__ "()\n");
...@@ -966,11 +987,12 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode) ...@@ -966,11 +987,12 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
return; return;
client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); 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 */ /* Check if we should notify client */
irlmp_notify_client(client, log, mode); 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) ...@@ -988,13 +1010,15 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
void irlmp_discovery_expiry(discovery_t *expiry) void irlmp_discovery_expiry(discovery_t *expiry)
{ {
irlmp_client_t *client; irlmp_client_t *client;
irlmp_client_t *client_next;
IRDA_DEBUG(3, __FUNCTION__ "()\n"); IRDA_DEBUG(3, __FUNCTION__ "()\n");
ASSERT(expiry != NULL, return;); ASSERT(expiry != NULL, return;);
client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); 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 */ /* Check if we should notify client */
if ((client->expir_callback) && if ((client->expir_callback) &&
(client->hint_mask & expiry->hints.word & 0x7f7f)) (client->hint_mask & expiry->hints.word & 0x7f7f))
...@@ -1002,7 +1026,7 @@ void irlmp_discovery_expiry(discovery_t *expiry) ...@@ -1002,7 +1026,7 @@ void irlmp_discovery_expiry(discovery_t *expiry)
client->priv); client->priv);
/* Next client */ /* 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, ...@@ -1197,11 +1221,9 @@ void irlmp_status_indication(struct lap_cb *self,
struct lsap_cb *curr; struct lsap_cb *curr;
/* Send status_indication to all LSAPs using this link */ /* Send status_indication to all LSAPs using this link */
next = (struct lsap_cb *) hashbin_get_first( self->lsaps); curr = (struct lsap_cb *) hashbin_get_first( self->lsaps);
while (next != NULL ) { while (NULL != hashbin_find_next(self->lsaps, (long) curr, NULL,
curr = next; (void *) &next) ) {
next = (struct lsap_cb *) hashbin_get_next(self->lsaps);
ASSERT(curr->magic == LMP_LSAP_MAGIC, return;); ASSERT(curr->magic == LMP_LSAP_MAGIC, return;);
/* /*
* Inform service user if he has requested it * Inform service user if he has requested it
...@@ -1211,6 +1233,8 @@ void irlmp_status_indication(struct lap_cb *self, ...@@ -1211,6 +1233,8 @@ void irlmp_status_indication(struct lap_cb *self,
link, lock); link, lock);
else else
IRDA_DEBUG(2, __FUNCTION__ "(), no handler\n"); IRDA_DEBUG(2, __FUNCTION__ "(), no handler\n");
curr = next;
} }
} }
...@@ -1246,29 +1270,15 @@ void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow) ...@@ -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)) { (IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) {
/* Try to find the next lsap we should poll. */ /* Try to find the next lsap we should poll. */
next = self->flow_next; 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 we have no lsap, restart from first one */
if(curr == NULL) if(next == NULL)
curr = (struct lsap_cb *) hashbin_get_first(self->lsaps); 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 */ /* Uh-oh... Paranoia */
if(curr == NULL) if(curr == NULL)
break; 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)); 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. */ /* Inform lsap user that it can send one more packet. */
...@@ -1446,6 +1456,7 @@ void *irlmp_register_service(__u16 hints) ...@@ -1446,6 +1456,7 @@ void *irlmp_register_service(__u16 hints)
int irlmp_unregister_service(void *handle) int irlmp_unregister_service(void *handle)
{ {
irlmp_service_t *service; irlmp_service_t *service;
unsigned long flags;
IRDA_DEBUG(4, __FUNCTION__ "()\n"); IRDA_DEBUG(4, __FUNCTION__ "()\n");
...@@ -1453,7 +1464,7 @@ int irlmp_unregister_service(void *handle) ...@@ -1453,7 +1464,7 @@ int irlmp_unregister_service(void *handle)
return -1; return -1;
/* Caller may call with invalid handle (it's legal) - Jean II */ /* 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) { if (!service) {
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown service!\n"); IRDA_DEBUG(1, __FUNCTION__ "(), Unknown service!\n");
return -1; return -1;
...@@ -1466,12 +1477,14 @@ int irlmp_unregister_service(void *handle) ...@@ -1466,12 +1477,14 @@ int irlmp_unregister_service(void *handle)
irlmp->hints.word = 0; irlmp->hints.word = 0;
/* Refresh current hint bits */ /* Refresh current hint bits */
spin_lock_irqsave(&irlmp->services->hb_spinlock, flags);
service = (irlmp_service_t *) hashbin_get_first(irlmp->services); service = (irlmp_service_t *) hashbin_get_first(irlmp->services);
while (service) { while (service) {
irlmp->hints.word |= service->hints; irlmp->hints.word |= service->hints;
service = (irlmp_service_t *)hashbin_get_next(irlmp->services); service = (irlmp_service_t *)hashbin_get_next(irlmp->services);
} }
spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags);
return 0; return 0;
} }
...@@ -1528,7 +1541,7 @@ int irlmp_update_client(void *handle, __u16 hint_mask, ...@@ -1528,7 +1541,7 @@ int irlmp_update_client(void *handle, __u16 hint_mask,
if (!handle) if (!handle)
return -1; return -1;
client = hashbin_find(irlmp->clients, (long) handle, NULL); client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
if (!client) { if (!client) {
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown client!\n"); IRDA_DEBUG(1, __FUNCTION__ "(), Unknown client!\n");
return -1; return -1;
...@@ -1558,7 +1571,7 @@ int irlmp_unregister_client(void *handle) ...@@ -1558,7 +1571,7 @@ int irlmp_unregister_client(void *handle)
return -1; return -1;
/* Caller may call with invalid handle (it's legal) - Jean II */ /* 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) { if (!client) {
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown client!\n"); IRDA_DEBUG(1, __FUNCTION__ "(), Unknown client!\n");
return -1; return -1;
...@@ -1580,6 +1593,7 @@ int irlmp_slsap_inuse(__u8 slsap_sel) ...@@ -1580,6 +1593,7 @@ int irlmp_slsap_inuse(__u8 slsap_sel)
{ {
struct lsap_cb *self; struct lsap_cb *self;
struct lap_cb *lap; struct lap_cb *lap;
unsigned long flags;
ASSERT(irlmp != NULL, return TRUE;); ASSERT(irlmp != NULL, return TRUE;);
ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;); ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;);
...@@ -1602,10 +1616,16 @@ int irlmp_slsap_inuse(__u8 slsap_sel) ...@@ -1602,10 +1616,16 @@ int irlmp_slsap_inuse(__u8 slsap_sel)
* every IrLAP connection and check every LSAP assosiated with each * every IrLAP connection and check every LSAP assosiated with each
* the connection. * the connection.
*/ */
spin_lock_irqsave(&irlmp->links->hb_spinlock, flags);
lap = (struct lap_cb *) hashbin_get_first(irlmp->links); lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
while (lap != NULL) { while (lap != NULL) {
ASSERT(lap->magic == LMP_LAP_MAGIC, return TRUE;); 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); self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
while (self != NULL) { while (self != NULL) {
ASSERT(self->magic == LMP_LSAP_MAGIC, return TRUE;); ASSERT(self->magic == LMP_LSAP_MAGIC, return TRUE;);
...@@ -1617,8 +1637,11 @@ int irlmp_slsap_inuse(__u8 slsap_sel) ...@@ -1617,8 +1637,11 @@ int irlmp_slsap_inuse(__u8 slsap_sel)
} }
self = (struct lsap_cb*) hashbin_get_next(lap->lsaps); 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); lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
} }
spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
return FALSE; return FALSE;
} }
...@@ -1727,15 +1750,13 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -1727,15 +1750,13 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int len)
ASSERT(irlmp != NULL, return 0;); ASSERT(irlmp != NULL, return 0;);
save_flags( flags);
cli();
len = 0; len = 0;
len += sprintf( buf+len, "Unconnected LSAPs:\n"); 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); self = (struct lsap_cb *) hashbin_get_first( irlmp->unconnected_lsaps);
while (self != NULL) { 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, ", len += sprintf(buf+len, "lsap state: %s, ",
irlsap_state[ self->lsap_state]); irlsap_state[ self->lsap_state]);
len += sprintf(buf+len, len += sprintf(buf+len,
...@@ -1747,9 +1768,10 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int 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( self = (struct lsap_cb *) hashbin_get_next(
irlmp->unconnected_lsaps); irlmp->unconnected_lsaps);
} }
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
len += sprintf(buf+len, "\nRegistred Link Layers:\n"); 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); lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
while (lap != NULL) { while (lap != NULL) {
len += sprintf(buf+len, "lap state: %s, ", len += sprintf(buf+len, "lap state: %s, ",
...@@ -1761,10 +1783,15 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -1761,10 +1783,15 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int len)
HASHBIN_GET_SIZE(lap->lsaps)); HASHBIN_GET_SIZE(lap->lsaps));
len += sprintf(buf+len, "\n"); 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"); len += sprintf(buf+len, "\n Connected LSAPs:\n");
self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
while (self != NULL) { 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, ", len += sprintf(buf+len, " lsap state: %s, ",
irlsap_state[ self->lsap_state]); irlsap_state[ self->lsap_state]);
len += sprintf(buf+len, len += sprintf(buf+len,
...@@ -1776,11 +1803,12 @@ int irlmp_proc_read(char *buf, char **start, off_t offset, int 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( self = (struct lsap_cb *) hashbin_get_next(
lap->lsaps); lap->lsaps);
} }
spin_unlock(&lap->lsaps->hb_spinlock);
len += sprintf(buf+len, "\n"); len += sprintf(buf+len, "\n");
lap = (struct lap_cb *) hashbin_get_next(irlmp->links); lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
} }
restore_flags(flags); spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
return len; return len;
} }
......
...@@ -207,6 +207,43 @@ void irlmp_idle_timer_expired(void *data) ...@@ -207,6 +207,43 @@ void irlmp_idle_timer_expired(void *data)
irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL); 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 * LAP connection control states
...@@ -274,9 +311,6 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, ...@@ -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, static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct lsap_cb *lsap;
struct lsap_cb *lsap_current;
IRDA_DEBUG(2, __FUNCTION__ "(), event=%s\n", irlmp_event[event]); IRDA_DEBUG(2, __FUNCTION__ "(), event=%s\n", irlmp_event[event]);
switch (event) { switch (event) {
...@@ -290,11 +324,9 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT 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 */ /* Just accept connection TODO, this should be fixed */
irlap_connect_response(self->irlap, skb); irlap_connect_response(self->irlap, skb);
lsap = (struct lsap_cb *) hashbin_get_first(self->lsaps); /* Tell LSAPs that they can start sending data */
while (lsap != NULL) { irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
irlmp_do_lsap_event(lsap, LM_LAP_CONNECT_CONFIRM, NULL);
lsap = (struct lsap_cb*) hashbin_get_next(self->lsaps);
}
/* Note : by the time we get there (LAP retries and co), /* Note : by the time we get there (LAP retries and co),
* the lsaps may already have gone. This avoid getting stuck * the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */ * forever in LAP_ACTIVE state - Jean II */
...@@ -310,11 +342,9 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, ...@@ -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 */ /* For all lsap_ce E Associated do LS_Connect_confirm */
irlmp_next_lap_state(self, LAP_ACTIVE); irlmp_next_lap_state(self, LAP_ACTIVE);
lsap = (struct lsap_cb *) hashbin_get_first(self->lsaps); /* Tell LSAPs that they can start sending data */
while (lsap != NULL) { irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
irlmp_do_lsap_event(lsap, LM_LAP_CONNECT_CONFIRM, NULL);
lsap = (struct lsap_cb*) hashbin_get_next(self->lsaps);
}
/* Note : by the time we get there (LAP retries and co), /* Note : by the time we get there (LAP retries and co),
* the lsaps may already have gone. This avoid getting stuck * the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */ * forever in LAP_ACTIVE state - Jean II */
...@@ -328,18 +358,8 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, ...@@ -328,18 +358,8 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
irlmp_next_lap_state(self, LAP_STANDBY); irlmp_next_lap_state(self, LAP_STANDBY);
/* Send disconnect event to all LSAPs using this link */ /* Send disconnect event to all LSAPs using this link */
lsap = (struct lsap_cb *) hashbin_get_first( self->lsaps); irlmp_do_all_lsap_event(self->lsaps,
while (lsap != NULL ) { LM_LAP_DISCONNECT_INDICATION);
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);
}
break; break;
case LM_LAP_DISCONNECT_REQUEST: case LM_LAP_DISCONNECT_REQUEST:
IRDA_DEBUG(4, __FUNCTION__ "(), LM_LAP_DISCONNECT_REQUEST\n"); 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, ...@@ -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, static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct lsap_cb *lsap;
struct lsap_cb *lsap_current;
IRDA_DEBUG(4, __FUNCTION__ "()\n"); IRDA_DEBUG(4, __FUNCTION__ "()\n");
switch (event) { switch (event) {
...@@ -383,22 +400,11 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT 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 * notify all LSAPs using this LAP, but that should be safe to
* do anyway. * do anyway.
*/ */
lsap = (struct lsap_cb *) hashbin_get_first(self->lsaps); irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
while (lsap != NULL) {
irlmp_do_lsap_event(lsap, LM_LAP_CONNECT_CONFIRM, NULL);
lsap = (struct lsap_cb*) hashbin_get_next(self->lsaps);
}
/* Needed by connect indication */ /* Needed by connect indication */
lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); irlmp_do_all_lsap_event(irlmp->unconnected_lsaps,
while (lsap != NULL) { LM_LAP_CONNECT_CONFIRM);
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);
}
/* Keep state */ /* Keep state */
break; break;
case LM_LAP_DISCONNECT_REQUEST: case LM_LAP_DISCONNECT_REQUEST:
...@@ -447,18 +453,8 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, ...@@ -447,18 +453,8 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
/* /*
* Inform all connected LSAP's using this link * Inform all connected LSAP's using this link
*/ */
lsap = (struct lsap_cb *) hashbin_get_first(self->lsaps); irlmp_do_all_lsap_event(self->lsaps,
while (lsap != NULL ) { LM_LAP_DISCONNECT_INDICATION);
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);
}
/* Force an expiry of the discovery log. /* Force an expiry of the discovery log.
* Now that the LAP is free, the system may attempt to * 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) ...@@ -210,6 +210,7 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
__u8 dlsap_sel; /* Destination LSAP address */ __u8 dlsap_sel; /* Destination LSAP address */
__u8 pid; /* Protocol identifier */ __u8 pid; /* Protocol identifier */
__u8 *fp; __u8 *fp;
unsigned long flags;
IRDA_DEBUG(4, __FUNCTION__ "()\n"); IRDA_DEBUG(4, __FUNCTION__ "()\n");
...@@ -242,6 +243,8 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb) ...@@ -242,6 +243,8 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
return; return;
} }
/* Search the connectionless LSAP */
spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
while (lsap != NULL) { while (lsap != NULL) {
/* /*
...@@ -255,6 +258,8 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb) ...@@ -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); lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
} }
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
if (lsap) if (lsap)
irlmp_connless_data_indication(lsap, skb); irlmp_connless_data_indication(lsap, skb);
else { else {
...@@ -374,6 +379,7 @@ void irlmp_link_discovery_indication(struct lap_cb *self, ...@@ -374,6 +379,7 @@ void irlmp_link_discovery_indication(struct lap_cb *self,
ASSERT(self != NULL, return;); ASSERT(self != NULL, return;);
ASSERT(self->magic == LMP_LAP_MAGIC, return;); ASSERT(self->magic == LMP_LAP_MAGIC, return;);
/* Add to main log, cleanup */
irlmp_add_discovery(irlmp->cachelog, discovery); irlmp_add_discovery(irlmp->cachelog, discovery);
/* Just handle it the same way as a discovery confirm, /* 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) ...@@ -396,6 +402,7 @@ void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
ASSERT(self != NULL, return;); ASSERT(self != NULL, return;);
ASSERT(self->magic == LMP_LAP_MAGIC, return;); ASSERT(self->magic == LMP_LAP_MAGIC, return;);
/* Add to main log, cleanup */
irlmp_add_discovery_log(irlmp->cachelog, log); irlmp_add_discovery_log(irlmp->cachelog, log);
/* Propagate event to various LSAPs registered for it. /* Propagate event to various LSAPs registered for it.
...@@ -411,6 +418,8 @@ void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log) ...@@ -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, static inline void irlmp_update_cache(struct lap_cb *lap,
struct lsap_cb *lsap) struct lsap_cb *lsap)
{ {
/* Prevent concurent read to get garbage */
lap->cache.valid = FALSE;
/* Update cache entry */ /* Update cache entry */
lap->cache.dlsap_sel = lsap->dlsap_sel; lap->cache.dlsap_sel = lsap->dlsap_sel;
lap->cache.slsap_sel = lsap->slsap_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, ...@@ -441,6 +450,7 @@ static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
hashbin_t *queue) hashbin_t *queue)
{ {
struct lsap_cb *lsap; struct lsap_cb *lsap;
unsigned long flags;
/* /*
* Optimize for the common case. We assume that the last frame * 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, ...@@ -455,6 +465,9 @@ static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
return (self->cache.lsap); return (self->cache.lsap);
} }
#endif #endif
spin_lock_irqsave(&queue->hb_spinlock, flags);
lsap = (struct lsap_cb *) hashbin_get_first(queue); lsap = (struct lsap_cb *) hashbin_get_first(queue);
while (lsap != NULL) { while (lsap != NULL) {
/* /*
...@@ -465,29 +478,27 @@ static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel, ...@@ -465,29 +478,27 @@ static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
*/ */
if ((status == CONNECT_CMD) && if ((status == CONNECT_CMD) &&
(lsap->slsap_sel == slsap_sel) && (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; lsap->dlsap_sel = dlsap_sel;
break;
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
irlmp_update_cache(self, lsap);
#endif
return lsap;
} }
/* /*
* Check if source LSAP and dest LSAP selectors match. * Check if source LSAP and dest LSAP selectors match.
*/ */
if ((lsap->slsap_sel == slsap_sel) && if ((lsap->slsap_sel == slsap_sel) &&
(lsap->dlsap_sel == dlsap_sel)) (lsap->dlsap_sel == dlsap_sel))
{ break;
lsap = (struct lsap_cb *) hashbin_get_next(queue);
}
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
if(lsap)
irlmp_update_cache(self, lsap); irlmp_update_cache(self, lsap);
#endif #endif
return lsap; spin_unlock_irqrestore(&queue->hb_spinlock, flags);
}
lsap = (struct lsap_cb *) hashbin_get_next(queue);
}
/* Sorry not found! */ /* Return what we've found or NULL */
return NULL; return lsap;
} }
...@@ -49,11 +49,397 @@ ...@@ -49,11 +49,397 @@
* Jean II * 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/irda.h>
#include <net/irda/irqueue.h> #include <net/irda/irqueue.h>
static irda_queue_t *dequeue_general( irda_queue_t **queue, irda_queue_t* element); /************************ QUEUE SUBROUTINES ************************/
static __u32 hash( char* name);
/*
* 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 ) * Function hashbin_create ( type, name )
...@@ -64,7 +450,6 @@ static __u32 hash( char* name); ...@@ -64,7 +450,6 @@ static __u32 hash( char* name);
hashbin_t *hashbin_new(int type) hashbin_t *hashbin_new(int type)
{ {
hashbin_t* hashbin; hashbin_t* hashbin;
int i;
/* /*
* Allocate new hashbin * Allocate new hashbin
...@@ -79,14 +464,17 @@ hashbin_t *hashbin_new(int type) ...@@ -79,14 +464,17 @@ hashbin_t *hashbin_new(int type)
memset(hashbin, 0, sizeof(hashbin_t)); memset(hashbin, 0, sizeof(hashbin_t));
hashbin->hb_type = type; hashbin->hb_type = type;
hashbin->magic = HB_MAGIC; hashbin->magic = HB_MAGIC;
//hashbin->hb_current = NULL;
/* Make sure all spinlock's are unlocked */ /* Make sure all spinlock's are unlocked */
for (i=0;i<HASHBIN_SIZE;i++) if ( hashbin->hb_type & HB_LOCK ) {
hashbin->hb_mutex[i] = SPIN_LOCK_UNLOCKED; spin_lock_init(&hashbin->hb_spinlock);
}
return hashbin; return hashbin;
} }
#ifdef HASHBIN_UNUSED
/* /*
* Function hashbin_clear (hashbin, free_func) * Function hashbin_clear (hashbin, free_func)
* *
...@@ -117,7 +505,7 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func) ...@@ -117,7 +505,7 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func)
return 0; return 0;
} }
#endif /* HASHBIN_UNUSED */
/* /*
* Function hashbin_delete (hashbin, free_func) * Function hashbin_delete (hashbin, free_func)
...@@ -129,11 +517,17 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC 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) int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
{ {
irda_queue_t* queue; irda_queue_t* queue;
unsigned long flags = 0;
int i; int i;
ASSERT(hashbin != NULL, return -1;); ASSERT(hashbin != NULL, return -1;);
ASSERT(hashbin->magic == HB_MAGIC, 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 * Free the entries in the hashbin, TODO: use hashbin_clear when
* it has been shown to work * it has been shown to work
...@@ -148,15 +542,25 @@ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) ...@@ -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 * Free the hashbin structure
*/ */
hashbin->magic = ~HB_MAGIC;
kfree(hashbin); kfree(hashbin);
return 0; return 0;
} }
/********************* HASHBIN LIST OPERATIONS *********************/
/* /*
* Function hashbin_insert (hashbin, entry, name) * Function hashbin_insert (hashbin, entry, name)
* *
...@@ -181,12 +585,8 @@ void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, char* n ...@@ -181,12 +585,8 @@ void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, char* n
bin = GET_HASHBIN( hashv ); bin = GET_HASHBIN( hashv );
/* Synchronize */ /* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) { if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags); spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} else if ( hashbin->hb_type & HB_LOCAL ) {
save_flags(flags);
cli();
} /* Default is no-lock */ } /* Default is no-lock */
/* /*
...@@ -209,102 +609,61 @@ void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, char* n ...@@ -209,102 +609,61 @@ void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, char* n
hashbin->hb_size++; hashbin->hb_size++;
/* Release lock */ /* Release lock */
if ( hashbin->hb_type & HB_GLOBAL) { if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags); } /* Default is no-lock */
} else if ( hashbin->hb_type & HB_LOCAL) {
restore_flags( flags);
}
} }
/* /*
* 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; unsigned long flags = 0;
irda_queue_t* entry; irda_queue_t *entry = NULL;
IRDA_DEBUG( 4, "hashbin_find()\n");
ASSERT( hashbin != NULL, return NULL;); /* Synchronize */
ASSERT( hashbin->magic == HB_MAGIC, return NULL;); 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 * Locate hashbin
*/ */
if ( name ) hashv = entry->q_hash;
hashv = hash( name );
bin = GET_HASHBIN( hashv ); 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 * Dequeue the entry...
*/
entry = hashbin->hb_queue[ bin];
if ( entry ) {
do {
/*
* Check for key
*/ */
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 ( entry == hashbin->hb_current)
if ( strcmp( entry->q_name, name ) == 0 ) { hashbin->hb_current = NULL;
found = TRUE;
break;
}
} else {
found = TRUE;
break;
}
}
entry = entry->q_next;
} while ( entry != hashbin->hb_queue[ bin ] );
} }
/* Release lock */ /* Release lock */
if ( hashbin->hb_type & HB_GLOBAL) { if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags); spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
} 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);
return entry; return entry;
} }
...@@ -343,12 +702,8 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, char* name) ...@@ -343,12 +702,8 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, char* name)
bin = GET_HASHBIN( hashv ); bin = GET_HASHBIN( hashv );
/* Synchronize */ /* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) { if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags); spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} else if ( hashbin->hb_type & HB_LOCAL ) {
save_flags(flags);
cli();
} /* Default is no-lock */ } /* Default is no-lock */
/* /*
...@@ -396,12 +751,9 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, char* name) ...@@ -396,12 +751,9 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, char* name)
} }
/* Release lock */ /* Release lock */
if ( hashbin->hb_type & HB_GLOBAL) { if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags); spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
} else if ( hashbin->hb_type & HB_LOCAL) {
restore_flags( flags);
}
/* Return */ /* Return */
...@@ -413,7 +765,7 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, char* name) ...@@ -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 * Remove entry with the given name
* *
...@@ -435,6 +787,11 @@ void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry) ...@@ -435,6 +787,11 @@ void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
ASSERT( hashbin->magic == HB_MAGIC, return NULL;); ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
ASSERT( entry != NULL, 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... */ /* Check if valid and not already removed... */
if((entry->q_next == NULL) || (entry->q_prev == NULL)) if((entry->q_next == NULL) || (entry->q_prev == NULL))
return NULL; return NULL;
...@@ -442,41 +799,151 @@ void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry) ...@@ -442,41 +799,151 @@ void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
/* /*
* Locate hashbin * Locate hashbin
*/ */
hashv = entry->q_hash; hashv = entry->q_hash;
bin = GET_HASHBIN( hashv ); 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 */ /* Synchronize */
if ( hashbin->hb_type & HB_GLOBAL ) { spin_lock_irqsave(&hashbin->hb_spinlock, flags);
spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
} else if ( hashbin->hb_type & HB_LOCAL ) {
save_flags(flags);
cli();
} /* Default is no-lock */
/* /*
* 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 ], entry = (irda_queue_t* ) hashbin_find( hashbin, hashv, name );
(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 * Trick hashbin_get_next() to return what we want
* that case we must reset hb_current
*/ */
if ( entry == hashbin->hb_current) if(entry) {
hashbin->hb_current = NULL; hashbin->hb_current = entry;
*pnext = hashbin_get_next( hashbin );
} else
*pnext = NULL;
/* Release lock */ /* Release lock */
if ( hashbin->hb_type & HB_GLOBAL) { spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags);
} else if ( hashbin->hb_type & HB_LOCAL) {
restore_flags( flags);
}
return entry; return entry;
} }
...@@ -519,6 +986,8 @@ irda_queue_t *hashbin_get_first( hashbin_t* hashbin) ...@@ -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 * be started by a call to hashbin_get_first(). The function returns
* NULL when all items have been traversed * 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) irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
{ {
...@@ -566,240 +1035,3 @@ irda_queue_t *hashbin_get_next( hashbin_t *hashbin) ...@@ -566,240 +1035,3 @@ irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
} }
return NULL; 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); ...@@ -132,12 +132,14 @@ EXPORT_SYMBOL(irlmp_dup);
EXPORT_SYMBOL(lmp_reasons); EXPORT_SYMBOL(lmp_reasons);
/* Queue */ /* Queue */
EXPORT_SYMBOL(hashbin_find);
EXPORT_SYMBOL(hashbin_new); EXPORT_SYMBOL(hashbin_new);
EXPORT_SYMBOL(hashbin_insert); EXPORT_SYMBOL(hashbin_insert);
EXPORT_SYMBOL(hashbin_delete); EXPORT_SYMBOL(hashbin_delete);
EXPORT_SYMBOL(hashbin_remove); EXPORT_SYMBOL(hashbin_remove);
EXPORT_SYMBOL(hashbin_remove_this); 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_next);
EXPORT_SYMBOL(hashbin_get_first); EXPORT_SYMBOL(hashbin_get_first);
......
...@@ -91,7 +91,7 @@ int __init irttp_init(void) ...@@ -91,7 +91,7 @@ int __init irttp_init(void)
irttp->magic = TTP_MAGIC; irttp->magic = TTP_MAGIC;
irttp->tsaps = hashbin_new(HB_LOCAL); irttp->tsaps = hashbin_new(HB_LOCK);
if (!irttp->tsaps) { if (!irttp->tsaps) {
ERROR("%s: can't allocate IrTTP hashbin!\n", __FUNCTION__); ERROR("%s: can't allocate IrTTP hashbin!\n", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
...@@ -1365,30 +1365,43 @@ int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, ...@@ -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 *irttp_dup(struct tsap_cb *orig, void *instance)
{ {
struct tsap_cb *new; struct tsap_cb *new;
unsigned long flags;
IRDA_DEBUG(1, __FUNCTION__ "()\n"); 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"); IRDA_DEBUG(0, __FUNCTION__ "(), unable to find TSAP\n");
spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
return NULL; return NULL;
} }
/* Allocate a new instance */
new = kmalloc(sizeof(struct tsap_cb), GFP_ATOMIC); new = kmalloc(sizeof(struct tsap_cb), GFP_ATOMIC);
if (!new) { if (!new) {
IRDA_DEBUG(0, __FUNCTION__ "(), unable to kmalloc\n"); IRDA_DEBUG(0, __FUNCTION__ "(), unable to kmalloc\n");
spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
return NULL; return NULL;
} }
/* Dup */ /* Dup */
memcpy(new, orig, sizeof(struct tsap_cb)); 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 */ /* Not everything should be copied */
new->notify.instance = instance;
new->lsap = irlmp_dup(orig->lsap, new);
init_timer(&new->todo_timer); init_timer(&new->todo_timer);
skb_queue_head_init(&new->rx_queue); skb_queue_head_init(&new->rx_queue);
skb_queue_head_init(&new->tx_queue); skb_queue_head_init(&new->tx_queue);
skb_queue_head_init(&new->rx_fragments); skb_queue_head_init(&new->rx_fragments);
/* This is locked */
hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL); hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL);
return new; return new;
...@@ -1723,8 +1736,8 @@ int irttp_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -1723,8 +1736,8 @@ int irttp_proc_read(char *buf, char **start, off_t offset, int len)
len = 0; len = 0;
save_flags(flags); /* Protect our access to the tsap list */
cli(); spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags);
self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps); self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps);
while (self != NULL) { while (self != NULL) {
...@@ -1770,7 +1783,7 @@ int irttp_proc_read(char *buf, char **start, off_t offset, int len) ...@@ -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); self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps);
} }
restore_flags(flags); spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
return len; 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