summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2015-03-04 15:00:20 +0000
committerglebius <glebius@FreeBSD.org>2015-03-04 15:00:20 +0000
commitdede3f548d282fb754686d2119052d9b67e63b3f (patch)
treed5f4d5d256d8c40bf2ed13895c955dac3a7c35bc
parent2a15661be248d64efdb90c76d6e120d1fdbbe2ad (diff)
downloadFreeBSD-src-dede3f548d282fb754686d2119052d9b67e63b3f.zip
FreeBSD-src-dede3f548d282fb754686d2119052d9b67e63b3f.tar.gz
Optimize SIOCGIFMEDIA handling removing malloc(9) and double
traversal of the list. Sponsored by: Nginx, Inc. Sponsored by: Netflix
-rw-r--r--sys/net/if_media.c72
1 files changed, 14 insertions, 58 deletions
diff --git a/sys/net/if_media.c b/sys/net/if_media.c
index 22bdc82..810db3f 100644
--- a/sys/net/if_media.c
+++ b/sys/net/if_media.c
@@ -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;
}
OpenPOWER on IntegriCloud