mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-28 11:31:16 +00:00
nwfilter: directly use poll to wait for packets instead of pcap_next
When a QEMU VM shuts down its TAP device gets deleted while nwfilter IP address learning thread is still capturing packets. It is seen that with TPACKET_V3 support in libcap, the pcap_next() call will not always exit its poll() when the NIC is removed. This prevents the learning thread from exiting which blocks the rest of libvirtd waiting on mutex acquisition. By switching to do poll() in libvirt code, we can ensure that we always exit the poll() at a time that is right for libvirt. Reviewed-by: John Ferlan <jferlan@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
parent
1e49132dde
commit
ab4591f1f4
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <net/ethernet.h>
|
#include <net/ethernet.h>
|
||||||
@ -414,6 +415,7 @@ learnIPAddressThread(void *arg)
|
|||||||
bool showError = true;
|
bool showError = true;
|
||||||
enum howDetect howDetected = 0;
|
enum howDetect howDetected = 0;
|
||||||
virNWFilterTechDriverPtr techdriver = req->techdriver;
|
virNWFilterTechDriverPtr techdriver = req->techdriver;
|
||||||
|
struct pollfd fds[1];
|
||||||
|
|
||||||
if (virNWFilterLockIface(req->ifname) < 0)
|
if (virNWFilterLockIface(req->ifname) < 0)
|
||||||
goto err_no_lock;
|
goto err_no_lock;
|
||||||
@ -435,6 +437,9 @@ learnIPAddressThread(void *arg)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fds[0].fd = pcap_fileno(handle);
|
||||||
|
fds[0].events = POLLIN | POLLERR;
|
||||||
|
|
||||||
virMacAddrFormat(&req->macaddr, macaddr);
|
virMacAddrFormat(&req->macaddr, macaddr);
|
||||||
|
|
||||||
if (req->howDetect == DETECT_DHCP) {
|
if (req->howDetect == DETECT_DHCP) {
|
||||||
@ -480,17 +485,45 @@ learnIPAddressThread(void *arg)
|
|||||||
pcap_freecode(&fp);
|
pcap_freecode(&fp);
|
||||||
|
|
||||||
while (req->status == 0 && vmaddr == 0) {
|
while (req->status == 0 && vmaddr == 0) {
|
||||||
|
int n = poll(fds, ARRAY_CARDINALITY(fds), PKT_TIMEOUT_MS);
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EINTR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
req->status = errno;
|
||||||
|
showError = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
if (threadsTerminate || req->terminate) {
|
||||||
|
VIR_DEBUG("Terminate request seen, cancelling pcap");
|
||||||
|
req->status = ECANCELED;
|
||||||
|
showError = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds[0].revents & (POLLHUP | POLLERR)) {
|
||||||
|
VIR_DEBUG("Error from FD probably dev deleted");
|
||||||
|
req->status = ENODEV;
|
||||||
|
showError = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
packet = pcap_next(handle, &header);
|
packet = pcap_next(handle, &header);
|
||||||
|
|
||||||
if (!packet) {
|
if (!packet) {
|
||||||
|
/* Already handled with poll, but lets be sure */
|
||||||
if (threadsTerminate || req->terminate) {
|
if (threadsTerminate || req->terminate) {
|
||||||
req->status = ECANCELED;
|
req->status = ECANCELED;
|
||||||
showError = false;
|
showError = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check whether VM's dev is still there */
|
/* Again, already handled above, but lets be sure */
|
||||||
if (virNetDevValidateConfig(req->ifname, NULL, req->ifindex) <= 0) {
|
if (virNetDevValidateConfig(req->ifname, NULL, req->ifindex) <= 0) {
|
||||||
virResetLastError();
|
virResetLastError();
|
||||||
req->status = ENODEV;
|
req->status = ENODEV;
|
||||||
|
Loading…
Reference in New Issue
Block a user