Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 26 Jul 2005 19:07:23 GMT
From:      Victor Cruceru <soc-victor@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 81019 for review
Message-ID:  <200507261907.j6QJ7Nsr052523@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=81019

Change 81019 by soc-victor@soc-victor_82.76.158.176 on 2005/07/26 19:07:03

	1)Added support for periodic timers (beside the existent one-shot timers)
	 into the SNMP library (libbsnmp, -lbsnmp).
	2)Adapted the instrumentation for hrProcessorTable so as the CPU load numbers 
	represent the average CPU load (per CPU) in the last minute ( via 4 samples per 
	minute ). This was implemented using a periodic timer.
	3)Changed the implementation of hrDeviceTable so as the SNMP agent get the 
	device tree change notifications via  /var/run/devd.pipe (if available).  
	This should reduce the overhead of scanning the system for device changes
	 to the minimum required.

Affected files ...

.. //depot/projects/soc2005/bsnmp/contrib/bsnmp/snmpd/main.c#2 edit
.. //depot/projects/soc2005/bsnmp/contrib/bsnmp/snmpd/snmpd.h#2 edit
.. //depot/projects/soc2005/bsnmp/contrib/bsnmp/snmpd/snmpmod.h#2 edit
.. //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile#13 edit
.. //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c#5 edit
.. //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c#3 edit
.. //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c#11 edit
.. //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h#15 edit

Differences ...

==== //depot/projects/soc2005/bsnmp/contrib/bsnmp/snmpd/main.c#2 (text+ko) ====

@@ -1621,21 +1621,27 @@
 #endif
 {
 	struct timer *tp = uap;
-
-	LIST_REMOVE(tp, link);
+	if (tp->periodic == 0){
+		LIST_REMOVE(tp, link);
+	}
+	
 	tp->func(tp->udata);
-	free(tp);
+	
+	if (tp->periodic == 0){
+		free(tp);
+	}
 }
 
 /*
  * Start a timer
  */
 void *
-timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod)
+timer_start(u_int ticks, u_int repeat_ticks, void (*func)(void *), void *udata, struct lmodule *mod)
 {
 	struct timer *tp;
 #ifndef USE_LIBBEGEMOT
 	struct timespec due;
+	struct timespec inter;
 #endif
 
 	if ((tp = malloc(sizeof(struct timer))) == NULL) {
@@ -1646,21 +1652,26 @@
 #ifndef USE_LIBBEGEMOT
 	due = evAddTime(evNowTime(),
 	    evConsTime(ticks / 100, (ticks % 100) * 10000));
+	inter = evAddTime(evNowTime(),
+	    evConsTime(repeat_ticks / 100, (ticks % 100) * 10000));
+	    
 #endif
 
 	tp->udata = udata;
 	tp->owner = mod;
 	tp->func = func;
-
+	tp->periodic = (repeat_ticks == 0 ? 0 : 1); /*for clarity*/
+	
 	LIST_INSERT_HEAD(&timer_list, tp, link);
 
 #ifdef USE_LIBBEGEMOT
-	if ((tp->id = poll_start_timer(ticks * 10, 0, tfunc, tp)) < 0) {
+	if ((tp->id = poll_start_timer( (repeat_ticks == 0 ? ticks * 10 : repeat_ticks *  10), 
+					tp->periodic, tfunc, tp)) < 0) {
 		syslog(LOG_ERR, "cannot set timer: %m");
 		exit(1);
 	}
 #else
-	if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id)
+	if (evSetTimer(evctx, tfunc, tp, due, inter, &tp->id)
 	    == -1) {
 		syslog(LOG_ERR, "cannot set timer: %m");
 		exit(1);

==== //depot/projects/soc2005/bsnmp/contrib/bsnmp/snmpd/snmpd.h#2 (text+ko) ====

@@ -92,6 +92,8 @@
 	void	*udata;		/* user data */
 	evTimerID id;		/* timer id */
 	struct lmodule *owner;	/* owner of the timer */
+	int	periodic;	/* flag to track periodic timers, 0 for one shot,
+				 1 for periodic timers*/	
 	LIST_ENTRY(timer) link;
 };
 

==== //depot/projects/soc2005/bsnmp/contrib/bsnmp/snmpd/snmpmod.h#2 (text+ko) ====

@@ -300,7 +300,7 @@
 /*
  * Timers.
  */
-void *timer_start(u_int, void (*)(void *), void *, struct lmodule *);
+void *timer_start(u_int, u_int ,void (*)(void *), void *, struct lmodule *);
 void timer_stop(void *);
 
 /*

==== //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile#13 (text+ko) ====


==== //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c#5 (text+ko) ====

@@ -36,6 +36,11 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <err.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
 
 /*just a prototype*/
 int hr_device_collector(struct devinfo_dev *dev, void *arg); 
@@ -289,7 +294,7 @@
 
 	struct hrDeviceTblEntry *entry = NULL, *entry_tmp = NULL;
 	
-	if ( this_tick <= hrState_g.hr_device_tick) {
+	if ( hrState_g.devd_sock < 0 && this_tick <= hrState_g.hr_device_tick) {
 		HR_DPRINTF((stderr, "%s: no refresh needed\n ",__func__));
 		return;
 	}
@@ -341,6 +346,65 @@
 }
 
 
+int create_devd_socket(void) {
+	static const char devd_pipe_name[]="/var/run/devd.pipe";
+	int d_sock = -1;
+ 	struct sockaddr_un devd_addr;
+ 
+ 	bzero(&devd_addr, sizeof(struct sockaddr_un));
+ 
+ 	if ((d_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ 		syslog(LOG_ERR,"Failed to create the socket for %s: %m", devd_pipe_name);
+ 		return (-1);
+ 	}
+ 
+ 	devd_addr.sun_family = PF_LOCAL;
+ 	strlcpy(devd_addr.sun_path, devd_pipe_name, sizeof(devd_addr.sun_path) - 1);
+ 
+ 	if (connect(d_sock, (struct sockaddr *)&devd_addr, 
+ 	    sizeof(struct sockaddr_un)) == -1) {
+ 		syslog(LOG_ERR,"Failed to connect the socket for %s: %m", devd_pipe_name);
+ 		if (close(d_sock) < 0 ){
+ 			syslog(LOG_ERR,"Failed to close the socket for %s: %m", devd_pipe_name);
+ 		}
+		return (-1);
+ 	}
+ 
+ 	return d_sock;
+}
+
+void devd_socket_callback(int fd , void* arg __unused) {
+	char buf[512];
+	int read_len = -1;
+	assert(fd == hrState_g.devd_sock);
+	HR_DPRINTF((stderr, "__hrDeviceTable__ %s: called\n ", __func__));
+	read_len = read(fd, buf, sizeof(buf) - 1);
+	if (read_len < 0) {
+		if(errno == EBADF){
+			hrState_g.devd_sock = -1;
+			if (hrState_g.devd_fd != NULL) {
+				fd_deselect(hrState_g.devd_fd);
+				hrState_g.devd_fd = NULL;
+			}			
+			syslog(LOG_ERR,"Closing devd_fd, revert to devinfo polling");
+		}
+		
+	} else if (read_len == 0) {
+		syslog(LOG_ERR,"zero bytes read from devd pipe....");
+	} else {
+		switch(buf[0]){
+			case '+':
+			case '-':
+			case '?':
+				refresh_hrDevice_tbl_v();
+				return;
+			default:
+				syslog(LOG_ERR,"unknown message read from devd socket");
+			
+		}
+	}
+}
+
 /*
  * This is the implementation for a generated (by a SNMP tool) 
  * function prototype, see hostres_tree.h 
@@ -359,7 +423,8 @@
 /*
 	refresh entries here?!	
 */
-	if ( (time(NULL) - hrState_g.hrDevice_tbl_age) > HR_DEVICE_TBL_REFRESH ) {
+	if ( hrState_g.devd_sock < 0 && 
+	     (time(NULL) - hrState_g.hrDevice_tbl_age) > HR_DEVICE_TBL_REFRESH ) {
 		HR_DPRINTF((stderr, "__hrDeviceTable__ %s: need refresh\n ", __func__));
 		refresh_hrDevice_tbl_v();
 	}

==== //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c#3 (text+ko) ====

@@ -43,6 +43,20 @@
 #include <sys/user.h>
 #include <math.h>
 
+static 
+int get_avg_load( struct hrProcessorTblEntry *entry ) {
+	int i = 0;
+	double sum = 0.0;
+	assert(entry != NULL);
+	for( i = 0; i < MAX_CPU_SAMPLES; i++ ){
+		sum += entry->samples[i];
+	}
+	return floor((double)sum/(double)MAX_CPU_SAMPLES);
+	
+	
+}
+
+
 static
 struct hrProcessorTblEntry *
 hrProcessorTblEntry_find_by_cpu_no(u_char cpu_no) {
@@ -119,6 +133,9 @@
 			entry = hrProcessorTblEntry_find_by_cpu_no(kp->ki_lastcpu);
 			
 			assert(entry != NULL); /*what? FIX ME!*/
+			if (entry == NULL) {
+				continue;
+			}
 			
 			entry->idle_pid = kp->ki_pid;
 			
@@ -128,7 +145,10 @@
 				entry->idle_pid));
 				
 
-			entry->load = floor((double)100.0 - hrProcessor_getpcpu(kp));	
+			entry->samples[entry->cur_sample_idx] = (double)100.0 - hrProcessor_getpcpu(plist);
+			/*this is fisrt time, thus no previous samples*/
+			entry->load = floor(entry->samples[entry->cur_sample_idx]); 
+			entry->cur_sample_idx = (entry->cur_sample_idx + 1) % MAX_CPU_SAMPLES;
 			
 		}
 		
@@ -175,6 +195,7 @@
 			entry->cpu_no = (u_char)cpu_no; 
 			entry->idle_pid = 0;
 			entry->frwId = oid_zeroDotZero; /*unknown id - FIX ME*/
+
 			INSERT_OBJECT_INT(entry, &hrState_g.hr_processor_tbl);
 			
 			hrState_g.detected_processor_count++;
@@ -188,7 +209,7 @@
 	HR_DPRINTF((stderr, "%s: %d CPUs detected\n", __func__, 
 		hrState_g.detected_processor_count));
 
-	hrProcessor_get_pids_v();				
+	hrProcessor_get_pids_v();	
 	 
 }
 
@@ -245,6 +266,12 @@
 }
 
 
+void get_cpus_samples(void* arg __unused) {
+	
+	HR_DPRINTF((stderr, "[hrProcessorTable] [%llu]: ENTER\n ", get_ticks()));
+	refresh_hrProcessor_tbl_v();	
+	HR_DPRINTF((stderr, "[hrProcessorTable] [%llu]: EXIT\n ", get_ticks() ));	
+}
 
 
 void refresh_hrProcessor_tbl_v(void) {
@@ -273,7 +300,9 @@
 			need_pids = 1;					 
 			continue;
 		}
-		entry->load = floor((double)100.0 - hrProcessor_getpcpu(plist));
+		entry->samples[entry->cur_sample_idx] = (double)100.0 - hrProcessor_getpcpu(plist);
+		entry->load = get_avg_load(entry);
+		entry->cur_sample_idx = (entry->cur_sample_idx + 1) % MAX_CPU_SAMPLES;
 		
 	}
 	if (need_pids == 1) {
@@ -295,14 +324,6 @@
 	struct hrProcessorTblEntry *entry =  NULL;
 	int ret =  SNMP_ERR_NOERROR;
 	
-	if ( (time(NULL) - hrState_g.hrProcessor_tbl_age) > HR_PROCESSOR_TBL_REFRESH ) {
-		HR_DPRINTF((stderr, "__hrProcessorTable__ %s: need refresh\n ", __func__));
-		refresh_hrProcessor_tbl_v();
-	}
-
-
-
-	
 	switch (curr_op) {
 
 	  case SNMP_OP_GETNEXT:

==== //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c#11 (text+ko) ====

@@ -37,6 +37,7 @@
 #include <paths.h> 
 #include <string.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 
 /*internal id got after we'll register this module with the agent */
@@ -129,7 +130,7 @@
 	hrState_g.hrFS_tbl_age = 0;		
 	hrState_g.hrSWRun_tbl_age = 0;	
 	hrState_g.hrDevice_tbl_age = 0;	
-	hrState_g.hrProcessor_tbl_age = 0;	
+	
 	
 	init_hrStorage_tbl_v();
 	init_hrFS_tbl_v();
@@ -137,9 +138,26 @@
 			       support is initialized here */
 	init_hrDevice_tbl_v();			       
 	init_hrProcessor_tbl_v();			       			
-				
-        HR_DPRINTF((stderr, "[%s] done.\n", __func__));		
+	
+	/*
+	 * Start the cpu stats collector
+	 * The semantics of timer_start parameters is in "SNMP ticks";  
+	 * we have 100 "SNMP ticks" per second, thus we are trying below
+	 * to get MAX_CPU_SAMPLES per minute
+	 */
+	
+	hrState_g.cpus_load_timer = 
+		timer_start(100, 100*60/MAX_CPU_SAMPLES, get_cpus_samples, NULL, mod);
+		
+        
 
+	if ((hrState_g.devd_sock = create_devd_socket()) < 0) {
+		HR_DPRINTF((stderr, "Failed to create the socket to devd pipe.\n"));			
+	}
+	
+	hrState_g.devd_fd = NULL;
+	
+	HR_DPRINTF((stderr, "[%s] done.\n", __func__));			
 	return (0);    
 }
 
@@ -151,6 +169,20 @@
 static 
 int hostres_fini(void)
 {
+	if (hrState_g.cpus_load_timer != NULL) {
+		timer_stop(hrState_g.cpus_load_timer);
+		hrState_g.cpus_load_timer =  NULL;
+	}
+	
+	if (hrState_g.devd_fd != NULL) {
+		fd_deselect(hrState_g.devd_fd);
+		hrState_g.devd_fd = NULL;
+	}
+	
+	if (hrState_g.devd_sock > 0) {
+		close(hrState_g.devd_sock);
+	}
+	
 	/* here I free the resources used by this module*/
 	if( hrState_g.utmp_fp != (FILE*)NULL ) {
 		if( fclose(hrState_g.utmp_fp) != 0 ) {
@@ -194,7 +226,7 @@
 	hrState_g.hrFS_tbl_age = 0;		
 	hrState_g.hrSWRun_tbl_age = 0;	
 	hrState_g.hrDevice_tbl_age = 0;
-	hrState_g.hrProcessor_tbl_age = 0;
+
 		
 	hrState_g.dev_root = NULL;
 	
@@ -230,17 +262,12 @@
 		HR_DPRINTF((stderr, "%s: hrSWRunTable needs refresh\n ", __func__));
 		refresh_hrSWRun_tbl_v();
 	}
-	
+/*	
 	if ( (time(NULL) - hrState_g.hrDevice_tbl_age) > HR_DEVICE_TBL_REFRESH ) {
 		HR_DPRINTF((stderr, "%s: hrDeviceTable needs refresh\n ", __func__));
 		refresh_hrDevice_tbl_v();
 	}
-
-	if ( (time(NULL) - hrState_g.hrProcessor_tbl_age) > HR_PROCESSOR_TBL_REFRESH ) {
-		HR_DPRINTF((stderr, "%s: hrProcessorTable needs refresh\n ", __func__));
-		refresh_hrProcessor_tbl_v();
-	}
-
+*/
 
         HR_DPRINTF((stderr, "[%s] done.\n ", __func__));		
 }
@@ -288,6 +315,13 @@
 	host_registration_id = or_register(&oid_host, 
 	   "The MIB module for host resource mib (rfc 2790).", 
 	   hostres_module);
+	   
+	if (hrState_g.devd_sock > 0) {   
+		hrState_g.devd_fd = fd_select(hrState_g.devd_sock, devd_socket_callback, NULL, hostres_module);
+		if (hrState_g.devd_fd == NULL) {
+			syslog(LOG_ERR, "fd_select failed on devd socket: %m");
+		}
+	}   
         HR_DPRINTF((stderr, "[%s] done.\n ", __func__));       
 }
 

==== //depot/projects/soc2005/bsnmp/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h#15 (text+ko) ====

@@ -195,11 +195,18 @@
 TAILQ_HEAD(swrun_tbl, hrSWRunTblEntry);
 
 /*
+ * The number of CPU load samples per one minute,
+ * per each CPU
+ */
+#define MAX_CPU_SAMPLES 4
+
+/*
  * This structure is used to hold a SNMP table entry
  * for HOST-RESOURCES-MIB's hrProcessorTable
  * Note that index is external being allocated & mainatined 
  * by the hrDeviceTable code.
  */
+
 struct	hrProcessorTblEntry {
 	int32_t		index;
 	struct asn_oid	frwId;
@@ -207,7 +214,11 @@
 	TAILQ_ENTRY(hrProcessorTblEntry) link;
 	/*not from SNMP table definition, only used internally*/
 	u_char 		cpu_no;		/*which cpu, counted from 0*/
-	pid_t		idle_pid;	/*the PID of ide process for this CPU */
+	pid_t		idle_pid;	/*the PID of idle process for this CPU */
+	double		samples[MAX_CPU_SAMPLES]; /*the samples from the last 
+						    minutes, as required by MIB*/
+	uint32_t	cur_sample_idx;		  /*current sample to fill in next time,
+						    must be < MAX_CPU_SAMPLES*/
 };
 
 TAILQ_HEAD(processor_tbl, hrProcessorTblEntry);
@@ -333,6 +344,8 @@
 	struct 
 	devinfo_dev	*dev_root;
 	
+	int 		devd_sock;		/*socket for /var/run/devd.pipe*/
+	void 		*devd_fd;		/*used to wait notifications from /var/run/devd.pipe */
 	/* 
 	 * next items are used for hrProcessorTable 
 	 */
@@ -343,8 +356,9 @@
 	fixpt_t		ccpu;			/* kernel _ccpu variable */
 	int		fscale;			/* kernel _fscale variable */
 	
-	time_t		hrProcessor_tbl_age; 
+ 
 	uint64_t 	hr_processor_tick;	/*last (agent) tick when hrProcessorTable was updated */
+	void*		cpus_load_timer;	/*periodic time used to get cpu laod stats*/
 											  						  					  
 				
 };
@@ -478,6 +492,20 @@
 void refresh_hrDevice_tbl_v(void);
 
 /*
+ * Creates the  sockect to devd pipe, aka
+ * /var/run/devd.pipe. Returs the socket descriptor or
+ * -1 in case of an error
+ */
+int create_devd_socket(void);
+
+/*
+ * Callback for fd_select function; called when
+ * device events are detected in order to trigger
+ * a device table refresh
+ */
+void devd_socket_callback(int, void*);
+
+/*
  * Init the things for hrProcessorTable 
  */
 void init_hrProcessor_tbl_v(void);
@@ -500,5 +528,10 @@
  */
 void refresh_hrProcessor_tbl_v(void);
 
+/*
+ *  The timer function used to collect the CPUs load samples
+ *  Each CPU is "visited" MAX_CPU_SAMPLES times per one minute
+ */
+void get_cpus_samples(void*);
 #endif /*__HOSTRES_SNMP_H_INCLUDED__ */
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200507261907.j6QJ7Nsr052523>