MFC 1.29, 1.30:

Rewrite linux_ifconf() to be more like ifconf() in net/if.c
  so that we do not call uiomove() while IFNET_RLOCK() is held.
  This eliminates the witness warning:

  Calling uiomove() with the following non-sleepable locks held:
  exclusive sleep mutex ifnet r = 0 (0xc096dd60) locked @
  /usr/src/sys/modules/linux/../../compat/linux/linux_ioctl.c:2170

Approved by:	re (scottl)
This commit is contained in:
rodrigc 2005-09-02 03:52:28 +00:00
parent 6cf5159da3
commit 05573dfb34

View File

@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <sys/linker_set.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/sbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/soundcard.h>
@ -2130,14 +2131,15 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
struct l_ifreq ifr;
struct ifnet *ifp;
struct ifaddr *ifa;
struct iovec iov;
struct uio uio;
int error, ethno;
struct sbuf *sb;
int error, ethno, full = 0, valid_len, max_len;
error = copyin(uifc, &ifc, sizeof(ifc));
if (error != 0)
return (error);
max_len = MAXPHYS - 1;
/* handle the 'request buffer size' case */
if (ifc.ifc_buf == PTROUT(NULL)) {
ifc.ifc_len = 0;
@ -2152,25 +2154,24 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
return (error);
}
/* much easier to use uiomove than keep track ourselves */
iov.iov_base = PTRIN(ifc.ifc_buf);
iov.iov_len = ifc.ifc_len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = 0;
uio.uio_resid = ifc.ifc_len;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_rw = UIO_READ;
uio.uio_td = td;
if (ifc.ifc_len <= 0)
return (EINVAL);
again:
/* Keep track of eth interfaces */
ethno = 0;
if (ifc.ifc_len <= max_len) {
max_len = ifc.ifc_len;
full = 1;
}
sb = sbuf_new(NULL, NULL, max_len + 1, SBUF_FIXEDLEN);
max_len = 0;
valid_len = 0;
/* Return all AF_INET addresses of all interfaces */
IFNET_RLOCK(); /* could sleep XXX */
TAILQ_FOREACH(ifp, &ifnet, if_link) {
if (uio.uio_resid <= 0)
break;
int addrs = 0;
bzero(&ifr, sizeof(ifr));
if (IFP_IS_ETH(ifp))
@ -2183,26 +2184,39 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
struct sockaddr *sa = ifa->ifa_addr;
if (uio.uio_resid <= 0)
break;
if (sa->sa_family == AF_INET) {
ifr.ifr_addr.sa_family = LINUX_AF_INET;
memcpy(ifr.ifr_addr.sa_data, sa->sa_data,
sizeof(ifr.ifr_addr.sa_data));
error = uiomove(&ifr, sizeof(ifr), &uio);
if (error != 0) {
IFNET_RUNLOCK();
return (error);
}
sbuf_bcat(sb, &ifr, sizeof(ifr));
max_len += sizeof(ifr);
addrs++;
}
if (!sbuf_overflowed(sb))
valid_len = sbuf_len(sb);
}
if (addrs == 0) {
bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
sbuf_bcat(sb, &ifr, sizeof(ifr));
max_len += sizeof(ifr);
if (!sbuf_overflowed(sb))
valid_len = sbuf_len(sb);
}
}
IFNET_RUNLOCK();
ifc.ifc_len -= uio.uio_resid;
if (valid_len != max_len && !full) {
sbuf_delete(sb);
goto again;
}
ifc.ifc_len = valid_len;
sbuf_finish(sb);
memcpy(PTRIN(ifc.ifc_buf), sbuf_data(sb), ifc.ifc_len);
error = copyout(&ifc, uifc, sizeof(ifc));
sbuf_delete(sb);
return (error);
}