Author: glebius
Date: Wed Mar  4 15:00:20 2015
New Revision: 279592
URL: https://svnweb.freebsd.org/changeset/base/279592

Log:
  Optimize SIOCGIFMEDIA handling removing malloc(9) and double
  traversal of the list.
  
  Sponsored by: Nginx, Inc.
  Sponsored by: Netflix

Modified:
  head/sys/net/if_media.c

Modified: head/sys/net/if_media.c
==============================================================================
--- head/sys/net/if_media.c     Wed Mar  4 14:30:09 2015        (r279591)
+++ head/sys/net/if_media.c     Wed Mar  4 15:00:20 2015        (r279592)
@@ -204,7 +204,7 @@ ifmedia_ioctl(ifp, ifr, ifm, cmd)
 {
        struct ifmedia_entry *match;
        struct ifmediareq *ifmr = (struct ifmediareq *) ifr;
-       int error = 0, sticky;
+       int error = 0;
 
        if (ifp == NULL || ifr == NULL || ifm == NULL)
                return(EINVAL);
@@ -273,10 +273,10 @@ ifmedia_ioctl(ifp, ifr, ifm, cmd)
        case  SIOCGIFMEDIA: 
        {
                struct ifmedia_entry *ep;
-               int *kptr, count;
-               int usermax;    /* user requested max */
+               int i;
 
-               kptr = NULL;            /* XXX gcc */
+               if (ifmr->ifm_count < 0)
+                       return (EINVAL);
 
                ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
                    ifm->ifm_cur->ifm_media : IFM_NONE;
@@ -284,67 +284,23 @@ ifmedia_ioctl(ifp, ifr, ifm, cmd)
                ifmr->ifm_status = 0;
                (*ifm->ifm_status)(ifp, ifmr);
 
-               count = 0;
-               usermax = 0;
-
                /*
                 * If there are more interfaces on the list, count
                 * them.  This allows the caller to set ifmr->ifm_count
                 * to 0 on the first call to know how much space to
                 * allocate.
                 */
+               i = 0;
                LIST_FOREACH(ep, &ifm->ifm_list, ifm_list)
-                       usermax++;
-
-               /*
-                * Don't allow the user to ask for too many
-                * or a negative number.
-                */
-               if (ifmr->ifm_count > usermax)
-                       ifmr->ifm_count = usermax;
-               else if (ifmr->ifm_count < 0)
-                       return (EINVAL);
-
-               if (ifmr->ifm_count != 0) {
-                       kptr = (int *)malloc(ifmr->ifm_count * sizeof(int),
-                           M_TEMP, M_NOWAIT);
-
-                       if (kptr == NULL)
-                               return (ENOMEM);
-                       /*
-                        * Get the media words from the interface's list.
-                        */
-                       ep = LIST_FIRST(&ifm->ifm_list);
-                       for (; ep != NULL && count < ifmr->ifm_count;
-                           ep = LIST_NEXT(ep, ifm_list), count++)
-                               kptr[count] = ep->ifm_media;
-
-                       if (ep != NULL)
-                               error = E2BIG;  /* oops! */
-               } else {
-                       count = usermax;
-               }
-
-               /*
-                * We do the copyout on E2BIG, because that's
-                * just our way of telling userland that there
-                * are more.  This is the behavior I've observed
-                * under BSD/OS 3.0
-                */
-               sticky = error;
-               if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) {
-                       error = copyout((caddr_t)kptr,
-                           (caddr_t)ifmr->ifm_ulist,
-                           ifmr->ifm_count * sizeof(int));
-               }
-
-               if (error == 0)
-                       error = sticky;
-
-               if (ifmr->ifm_count != 0)
-                       free(kptr, M_TEMP);
-
-               ifmr->ifm_count = count;
+                       if (i++ < ifmr->ifm_count) {
+                               error = copyout(&ep->ifm_media,
+                                   ifmr->ifm_ulist + i - 1, sizeof(int));
+                               if (error)
+                                       break;
+                       }
+               if (error == 0 && i > ifmr->ifm_count)
+                       error = ifmr->ifm_count ? E2BIG : 0;
+               ifmr->ifm_count = i;
                break;
        }
 
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to