On Fri, 4 Jun 2004, Jeff Moyer wrote:
A couple of things ...
>
> jmoyer> I've strace'd the automount process, and a lot of time is spent in
> jmoyer> the clnt_create, for both UDP and the TCP. I implemented a
> jmoyer> non-blocking connect, and it took my ls time down from 8m21s to
> jmoyer> 2m4s. I'm still looking into why the UDP stuff isn't timing out
> jmoyer> sooner.
>
> The clnt_create call does not take a timeout. One of the things it does is
> to contact the remote portmapper to get the port for the specified program
> number. There is a hard-coded default timeout in that code of 60 seconds,
> which explains my 2 minutes.
>
> This patch simply implements an icmp ping (code shamelessly stolen from
> clumanager), and does this before even attempting to do the rpc pings. If
> the icmp ping works, then we continue as usual. This takes care of the
> host down case quite nicely:
>
> One nit: icmp_ping_host takes a char * for the hostname, whereas rpc_ping
> passes a const char *. As such, we get a 'discards qualifier' warning. I
> didn't think it warrented converting the whole call chain to consts, but if
> you're especially concerned about that, Ian, I'd be happy to resubmit.
I've put a cast in to eliminate the warning.
I added the Makefile glue and ...
I noticed that, for the case the request is not a replicated server entry
a mount is still attempted so I added a ping check.
I've attached the complete patch but here is what I added. Can you test
it in your environment before I commit it please:
diff -Nur autofs-4.1.3.orig/lib/Makefile autofs-4.1.3/lib/Makefile
--- autofs-4.1.3.orig/lib/Makefile 2004-03-07 20:17:54.000000000 +0800
+++ autofs-4.1.3/lib/Makefile 2004-06-05 13:13:05.000000000 +0800
@@ -9,9 +9,9 @@
RPCGEN = /usr/bin/rpcgen
RANLIB = /usr/bin/ranlib
-SRCS = cache.c listmount.c cat_path.c rpc_subs.c
+SRCS = cache.c listmount.c cat_path.c rpc_subs.c ping.c
RPCS = mount.h mount_clnt.c mount_xdr.c
-OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o cat_path.o rpc_subs.o
+OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o cat_path.o rpc_subs.o ping.o
LIB = autofs.a
diff -Nur autofs-4.1.3.orig/lib/rpc_subs.c autofs-4.1.3/lib/rpc_subs.c
--- autofs-4.1.3.orig/lib/rpc_subs.c 2004-06-05 13:13:43.000000000 +0800
+++ autofs-4.1.3/lib/rpc_subs.c 2004-06-05 13:13:05.000000000 +0800
@@ -72,7 +72,7 @@
{
unsigned int status;
- status = icmp_ping_host(host, 0, seconds, micros);
+ status = icmp_ping_host((char *) host, 0, seconds, micros);
if (status)
return 0;
diff -Nur autofs-4.1.3.orig/modules/mount_nfs.c autofs-4.1.3/modules/mount_nfs.c
--- autofs-4.1.3.orig/modules/mount_nfs.c 2004-05-18 20:20:08.000000000 +0800
+++ autofs-4.1.3/modules/mount_nfs.c 2004-06-05 13:13:05.000000000 +0800
@@ -142,6 +142,7 @@
while (p && *p) {
char *next;
unsigned int ping_stat = 0;
+ unsigned int status;
p += strspn(p, " \t,");
delim = strpbrk(p, "(, \t:");
@@ -216,13 +217,24 @@
break;
}
- /*
- * If it's not local and it's a replicated server map entry
- * is it alive
- */
- if (!local && is_replicated && !(ping_stat = rpc_ping(p, sec,
micros))) {
- p = next;
- continue;
+ /* If it's not local */
+ if (!local) {
+ /* and it's a replicated server map entry, check
+ * if it's alive and servicing NFS requests */
+ if (is_replicated) {
+ ping_stat = rpc_ping(p, sec, micros);
+ if (!ping_stat) {
+ p = next;
+ continue;
+ }
+ /* otherwise just check if it's alive */
+ } else {
+ status = icmp_ping_host(p, 0, sec, micros);
+ if (status) {
+ *what = '\0';
+ return 0;
+ }
+ }
}
/* see if we have a previous 'winner' */
diff -Nur autofs-4.1.3.orig/include/ping.h autofs-4.1.3/include/ping.h
--- autofs-4.1.3.orig/include/ping.h 1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.3/include/ping.h 2004-06-05 11:00:09.000000000 +0800
@@ -0,0 +1,60 @@
+/*
+ Copyright Red Hat, Inc. 2003
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+ */
+/** @file
+ * Header for ping.c.
+ */
+#ifndef _PING_H
+#define _PING_H
+
+#include <netinet/ip_icmp.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define PING_ERRNO -1
+#define PING_SUCCESS 0
+#define PING_ALIVE PING_SUCCESS
+#define PING_TIMEOUT 1
+#define PING_HOST_UNREACH 2
+#define PING_HOST_NOT_FOUND 3
+#define PING_INVALID_CHECKSUM 4
+#define PING_INVALID_RESPONSE 5
+#define PING_INVALID_SIZE 6
+#define PING_INVALID_ID 7
+
+int32_t icmp_socket(void);
+int32_t icmp_ping_hostfd(int32_t sock, char *hostname, uint32_t seq,
+ long seconds, long micros);
+int32_t icmp_ping_host(char *hostname, uint32_t seq, long seconds,long micros);
+
+int32_t icmp_ping_addrfd(int32_t sock, struct sockaddr_in *sin_send,
+ uint32_t seq, long seconds, long micros);
+int32_t icmp_ping_addr(struct sockaddr_in *sin_send, uint32_t seq,
+ long seconds, long micros);
+
+
+#define net_icmp_close(sock) close(sock)
+
+#endif /* _PING_H */
diff -Nur autofs-4.1.3.orig/lib/Makefile autofs-4.1.3/lib/Makefile
--- autofs-4.1.3.orig/lib/Makefile 2004-05-30 10:35:40.000000000 +0800
+++ autofs-4.1.3/lib/Makefile 2004-06-05 11:03:56.000000000 +0800
@@ -9,9 +9,9 @@
RPCGEN = /usr/bin/rpcgen
RANLIB = /usr/bin/ranlib
-SRCS = cache.c listmount.c cat_path.c rpc_subs.c
+SRCS = cache.c listmount.c cat_path.c rpc_subs.c ping.c
RPCS = mount.h mount_clnt.c mount_xdr.c
-OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o cat_path.o rpc_subs.o
+OBJS = cache.o mount_clnt.o mount_xdr.o listmount.o cat_path.o rpc_subs.o ping.o
LIB = autofs.a
diff -Nur autofs-4.1.3.orig/lib/ping.c autofs-4.1.3/lib/ping.c
--- autofs-4.1.3.orig/lib/ping.c 1970-01-01 08:00:00.000000000 +0800
+++ autofs-4.1.3/lib/ping.c 2004-06-05 11:00:09.000000000 +0800
@@ -0,0 +1,384 @@
+/*
+ Copyright Red Hat, Inc. 2003
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/** @file
+ * Ping functions for Red Hat Cluster Manager (RFC777)
+ *
+ * These functions provide internal ping functionality. These functions are
+ * used to help determine quorum when a two or four node cluster has an
+ * even split.
+ */
+
+#include <ping.h>
+
+/**
+ * From RFC 777:
+ *
+ * [ICMP] Header Checksum
+ * The 16 bit one's complement of the one's complement sum of all 16
+ * bit words in the header. For computing the checksum, the checksum
+ * field should be zero. This checksum may be replaced in the
+ * future.
+ *
+ * @param buf 16-but word array
+ * @param buflen Length (in 8-bit bytes!) of buf
+ * @return ICMP header checksum of buf.
+ */
+uint16_t
+icmp_checksum(uint16_t *buf, uint32_t buflen)
+{
+ uint32_t remain = buflen, sum = 0;
+ char *data = (char *)buf;
+
+ while (remain > 1) {
+ sum += *((uint16_t *)data);
+ data += 2;
+ remain -= 2;
+ }
+
+ /*
+ * Clean up the last byte (if there is one)
+ */
+ if (remain)
+ sum += *data;
+
+ sum = (sum>>16)+(sum&0xffff);
+ sum += (sum>>16);
+
+ return (uint16_t)((~sum)&0xffff);
+}
+
+
+/**
+ * Set up an ICMP socket (basically, a raw socket). This requires root
+ * privileges.
+ *
+ * @return See socket(2).
+ */
+int32_t
+icmp_socket(void)
+{
+ return socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+}
+
+
+/**
+ * Call gethostbyname and return an appropriate PING return value based on
+ * the response we get. Fill in sin_send.
+ *
+ * @param hostname Hostname to look up.
+ * @param sin_send IP address structure (pre-allocated).
+ * @return PING_HOST_NOT_FOUND if the host is not found, 0
+ * on success, -1 on other error.
+ * @see gethostbyname
+ */
+int32_t
+icmp_ping_getaddr(char *hostname, struct sockaddr_in *sin_send)
+{
+ struct hostent *hp;
+
+ /*
+ * Grab the hostname
+ */
+ while ((hp = gethostbyname(hostname)) == NULL) {
+ switch(h_errno) {
+ case TRY_AGAIN:
+ continue;
+ case HOST_NOT_FOUND:
+ case NO_ADDRESS:
+ case NO_RECOVERY:
+ return PING_HOST_NOT_FOUND;
+ }
+ return -1;
+ }
+
+ /*
+ * Set up send address
+ */
+ memset(sin_send, 0, sizeof(*sin_send));
+ sin_send->sin_family = AF_INET;
+ sin_send->sin_port = 0;
+ memcpy(&sin_send->sin_addr, hp->h_addr, sizeof(sin_send->sin_addr));
+
+ return 0;
+}
+
+
+/**
+ * Send a ping (ICMP_ECHO) to a given IP address and file descriptor.
+ * This is set up so that a daemon can drop privileges after binding to a raw
+ * socket (perhaps preserving a static ping socket), but still be able to use
+ * ping. This is a pretty long function.
+ *
+ * @param sock Socket to send on.
+ * @param sin_send Address to send to.
+ * @param seq Sequence number.
+ * @param timeout Timeout (in seconds)
+ * @return -1 on syscall error, 0 on success.
+ * See ping.h for list of return values >0.
+ * @see icmp_ping_host icmp_ping_hostfd icmp_ping_addr
+ */
+int32_t
+icmp_ping_addrfd(int32_t sock, struct sockaddr_in *sin_send, uint32_t seq,
+ long seconds, long micros)
+{
+ char buffer[256]; /* XXX */
+ struct icmp *packetp;
+ struct ip *ipp;
+ struct sockaddr_in sin_recv;
+ struct timeval tv;
+ fd_set rfds;
+ uint16_t checksum;
+ uint32_t x, sin_recv_len = sizeof(sin_recv);
+ int timeout = (seconds || micros);
+
+ /*
+ * Set up ICMP echo packet
+ */
+ packetp = (struct icmp *)buffer;
+
+ memset(packetp, 0, ICMP_MINLEN);
+ packetp->icmp_type = ICMP_ECHO;
+ packetp->icmp_seq = seq;
+ packetp->icmp_id = getpid();
+ packetp->icmp_cksum = icmp_checksum((uint16_t *)packetp,
+ ICMP_MINLEN);
+
+ /*
+ * Send the packet
+ */
+ while ((x = sendto(sock, packetp, ICMP_MINLEN, 0,
+ (struct sockaddr *)sin_send,
+ sizeof(*sin_send))) < ICMP_MINLEN)
+ if (x < 0)
+ return -1;
+
+ if (timeout) {
+ tv.tv_sec = seconds;
+ tv.tv_usec = micros;
+ }
+
+ /*
+ * Wait for response
+ */
+ while (1) {
+
+ /*
+ * Set up select call...
+ */
+ FD_ZERO(&rfds);
+ FD_SET(sock,&rfds);
+ while ((x = select(sock+1, &rfds, NULL, NULL,
+ timeout ? &tv : NULL)) <= 0) {
+ if (!x)
+ return PING_TIMEOUT;
+ return -1;
+ }
+
+ /*
+ * Receive response
+ */
+ if ((x = recvfrom(sock, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&sin_recv,
+ &sin_recv_len)) < 0) {
+ return -1;
+ }
+
+ /*
+ * Ensure it's the proper size...
+ * - (ipp->ip_hl << 2) is because IP header length is in
+ * 32-bit words instead of bytes.
+ * - ICMP_MINLEN is defined in netinet/ip_icmp.h.
+ */
+ ipp = (struct ip *)buffer;
+ if (x < ((ipp->ip_hl << 2) + ICMP_MINLEN)) {
+ if (timeout)
+ continue;
+ return PING_INVALID_SIZE;
+ }
+
+ /*
+ * Validate the checksum. The checksum needs to be set to
+ * 0 for validation purposes.
+ */
+ packetp = (struct icmp *)((buffer + (ipp->ip_hl << 2)));
+ checksum = packetp->icmp_cksum;
+ packetp->icmp_cksum = 0;
+ if (checksum != icmp_checksum((uint16_t*)packetp,
+ ICMP_MINLEN)) {
+ if (timeout)
+ continue;
+ return PING_INVALID_CHECKSUM;
+ }
+
+ /*
+ * Ensure it's the proper id...
+ */
+ switch (packetp->icmp_type) {
+ case ICMP_ECHOREPLY:
+ if (packetp->icmp_id != getpid()) {
+ if (timeout)
+ continue;
+ return PING_INVALID_ID;
+ }
+ return PING_SUCCESS;
+ case ICMP_DEST_UNREACH:
+ return PING_HOST_UNREACH;
+ }
+
+ /* XXX */
+ return PING_INVALID_RESPONSE;
+ }
+}
+
+
+/**
+ * Send a ping (ICMP_ECHO) to a given IP address. This is set up so that a
+ * daemon can drop privileges after binding to a raw socket (perhaps
+ * preserving a static ping socket), but still be able to use ping calls.
+ *
+ * @param sock Socket to send on
+ * @param hostname Host name to ping
+ * @param seq Sequence number.
+ * @param timeout Timeout (in seconds)
+ * @return -1 on syscall error, 0 on success.
+ * See ping.h for list of return values >0.
+ * @see icmp_ping_getaddr icmp_ping_addrfd icmp_ping
+ */
+int32_t
+icmp_ping_hostfd(int32_t sock, char *hostname, uint32_t seq,
+ long seconds, long micros)
+{
+ struct sockaddr_in sin_send;
+ int rv;
+
+ rv = icmp_ping_getaddr(hostname, &sin_send);
+ if (rv)
+ return rv;
+
+ return icmp_ping_addrfd(sock, &sin_send, seq, seconds, micros);
+}
+
+
+/**
+ * Send a ping (ICMP_ECHO) to a given IP address.
+ *
+ * @param sin_send Address we want to send to.
+ * @param seq Sequence number (user defined)
+ * @param timeout Timeout (in seconds)
+ * @return -1 on syscall error, 0 on success.
+ * See ping.h for list of return values >0.
+ * @see icmp_ping_addrfd icmp_socket icmp_ping_host icmp_ping_hostfd
+ */
+int32_t
+icmp_ping_addr(struct sockaddr_in *sin_send, uint32_t seq,
+ long seconds, long micros)
+{
+ int sock, rv, esv;
+
+ sock = icmp_socket();
+ if (sock < 0)
+ return -1;
+
+ rv = icmp_ping_addrfd(sock, sin_send, seq, seconds, micros);
+
+ esv = errno;
+ close(sock);
+ errno = esv;
+
+ return rv;
+}
+
+
+/**
+ * Send a ping (ICMP_ECHO) to a given hostname.
+ *
+ * @param hostname Target host
+ * @param seq Sequence number (user defined)
+ * @param timeout Timeout (in seconds)
+ * @return -1 on syscall error, 0 on success.
+ * See ping.h for list of return values >0.
+ * @see icmp_socket icmp_ping_hostfd icmp_ping_addr icmp_ping_addrfd
+ */
+int32_t
+icmp_ping_host(char *hostname, uint32_t seq, long seconds, long micros)
+{
+ uint32_t rv, sock, esv;
+
+ sock = icmp_socket();
+ if (sock == -1)
+ return -1;
+
+ rv = icmp_ping_hostfd(sock, hostname, seq, seconds, micros);
+
+ esv = errno;
+ close(sock);
+ errno = esv;
+
+ return rv;
+}
+
+
+#ifdef STANDALONE
+int
+main(int argc, char **argv)
+{
+ int timeout = 0, rv;
+
+ if (argc < 2) {
+ printf("usage: %s <host> [timeout]\n", argv[0]);
+ return 2;
+ }
+
+ if (argc == 3)
+ timeout = atoi(argv[2]);
+
+ rv = icmp_ping_host(argv[1], 1, timeout);
+
+ switch(rv) {
+ case PING_ERRNO:
+ perror("icmp_ping_host");
+ break;
+ case PING_SUCCESS:
+ printf("%s is alive\n", argv[1]);
+ break;
+ case PING_TIMEOUT:
+ printf("%s timed out\n", argv[1]);
+ break;
+ case PING_HOST_UNREACH:
+ printf("%s is unreachable\n", argv[1]);
+ break;
+ case PING_HOST_NOT_FOUND:
+ printf("Host %s not found!\n", argv[1]);
+ break;
+ case PING_INVALID_CHECKSUM:
+ printf("Invalid checksum in reply.\n");
+ break;
+ case PING_INVALID_SIZE:
+ printf("Invalid size of reply packet.\n");
+ break;
+ case PING_INVALID_RESPONSE:
+ printf("Invalid response.\n");
+ break;
+ case PING_INVALID_ID:
+ printf("Invalid ID in response.\n");
+ break;
+ }
+ return rv;
+}
+#endif
diff -Nur autofs-4.1.3.orig/lib/rpc_subs.c autofs-4.1.3/lib/rpc_subs.c
--- autofs-4.1.3.orig/lib/rpc_subs.c 2004-05-18 20:20:08.000000000 +0800
+++ autofs-4.1.3/lib/rpc_subs.c 2004-06-05 12:47:13.000000000 +0800
@@ -5,6 +5,7 @@
#include <rpc/xdr.h>
#include "automount.h"
+#include "ping.h"
static int rpc_ping_proto(const char *host,
unsigned long nfs_version, const char *proto,
@@ -71,6 +72,10 @@
{
unsigned int status;
+ status = icmp_ping_host((char *) host, 0, seconds, micros);
+ if (status)
+ return 0;
+
status = rpc_ping_v2(host, seconds, micros);
if (status)
return status;
@@ -114,4 +119,3 @@
return status;
}
-
diff -Nur autofs-4.1.3.orig/modules/mount_nfs.c autofs-4.1.3/modules/mount_nfs.c
--- autofs-4.1.3.orig/modules/mount_nfs.c 2004-05-30 11:25:07.000000000 +0800
+++ autofs-4.1.3/modules/mount_nfs.c 2004-06-05 12:48:05.000000000 +0800
@@ -142,6 +142,7 @@
while (p && *p) {
char *next;
unsigned int ping_stat = 0;
+ unsigned int status;
p += strspn(p, " \t,");
delim = strpbrk(p, "(, \t:");
@@ -216,13 +217,24 @@
break;
}
- /*
- * If it's not local and it's a replicated server map entry
- * is it alive
- */
- if (!local && is_replicated && !(ping_stat = rpc_ping(p, sec,
micros))) {
- p = next;
- continue;
+ /* If it's not local */
+ if (!local) {
+ /* and it's a replicated server map entry, check
+ * if it's alive and servicing NFS requests */
+ if (is_replicated) {
+ ping_stat = rpc_ping(p, sec, micros);
+ if (!ping_stat) {
+ p = next;
+ continue;
+ }
+ /* otherwise just check if it's alive */
+ } else {
+ status = icmp_ping_host(p, 0, sec, micros);
+ if (status) {
+ *what = '\0';
+ return 0;
+ }
+ }
}
/* see if we have a previous 'winner' */
_______________________________________________
autofs mailing list
[EMAIL PROTECTED]
http://linux.kernel.org/mailman/listinfo/autofs