Optimize SIOCGIFMEDIA handling removing malloc(9) and double
traversal of the list. Sponsored by: Nginx, Inc. Sponsored by: Netflix
This commit is contained in:
parent
2a15661be2
commit
dede3f548d
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user