--- old/drivers/atm/nicstar.c Wed Apr 5 15:15:43 2000 +++ new/drivers/atm/nicstar.c Wed Apr 5 15:18:17 2000 @@ -21,6 +21,18 @@ ******************************************************************************/ +/**** IMPORTANT INFORMATION *************************************************** + * + * There are currently three types of spinlocks: + * + * 1 - Per card interrupt spinlock (to protect structures and such) + * 2 - Per SCQ scq spinlock + * 3 - Per card resource spinlock (to access registers, etc.) + * + * These must NEVER be grabbed in reverse order. + * + ******************************************************************************/ + /* Header files ***************************************************************/ #include @@ -115,6 +127,85 @@ #define ATM_SKB(s) (&(s)->atm) #endif + /* Spinlock debugging stuff */ +#ifdef NS_DEBUG_SPINLOCKS /* See nicstar.h */ +#define ns_grab_int_lock(card,flags) \ + do { \ + unsigned long nsdsf, nsdsf2; \ + local_irq_save(flags); \ + save_flags(nsdsf); cli();\ + if (nsdsf & (1<<9)) printk ("nicstar.c: ints %sabled -> enabled.\n", \ + (flags)&(1<<9)?"en":"dis"); \ + if (spin_is_locked(&(card)->int_lock) && \ + (card)->cpu_int == smp_processor_id()) { \ + printk("nicstar.c: line %d (cpu %d) int_lock already locked at line %d (cpu %d)\n", \ + __LINE__, smp_processor_id(), (card)->has_int_lock, \ + (card)->cpu_int); \ + printk("nicstar.c: ints were %sabled.\n", ((flags)&(1<<9)?"en":"dis")); \ + } \ + if (spin_is_locked(&(card)->res_lock) && \ + (card)->cpu_res == smp_processor_id()) { \ + printk("nicstar.c: line %d (cpu %d) res_lock locked at line %d (cpu %d)(trying int)\n", \ + __LINE__, smp_processor_id(), (card)->has_res_lock, \ + (card)->cpu_res); \ + printk("nicstar.c: ints were %sabled.\n", ((flags)&(1<<9)?"en":"dis")); \ + } \ + spin_lock_irq(&(card)->int_lock); \ + (card)->has_int_lock = __LINE__; \ + (card)->cpu_int = smp_processor_id(); \ + restore_flags(nsdsf); } while (0) +#define ns_grab_res_lock(card,flags) \ + do { \ + unsigned long nsdsf, nsdsf2; \ + local_irq_save(flags); \ + save_flags(nsdsf); cli();\ + if (nsdsf & (1<<9)) printk ("nicstar.c: ints %sabled -> enabled.\n", \ + (flags)&(1<<9)?"en":"dis"); \ + if (spin_is_locked(&(card)->res_lock) && \ + (card)->cpu_res == smp_processor_id()) { \ + printk("nicstar.c: line %d (cpu %d) res_lock already locked at line %d (cpu %d)\n", \ + __LINE__, smp_processor_id(), (card)->has_res_lock, \ + (card)->cpu_res); \ + printk("nicstar.c: ints were %sabled.\n", ((flags)&(1<<9)?"en":"dis")); \ + } \ + spin_lock_irq(&(card)->res_lock); \ + (card)->has_res_lock = __LINE__; \ + (card)->cpu_res = smp_processor_id(); \ + restore_flags(nsdsf); } while (0) +#define ns_grab_scq_lock(card,scq,flags) \ + do { \ + unsigned long nsdsf, nsdsf2; \ + local_irq_save(flags); \ + save_flags(nsdsf); cli();\ + if (nsdsf & (1<<9)) printk ("nicstar.c: ints %sabled -> enabled.\n", \ + (flags)&(1<<9)?"en":"dis"); \ + if (spin_is_locked(&(scq)->lock) && \ + (scq)->cpu_lock == smp_processor_id()) { \ + printk("nicstar.c: line %d (cpu %d) this scq_lock already locked at line %d (cpu %d)\n", \ + __LINE__, smp_processor_id(), (scq)->has_lock, \ + (scq)->cpu_lock); \ + printk("nicstar.c: ints were %sabled.\n", ((flags)&(1<<9)?"en":"dis")); \ + } \ + if (spin_is_locked(&(card)->res_lock) && \ + (card)->cpu_res == smp_processor_id()) { \ + printk("nicstar.c: line %d (cpu %d) res_lock locked at line %d (cpu %d)(trying scq)\n", \ + __LINE__, smp_processor_id(), (card)->has_res_lock, \ + (card)->cpu_res); \ + printk("nicstar.c: ints were %sabled.\n", ((flags)&(1<<9)?"en":"dis")); \ + } \ + spin_lock_irq(&(scq)->lock); \ + (scq)->has_lock = __LINE__; \ + (scq)->cpu_lock = smp_processor_id(); \ + restore_flags(nsdsf); } while (0) +#else /* !NS_DEBUG_SPINLOCKS */ +#define ns_grab_int_lock(card,flags) \ + spin_lock_irqsave(&(card)->int_lock,(flags)) +#define ns_grab_res_lock(card,flags) \ + spin_lock_irqsave(&(card)->res_lock,(flags)) +#define ns_grab_scq_lock(card,scq,flags) \ + spin_lock_irqsave(&(scq)->lock,flags) +#endif /* NS_DEBUG_SPINLOCKS */ + /* Version definition *********************************************************/ /* @@ -406,12 +497,12 @@ sram_address <<= 2; sram_address &= 0x0007FFFC; /* address must be dword aligned */ sram_address |= 0x50000000; /* SRAM read command */ - save_flags(flags); cli(); + ns_grab_res_lock(card, flags); while (CMD_BUSY(card)); writel(sram_address, card->membase + CMD); while (CMD_BUSY(card)); data = readl(card->membase + DR0); - restore_flags(flags); + spin_unlock_irqrestore(&card->res_lock, flags); return data; } @@ -424,7 +515,7 @@ count--; /* count range now is 0..3 instead of 1..4 */ c = count; c <<= 2; /* to use increments of 4 */ - save_flags(flags); cli(); + ns_grab_res_lock(card, flags); while (CMD_BUSY(card)); for (i = 0; i <= c; i += 4) writel(*(value++), card->membase + i); @@ -434,7 +525,7 @@ sram_address &= 0x0007FFFC; sram_address |= (0x40000000 | count); writel(sram_address, card->membase + CMD); - restore_flags(flags); + spin_unlock_irqrestore(&card->res_lock, flags); } @@ -460,6 +551,8 @@ return error; } cards[i] = card; + spin_lock_init(&card->int_lock); + spin_lock_init(&card->res_lock); card->index = i; card->atmdev = NULL; @@ -845,9 +938,6 @@ card->iovpool.count++; } - - card->in_handler = 0; - card->in_poll = 0; card->intcnt = 0; /* Configure NICStAR */ @@ -1018,6 +1108,7 @@ scq->tbd_count = 0; init_waitqueue_head(&scq->scqfull_waitq); scq->full = 0; + spin_lock_init(&scq->lock); for (i = 0; i < scq->num_entries; i++) scq->skb[i] = NULL; @@ -1154,7 +1245,7 @@ card->lbfqc += 2; } - save_flags(flags); cli(); + ns_grab_res_lock(card, flags); while (CMD_BUSY(card)); writel(addr2, card->membase + DR3); @@ -1163,7 +1254,7 @@ writel(handle1, card->membase + DR0); writel(NS_CMD_WRITE_FREEBUFQ | (u32) type, card->membase + CMD); - restore_flags(flags); + spin_unlock_irqrestore(&card->res_lock, flags); XPRINTK("nicstar%d: Pushing %s buffers at 0x%x and 0x%x.\n", card->index, (type == BUF_SM ? "small" : "large"), addr1, addr2); @@ -1186,6 +1277,7 @@ u32 stat_r; ns_dev *card; struct atm_dev *dev; + unsigned long flags; card = (ns_dev *) dev_id; dev = card->atmdev; @@ -1193,19 +1285,7 @@ PRINTK("nicstar%d: NICStAR generated an interrupt\n", card->index); - if (card->in_handler) - { - printk("nicstar%d: Re-entering ns_irq_handler()???\n", card->index); - return; - } - card->in_handler = 1; - if (card->in_poll) - { - card->in_handler = 0; - printk("nicstar%d: Called irq handler while in ns_poll()!?\n", - card->index); - return; - } + ns_grab_int_lock(card, flags); stat_r = readl(card->membase + STAT); @@ -1370,7 +1450,7 @@ process_rsq(card); } - card->in_handler = 0; + spin_unlock_irqrestore(&card->int_lock, flags); PRINTK("nicstar%d: end of interrupt service\n", card->index); } @@ -1588,10 +1668,10 @@ unsigned long flags; addr = NS_RCT + (vcc->vpi << card->vcibits | vcc->vci) * NS_RCT_ENTRY_SIZE; - save_flags(flags); cli(); + ns_grab_res_lock(card, flags); while(CMD_BUSY(card)); writel(NS_CMD_CLOSE_CONNECTION | addr << 2, card->membase + CMD); - restore_flags(flags); + spin_unlock_irqrestore(&card->res_lock, flags); vc->rx = 0; if (vc->rx_iov != NULL) @@ -1610,9 +1690,9 @@ ATM_SKB(iovb)->iovcnt); ATM_SKB(iovb)->iovcnt = 0; ATM_SKB(iovb)->vcc = NULL; - save_flags(flags); cli(); + ns_grab_int_lock(card, flags); recycle_iov_buf(card, iovb); - restore_flags(flags); + spin_unlock_irqrestore(&card->int_lock, flags); vc->rx_iov = NULL; } } @@ -1632,7 +1712,7 @@ for (;;) { - save_flags(flags); cli(); + ns_grab_scq_lock(card, scq, flags); scqep = scq->next; if (scqep == scq->base) scqep = scq->last; @@ -1640,7 +1720,7 @@ scqep--; if (scqep == scq->tail) { - restore_flags(flags); + spin_unlock_irqrestore(&scq->lock, flags); break; } /* If the last entry is not a TSR, place one in the SCQ in order to @@ -1668,8 +1748,8 @@ data = (u32) virt_to_bus(scq->next); ns_write_sram(card, scq->scd, &data, 1); } + spin_unlock_irqrestore(&scq->lock, flags); schedule(); - restore_flags(flags); } /* Free all TST entries */ @@ -1877,19 +1957,22 @@ u32 data; int index; - if (scq->tail == scq->next) + ns_grab_scq_lock(card, scq, flags); + while (scq->tail == scq->next) { if (in_interrupt()) { + spin_unlock_irqrestore(&scq->lock, flags); printk("nicstar%d: Error pushing TBD.\n", card->index); return 1; } - save_flags(flags); cli(); scq->full = 1; + spin_unlock_irqrestore(&scq->lock, flags); interruptible_sleep_on_timeout(&scq->scqfull_waitq, SCQFULL_TIMEOUT); - restore_flags(flags); + ns_grab_scq_lock(card, scq, flags); if (scq->full) { + spin_unlock_irqrestore(&scq->lock, flags); printk("nicstar%d: Timeout pushing TBD.\n", card->index); return 1; } @@ -1919,19 +2002,23 @@ if (vc->tbd_count >= MAX_TBD_PER_VC || scq->tbd_count >= MAX_TBD_PER_SCQ) { - if (scq->tail == scq->next) + int has_run = 0; + + while (scq->tail == scq->next) { if (in_interrupt()) { data = (u32) virt_to_bus(scq->next); ns_write_sram(card, scq->scd, &data, 1); + spin_unlock_irqrestore(&scq->lock, flags); printk("nicstar%d: Error pushing TSR.\n", card->index); return 0; } - save_flags(flags); cli(); scq->full = 1; + if (has_run++) break; + spin_unlock_irqrestore(&scq->lock, flags); interruptible_sleep_on_timeout(&scq->scqfull_waitq, SCQFULL_TIMEOUT); - restore_flags(flags); + ns_grab_scq_lock(card, scq, flags); } if (!scq->full) @@ -1963,10 +2050,11 @@ else PRINTK("nicstar%d: Timeout pushing TSR.\n", card->index); } - data = (u32) virt_to_bus(scq->next); ns_write_sram(card, scq->scd, &data, 1); + spin_unlock_irqrestore(&scq->lock, flags); + return 0; } @@ -2057,6 +2145,7 @@ struct atm_vcc *vcc; struct sk_buff *skb; int i; + unsigned long flags; XPRINTK("nicstar%d: drain_scq() called, scq at 0x%x, pos %d.\n", card->index, (u32) scq, pos); @@ -2066,6 +2155,7 @@ return; } + ns_grab_scq_lock(card, scq, flags); i = (int) (scq->tail - scq->base); if (++i == scq->num_entries) i = 0; @@ -2077,16 +2167,18 @@ if (skb != NULL) { vcc = ATM_SKB(skb)->vcc; - if (vcc->pop != NULL) + if (vcc->pop != NULL) { vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); + } else { + dev_kfree_skb_irq(skb); + } scq->skb[i] = NULL; } if (++i == scq->num_entries) i = 0; } scq->tail = scq->base + pos; + spin_unlock_irqrestore(&scq->lock, flags); } @@ -2883,10 +2975,10 @@ { struct sk_buff *hb; - save_flags(flags); cli(); + ns_grab_int_lock(card, flags); hb = skb_dequeue(&card->hbpool.queue); card->hbpool.count--; - restore_flags(flags); + spin_unlock_irqrestore(&card->int_lock, flags); if (hb == NULL) printk("nicstar%d: huge buffer count inconsistent.\n", card->index); @@ -2901,10 +2993,10 @@ hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL); if (hb == NULL) return -ENOMEM; - save_flags(flags); cli(); + ns_grab_int_lock(card, flags); skb_queue_tail(&card->hbpool.queue, hb); card->hbpool.count++; - restore_flags(flags); + spin_unlock_irqrestore(&card->int_lock, flags); } break; @@ -2913,10 +3005,10 @@ { struct sk_buff *iovb; - save_flags(flags); cli(); + ns_grab_int_lock(card, flags); iovb = skb_dequeue(&card->iovpool.queue); card->iovpool.count--; - restore_flags(flags); + spin_unlock_irqrestore(&card->int_lock, flags); if (iovb == NULL) printk("nicstar%d: iovec buffer count inconsistent.\n", card->index); @@ -2931,10 +3023,10 @@ iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL); if (iovb == NULL) return -ENOMEM; - save_flags(flags); cli(); + ns_grab_int_lock(card, flags); skb_queue_tail(&card->iovpool.queue, iovb); card->iovpool.count++; - restore_flags(flags); + spin_unlock_irqrestore(&card->int_lock, flags); } break; @@ -2979,22 +3071,11 @@ for (i = 0; i < num_cards; i++) { card = cards[i]; - save_flags(flags); cli(); - if (card->in_poll) - { - printk("nicstar: Re-entering ns_poll()???\n"); - restore_flags(flags); - continue; - } - card->in_poll = 1; - if (card->in_handler) - { - card->in_poll = 0; - printk("nicstar%d: ns_poll called while in interrupt handler!?\n", - card->index); - restore_flags(flags); + if (spin_is_locked(&card->int_lock)) { + /* Probably it isn't worth spinning */ continue; } + ns_grab_int_lock(card, flags); stat_w = 0; stat_r = readl(card->membase + STAT); @@ -3007,8 +3088,7 @@ process_rsq(card); writel(stat_w, card->membase + STAT); - card->in_poll = 0; - restore_flags(flags); + spin_unlock_irqrestore(&card->int_lock, flags); } mod_timer(&ns_timer, jiffies + NS_POLL_PERIOD); PRINTK("nicstar: Leaving ns_poll().\n"); @@ -3062,12 +3142,12 @@ unsigned long flags; card = dev->dev_data; - save_flags(flags); cli(); + ns_grab_res_lock(card, flags); while(CMD_BUSY(card)); writel((unsigned long) value, card->membase + DR0); writel(NS_CMD_WRITE_UTILITY | 0x00000200 | (addr & 0x000000FF), card->membase + CMD); - restore_flags(flags); + spin_unlock_irqrestore(&card->res_lock, flags); } @@ -3079,12 +3159,12 @@ unsigned long data; card = dev->dev_data; - save_flags(flags); cli(); + ns_grab_res_lock(card, flags); while(CMD_BUSY(card)); writel(NS_CMD_READ_UTILITY | 0x00000200 | (addr & 0x000000FF), card->membase + CMD); while(CMD_BUSY(card)); data = readl(card->membase + DR0) & 0x000000FF; - restore_flags(flags); + spin_unlock_irqrestore(&card->res_lock, flags); return (unsigned char) data; } --- old/drivers/atm/nicstar.h Wed Apr 5 15:15:43 2000 +++ new/drivers/atm/nicstar.h Wed Apr 5 15:18:17 2000 @@ -28,6 +28,8 @@ /* Options ********************************************************************/ +#undef NS_DEBUG_SPINLOCKS + #define NS_MAX_CARDS 4 /* Maximum number of NICStAR based cards controlled by the device driver. Must be <= 5 */ @@ -705,6 +707,11 @@ int tbd_count; /* Only meaningful on variable rate */ wait_queue_head_t scqfull_waitq; volatile char full; /* SCQ full indicator */ + spinlock_t lock; /* SCQ spinlock */ +#ifdef NS_DEBUG_SPINLOCKS + volatile long has_lock; + volatile int cpu_lock; +#endif /* NS_DEBUG_SPINLOCKS */ } scq_info; @@ -779,8 +786,14 @@ struct sk_buff *rcbuf; /* Current raw cell buffer */ u32 rawch; /* Raw cell queue head */ unsigned intcnt; /* Interrupt counter */ - volatile int in_handler: 1; - volatile int in_poll: 1; + spinlock_t int_lock; /* Interrupt lock */ + spinlock_t res_lock; /* Card resource lock */ +#ifdef NS_DEBUG_SPINLOCKS + volatile long has_int_lock; + volatile int cpu_int; + volatile long has_res_lock; + volatile int cpu_res; +#endif /* NS_DEBUG_SPINLOCKS */ } ns_dev;