From: Stephen H. <she...@os...> - 2003-10-03 17:22:25
|
Revised version of the dongle module owner patch, incorporating Jean's comments. This replaces the original patch. It provides an owner field an appropriate ref counting for IRDA dongles. Changes since list version: s/dongle_lock/dongles->hb_spinlock/ get lock on device_cleanup replace ASSERT() about in_interrupt with might_sleep(). In case your curious about locking, the callers are: module_init -> irda_device_init module_exit -> irda_device_cleanup irport_net_ioctl -> irda_device_dongle_init module_init -> XXX_dongle_init -> irda_device_register_dongle module_exit -> XXX_dongle_exit -> irda_device_unregister_dongle In other words, no interrupt or BH access to the dongle list. Obviously, hashing this list is overkill, but "when in Rome"... --- linux-2.5/net/irda/irda_device.c 2003-10-01 13:40:11.000000000 -0700 +++ irda-2.5/net/irda/irda_device.c 2003-10-03 10:03:52.497879915 -0700 @@ -85,7 +85,7 @@ static void irda_task_timer_expired(void int __init irda_device_init( void) { - dongles = hashbin_new(HB_LOCK); + dongles = hashbin_new(HB_NOLOCK); if (dongles == NULL) { printk(KERN_WARNING "IrDA: Can't allocate dongles hashbin!\n"); return -ENOMEM; @@ -109,7 +109,9 @@ void __exit irda_device_cleanup(void) IRDA_DEBUG(4, "%s()\n", __FUNCTION__); hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete); + spin_lock(&dongles->hb_spinlock); hashbin_delete(dongles, NULL); + spin_unlock(&dongles->hb_spinlock); } /* @@ -424,25 +426,34 @@ int irda_device_txqueue_empty(struct net dongle_t *irda_device_dongle_init(struct net_device *dev, int type) { struct dongle_reg *reg; - dongle_t *dongle; + dongle_t *dongle = NULL; - ASSERT(dev != NULL, return NULL;); + might_sleep(); + + spin_lock(&dongles->hb_spinlock); + reg = hashbin_find(dongles, type, NULL); #ifdef CONFIG_KMOD - ASSERT(!in_interrupt(), return NULL;); /* Try to load the module needed */ - request_module("irda-dongle-%d", type); + if (!reg && capable(CAP_SYS_MODULE)) { + spin_unlock(&dongles->hb_spinlock); + + request_module("irda-dongle-%d", type); + + spin_lock(&dongles->hb_spinlock); + reg = hashbin_find(dongles, type, NULL); + } #endif - if (!(reg = hashbin_lock_find(dongles, type, NULL))) { - ERROR("IrDA: Unable to find requested dongle\n"); - return NULL; + if (!reg || !try_module_get(reg->owner) ) { + ERROR("IrDA: Unable to find requested dongle type %x\n", type); + goto out; } /* Allocate dongle info for this instance */ dongle = kmalloc(sizeof(dongle_t), GFP_KERNEL); if (!dongle) - return NULL; + goto out; memset(dongle, 0, sizeof(dongle_t)); @@ -450,6 +461,8 @@ dongle_t *irda_device_dongle_init(struct dongle->issue = reg; dongle->dev = dev; + out: + spin_unlock(&dongles->hb_spinlock); return dongle; } @@ -461,7 +474,7 @@ int irda_device_dongle_cleanup(dongle_t ASSERT(dongle != NULL, return -1;); dongle->issue->close(dongle); - + module_put(dongle->issue->owner); kfree(dongle); return 0; @@ -472,14 +485,16 @@ int irda_device_dongle_cleanup(dongle_t */ int irda_device_register_dongle(struct dongle_reg *new) { + spin_lock(&dongles->hb_spinlock); /* Check if this dongle has been registered before */ - if (hashbin_lock_find(dongles, new->type, NULL)) { - MESSAGE("%s: Dongle already registered\n", __FUNCTION__); - return 0; - } - - /* Insert IrDA dongle into hashbin */ - hashbin_insert(dongles, (irda_queue_t *) new, new->type, NULL); + if (hashbin_find(dongles, new->type, NULL)) { + MESSAGE("%s: Dongle type %x already registered\n", + __FUNCTION__, new->type); + } else { + /* Insert IrDA dongle into hashbin */ + hashbin_insert(dongles, (irda_queue_t *) new, new->type, NULL); + } + spin_unlock(&dongles->hb_spinlock); return 0; } @@ -494,11 +509,11 @@ void irda_device_unregister_dongle(struc { struct dongle *node; + spin_lock(&dongles->hb_spinlock); node = hashbin_remove(dongles, dongle->type, NULL); - if (!node) { + if (!node) ERROR("%s: dongle not found!\n", __FUNCTION__); - return; - } + spin_unlock(&dongles->hb_spinlock); } /* |