From 46e6df0fc5cd9319d4e42586f0d1f601a180d058 Mon Sep 17 00:00:00 2001 From: beveloper Date: Mon, 3 May 2004 23:09:50 +0000 Subject: [PATCH] Reacting on a link change inside the interrupt handler is plain crazy, as this involves some long delays like eeprom reading, etc. Need to snooze() there, and this moved this into a separate thread. Obviously, the FreeBSD driver isn't doing prober locking yet, so it's not really save. But it's the correct way to do it, and it doesn't crash. Inside the interrupt handler it did crash when reconnecting the cable... git-svn-id: file:///srv/svn/repos/haiku/trunk/current@7393 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/drivers/network/ipro1000/if_em.c | 108 ++++++++++++++++-- .../kernel/drivers/network/ipro1000/if_em.h | 7 ++ 2 files changed, 105 insertions(+), 10 deletions(-) diff --git a/src/add-ons/kernel/drivers/network/ipro1000/if_em.c b/src/add-ons/kernel/drivers/network/ipro1000/if_em.c index 2185bb4f3e..f0ebec4a21 100644 --- a/src/add-ons/kernel/drivers/network/ipro1000/if_em.c +++ b/src/add-ons/kernel/drivers/network/ipro1000/if_em.c @@ -48,6 +48,9 @@ int em_display_debug_stats = 1; int em_attach(device_t); int em_detach(device_t); static int32 em_intr(void *); +static int32 event_handler(void *); +static int start_event_thread(struct adapter *); +static void stop_event_thread(struct adapter *); static void em_start(struct ifnet *); static int em_ioctl(struct ifnet *, u_long, caddr_t); static void em_watchdog(struct ifnet *); @@ -197,6 +200,12 @@ em_attach(device_t dev) callout_handle_init(&adapter->timer_handle); callout_handle_init(&adapter->tx_fifo_timer_handle); + /* create event processing thread */ + if (start_event_thread(adapter) < 0) { + error = EIO; + goto err_event; + } + /* Determine hardware revision */ em_identify_hardware(adapter); @@ -369,6 +378,7 @@ err_rx_desc: err_tx_desc: err_pci: em_free_pci_resources(adapter); +err_event: sysctl_ctx_free(&adapter->sysctl_ctx); err_sysctl: splx(s); @@ -400,6 +410,9 @@ em_detach(device_t dev) em_stop(adapter); em_phy_hw_reset(&adapter->hw); + + stop_event_thread(adapter); + #if __FreeBSD_version < 500000 ether_ifdetach(&adapter->interface_data.ac_if, ETHER_BPF_SUPPORTED); #else @@ -436,6 +449,45 @@ em_detach(device_t dev) return(0); } +static int +start_event_thread(struct adapter *adapter) +{ + TRACE("start_event_thread enter\n"); + + adapter->event_thread = spawn_kernel_thread(event_handler, "ipro1000 event", 80, adapter); + adapter->event_sem = create_sem(0, "ipro1000 event"); + adapter->event_flags = 0; + + if (adapter->event_thread >= 0 && adapter->event_sem >= 0) { + resume_thread(adapter->event_thread); + TRACE("start_event_thread leave\n"); + return 0; + } + + TRACE("start_event_thread failed\n"); + + delete_sem(adapter->event_sem); + kill_thread(adapter->event_thread); + + TRACE("start_event_thread leave\n"); + return -1; +} + +static void +stop_event_thread(struct adapter *adapter) +{ + status_t thread_return_value; + + TRACE("stop_event_thread enter\n"); + + delete_sem(adapter->event_sem); + wait_for_thread(adapter->event_thread, &thread_return_value); + adapter->event_thread = -1; + adapter->event_sem = -1; + + TRACE("stop_event_thread leave\n"); +} + /********************************************************************* * Transmit entry point @@ -447,6 +499,7 @@ em_detach(device_t dev) * the packet is requeued. **********************************************************************/ +// can be called from within interrupt static void em_start(struct ifnet *ifp) { @@ -756,6 +809,7 @@ em_intr(void *arg) u_int32_t reg_icr; struct ifnet *ifp; struct adapter *adapter = arg; + bool release_event_sem = false; ifp = &adapter->interface_data.ac_if; @@ -778,15 +832,11 @@ em_intr(void *arg) /* Link status change */ if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { - untimeout(em_local_timer, adapter, - adapter->timer_handle); - adapter->hw.get_link_status = 1; - em_check_for_link(&adapter->hw); - em_print_link_status(adapter); - adapter->timer_handle = - timeout(em_local_timer, adapter, 2*hz); + atomic_or(&adapter->event_flags, EVENT_LINK_CHANGED); + release_event_sem = true; } + while (loop_cnt > 0) { if (ifp->if_flags & IFF_RUNNING) { em_process_receive_interrupts(adapter, -1); @@ -795,10 +845,46 @@ em_intr(void *arg) loop_cnt--; } - if (ifp->if_flags & IFF_RUNNING && ifp->if_snd.ifq_head != NULL) - em_start(ifp); + if (ifp->if_flags & IFF_RUNNING && ifp->if_snd.ifq_head != NULL) { + atomic_or(&adapter->event_flags, EVENT_RESTART_TX); + release_event_sem = true; + } + + if (release_event_sem) + release_sem_etc(adapter->event_sem, 1, B_DO_NOT_RESCHEDULE); - return B_HANDLED_INTERRUPT; + return B_INVOKE_SCHEDULER; +} + +static int32 +event_handler(void *cookie) +{ + struct adapter * adapter = cookie; + int32 events; + + for (;;) { + if (acquire_sem_etc(adapter->event_sem, 1, B_CAN_INTERRUPT, 0) != B_OK) + return 0; + + events = atomic_read(&adapter->event_flags); // read + atomic_and(&adapter->event_flags, ~events); // and clear + + if (events & EVENT_LINK_CHANGED) { + TRACE("EVENT_LINK_CHANGED\n"); + untimeout(em_local_timer, adapter, adapter->timer_handle); + adapter->hw.get_link_status = 1; + em_check_for_link(&adapter->hw); + em_print_link_status(adapter); + adapter->timer_handle = timeout(em_local_timer, adapter, 2*hz); + } + + if (events & EVENT_RESTART_TX) { + TRACE("EVENT_RESTART_TX\n"); +// XXX not multithread save? +// if (ifp->if_flags & IFF_RUNNING && ifp->if_snd.ifq_head != NULL) +// em_start(ifp); + } + } } @@ -2504,6 +2590,7 @@ em_enable_vlans(struct adapter *adapter) return; } +// can be called from within interrupt static void em_enable_intr(struct adapter * adapter) { @@ -2511,6 +2598,7 @@ em_enable_intr(struct adapter * adapter) return; } +// can be called from within interrupt static void em_disable_intr(struct adapter *adapter) { diff --git a/src/add-ons/kernel/drivers/network/ipro1000/if_em.h b/src/add-ons/kernel/drivers/network/ipro1000/if_em.h index a5432821cd..0bd9fc004f 100644 --- a/src/add-ons/kernel/drivers/network/ipro1000/if_em.h +++ b/src/add-ons/kernel/drivers/network/ipro1000/if_em.h @@ -268,6 +268,13 @@ struct adapter { int io_rid; u_int8_t unit; + /* Even processing thread, to move link status change out of the interrupt */ + thread_id event_thread; + sem_id event_sem; + volatile int32 event_flags; + enum { EVENT_LINK_CHANGED = 0x1, EVENT_RESTART_TX = 0x02 }; + #define atomic_read(a) atomic_or(a, 0) + /* Info about the board itself */ u_int32_t part_num; u_int8_t link_active;