Subject: Bug#3731: snmpd: patch for hrProcessorLoad MIB variable

From: Patrick Weemeeuw <[email-masked]>
Date: Tue, 9 Jul 1996 16:29:36 +0200

Package: snmp
Version: 2.1.2l4-1

The hrProcessorLoad MIB variable is not computed correctly in snmp
2.1.2l4: the value returned is max(loadavg*100, 100).  Under linux, we
can compute the correct value by regurarly polling the idle jiffies
counter available in /proc/stat.  The patch below adds a signal
handler to correctly keep track of the cpu load over the last minute.

Patrick Weemeeuw, network manager
K.U.Leuven, KULeuvenNet, currently at the Dept. of Computer Science
	Celestijnenlaan 200 A, B-3001 Leuven, Belgium
Tel: +32 16 327635 Fax: +32 16 327996
E-mail: [email-masked]
PGP key: ftp://ftp.kulnet.kuleuven.ac.be/pub/people/patrick/pgpkey.asc

======================================================================
diff --recursive -u --new-file snmp-2.1.2l4/agent/Makefile.in snmp-2.1.2l4.patched/agent/Makefile.in
--- snmp-2.1.2l4/agent/Makefile.in	Tue Jul 25 00:53:35 1995
+++ snmp-2.1.2l4.patched/agent/Makefile.in	Tue Jul  9 13:46:00 1996
@@ -14,9 +14,9 @@
 
 snmpd:	snmpd.o kernel.o snmp_agent.o snmp_vars.o snmp_var_route.o \
 	party_vars.o context_vars.o acl_vars.o view_vars.o view_parse.o \
-	alarm.o event.o route_write.o ${LIBS}
+	alarm.o event.o route_write.o hr_processor_load.o ${LIBS}
 	rm -f snmpd || true
-	${CC} -o $@ snmpd.o kernel.o snmp_agent.o snmp_vars.o snmp_var_route.o party_vars.o context_vars.o acl_vars.o view_vars.o view_parse.o alarm.o event.o route_write.o  ${LPATH}
+	${CC} -o $@ snmpd.o kernel.o snmp_agent.o snmp_vars.o snmp_var_route.o party_vars.o context_vars.o acl_vars.o view_vars.o view_parse.o alarm.o event.o route_write.o hr_processor_load.o ${LPATH}
 
 sabersnmpd:	snmpd.c kernel.o snmp_agent.c snmp_vars.o snmp_var_route.o party_vars.c context_vars.c acl_vars.c view_vars.c view_parse.c ../snmplib/acl.c ../snmplib/acl_parse.o ../snmplib/asn1.c ../snmplib/md5.o ../snmplib/mib.o ../snmplib/parse.o ../snmplib/party.c ../snmplib/context.c ../snmplib/party_parse.o ../snmplib/context_parse.o ../snmplib/snmp.c ../snmplib/snmp_api.c ../snmplib/snmp_auth.c ../snmplib/snmp_client.o ../snmplib/view.c alarm.c event.c route_write.c ../snmplib/system.o ../lib/libdes.a /lib/libc.a
 	#load $(CFLAGS) snmpd.c kernel.o snmp_agent.c snmp_vars.o snmp_var_route.o party_vars.c context_vars.c acl_vars.c view_vars.c view_parse.c ../snmplib/acl.c ../snmplib/acl_parse.o ../snmplib/asn1.c ../snmplib/md5.o ../snmplib/mib.o ../snmplib/parse.o ../snmplib/party.c ../snmplib/context.c ../snmplib/party_parse.o ../snmplib/context_parse.o ../snmplib/snmp.c ../snmplib/snmp_api.c ../snmplib/snmp_auth.c ../snmplib/snmp_client.o ../snmplib/view.c alarm.c event.c route_write.c ../snmplib/system.o ../lib/libdes.a /lib/libc.a
@@ -38,4 +38,4 @@
 alarm.c:  ../snmplib/party.h
 event.c:  ../snmplib/party.h
 snmpd.c:  ../snmplib/party.h
-
+hr_processor_load.o: hr_processor_load.h
diff --recursive -u --new-file snmp-2.1.2l4/agent/hr_processor_load.c snmp-2.1.2l4.patched/agent/hr_processor_load.c
--- snmp-2.1.2l4/agent/hr_processor_load.c	Thu Jan  1 01:00:00 1970
+++ snmp-2.1.2l4.patched/agent/hr_processor_load.c	Tue Jul  9 13:46:00 1996
@@ -0,0 +1,120 @@
+/*
+  Linux specific code to maintain and compute the hrProcessorLoad
+  MIB value.
+
+  Copyright (C) Patrick Weemeeuw, 1996.
+  
+  This code is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published
+  by the Free Software Foundation; either version 2 of the License, or (at
+  your option) any later version.
+     
+  This library 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
+  Library General Public License for more details.
+     
+  You should have received a copy of the GNU Library General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+   
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "hr_processor_load.h"
+
+#define NROFSAMPLES 12		/* number of samples per minute;
+				   minimum 2 */
+
+static int interval = 60/NROFSAMPLES;
+static long int idleJiffies[NROFSAMPLES+1];
+				/* cyclic array with idle jiffies sampled */
+static time_t sampleTimes[NROFSAMPLES+1]; /* time of each sample */
+static int index;		/* index of the most recent sample */
+
+static void update_HrProcessorLoad();
+
+static long int get_IdleJiffies()
+{
+  FILE *in;
+  long int user, nice, system, idle;
+
+  if((in = fopen("/proc/stat", "r")))
+    {
+      (void) fscanf (in, "cpu %ld %ld %ld %ld", &user, &nice, &system, &idle);
+      fclose (in);
+      return idle;
+    }
+  else
+    {
+      fprintf (stderr, "testhandler: cannot open /proc/stat - please make sure /proc is mounted.\n");
+      exit(1);
+    }
+}
+
+void init_HrProcessorLoad()
+{
+  int i;
+  time_t t = time(0);
+  long int ij = get_IdleJiffies();
+  struct sigaction action;
+    
+  for(i=0;i<=NROFSAMPLES;i++)
+    {
+      idleJiffies[i] = ij;
+      sampleTimes[i] = t;
+    }
+  index = 0;
+
+  action.sa_handler = update_HrProcessorLoad;
+  sigemptyset(&action.sa_mask);
+  action.sa_flags = 0;
+  if(sigaction(SIGALRM, &action, NULL) != 0) {
+    perror(NULL);
+    exit(1);
+  }
+  (void) alarm(interval);	/* schedule first timer interrupt */
+}
+
+static void update_HrProcessorLoad()
+{
+  int oldmask;
+  int cpuLoad;
+  int i;
+
+  index = (index+1)%(NROFSAMPLES+1);
+  sampleTimes[index] = time(0);
+  idleJiffies[index] = get_IdleJiffies();
+
+  /* reschedule for next interrupt */
+  (void) alarm(interval);
+}
+
+int get_HrProcessorLoad()
+{
+  long int elapsedJiffies, nonIdleJiffies;
+  sigset_t currentBlocked, previousBlocked;
+  int oldestIndex = (index+1)%(NROFSAMPLES+1);
+
+  /* prevent signal handler from clobbering the data structures */
+  (void) sigemptyset(&currentBlocked);
+  (void) sigaddset(&currentBlocked, SIGALRM);
+  sigprocmask(SIG_BLOCK, &currentBlocked, &previousBlocked);
+
+  /* actual computation */
+  elapsedJiffies = (sampleTimes[index] - sampleTimes[oldestIndex])*100;
+  nonIdleJiffies =
+    elapsedJiffies - (idleJiffies[index] - idleJiffies[oldestIndex]);
+
+  /* restore signal mask */
+  sigprocmask(SIG_SETMASK, &previousBlocked, NULL);
+  
+  /*
+    printf("non idle: %d elapsed: %d\n", nonIdleJiffies, elapsedJiffies);
+   */
+  return (int) (100 * nonIdleJiffies / elapsedJiffies + 0.5);
+}
diff --recursive -u --new-file snmp-2.1.2l4/agent/hr_processor_load.h snmp-2.1.2l4.patched/agent/hr_processor_load.h
--- snmp-2.1.2l4/agent/hr_processor_load.h	Thu Jan  1 01:00:00 1970
+++ snmp-2.1.2l4.patched/agent/hr_processor_load.h	Tue Jul  9 13:46:00 1996
@@ -0,0 +1,11 @@
+
+#ifndef HR_PROCESSOR_LOAD_H
+
+#define HR_PROCESSOR_LOAD_H
+
+extern void init_HrProcessorLoad(); /* initialise data structures,
+				      adds signal handler
+				    */
+extern int get_HrProcessorLoad(); /* return current load */
+
+#endif
diff --recursive -u --new-file snmp-2.1.2l4/agent/snmp_vars.c snmp-2.1.2l4.patched/agent/snmp_vars.c
--- snmp-2.1.2l4/agent/snmp_vars.c	Sun Aug 20 23:02:21 1995
+++ snmp-2.1.2l4.patched/agent/snmp_vars.c	Tue Jul  9 15:30:22 1996
@@ -31,6 +31,13 @@
  * ([email-masked]) 1994/1995.
  */
 
+/*
+ * Small Linux specific patch by Patrick Weemeeuw
+ * ([email-masked])
+ * to keep track of HrProcessorLoad more accurately.
+ * -- 8 July 1996
+ */
+
 #define USE_NAME_AS_DESCRIPTION /*"se0" instead of text */
 #define GATEWAY			/* MultiNet is always configured this way! */
 #include <stdio.h>
@@ -99,6 +106,7 @@
 
 #ifdef linux
 
+#include "hr_processor_load.h"
 #include <pwd.h>
 
 #include <sys/time.h>
@@ -3671,6 +3679,11 @@
 	    return (char *) nulloid;
 	}
 	else {
+#ifdef linux
+	  *var_len = sizeof(long);
+	  long_return = get_HrProcessorLoad();
+	  return (char *) &long_return;
+#else
 	    /* guess a processorload from the avenrun index (over the
                last minute): */
 	    { FILE *in = fopen ("/proc/loadavg", "r");
@@ -3684,6 +3697,7 @@
 	      *var_len = sizeof(long);
 	      return (char *) &long_return;
 	    }
+#endif
 	}
 
     } else if (vp->magic <= HRFSLASTPARTIALBACKUPDATE) {
diff --recursive -u --new-file snmp-2.1.2l4/agent/snmpd.c snmp-2.1.2l4.patched/agent/snmpd.c
--- snmp-2.1.2l4/agent/snmpd.c	Fri Aug 25 10:43:59 1995
+++ snmp-2.1.2l4.patched/agent/snmpd.c	Tue Jul  9 13:46:00 1996
@@ -32,6 +32,13 @@
  * enhancements.
  */
 
+/*
+ * Small Linux specific patch by Patrick Weemeeuw
+ * ([email-masked])
+ * to keep track of HrProcessorLoad more accurately.
+ * -- 8 July 1996
+ */
+
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <stdio.h>
@@ -62,6 +69,9 @@
 #include "snmp_groupvars.h"
 #include "snmp_vars.h"
 #include "event.h"
+#ifdef linux
+#include "hr_processor_load.h"
+#endif
 
 extern int  errno;
 #if 0
@@ -460,6 +470,10 @@
 
     init_snmp();
     init_mib();
+#ifdef linux
+    init_HrProcessorLoad();	/* starts signal handler to keep track of
+				   cpu load */
+#endif
     if (read_party_database(party_conf ()) > 0){
 	fprintf(stderr, "Couldn't read party database from %s\n",
 		party_conf ());

---

From: Ian Jackson <[email-masked]>
Date: Tue, 16 Jul 96 13:50 BST

Patrick Weemeeuw writes ("Bug#3731: snmpd: patch for hrProcessorLoad MIB variable"):
...
> handler to correctly keep track of the cpu load over the last minute.

Is this really such a good idea ?  It makes the program have to wake
up 5 or 10 times a minute to do the calculation.

Ian.

---

From: Patrick Weemeeuw <[email-masked]>
Date: Wed, 17 Jul 1996 08:21:46 +0200

Date: Tue, 16 Jul 96 13:50 BST
   From: Ian Jackson <[email-masked]>
   Newsgroups: chiark.mail.debian.devel
   References: <[email-masked]>

   Ian.

Well, the original code is simply not correct, and useless, as one
background job (e.g. a backup program that is IO bound) is enough to
get de loadavg above 1, while the cpuload may be only 3%.  On the other
hand, if implemented correctly, this metric is very usable, as a cpu load
of 100% typically indicates a runaway process on one of my dedicated
server machines (which run all IO bound services).

The host resources MIB describes hrProcessorLoad as "The average, over
the last minute, of the percentage of time that this processor was not
idle."  IMHO, this doesn't leave much room for interpretation.

However, your point is worth raising, as this is one of my areas of
concern as well; but I simply don't see any better method of obtaining
this value than polling twice per minute.  [polling 2 times per minute
should be sufficient; my patch polls 12 times, which is overkill meant
only for debugging purposes.]

Patrick.