#include <features.h>
#endif
-#if defined(HAVE_LINUX_ERRQUEUE_H) && defined(HAVE_SETSOCKOPT_IP_RECVERR)
+#if defined(HAVE_LINUX_ERRQUEUE_H) && defined(HAVE_SETSOCKOPT_IP_RECVERR) && !defined(UKERNEL)
# define AFS_RXERRQ_ENV
#endif
#ifdef AFS_RXERRQ_ENV
}
#ifdef AFS_RXERRQ_ENV
-static int
-osi_HandleSocketError(osi_socket so, char *cmsgbuf, size_t cmsgbuf_len)
+int
+osi_HandleSocketError(osi_socket so, void *cmsgbuf, size_t cmsgbuf_len)
{
struct msghdr msg;
struct cmsghdr *cmsg;
}
#endif
-static void
-do_handlesocketerror(osi_socket so)
-{
-#ifdef AFS_RXERRQ_ENV
- char *cmsgbuf;
- size_t cmsgbuf_len;
-
- cmsgbuf_len = 256;
- cmsgbuf = rxi_Alloc(cmsgbuf_len);
- if (!cmsgbuf) {
- return;
- }
-
- while (osi_HandleSocketError(so, cmsgbuf, cmsgbuf_len))
- ;
-
- rxi_Free(cmsgbuf, cmsgbuf_len);
-#endif
-}
-
/* osi_NetSend
*
* Return codes:
code = kernel_sendmsg(sop, &msg, (struct kvec *) iovec, iovcnt, size);
- if (code < 0) {
- do_handlesocketerror(sop);
- }
-
return (code < 0) ? code : 0;
}
rxk_lastSocketError = code;
rxk_nSocketErrors++;
- do_handlesocketerror(so);
+ rxi_HandleSocketErrors(so);
} else {
*lengthp = code;
code = 0;
}
#endif
+#ifdef AFS_RXERRQ_ENV
+void
+rxi_HandleSocketErrors(osi_socket sock)
+{
+ size_t cmsgbuf_len = 256;
+ void *cmsgbuf;
+# ifndef KERNEL
+ int errno_save = errno;
+# endif
+
+ cmsgbuf = rxi_Alloc(cmsgbuf_len);
+ if (cmsgbuf == NULL) {
+ goto done;
+ }
+
+ while (osi_HandleSocketError(sock, cmsgbuf, cmsgbuf_len))
+ ;
+
+ rxi_Free(cmsgbuf, cmsgbuf_len);
+
+ done:
+# ifndef KERNEL
+ errno = errno_save;
+# endif
+ return;
+}
+
+static int
+NetSend_retry(osi_socket sock, void *addr, struct iovec *dvec, int nvecs,
+ int length, int istack)
+{
+ int code;
+ int safety;
+ /*
+ * If an ICMP error comes in for any peer, sendmsg() can return -1 with an
+ * errno of EHOSTUNREACH, ENETUNREACH, etc. There may be no problem with
+ * sending this packet (an error is returned just to indicate we need to
+ * read in pending errors), but the packet wasn't actually sent.
+ *
+ * It's difficult to determine in general whether sendmsg() is returning an
+ * error due to a received ICMP error, or we're getting an actual error for
+ * this specific sendmsg() call, since there may be other threads running
+ * sendmsg/recvmsg/rxi_HandleSocketErrors at the same time. So, just retry
+ * the sendmsg a few times; make sure not to retry forever, in case we are
+ * getting an actual error from this sendmsg() call.
+ *
+ * Also note that if we accidentally drop a packet here that we didn't need
+ * to, it's not the end of the world. Packets get dropped, and we should be
+ * able to recover.
+ */
+ for (safety = 0; safety < RXI_SENDMSG_RETRY; safety++) {
+ code = osi_NetSend(sock, addr, dvec, nvecs, length, istack);
+ if (code == 0) {
+ return 0;
+ }
+ rxi_HandleSocketErrors(sock);
+ }
+ return code;
+
+}
+#endif
+
int
rxi_NetSend(osi_socket socket, void *addr, struct iovec *dvec,
int nvecs, int length, int istack)
{
if (rxi_IsRunning()) {
+#ifdef AFS_RXERRQ_ENV
+ return NetSend_retry(socket, addr, dvec, nvecs, length, istack);
+#else
return osi_NetSend(socket, addr, dvec, nvecs, length, istack);
+#endif
}
#ifdef AFS_NT40_ENV
return WSAESHUTDOWN;
extern rx_atomic_t rx_nWaiting;
extern rx_atomic_t rx_nWaited;
+/* How many times to retry sendmsg()-equivalent calls for AFS_RXERRQ_ENV. */
+#define RXI_SENDMSG_RETRY 8
+
/* Prototypes for internal functions */
/* rx.c */
#ifdef AFS_RXERRQ_ENV
extern void rxi_ProcessNetError(struct sock_extended_err *err,
afs_uint32 addr, afs_uint16 port);
+extern int osi_HandleSocketError(osi_socket sock, void *cmsgbuf,
+ size_t cmsgbuf_len);
+extern void rxi_HandleSocketErrors(osi_socket sock);
+#else
+# define rxi_HandleSocketErrors(sock) do { } while (0)
#endif
extern struct rx_peer *rxi_FindPeer(afs_uint32 host, u_short port,
int create);
int code;
code = recvmsg(socket, msg_p, flags);
-#ifdef AFS_RXERRQ_ENV
if (code < 0) {
- while((rxi_HandleSocketError(socket)) > 0)
- ;
+ rxi_HandleSocketErrors(socket);
}
-#endif
return code;
}
}
FD_SET(socket, sfds);
}
-#ifdef AFS_RXERRQ_ENV
- while((rxi_HandleSocketError(socket)) > 0)
- ;
-#endif
#ifdef AFS_NT40_ENV
if (err)
#elif defined(AFS_LINUX22_ENV)
#endif
extern osi_socket rxi_GetUDPSocket(u_short port);
extern void rxi_InitPeerParams(struct rx_peer *pp);
-extern int rxi_HandleSocketError(int socket);
#if defined(AFS_AIX32_ENV) && !defined(KERNEL)
#ifndef osi_Alloc
int ret;
ret = recvmsg(socket, msg_p, flags);
-#ifdef AFS_RXERRQ_ENV
if (ret < 0) {
- while (rxi_HandleSocketError(socket) > 0)
- ;
+ rxi_HandleSocketErrors(socket);
}
-#endif
return ret;
}
err = errno;
#endif
-#ifdef AFS_RXERRQ_ENV
- while (rxi_HandleSocketError(socket) > 0)
- ;
-#else
+#ifndef AFS_RXERRQ_ENV
# ifdef AFS_LINUX22_ENV
/* linux unfortunately returns ECONNREFUSED if the target port
* is no longer in use */
#ifdef AFS_RXERRQ_ENV
int
-rxi_HandleSocketError(int socket)
+osi_HandleSocketError(int socket, void *cmsgbuf, size_t cmsgbuf_len)
{
struct msghdr msg;
struct cmsghdr *cmsg;
struct sock_extended_err *err;
struct sockaddr_in addr;
- char controlmsgbuf[256];
int code;
msg.msg_name = &addr;
msg.msg_namelen = sizeof(addr);
msg.msg_iov = NULL;
msg.msg_iovlen = 0;
- msg.msg_control = controlmsgbuf;
- msg.msg_controllen = 256;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = cmsgbuf_len;
msg.msg_flags = 0;
code = recvmsg(socket, &msg, MSG_ERRQUEUE|MSG_DONTWAIT|MSG_TRUNC);