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

Reply via email to