Index: highlevel.c =================================================================== --- highlevel.c (revision 1073) +++ highlevel.c (working copy) @@ -37,9 +37,18 @@ }; +/* Double bookkeeping: hl_drivers and hl_sem_drivers are essentially + * the same lists, but hl_sem_list is protected by a semaphore and is + * used to notify highlevel drivers when we add and remove hosts. The + * other, hl_drivers is protected by an rw spinlock, and is used when + * we notify highlevel drivers of bus resets and incoming packets. */ + static LIST_HEAD(hl_drivers); static rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED; +static LIST_HEAD(hl_sem_drivers); +static DECLARE_MUTEX(hl_drivers_sem); + static LIST_HEAD(addr_space); static rwlock_t addr_space_lock = RW_LOCK_UNLOCKED; @@ -238,6 +247,10 @@ rwlock_init(&hl->host_info_lock); + down(&hl_drivers_sem); + list_add_tail(&hl->hl_sem_list, &hl_sem_drivers); + up(&hl_drivers_sem); + write_lock_irqsave(&hl_drivers_lock, flags); list_add_tail(&hl->hl_list, &hl_drivers); write_unlock_irqrestore(&hl_drivers_lock, flags); @@ -272,6 +285,10 @@ list_del(&hl->hl_list); write_unlock_irqrestore(&hl_drivers_lock, flags); + down(&hl_drivers_sem); + list_del(&hl->hl_sem_list); + up(&hl_drivers_sem); + if (hl->remove_host) { down(&hpsb_hosts_lock); list_for_each(lh, &hpsb_hosts) { @@ -391,33 +408,28 @@ void highlevel_add_host(struct hpsb_host *host) { - struct list_head *entry; struct hpsb_highlevel *hl; - read_lock(&hl_drivers_lock); - list_for_each(entry, &hl_drivers) { - hl = list_entry(entry, struct hpsb_highlevel, hl_list); + down(&hl_drivers_sem); + list_for_each_entry(hl, &hl_sem_drivers, hl_sem_list) { if (hl->add_host) hl->add_host(host); } - read_unlock(&hl_drivers_lock); + up(&hl_drivers_sem); } void highlevel_remove_host(struct hpsb_host *host) { - struct list_head *entry; struct hpsb_highlevel *hl; - read_lock(&hl_drivers_lock); - list_for_each(entry, &hl_drivers) { - hl = list_entry(entry, struct hpsb_highlevel, hl_list); - + down(&hl_drivers_sem); + list_for_each_entry(hl, &hl_sem_drivers, hl_sem_list) { if (hl->remove_host) { hl->remove_host(host); hpsb_destroy_hostinfo(hl, host); } } - read_unlock(&hl_drivers_lock); + up(&hl_drivers_sem); } void highlevel_host_reset(struct hpsb_host *host) Index: highlevel.h =================================================================== --- highlevel.h (revision 1073) +++ highlevel.h (working copy) @@ -56,6 +56,7 @@ int cts, u8 *data, size_t length); + struct list_head hl_sem_list; struct list_head hl_list; struct list_head addr_list;