From owner-p4-projects@FreeBSD.ORG Tue Jul 26 19:07:24 2005 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 025AC16A421; Tue, 26 Jul 2005 19:07:24 +0000 (GMT) X-Original-To: perforce@freebsd.org Delivered-To: perforce@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id CE65E16A41F for ; Tue, 26 Jul 2005 19:07:23 +0000 (GMT) (envelope-from soc-victor@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id 5276743D58 for ; Tue, 26 Jul 2005 19:07:23 +0000 (GMT) (envelope-from soc-victor@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.1/8.13.1) with ESMTP id j6QJ7NM2052526 for ; Tue, 26 Jul 2005 19:07:23 GMT (envelope-from soc-victor@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.1/8.13.1/Submit) id j6QJ7Nsr052523 for perforce@freebsd.org; Tue, 26 Jul 2005 19:07:23 GMT (envelope-from soc-victor@freebsd.org) Date: Tue, 26 Jul 2005 19:07:23 GMT Message-Id: <200507261907.j6QJ7Nsr052523@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to soc-victor@freebsd.org using -f From: Victor Cruceru To: Perforce Change Reviews Cc: Subject: PERFORCE change 81019 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 26 Jul 2005 19:07:25 -0000 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 #include #include +#include +#include +#include +#include +#include /*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 #include +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 #include #include +#include /*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__ */