Optimize SIOCGIFMEDIA handling removing malloc(9) and double

traversal of the list.

Sponsored by:	Nginx, Inc.
Sponsored by:	Netflix
This commit is contained in:
glebius 2015-03-04 15:00:20 +00:00
parent 2a15661be2
commit dede3f548d

View File

@ -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;
}