Author: thompsa
Date: Fri Apr 20 10:06:28 2012
New Revision: 234488
URL: http://svn.freebsd.org/changeset/base/234488

Log:
  Move the interface media check to a taskqueue, some interfaces (usb) sleep
  during SIOCGIFMEDIA and we were holding locks.

Modified:
  head/sys/net/bridgestp.c
  head/sys/net/bridgestp.h

Modified: head/sys/net/bridgestp.c
==============================================================================
--- head/sys/net/bridgestp.c    Fri Apr 20 09:55:50 2012        (r234487)
+++ head/sys/net/bridgestp.c    Fri Apr 20 10:06:28 2012        (r234488)
@@ -127,7 +127,7 @@ static int  bstp_rerooted(struct bstp_sta
 static uint32_t        bstp_calc_path_cost(struct bstp_port *);
 static void    bstp_notify_state(void *, int);
 static void    bstp_notify_rtage(void *, int);
-static void    bstp_ifupdstatus(struct bstp_state *, struct bstp_port *);
+static void    bstp_ifupdstatus(void *, int);
 static void    bstp_enable_port(struct bstp_state *, struct bstp_port *);
 static void    bstp_disable_port(struct bstp_state *, struct bstp_port *);
 static void    bstp_tick(void *);
@@ -1677,7 +1677,7 @@ bstp_set_autoptp(struct bstp_port *bp, i
        if (set) {
                bp->bp_flags |= BSTP_PORT_AUTOPTP;
                if (bp->bp_role != BSTP_ROLE_DISABLED)
-                       bstp_ifupdstatus(bs, bp);
+                       taskqueue_enqueue(taskqueue_swi, &bp->bp_mediatask);
        } else
                bp->bp_flags &= ~BSTP_PORT_AUTOPTP;
        BSTP_UNLOCK(bs);
@@ -1771,69 +1771,89 @@ bstp_linkstate(struct bstp_port *bp)
 {
        struct bstp_state *bs = bp->bp_bs;
 
+       if (!bp->bp_active)
+               return;
+
+       bstp_ifupdstatus(bp, 0);
        BSTP_LOCK(bs);
-       if (bp->bp_active) {
-               bstp_ifupdstatus(bs, bp);
-               bstp_update_state(bs, bp);
-       }
+       bstp_update_state(bs, bp);
        BSTP_UNLOCK(bs);
 }
 
 static void
-bstp_ifupdstatus(struct bstp_state *bs, struct bstp_port *bp)
+bstp_ifupdstatus(void *arg, int pending)
 {
+       struct bstp_port *bp = (struct bstp_port *)arg;
+       struct bstp_state *bs = bp->bp_bs;
        struct ifnet *ifp = bp->bp_ifp;
        struct ifmediareq ifmr;
-       int error = 0;
+       int error, changed;
 
-       BSTP_LOCK_ASSERT(bs);
+       if (!bp->bp_active)
+               return;
 
        bzero((char *)&ifmr, sizeof(ifmr));
        error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr);
 
+       BSTP_LOCK(bs);
+       changed = 0;
        if ((error == 0) && (ifp->if_flags & IFF_UP)) {
                if (ifmr.ifm_status & IFM_ACTIVE) {
                        /* A full-duplex link is assumed to be point to point */
                        if (bp->bp_flags & BSTP_PORT_AUTOPTP) {
-                               bp->bp_ptp_link =
-                                   ifmr.ifm_active & IFM_FDX ? 1 : 0;
+                               int fdx;
+
+                               fdx = ifmr.ifm_active & IFM_FDX ? 1 : 0;
+                               if (bp->bp_ptp_link ^ fdx) {
+                                       bp->bp_ptp_link = fdx;
+                                       changed = 1;
+                               }
                        }
 
                        /* Calc the cost if the link was down previously */
                        if (bp->bp_flags & BSTP_PORT_PNDCOST) {
-                               bp->bp_path_cost = bstp_calc_path_cost(bp);
+                               uint32_t cost;
+
+                               cost = bstp_calc_path_cost(bp);
+                               if (bp->bp_path_cost != cost) {
+                                       bp->bp_path_cost = cost;
+                                       changed = 1;
+                               }
                                bp->bp_flags &= ~BSTP_PORT_PNDCOST;
                        }
 
-                       if (bp->bp_role == BSTP_ROLE_DISABLED)
+                       if (bp->bp_role == BSTP_ROLE_DISABLED) {
                                bstp_enable_port(bs, bp);
+                               changed = 1;
+                       }
                } else {
                        if (bp->bp_role != BSTP_ROLE_DISABLED) {
                                bstp_disable_port(bs, bp);
+                               changed = 1;
                                if ((bp->bp_flags & BSTP_PORT_ADMEDGE) &&
                                    bp->bp_protover == BSTP_PROTO_RSTP)
                                        bp->bp_operedge = 1;
                        }
                }
-               return;
-       }
-
-       if (bp->bp_infois != BSTP_INFO_DISABLED)
+       } else if (bp->bp_infois != BSTP_INFO_DISABLED) {
                bstp_disable_port(bs, bp);
+               changed = 1;
+       }
+       if (changed)
+               bstp_assign_roles(bs);
+       BSTP_UNLOCK(bs);
 }
 
 static void
 bstp_enable_port(struct bstp_state *bs, struct bstp_port *bp)
 {
        bp->bp_infois = BSTP_INFO_AGED;
-       bstp_assign_roles(bs);
 }
 
 static void
 bstp_disable_port(struct bstp_state *bs, struct bstp_port *bp)
 {
        bp->bp_infois = BSTP_INFO_DISABLED;
-       bstp_assign_roles(bs);
 }
 
 static void
@@ -1853,7 +1873,7 @@ bstp_tick(void *arg)
        if (bstp_timer_dectest(&bs->bs_link_timer)) {
                LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
                        if (!(bp->bp_ifp->if_capabilities & IFCAP_LINKSTATE))
-                               bstp_ifupdstatus(bs, bp);
+                               taskqueue_enqueue(taskqueue_swi, 
&bp->bp_mediatask);
                }
                bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
        }
@@ -2063,7 +2083,7 @@ bstp_reinit(struct bstp_state *bs)
        LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
                bp->bp_port_id = (bp->bp_priority << 8) |
                    (bp->bp_ifp->if_index  & 0xfff);
-               bstp_ifupdstatus(bs, bp);
+               taskqueue_enqueue(taskqueue_swi, &bp->bp_mediatask);
        }
 
        bstp_assign_roles(bs);
@@ -2184,6 +2204,7 @@ bstp_create(struct bstp_state *bs, struc
        bp->bp_priority = BSTP_DEFAULT_PORT_PRIORITY;
        TASK_INIT(&bp->bp_statetask, 0, bstp_notify_state, bp);
        TASK_INIT(&bp->bp_rtagetask, 0, bstp_notify_rtage, bp);
+       TASK_INIT(&bp->bp_mediatask, 0, bstp_ifupdstatus, bp);
 
        /* Init state */
        bp->bp_infois = BSTP_INFO_DISABLED;
@@ -2247,4 +2268,5 @@ bstp_destroy(struct bstp_port *bp)
        KASSERT(bp->bp_active == 0, ("port is still attached"));
        taskqueue_drain(taskqueue_swi, &bp->bp_statetask);
        taskqueue_drain(taskqueue_swi, &bp->bp_rtagetask);
+       taskqueue_drain(taskqueue_swi, &bp->bp_mediatask);
 }

Modified: head/sys/net/bridgestp.h
==============================================================================
--- head/sys/net/bridgestp.h    Fri Apr 20 09:55:50 2012        (r234487)
+++ head/sys/net/bridgestp.h    Fri Apr 20 10:06:28 2012        (r234488)
@@ -326,6 +326,7 @@ struct bstp_port {
        uint8_t                 bp_txcount;
        struct task             bp_statetask;
        struct task             bp_rtagetask;
+       struct task             bp_mediatask;
 };
 
 /*
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to