From 14e77fb55af9ee165d54daa578f17f63329fe7c0 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Mon, 30 Oct 2023 20:43:29 +0000 Subject: [PATCH 01/13] Fix typo in ldms_stream_cb (LDMS_STREAM_EVENT_UNSUBSCRIBE_STATUS) This was a copy-paste typo. It should be LDMS_STREAM_EVENT_UNSUBSCRIBE_STATUS, not LDMS_STREAM_EVENT_SUBSCRIBE_STATUS. --- ldms/src/core/ldms_stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldms/src/core/ldms_stream.c b/ldms/src/core/ldms_stream.c index 899763bcb..d2d604a55 100644 --- a/ldms/src/core/ldms_stream.c +++ b/ldms/src/core/ldms_stream.c @@ -1366,7 +1366,7 @@ void __stream_reply_recv(ldms_t x, int cmd, struct ldms_reply *reply) __process_stream_subunsub_reply(x, reply, LDMS_STREAM_EVENT_SUBSCRIBE_STATUS); break; case LDMS_CMD_STREAM_UNSUB_REPLY: - __process_stream_subunsub_reply(x, reply, LDMS_STREAM_EVENT_SUBSCRIBE_STATUS); + __process_stream_subunsub_reply(x, reply, LDMS_STREAM_EVENT_UNSUBSCRIBE_STATUS); break; default: assert(0 == "Unexpected reply"); From da8a54c67bc7e82132e912417b250be1a1b2fab7 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Mon, 27 Nov 2023 16:31:09 -0600 Subject: [PATCH 02/13] Add LDMS_STREAM_EVENT_CLOSE --- ldms/python/ldms.pyx | 7 +++- ldms/src/core/ldms.h | 9 +++++ ldms/src/core/ldms_stream.c | 66 +++++++++++++++++++++++++++++++++---- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/ldms/python/ldms.pyx b/ldms/python/ldms.pyx index 5be1cae02..809ea3c14 100644 --- a/ldms/python/ldms.pyx +++ b/ldms/python/ldms.pyx @@ -3875,7 +3875,12 @@ cdef class StreamData(object): cdef int __stream_client_cb(ldms_stream_event_t ev, void *arg) with gil: cdef StreamClient c = arg - cdef StreamData sdata = StreamData.from_ldms_stream_event(PTR(ev)) + cdef StreamData sdata + + if ev.type != LDMS_STREAM_EVENT_RECV: + return 0 + + sdata = StreamData.from_ldms_stream_event(PTR(ev)) if c.cb: c.cb(c, sdata, c.cb_arg) else: diff --git a/ldms/src/core/ldms.h b/ldms/src/core/ldms.h index f5bfa3302..8f79d5b2d 100644 --- a/ldms/src/core/ldms.h +++ b/ldms/src/core/ldms.h @@ -1114,6 +1114,9 @@ enum ldms_stream_event_type { LDMS_STREAM_EVENT_RECV, /* stream data received */ LDMS_STREAM_EVENT_SUBSCRIBE_STATUS, /* reporting subscription status */ LDMS_STREAM_EVENT_UNSUBSCRIBE_STATUS, /* reporting unsubscription status */ + LDMS_STREAM_EVENT_CLOSE, /* reporting stream client close event. + * This is the last event to deliver from a + * client. */ }; /* For stream data delivery to the application */ @@ -1138,12 +1141,18 @@ struct ldms_stream_return_status_s { int status; }; +/* For stream close event */ +struct ldms_stream_close_event_s { + ldms_stream_client_t client; +}; + typedef struct ldms_stream_event_s { ldms_t r; /* rail */ enum ldms_stream_event_type type; union { struct ldms_stream_recv_data_s recv; struct ldms_stream_return_status_s status; + struct ldms_stream_close_event_s close; }; } *ldms_stream_event_t; diff --git a/ldms/src/core/ldms_stream.c b/ldms/src/core/ldms_stream.c index d2d604a55..3dac1eb2a 100644 --- a/ldms/src/core/ldms_stream.c +++ b/ldms/src/core/ldms_stream.c @@ -120,6 +120,12 @@ TAILQ_HEAD(, ldms_stream_client_s) __regex_client_tq = TAILQ_HEAD_INITIALIZER(__ static uint64_t stream_gn = 0; +static pthread_mutex_t __stream_close_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t __stream_close_cond = PTHREAD_COND_INITIALIZER; +static pthread_t __stream_close_thread; +static TAILQ_HEAD(, ldms_stream_client_s) + __stream_close_tq = TAILQ_HEAD_INITIALIZER(__stream_close_tq); + int __rail_rep_send_raw(struct ldms_rail_ep_s *rep, void *data, int len); /* @@ -253,6 +259,8 @@ __remote_client_cb(ldms_stream_event_t ev, void *cb_arg) ldms_rail_t r; int ep_idx; int rc; + if (ev->type == LDMS_STREAM_EVENT_CLOSE) + return 0; assert( ev->type == LDMS_STREAM_EVENT_RECV ); if (!XTYPE_IS_RAIL(ev->recv.client->x->xtype)) return ENOTSUP; @@ -964,7 +972,12 @@ void ldms_stream_close(ldms_stream_client_t c) ref_put(&c->ref, "__regex_client_tq"); } __STREAM_UNLOCK(); - ref_put(&c->ref, "init"); + + /* reuse the c->entry for 'close' event queing */ + pthread_mutex_lock(&__stream_close_mutex); + TAILQ_INSERT_TAIL(&__stream_close_tq, c, entry); + pthread_cond_signal(&__stream_close_cond); + pthread_mutex_unlock(&__stream_close_mutex); } struct __sub_req_ctxt_s { @@ -2032,12 +2045,53 @@ int ldms_stream_publish_file(ldms_t x, const char *stream_name, return rc; } +static void __ldms_stream_init(); + +static void *__stream_close_proc(void *arg) +{ + struct ldms_stream_client_s *c; + struct ldms_stream_event_s ev; + + pthread_atfork(NULL, NULL, __ldms_stream_init); /* re-initialize at fork */ + + pthread_mutex_lock(&__stream_close_mutex); + loop: + c = TAILQ_FIRST(&__stream_close_tq); + if (!c) { + pthread_cond_wait(&__stream_close_cond, &__stream_close_mutex); + goto loop; + } + TAILQ_REMOVE(&__stream_close_tq, c, entry); + pthread_mutex_unlock(&__stream_close_mutex); + + ev.r = c->x; + ev.type = LDMS_STREAM_EVENT_CLOSE; + ev.close.client = c; + + ref_get(&c->ref, "cb"); + c->cb_fn(&ev, c->cb_arg); + ref_put(&c->ref, "cb"); + + ref_put(&c->ref, "init"); + + pthread_mutex_lock(&__stream_close_mutex); + goto loop; + + return NULL; +} + __attribute__((constructor)) static void __ldms_stream_init() { - static int once = 0; - if (once) - return ; - __ldms_stream_log = ovis_log_register("ldms.stream", "LDMS Stream Library"); - once = 1; + int rc; + pthread_mutex_init(&__stream_close_mutex, NULL); + pthread_cond_init(&__stream_close_cond, NULL); + if (!__ldms_stream_log) + __ldms_stream_log = ovis_log_register("ldms.stream", "LDMS Stream Library"); + rc = pthread_create(&__stream_close_thread, NULL, __stream_close_proc, NULL); + if (rc) { + __ERROR("cannot create ldms_stream_close thread, rc: %d, errno: %d\n", rc, errno); + } else { + pthread_setname_np(__stream_close_thread, "ldms_strm_cls"); + } } From cfefbfc68f5421e40b1de086adf65321065e90d9 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Tue, 7 Nov 2023 21:54:59 -0600 Subject: [PATCH 03/13] Add multi-instance plugin support in ldmsd This patch add multi-instance plugin infrastructure in ldmsd. --- ldms/src/ldmsd/ldmsd.c | 161 ++++++++------ ldms/src/ldmsd/ldmsd.h | 67 +++--- ldms/src/ldmsd/ldmsd_cfgobj.c | 41 +++- ldms/src/ldmsd/ldmsd_config.c | 392 +++++++++++++++++++++++++-------- ldms/src/ldmsd/ldmsd_request.c | 106 +++++---- ldms/src/ldmsd/ldmsd_strgp.c | 23 +- 6 files changed, 542 insertions(+), 248 deletions(-) diff --git a/ldms/src/ldmsd/ldmsd.c b/ldms/src/ldmsd/ldmsd.c index b8af85082..b8033e590 100644 --- a/ldms/src/ldmsd/ldmsd.c +++ b/ldms/src/ldmsd/ldmsd.c @@ -538,21 +538,23 @@ int publish_kernel(const char *setfile) return 0; } -static void stop_sampler(struct ldmsd_plugin_cfg *pi) +static void stop_sampler(struct ldmsd_sampler *samp) { - ovis_scheduler_event_del(pi->os, &pi->oev); - release_ovis_scheduler(pi->thread_id); - pi->ref_count--; - pi->os = NULL; - pi->thread_id = -1; + ovis_scheduler_event_del(samp->os, &samp->oev); + release_ovis_scheduler(samp->thread_id); + samp->os = NULL; + samp->thread_id = -1; + ldmsd_cfgobj_put(&samp->base.cfgobj); } void plugin_sampler_cb(ovis_event_t oev) { - struct ldmsd_plugin_cfg *pi = oev->param.ctxt; - pthread_mutex_lock(&pi->lock); - assert(pi->plugin->type == LDMSD_PLUGIN_SAMPLER); - int rc = pi->sampler->sample(pi->sampler); + struct ldmsd_plugin *pi = oev->param.ctxt; + struct ldmsd_sampler *samp = (void*)pi; + ldmsd_cfgobj_get(&samp->base.cfgobj); + ldmsd_cfgobj_lock(&pi->cfgobj); + assert(pi->type == LDMSD_PLUGIN_SAMPLER); + int rc = samp->sample(samp); if (rc) { /* * If the sampler reports an error don't reschedule @@ -561,9 +563,10 @@ void plugin_sampler_cb(ovis_event_t oev) */ ovis_log(sampler_log, OVIS_LERROR, "'%s': failed to sample. Stopping " "the plug-in.\n", pi->name); - stop_sampler(pi); + stop_sampler(samp); } - pthread_mutex_unlock(&pi->lock); + ldmsd_cfgobj_unlock(&pi->cfgobj); + ldmsd_cfgobj_put(&samp->base.cfgobj); } void ldmsd_set_tree_lock() @@ -631,7 +634,7 @@ int ldmsd_set_register(ldms_set_t set, const char *plugin_name) struct rbn *rbn; ldmsd_plugin_set_t s; ldmsd_plugin_set_list_t list; - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi; int rc; s = malloc(sizeof(*s)); @@ -684,11 +687,13 @@ int ldmsd_set_register(ldms_set_t set, const char *plugin_name) ldmsd_set_deregister(s->inst_name, plugin_name); return EINVAL; } - if (pi->plugin->type == LDMSD_PLUGIN_SAMPLER) { - if (pi->sample_interval_us) { + if (pi->type == LDMSD_PLUGIN_SAMPLER) { + struct ldmsd_sampler *samp = LDMSD_SAMPLER(pi); + if (samp->sample_interval_us) { /* Add the update hint to the set_info */ rc = ldmsd_set_update_hint_set(s->set, - pi->sample_interval_us, pi->sample_offset_us); + samp->sample_interval_us, + samp->sample_offset_us); if (rc) { /* Leave the ldmsd plugin set in the tree, so return 0. */ ovis_log(sampler_log, OVIS_LERROR, "Error %d: Failed to add " @@ -697,6 +702,7 @@ int ldmsd_set_register(ldms_set_t set, const char *plugin_name) } } } + ldmsd_put_plugin(pi); return 0; free_inst_name: free(s->inst_name); @@ -953,7 +959,7 @@ ldmsd_set_info_t ldmsd_set_info_get(const char *inst_name) struct timespec dur; struct ldmsd_plugin_set_list *plugn_set_list; struct ldmsd_plugin_set *plugn_set = NULL; - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi; ldms_set_t lset = ldms_set_by_name(inst_name); if (!lset) @@ -986,11 +992,10 @@ ldmsd_set_info_t ldmsd_set_info_get(const char *inst_name) "an unloaded plugin '%s'\n", inst_name, plugn_set->plugin_name); } else { - pi->ref_count++; - info->interval_us = pi->sample_interval_us; - info->offset_us = pi->sample_offset_us; + info->interval_us = LDMSD_SAMPLER(pi)->sample_interval_us; + info->offset_us = LDMSD_SAMPLER(pi)->sample_offset_us; info->sync = 1; /* Sampling is always synchronous. */ - info->pi = pi; + info->pi = pi; /* pi ref already taken in ldmsd_get_plugin */ } info->origin_name = strdup(plugn_set->plugin_name); info->origin_type = LDMSD_SET_ORIGIN_SAMP_PI; @@ -1069,7 +1074,7 @@ void ldmsd_set_info_delete(ldmsd_set_info_t info) info->prd_set = NULL; } if (info->pi) { - info->pi->ref_count--; + ldmsd_cfgobj_put(&info->pi->cfgobj); info->pi = NULL; } free(info); @@ -1103,7 +1108,8 @@ int ldmsd_start_sampler(char *plugin_name, char *interval, char *offset) int rc = 0; long sample_interval; long sample_offset = 0; - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi; + struct ldmsd_sampler *samp; rc = ovis_time_str2us(interval, &sample_interval); if (rc) @@ -1113,17 +1119,18 @@ int ldmsd_start_sampler(char *plugin_name, char *interval, char *offset) if (!pi) return ENOENT; - pthread_mutex_lock(&pi->lock); - if (pi->plugin->type != LDMSD_PLUGIN_SAMPLER) { - rc = -EINVAL; + ldmsd_cfgobj_lock(&pi->cfgobj); + if (pi->type != LDMSD_PLUGIN_SAMPLER) { + rc = EINVAL; goto out; } - if (pi->thread_id >= 0) { + samp = LDMSD_SAMPLER(pi); + if (samp->thread_id >= 0) { rc = EBUSY; goto out; } - pi->sample_interval_us = sample_interval; + samp->sample_interval_us = sample_interval; if (offset) { rc = ovis_time_str2us(offset, &sample_offset); if (rc) { @@ -1136,32 +1143,39 @@ int ldmsd_start_sampler(char *plugin_name, char *interval, char *offset) goto out; } } - pi->sample_offset_us = sample_offset; + samp->sample_offset_us = sample_offset; - rc = __sampler_set_info_add(pi->plugin, sample_interval, + rc = __sampler_set_info_add(pi, sample_interval, sample_offset); if (rc) goto out; - OVIS_EVENT_INIT(&pi->oev); - pi->oev.param.type = OVIS_EVENT_PERIODIC; - pi->oev.param.periodic.period_us = sample_interval; - pi->oev.param.periodic.phase_us = sample_offset; - pi->oev.param.ctxt = pi; - pi->oev.param.cb_fn = plugin_sampler_cb; - - pi->ref_count++; - - pi->thread_id = find_least_busy_thread(); - pi->os = get_ovis_scheduler(pi->thread_id); - rc = ovis_scheduler_event_add(pi->os, &pi->oev); + OVIS_EVENT_INIT(&samp->oev); + samp->oev.param.type = OVIS_EVENT_PERIODIC; + samp->oev.param.periodic.period_us = sample_interval; + samp->oev.param.periodic.phase_us = sample_offset; + samp->oev.param.ctxt = pi; + samp->oev.param.cb_fn = plugin_sampler_cb; + + samp->thread_id = find_least_busy_thread(); + samp->os = get_ovis_scheduler(samp->thread_id); + rc = ovis_scheduler_event_add(samp->os, &samp->oev); + if (0 == rc) { + ldmsd_cfgobj_get(&pi->cfgobj); + /* this ref will be put down in ldmsd_stop_sampler() */ + } else { + samp->os = NULL; + release_ovis_scheduler(samp->thread_id); + samp->thread_id = -1; + } out: - pthread_mutex_unlock(&pi->lock); + ldmsd_cfgobj_unlock(&pi->cfgobj); + ldmsd_put_plugin(pi); return rc; } struct oneshot { - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi; ovis_scheduler_t os; struct ovis_event_s oev; }; @@ -1169,22 +1183,25 @@ struct oneshot { void oneshot_sample_cb(ovis_event_t ev) { struct oneshot *os = ev->param.ctxt; - struct ldmsd_plugin_cfg *pi = os->pi; + struct ldmsd_plugin *pi = os->pi; + struct ldmsd_sampler *samp = LDMSD_SAMPLER(pi); ovis_scheduler_event_del(os->os, ev); - pthread_mutex_lock(&pi->lock); - assert(pi->plugin->type == LDMSD_PLUGIN_SAMPLER); - pi->sampler->sample(pi->sampler); - pi->ref_count--; - release_ovis_scheduler(pi->thread_id); + ldmsd_cfgobj_lock(&pi->cfgobj); + assert(pi->type == LDMSD_PLUGIN_SAMPLER); + samp->sample(samp); + release_ovis_scheduler(samp->thread_id); free(os); - pthread_mutex_unlock(&pi->lock); + ldmsd_cfgobj_unlock(&pi->cfgobj); + + ldmsd_cfgobj_put(&pi->cfgobj); } int ldmsd_oneshot_sample(const char *plugin_name, const char *ts, char *errstr, size_t errlen) { int rc = 0; - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi; + struct ldmsd_sampler *samp; time_t now, sched; struct timeval tv; @@ -1226,22 +1243,23 @@ int ldmsd_oneshot_sample(const char *plugin_name, const char *ts, free(ossample); return rc; } - pthread_mutex_lock(&pi->lock); - if (pi->plugin->type != LDMSD_PLUGIN_SAMPLER) { + samp = LDMSD_SAMPLER(pi); + ldmsd_cfgobj_lock(&pi->cfgobj); + if (pi->type != LDMSD_PLUGIN_SAMPLER) { rc = EINVAL; snprintf(errstr, errlen, "The specified plugin is not a sampler."); goto err; } - pi->ref_count++; + ldmsd_cfgobj_get(&pi->cfgobj); ossample->pi = pi; - if (pi->thread_id < 0) { + if (samp->thread_id < 0) { snprintf(errstr, errlen, "Sampler '%s' not started yet.", plugin_name); rc = EPERM; goto err; } - ossample->os = get_ovis_scheduler(pi->thread_id); + ossample->os = get_ovis_scheduler(samp->thread_id); OVIS_EVENT_INIT(&ossample->oev); ossample->oev.param.type = OVIS_EVENT_TIMEOUT; ossample->oev.param.ctxt = ossample; @@ -1256,7 +1274,7 @@ int ldmsd_oneshot_sample(const char *plugin_name, const char *ts, err: free(ossample); out: - pthread_mutex_unlock(&pi->lock); + ldmsd_cfgobj_unlock(&pi->cfgobj); return rc; } @@ -1266,28 +1284,29 @@ int ldmsd_oneshot_sample(const char *plugin_name, const char *ts, int ldmsd_stop_sampler(char *plugin_name) { int rc = 0; - struct ldmsd_plugin_cfg *pi; + struct ldmsd_sampler *samp; - pi = ldmsd_get_plugin(plugin_name); - if (!pi) + samp = (struct ldmsd_sampler *)ldmsd_get_plugin(plugin_name); + if (!samp) return ENOENT; - pthread_mutex_lock(&pi->lock); + ldmsd_cfgobj_lock(&samp->base.cfgobj); /* Ensure this is a sampler */ - if (pi->plugin->type != LDMSD_PLUGIN_SAMPLER) { + if (samp->base.type != LDMSD_PLUGIN_SAMPLER) { rc = EINVAL; goto out; } - if (pi->os) { - ovis_scheduler_event_del(pi->os, &pi->oev); - pi->os = NULL; - release_ovis_scheduler(pi->thread_id); - pi->thread_id = -1; - pi->ref_count--; + if (samp->os) { + ovis_scheduler_event_del(samp->os, &samp->oev); + samp->os = NULL; + release_ovis_scheduler(samp->thread_id); + samp->thread_id = -1; + ldmsd_cfgobj_put(&samp->base.cfgobj); } else { - rc = -EBUSY; + rc = EBUSY; } out: - pthread_mutex_unlock(&pi->lock); + ldmsd_cfgobj_unlock(&samp->base.cfgobj); + ldmsd_put_plugin(&samp->base); return rc; } diff --git a/ldms/src/ldmsd/ldmsd.h b/ldms/src/ldmsd/ldmsd.h index 39fe9bcc8..8115187c1 100644 --- a/ldms/src/ldmsd/ldmsd.h +++ b/ldms/src/ldmsd/ldmsd.h @@ -148,11 +148,14 @@ typedef struct ldmsd_sec_ctxt { } *ldmsd_sec_ctxt_t; typedef enum ldmsd_cfgobj_type { - LDMSD_CFGOBJ_PRDCR = 1, + LDMSD_CFGOBJ_FIRST = 1, + LDMSD_CFGOBJ_PRDCR = LDMSD_CFGOBJ_FIRST, LDMSD_CFGOBJ_UPDTR, LDMSD_CFGOBJ_STRGP, LDMSD_CFGOBJ_LISTEN, LDMSD_CFGOBJ_AUTH, + LDMSD_CFGOBJ_PLUGIN, + LDMSD_CFGOBJ_LAST = LDMSD_CFGOBJ_PLUGIN, } ldmsd_cfgobj_type_t; struct ldmsd_cfgobj; @@ -178,7 +181,7 @@ typedef void (*ldmsd_cfgobj_del_fn_t)(struct ldmsd_cfgobj *); #define LDMSD_PERM_FAILOVER_ALLOWED 04000 typedef struct ldmsd_cfgobj { - char *name; /* Unique producer name */ + char *name; /* Unique cfgobj name */ uint32_t ref_count; ldmsd_cfgobj_type_t type; ldmsd_cfgobj_del_fn_t __del; @@ -718,7 +721,7 @@ typedef struct ldmsd_set_info { struct timespec start; /* Latest sampling/update timestamp */ struct timespec end; /* latest sampling/update timestamp */ union { - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi; ldmsd_prdcr_set_t prd_set; }; } *ldmsd_set_info_t; @@ -755,7 +758,8 @@ struct avl_q_item { }; TAILQ_HEAD(avl_q, avl_q_item); struct ldmsd_plugin { - char name[LDMSD_MAX_PLUGIN_NAME_LEN]; + struct ldmsd_cfgobj cfgobj; + char name[LDMSD_MAX_PLUGIN_NAME_LEN]; /* plugin name (e.g. meminfo) */ struct avl_q avl_q; struct avl_q kwl_q; enum ldmsd_plugin_type { @@ -763,7 +767,9 @@ struct ldmsd_plugin { LDMSD_PLUGIN_SAMPLER, LDMSD_PLUGIN_STORE } type; - struct ldmsd_plugin_cfg *pi; + + char *libpath; + enum ldmsd_plugin_type (*get_type)(struct ldmsd_plugin *self); int (*config)(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct attr_value_list *avl); void (*term)(struct ldmsd_plugin *self); @@ -772,30 +778,19 @@ struct ldmsd_plugin { struct ldmsd_sampler { struct ldmsd_plugin base; - ldms_set_t (*get_set)(struct ldmsd_sampler *self); - int (*sample)(struct ldmsd_sampler *self); -}; -struct ldmsd_plugin_cfg { - void *handle; - char *name; - char *libpath; unsigned long sample_interval_us; long sample_offset_us; + int thread_id; - int ref_count; - union { - struct ldmsd_plugin *plugin; - struct ldmsd_sampler *sampler; - struct ldmsd_store *store; - }; - struct timeval timeout; - pthread_mutex_t lock; ovis_scheduler_t os; struct ovis_event_s oev; - LIST_ENTRY(ldmsd_plugin_cfg) entry; + + ldms_set_t (*get_set)(struct ldmsd_sampler *self); + int (*sample)(struct ldmsd_sampler *self); }; -LIST_HEAD(plugin_list, ldmsd_plugin_cfg); +#define LDMSD_SAMPLER(ptr) ((struct ldmsd_sampler *)(ptr)) + #define LDMSD_DEFAULT_SAMPLE_INTERVAL 1000000 /** Metric name for component ids (u64). */ @@ -803,9 +798,20 @@ LIST_HEAD(plugin_list, ldmsd_plugin_cfg); /** Metric name for job id number */ #define LDMSD_JOBID "job_id" -extern void ldmsd_config_cleanup(void); -extern int ldmsd_config_init(char *name); -struct ldmsd_plugin_cfg *ldmsd_get_plugin(char *name); +struct ldmsd_plugin *ldmsd_get_plugin(const char *name); +void ldmsd_put_plugin(struct ldmsd_plugin *pi); + +struct ldmsd_sampler *ldmsd_sampler_alloc(const char *name, size_t sz, + ldmsd_cfgobj_del_fn_t __del, + uid_t uid, gid_t gid, int perm); + +void ldmsd_sampler_cleanup(struct ldmsd_sampler *samp); + +struct ldmsd_store *ldmsd_store_alloc(const char *name, size_t sz, + ldmsd_cfgobj_del_fn_t __del, + uid_t uid, gid_t gid, int perm); + +void ldmsd_store_cleanup(struct ldmsd_store *store); /** * \brief ldmsd_set_register @@ -871,6 +877,7 @@ struct ldmsd_store { int (*commit)(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, int row_count); }; +#define LDMSD_STORE(ptr) ((struct ldmsd_store *)(ptr)) /** * \brief Get the security context (uid, gid) of the daemon. @@ -907,6 +914,8 @@ ldmsd_store_close(struct ldmsd_store *store, ldmsd_store_handle_t sh) } typedef struct ldmsd_plugin *(*ldmsd_plugin_get_f)(); +typedef struct ldmsd_plugin *(*ldmsd_plugin_instance_get_f) + (const char *name, uid_t uid, gid_t gid, int perm); /* ldmsctl command callback function definition */ typedef int (*ldmsctl_cmd_fn_t)(char *, struct attr_value_list*, struct attr_value_list *); @@ -999,6 +1008,8 @@ void ldmsd_cfgobj_del(const char *name, ldmsd_cfgobj_type_t type); ldmsd_cfgobj_t ldmsd_cfgobj_first(ldmsd_cfgobj_type_t type); ldmsd_cfgobj_t ldmsd_cfgobj_next(ldmsd_cfgobj_t obj); int ldmsd_cfgobj_access_check(ldmsd_cfgobj_t obj, int acc, ldmsd_sec_ctxt_t ctxt); +int ldmsd_cfgobj_add(ldmsd_cfgobj_t obj); +void ldmsd_cfgobj_rm(ldmsd_cfgobj_t obj); #define LDMSD_CFGOBJ_FOREACH(obj, type) \ for ((obj) = ldmsd_cfgobj_first(type); (obj); \ @@ -1234,6 +1245,12 @@ ldmsd_plugin_set_t ldmsd_plugin_set_next(ldmsd_plugin_set_t set); int ldmsd_set_update_hint_set(ldms_set_t set, long interval_us, long offset_us); int ldmsd_set_update_hint_get(ldms_set_t set, long *interva_us, long *offset_us); +static inline const char * +ldmsd_plugin_inst_name(struct ldmsd_plugin *p) +{ + return p->cfgobj.name; +} + /** Regular expressions */ int ldmsd_compile_regex(regex_t *regex, const char *ex, char *errbuf, size_t errsz); diff --git a/ldms/src/ldmsd/ldmsd_cfgobj.c b/ldms/src/ldmsd/ldmsd_cfgobj.c index 6a3d23eea..db8ca2ad3 100644 --- a/ldms/src/ldmsd/ldmsd_cfgobj.c +++ b/ldms/src/ldmsd/ldmsd_cfgobj.c @@ -84,12 +84,16 @@ pthread_mutex_t listen_tree_lock = PTHREAD_MUTEX_INITIALIZER; struct rbt auth_tree = RBT_INITIALIZER(cfgobj_cmp); pthread_mutex_t auth_tree_lock = PTHREAD_MUTEX_INITIALIZER; +struct rbt plugin_tree = RBT_INITIALIZER(cfgobj_cmp); +pthread_mutex_t plugin_tree_lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_t *cfgobj_locks[] = { [LDMSD_CFGOBJ_PRDCR] = &prdcr_tree_lock, [LDMSD_CFGOBJ_UPDTR] = &updtr_tree_lock, [LDMSD_CFGOBJ_STRGP] = &strgp_tree_lock, [LDMSD_CFGOBJ_LISTEN] = &listen_tree_lock, [LDMSD_CFGOBJ_AUTH] = &auth_tree_lock, + [LDMSD_CFGOBJ_PLUGIN] = &plugin_tree_lock, }; struct rbt *cfgobj_trees[] = { @@ -98,17 +102,9 @@ struct rbt *cfgobj_trees[] = { [LDMSD_CFGOBJ_STRGP] = &strgp_tree, [LDMSD_CFGOBJ_LISTEN] = &listen_tree, [LDMSD_CFGOBJ_AUTH] = &auth_tree, + [LDMSD_CFGOBJ_PLUGIN] = &plugin_tree, }; -void ldmsd_cfgobj_init(void) -{ - rbt_init(&prdcr_tree, cfgobj_cmp); - rbt_init(&updtr_tree, cfgobj_cmp); - rbt_init(&strgp_tree, cfgobj_cmp); - rbt_init(&listen_tree, cfgobj_cmp); - rbt_init(&auth_tree, cfgobj_cmp); -} - void ldmsd_cfgobj___del(ldmsd_cfgobj_t obj) { free(obj->name); @@ -135,6 +131,33 @@ void ldmsd_cfgobj_unlock(ldmsd_cfgobj_t obj) pthread_mutex_unlock(&obj->lock); } +int ldmsd_cfgobj_add(ldmsd_cfgobj_t obj) +{ + int rc = EEXIST; + struct rbn *n; + if (obj->type < LDMSD_CFGOBJ_FIRST || LDMSD_CFGOBJ_LAST < obj->type) + return EINVAL; + pthread_mutex_lock(cfgobj_locks[obj->type]); + n = rbt_find(cfgobj_trees[obj->type], obj->name); + if (n) + goto out; + rbn_init(&obj->rbn, obj->name); + rbt_ins(cfgobj_trees[obj->type], &obj->rbn); + rc = 0; + ldmsd_cfgobj_get(obj); /* put in `rm` */ + out: + pthread_mutex_unlock(cfgobj_locks[obj->type]); + return rc; +} + +void ldmsd_cfgobj_rm(ldmsd_cfgobj_t obj) +{ + pthread_mutex_lock(cfgobj_locks[obj->type]); + rbt_del(cfgobj_trees[obj->type], &obj->rbn); + pthread_mutex_unlock(cfgobj_locks[obj->type]); + ldmsd_cfgobj_put(obj); /* from `add` */ +} + ldmsd_cfgobj_t ldmsd_cfgobj_new_with_auth(const char *name, ldmsd_cfgobj_type_t type, size_t obj_size, diff --git a/ldms/src/ldmsd/ldmsd_config.c b/ldms/src/ldmsd/ldmsd_config.c index 3ec25e201..c8212a824 100644 --- a/ldms/src/ldmsd/ldmsd_config.c +++ b/ldms/src/ldmsd/ldmsd_config.c @@ -90,35 +90,203 @@ LIST_HEAD(host_list_s, hostspec) host_list; pthread_mutex_t sp_list_lock = PTHREAD_MUTEX_INITIALIZER; #define LDMSD_PLUGIN_LIBPATH_MAX 1024 -struct plugin_list plugin_list; void ldmsd_cfg_ldms_xprt_cleanup(ldmsd_cfg_xprt_t xprt) { /* nothing to do */ } -struct ldmsd_plugin_cfg *ldmsd_get_plugin(char *name) +struct ldmsd_plugin *ldmsd_get_plugin(const char *inst_name) { - struct ldmsd_plugin_cfg *p; - LIST_FOREACH(p, &plugin_list, entry) { - if (0 == strcmp(p->name, name)) - return p; + struct ldmsd_cfgobj *obj = ldmsd_cfgobj_find(inst_name, LDMSD_CFGOBJ_PLUGIN); + return (struct ldmsd_plugin*)obj; +} + +void ldmsd_put_plugin(struct ldmsd_plugin *pi) +{ + ldmsd_cfgobj_put(&pi->cfgobj); +} + +void ldmsd_cfgobj_plugin_cleanup(struct ldmsd_cfgobj *obj) +{ + struct ldmsd_plugin *pi = container_of(obj, struct ldmsd_plugin, cfgobj); + struct avl_q_item *avl; + struct avl_q_item *kwl; + + free(pi->cfgobj.name); + + free(pi->libpath); + pi->libpath = NULL; + + while ((avl = TAILQ_FIRST(&pi->avl_q))) { + TAILQ_REMOVE(&pi->avl_q, avl, entry); + free(avl->av_list); + free(avl); } - return NULL; + + while ((kwl = TAILQ_FIRST(&pi->kwl_q))) { + TAILQ_REMOVE(&pi->kwl_q, kwl, entry); + free(kwl->av_list); + free(kwl); + } +} + +void ldmsd_sampler_cleanup(struct ldmsd_sampler *samp) +{ + ldmsd_cfgobj_plugin_cleanup(&samp->base.cfgobj); +} + +void ldmsd_store_cleanup(struct ldmsd_store *store) +{ + ldmsd_cfgobj_plugin_cleanup(&store->base.cfgobj); +} + +/* + * For old-style plugin that does not initalize cfgobj + */ +int ldmsd_plugin_cfgobj_init(struct ldmsd_plugin *pi, const char *inst_name) +{ + struct ldmsd_cfgobj *obj = &pi->cfgobj; + + obj->name = strdup(inst_name); + if (!obj->name) + return ENOMEM; + + obj->gid = getegid(); + obj->uid = geteuid(); + obj->perm = 0770; + + pthread_mutex_init(&obj->lock, NULL); + obj->ref_count = 1; + obj->type = LDMSD_CFGOBJ_PLUGIN; + + obj->__del = ldmsd_cfgobj_plugin_cleanup; + return 0; +} + +void ldmsd_plugin_init(struct ldmsd_plugin *pi) +{ + /* Initialize only the plugin part, leave the cfgobj alone */ + TAILQ_INIT(&pi->avl_q); + TAILQ_INIT(&pi->kwl_q); +} + +void ldmsd_sampler_init(struct ldmsd_sampler *samp) +{ + /* Initialize only the sampler part, leave the plugin alone, except type */ + samp->base.type = LDMSD_PLUGIN_SAMPLER; + samp->os = NULL; + samp->thread_id = -1; + samp->sample_interval_us = 1000000; + samp->sample_offset_us = 0; +} + +void ldmsd_store_init(struct ldmsd_store *st) +{ + st->base.type = LDMSD_PLUGIN_STORE; +} + +struct ldmsd_sampler *ldmsd_sampler_alloc(const char *name, size_t sz, + ldmsd_cfgobj_del_fn_t __del, + uid_t uid, gid_t gid, int perm) +{ + struct ldmsd_sampler *samp = NULL; + + if (sz < sizeof(*samp)) { + errno = EINVAL; + goto out; + } + + samp = (void*)ldmsd_cfgobj_new_with_auth(name, LDMSD_CFGOBJ_PLUGIN, sz, + __del, uid, gid, perm); + if (!samp) + goto out; + + ldmsd_plugin_init(&samp->base); + ldmsd_sampler_init(samp); + + out: + return samp; +} + +struct ldmsd_store *ldmsd_store_alloc(const char *name, size_t sz, + ldmsd_cfgobj_del_fn_t __del, + uid_t uid, gid_t gid, int perm) +{ + struct ldmsd_store *st = NULL; + + if (sz < sizeof(*st)) { + errno = EINVAL; + goto out; + } + + st = (void*)ldmsd_cfgobj_new_with_auth(name, LDMSD_CFGOBJ_PLUGIN, sz, + __del, uid, gid, perm); + if (!st) + goto out; + + ldmsd_plugin_init(&st->base); + ldmsd_store_init(st); + + out: + return st; } -struct ldmsd_plugin_cfg *new_plugin(char *plugin_name, +/* These are for tracking the single instance plugins, preventing them from + * multiple `load` (but load, term, load is OK). */ +static int __plugin1_ent_cmp(void *tree_key, const void *key) +{ + return strcmp(tree_key, key); +} +static struct rbt __plugin1_rbt = RBT_INITIALIZER(__plugin1_ent_cmp); +struct plugin1_ent { + struct rbn rbn; + struct ldmsd_plugin *p; +}; +static pthread_mutex_t __plugin1_rbt_mutex = PTHREAD_MUTEX_INITIALIZER; + +#define PLUGIN1_TREE_LOCK() pthread_mutex_lock(&__plugin1_rbt_mutex) +#define PLUGIN1_TREE_UNLOCK() pthread_mutex_unlock(&__plugin1_rbt_mutex) + +static struct plugin1_ent *__plugin1_find(const char *name) +{ + return (void*)rbt_find(&__plugin1_rbt, name); +} + +static struct plugin1_ent *__plugin1_ins(struct ldmsd_plugin *pi) +{ + struct plugin1_ent *ent; + + ent = calloc(1, sizeof(*ent)); + if (!ent) + return NULL; + ent->p = pi; + ldmsd_cfgobj_get(&pi->cfgobj); + rbn_init(&ent->rbn, pi->name); + rbt_ins(&__plugin1_rbt, &ent->rbn); + return ent; +} + +static void __plugin1_del(struct plugin1_ent *ent) +{ + rbt_del(&__plugin1_rbt, &ent->rbn); + ldmsd_cfgobj_put(&ent->p->cfgobj); + free(ent); +} + +struct ldmsd_plugin *new_plugin(const char *inst_name, + char *plugin_name, char *errstr, size_t errlen) { char library_name[LDMSD_PLUGIN_LIBPATH_MAX]; char library_path[LDMSD_PLUGIN_LIBPATH_MAX]; - struct ldmsd_plugin *lpi; - struct ldmsd_plugin_cfg *pi = NULL; + struct ldmsd_plugin *pi = NULL; char *pathdir = library_path; char *libpath; char *saveptr = NULL; char *path = getenv("LDMSD_PLUGIN_LIBPATH"); void *d = NULL; + int rc; if (!path) path = LDMSD_PLUGIN_LIBPATH_DEFAULT; @@ -142,7 +310,7 @@ struct ldmsd_plugin_cfg *new_plugin(char *plugin_name, "'%s': dlerror %s\n", plugin_name, dlerr); snprintf(errstr, errlen, "Bad plugin" " '%s'. dlerror %s", plugin_name, dlerr); - goto err; + goto err_0; } } @@ -152,83 +320,114 @@ struct ldmsd_plugin_cfg *new_plugin(char *plugin_name, "dlerror %s\n", plugin_name, dlerr); snprintf(errstr, errlen, "Failed to load the plugin '%s'. " "dlerror %s", plugin_name, dlerr); - goto err; + goto err_0; + } + + ldmsd_plugin_instance_get_f pi_get; + pi_get = dlsym(d, "get_plugin_instance"); + if (pi_get) { + /* Plugin instance */ + /* TODO uid, gid, perm ... */ + pi = pi_get(inst_name, geteuid(), getegid(), 0770); + if (!pi) { + snprintf(errstr, errlen, + "The plugin '%s' (%s) could not be loaded.", + inst_name, plugin_name); + goto err_0; + } + /* The returned `pi` is locked, and it is already in the + * plugin cfgobj tree. */ + ldmsd_cfgobj_unlock(&pi->cfgobj); + goto out; } - ldmsd_plugin_get_f pget = dlsym(d, "get_plugin"); + /* else, let through */ + + struct plugin1_ent *ent; + + /* Old style plugin */ + PLUGIN1_TREE_LOCK(); + /* make sure that the single-instance plugins are not loaded multiple + * times. */ + ent = __plugin1_find(plugin_name); + if (ent) { + errno = EEXIST; + snprintf(errstr, errlen, "The library, '%s' does not support " + "multiple instances." , plugin_name); + goto err_1; + } + + ldmsd_plugin_get_f pget; + pget = dlsym(d, "get_plugin"); if (!pget) { snprintf(errstr, errlen, "The library, '%s', is missing the get_plugin() " "function.", plugin_name); - goto err; + goto err_1; } - lpi = pget(); - if (!lpi) { + pi = pget(); + if (!pi) { snprintf(errstr, errlen, "The plugin '%s' could not be loaded.", plugin_name); - goto err; + goto err_1; + } + switch (pi->type) { + case LDMSD_PLUGIN_SAMPLER: + rc = ldmsd_plugin_cfgobj_init(pi, inst_name); + if (rc) + goto err_1; + ldmsd_sampler_init((void*)pi); + break; + case LDMSD_PLUGIN_STORE: + rc = ldmsd_plugin_cfgobj_init(pi, inst_name); + if (rc) + goto err_1; + ldmsd_store_init((void*)pi); + break; + default: + snprintf(errstr, errlen, + "Plugin '%s' (%s) has an unknown type: %d.", + inst_name, plugin_name, pi->type); + goto err_1; } - pi = calloc(1, sizeof *pi); - if (!pi) - goto enomem; - pthread_mutex_init(&pi->lock, NULL); - pi->thread_id = -1; - pi->handle = d; - pi->name = strdup(plugin_name); - if (!pi->name) - goto enomem; pi->libpath = strdup(library_name); - if (!pi->libpath) - goto enomem; - pi->plugin = lpi; - TAILQ_INIT(&lpi->avl_q); - TAILQ_INIT(&lpi->kwl_q); - lpi->pi = pi; - pi->sample_interval_us = 1000000; - pi->sample_offset_us = 0; - LIST_INSERT_HEAD(&plugin_list, pi, entry); + if (!pi->libpath) { + snprintf(errstr, errlen, "No memory"); + goto err_1; + } + TAILQ_INIT(&pi->avl_q); + TAILQ_INIT(&pi->kwl_q); + ent = __plugin1_ins(pi); + if (!ent) { + snprintf(errstr, errlen, + "Cannot add plugin '%s', error: %d", + plugin_name, errno); + goto err_1; + } + rc = ldmsd_cfgobj_add(&pi->cfgobj); + if (rc) { + snprintf(errstr, errlen, + "Cannot add plugin instance '%s' (%s), error: %d", + inst_name, plugin_name, rc); + goto err_2; + } + PLUGIN1_TREE_UNLOCK(); +out: return pi; -enomem: - snprintf(errstr, errlen, "No memory"); -err: + +err_2: + __plugin1_del(ent); +err_1: + PLUGIN1_TREE_UNLOCK(); +err_0: if (pi) { - pthread_mutex_destroy(&pi->lock); - if (pi->name) - free(pi->name); - if (pi->libpath) - free(pi->libpath); - free(pi); + ldmsd_cfgobj_put(&pi->cfgobj); } if (d) dlclose(d); return NULL; } -void destroy_plugin(struct ldmsd_plugin_cfg *p) -{ - struct avl_q_item *avl; - struct avl_q_item *kwl; - free(p->libpath); - free(p->name); - - /* - * Assume that the length of av_list_q and - * the length of kw_list_q are equal. - */ - while ((avl = TAILQ_FIRST(&p->plugin->avl_q)) && - (kwl = TAILQ_FIRST(&p->plugin->kwl_q))) { - TAILQ_REMOVE(&p->plugin->avl_q, avl, entry); - TAILQ_REMOVE(&p->plugin->kwl_q, kwl, entry); - free(avl->av_list); - free(avl); - free(kwl->av_list); - free(kwl); - } - LIST_REMOVE(p, entry); - dlclose(p->handle); - free(p); -} - const char *prdcr_state_str(enum ldmsd_prdcr_state state) { switch (state) { @@ -277,15 +476,17 @@ int ldmsd_compile_regex(regex_t *regex, const char *regex_str, /* * Load a plugin */ -int ldmsd_load_plugin(char *plugin_name, char *errstr, size_t errlen) +int ldmsd_load_plugin(const char *inst_name, char *plugin_name, + char *errstr, size_t errlen) { - struct ldmsd_plugin_cfg *pi = ldmsd_get_plugin(plugin_name); + struct ldmsd_plugin *pi = ldmsd_get_plugin(inst_name); if (pi) { - snprintf(errstr, errlen, "Plugin '%s' already loaded", - plugin_name); + snprintf(errstr, errlen, "Plugin '%s' (%s) already loaded", + inst_name, plugin_name); + ldmsd_put_plugin(pi); return EEXIST; } - pi = new_plugin(plugin_name, errstr, errlen); + pi = new_plugin(inst_name, plugin_name, errstr, errlen); if (!pi) return -1; return 0; @@ -294,25 +495,25 @@ int ldmsd_load_plugin(char *plugin_name, char *errstr, size_t errlen) /* * Destroy and unload the plugin */ -int ldmsd_term_plugin(char *plugin_name) +int ldmsd_term_plugin(char *inst_name) { int rc = 0; - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi; - pi = ldmsd_get_plugin(plugin_name); + pi = ldmsd_get_plugin(inst_name); if (!pi) return ENOENT; - pthread_mutex_lock(&pi->lock); - if (pi->ref_count) { - rc = EINVAL; - pthread_mutex_unlock(&pi->lock); + ldmsd_cfgobj_lock(&pi->cfgobj); + if (pi->type == LDMSD_PLUGIN_SAMPLER && LDMSD_SAMPLER(pi)->thread_id >= 0) { + rc = EBUSY; goto out; } - pi->plugin->term(pi->plugin); - pthread_mutex_unlock(&pi->lock); - destroy_plugin(pi); + pi->term(pi); + ldmsd_cfgobj_rm(&pi->cfgobj); out: + ldmsd_cfgobj_unlock(&pi->cfgobj); + ldmsd_put_plugin(pi); return rc; } @@ -324,7 +525,7 @@ int ldmsd_config_plugin(char *plugin_name, struct attr_value_list *_kw_list) { int rc = 0; - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi; struct avl_q_item *avl; struct avl_q_item *kwl; @@ -341,11 +542,12 @@ int ldmsd_config_plugin(char *plugin_name, if (!pi) return ENOENT; - pthread_mutex_lock(&pi->lock); - rc = pi->plugin->config(pi->plugin, _kw_list, _av_list); - TAILQ_INSERT_TAIL(&pi->plugin->kwl_q, kwl, entry); - TAILQ_INSERT_TAIL(&pi->plugin->avl_q, avl, entry); - pthread_mutex_unlock(&pi->lock); + ldmsd_cfgobj_lock(&pi->cfgobj); + rc = pi->config(pi, _kw_list, _av_list); + TAILQ_INSERT_TAIL(&pi->kwl_q, kwl, entry); + TAILQ_INSERT_TAIL(&pi->avl_q, avl, entry); + ldmsd_cfgobj_unlock(&pi->cfgobj); + ldmsd_put_plugin(pi); return rc; } @@ -1286,19 +1488,19 @@ static int ldmsd_plugins_usage_dir(const char *path, const char *plugname) assert(suff != NULL || NULL == "plugin glob match means . will be found always"); *suff = '\0'; char err_str[LEN_ERRSTR]; - if (ldmsd_load_plugin(b, err_str, LEN_ERRSTR)) { + if (ldmsd_load_plugin(b, b, err_str, LEN_ERRSTR)) { fprintf(stderr, "Unable to load plugin %s: %s\n", b, err_str); goto next; } - struct ldmsd_plugin_cfg *pi = ldmsd_get_plugin(b); + struct ldmsd_plugin *pi = ldmsd_get_plugin(b); if (!pi) { fprintf(stderr, "Unable to get plugin %s\n", b); goto next; } const char *ptype; - switch (pi->plugin->type) { + switch (pi->type) { case LDMSD_PLUGIN_OTHER: ptype = "OTHER"; break; @@ -1312,10 +1514,10 @@ static int ldmsd_plugins_usage_dir(const char *path, const char *plugname) ptype = "BAD plugin"; break; } - if (matchtype && tmatch != pi->plugin->type) + if (matchtype && tmatch != pi->type) goto next; printf("======= %s %s:\n", ptype, b); - const char *u = pi->plugin->usage(pi->plugin); + const char *u = pi->usage(pi); printf("%s\n", u); printf("=========================\n"); rc = ldmsd_term_plugin(b); diff --git a/ldms/src/ldmsd/ldmsd_request.c b/ldms/src/ldmsd/ldmsd_request.c index 1869d15d4..7b17dbf70 100644 --- a/ldms/src/ldmsd/ldmsd_request.c +++ b/ldms/src/ldmsd/ldmsd_request.c @@ -2591,8 +2591,8 @@ static int strgp_add_handler(ldmsd_req_ctxt_t reqc) } - struct ldmsd_plugin_cfg *store; - store = ldmsd_get_plugin(plugin); + struct ldmsd_store *store; + store = (void*)ldmsd_get_plugin(plugin); if (!store) { reqc->errcode = ENOENT; cnt = Snprintf(&reqc->line_buf, &reqc->line_len, @@ -2618,8 +2618,7 @@ static int strgp_add_handler(ldmsd_req_ctxt_t reqc) goto enomem; } - __atomic_add_fetch(&store->ref_count, 1, __ATOMIC_SEQ_CST); /* Release in strgp_del */ - strgp->store = store->store; + strgp->store = store; /* cfgobj ref is released in strgp_del */ strgp->plugin_name = strdup(plugin); if (!strgp->plugin_name) goto enomem; @@ -4917,7 +4916,8 @@ static char *plugn_state_str(enum ldmsd_plugin_type type) extern int ldmsd_start_sampler(char *plugin_name, char *interval, char *offset); extern int ldmsd_stop_sampler(char *plugin); -extern int ldmsd_load_plugin(char *plugin_name, char *errstr, size_t errlen); +extern int ldmsd_load_plugin(const char *inst_name, char *plugin_name, + char *errstr, size_t errlen); extern int ldmsd_term_plugin(char *plugin_name); extern int ldmsd_config_plugin(char *plugin_name, struct attr_value_list *_av_list, @@ -5034,36 +5034,52 @@ static int plugn_stop_handler(ldmsd_req_ctxt_t reqc) int __plugn_status_json_obj(ldmsd_req_ctxt_t reqc) { - extern struct plugin_list plugin_list; - struct ldmsd_plugin_cfg *p; + struct ldmsd_plugin *p; int rc, count; + struct ldmsd_cfgobj *obj; + long interval_us; + long offset_us; reqc->errcode = 0; rc = linebuf_printf(reqc, "["); if (rc) return rc; count = 0; - LIST_FOREACH(p, &plugin_list, entry) { + ldmsd_cfg_lock(LDMSD_CFGOBJ_PLUGIN); + for (obj = ldmsd_cfgobj_first(LDMSD_CFGOBJ_PLUGIN); + obj; obj = ldmsd_cfgobj_next(obj)) { + if (count) { rc = linebuf_printf(reqc, ",\n"); if (rc) - return rc; + goto out; } + p = container_of(obj, struct ldmsd_plugin, cfgobj); count++; + if (p->type == LDMSD_PLUGIN_SAMPLER) { + interval_us = LDMSD_SAMPLER(p)->sample_interval_us; + offset_us = LDMSD_SAMPLER(p)->sample_offset_us; + } else { + interval_us = 0; + offset_us = 0; + } rc = linebuf_printf(reqc, - "{\"name\":\"%s\",\"type\":\"%s\"," + "{\"name\":\"%s (%s)\",\"type\":\"%s\"," "\"sample_interval_us\":%ld," "\"sample_offset_us\":%ld," "\"libpath\":\"%s\"}", - p->plugin->name, - plugn_state_str(p->plugin->type), - p->sample_interval_us, p->sample_offset_us, + p->cfgobj.name, + p->name, + plugn_state_str(p->type), + interval_us, offset_us, p->libpath); if (rc) - return rc; + goto out; } rc = linebuf_printf(reqc, "]"); + out: + ldmsd_cfg_unlock(LDMSD_CFGOBJ_PLUGIN); return rc; } @@ -5096,23 +5112,28 @@ static int plugn_status_handler(ldmsd_req_ctxt_t reqc) static int plugn_load_handler(ldmsd_req_ctxt_t reqc) { - char *plugin_name, *attr_name; - plugin_name = NULL; + char *name, *attr_name, *plugin; + name = NULL; + plugin = NULL; size_t cnt = 0; attr_name = "name"; - plugin_name = ldmsd_req_attr_str_value_get_by_id(reqc, LDMSD_ATTR_NAME); - if (!plugin_name) { + name = ldmsd_req_attr_str_value_get_by_id(reqc, LDMSD_ATTR_NAME); + if (!name) { ovis_log(config_log, OVIS_LERROR, "load plugin called without name=$plugin"); goto einval; } - reqc->errcode = ldmsd_load_plugin(plugin_name, reqc->line_buf, - reqc->line_len); + attr_name = "plugin"; + plugin = ldmsd_req_attr_str_value_get_by_id(reqc, LDMSD_ATTR_PLUGIN); + + reqc->errcode = ldmsd_load_plugin(name, plugin?plugin:name, + reqc->line_buf, + reqc->line_len); if (reqc->errcode) cnt = strlen(reqc->line_buf) + 1; else - __dlog(DLOG_CFGOK, "load name=%s\n", plugin_name); + __dlog(DLOG_CFGOK, "load name=%s\n", name); goto send_reply; einval: @@ -5121,7 +5142,8 @@ static int plugn_load_handler(ldmsd_req_ctxt_t reqc) "The attribute '%s' is required by load.", attr_name); send_reply: ldmsd_send_req_response(reqc, reqc->line_buf); - free(plugin_name); + free(name); + free(plugin); return 0; } @@ -5250,23 +5272,28 @@ static int plugn_config_handler(ldmsd_req_ctxt_t reqc) return 0; } -extern struct plugin_list plugin_list; int __plugn_list_string(ldmsd_req_ctxt_t reqc) { char *name = NULL; int rc, count = 0; - struct ldmsd_plugin_cfg *p; + struct ldmsd_cfgobj *obj; + struct ldmsd_plugin *p; rc = 0; name = ldmsd_req_attr_str_value_get_by_id(reqc, LDMSD_ATTR_NAME); + ldmsd_cfg_lock(LDMSD_CFGOBJ_PLUGIN); + + for (obj = ldmsd_cfgobj_first(LDMSD_CFGOBJ_PLUGIN); obj; + obj = ldmsd_cfgobj_next(obj)) { + + p = container_of(obj, struct ldmsd_plugin, cfgobj); - LIST_FOREACH(p, &plugin_list, entry) { if (name && (0 != strcmp(name, p->name))) continue; - if (p->plugin->usage) { + if (p->usage) { rc = linebuf_printf(reqc, "%s\n%s", - p->name, p->plugin->usage(p->plugin)); + p->name, p->usage(p)); } else { rc = linebuf_printf(reqc, "%s\n", p->name); } @@ -5274,6 +5301,7 @@ int __plugn_list_string(ldmsd_req_ctxt_t reqc) goto out; count++; } + ldmsd_cfg_unlock(LDMSD_CFGOBJ_PLUGIN); if (name && (0 == count)) { reqc->line_off = snprintf(reqc->line_buf, reqc->line_len, "Plugin '%s' not loaded.", name); @@ -6222,8 +6250,8 @@ static int dump_cfg_handler(ldmsd_req_ctxt_t reqc) { FILE *fp = NULL; char *filename = NULL; - extern struct plugin_list plugin_list; - struct ldmsd_plugin_cfg *p; + struct ldmsd_cfgobj *obj; + struct ldmsd_plugin *p; int rc; int i; char hostname[128], port_no[32]; @@ -6335,10 +6363,13 @@ static int dump_cfg_handler(ldmsd_req_ctxt_t reqc) /* Plugins */ struct avl_q_item *avl; struct avl_q_item *kwl; - LIST_FOREACH(p, &plugin_list, entry) { + ldmsd_cfg_lock(LDMSD_CFGOBJ_PLUGIN); + for (obj = ldmsd_cfgobj_first(LDMSD_CFGOBJ_PLUGIN); obj; + obj = ldmsd_cfgobj_next(obj)) { + p = container_of(obj, struct ldmsd_plugin, cfgobj); fprintf(fp, "load name=%s\n", p->name); - avl = TAILQ_FIRST(&p->plugin->avl_q); - kwl = TAILQ_FIRST(&p->plugin->kwl_q); + avl = TAILQ_FIRST(&p->avl_q); + kwl = TAILQ_FIRST(&p->kwl_q); /* Assume that the lengths of av_list_q and kw_list_q are equal. */ while (avl) { fprintf(fp, "config name=%s ", p->name); @@ -6359,16 +6390,17 @@ static int dump_cfg_handler(ldmsd_req_ctxt_t reqc) kwl = TAILQ_NEXT(kwl, entry); } - if (p->plugin->type == LDMSD_PLUGIN_SAMPLER) { - if (p->os) { + if (p->type == LDMSD_PLUGIN_SAMPLER) { + if (LDMSD_SAMPLER(p)->os) { /* Plugin is running. */ fprintf(fp, "start name=%s interval=%ld offset=%ld\n", - p->plugin->name, - p->sample_interval_us, - p->sample_offset_us); + p->name, + LDMSD_SAMPLER(p)->sample_interval_us, + LDMSD_SAMPLER(p)->sample_offset_us); } } } + ldmsd_cfg_unlock(LDMSD_CFGOBJ_PLUGIN); /* Updaters */ ldmsd_name_match_t match; ldmsd_updtr_t updtr; diff --git a/ldms/src/ldmsd/ldmsd_strgp.c b/ldms/src/ldmsd/ldmsd_strgp.c index e28672df3..36934c50a 100644 --- a/ldms/src/ldmsd/ldmsd_strgp.c +++ b/ldms/src/ldmsd/ldmsd_strgp.c @@ -519,17 +519,17 @@ static int strgp_open(ldmsd_strgp_t strgp, ldmsd_prdcr_set_t prd_set) int i, idx, rc; const char *name; ldmsd_strgp_metric_t metric; - struct ldmsd_plugin_cfg *store; + struct ldmsd_plugin *pi; int alloc_digest = 0; if (!prd_set->set) return ENOENT; if (!strgp->store) { - store = ldmsd_get_plugin(strgp->plugin_name); - if (!store) + pi = ldmsd_get_plugin(strgp->plugin_name); + if (!pi) return ENOENT; - strgp->store = store->store; + strgp->store = container_of(pi, struct ldmsd_store, base); } /* Build metric list from the schema in the producer set */ strgp->metric_count = 0; @@ -611,11 +611,11 @@ int strgp_decomp_init(ldmsd_strgp_t strgp, ldmsd_req_ctxt_t reqc) if (!strgp->store) { /* load store */ - struct ldmsd_plugin_cfg *store; - store = ldmsd_get_plugin(strgp->plugin_name); - if (!store) + struct ldmsd_plugin *pi; + pi = ldmsd_get_plugin(strgp->plugin_name); + if (!pi) return ENOENT; - strgp->store = store->store; + strgp->store = container_of(pi, struct ldmsd_store, base); } assert(!strgp->decomp); return ldmsd_decomp_config(strgp, strgp->decomp_name, reqc); @@ -795,7 +795,7 @@ int ldmsd_strgp_del(const char *strgp_name, ldmsd_sec_ctxt_t ctxt) { int rc = 0; ldmsd_strgp_t strgp; - struct ldmsd_plugin_cfg *pi; + struct ldmsd_plugin *pi = NULL; pthread_mutex_lock(cfgobj_locks[LDMSD_CFGOBJ_STRGP]); strgp = (ldmsd_strgp_t)__cfgobj_find(strgp_name, LDMSD_CFGOBJ_STRGP); @@ -818,8 +818,7 @@ int ldmsd_strgp_del(const char *strgp_name, ldmsd_sec_ctxt_t ctxt) } /* Put back the reference taken when linking the plugin to the strgp. */ - pi = strgp->store->base.pi; - __atomic_sub_fetch(&pi->ref_count, 1, __ATOMIC_SEQ_CST); + pi = &strgp->store->base; rbt_del(cfgobj_trees[LDMSD_CFGOBJ_STRGP], &strgp->obj.rbn); ldmsd_strgp_put(strgp); /* tree reference */ @@ -835,6 +834,8 @@ int ldmsd_strgp_del(const char *strgp_name, ldmsd_sec_ctxt_t ctxt) pthread_mutex_unlock(cfgobj_locks[LDMSD_CFGOBJ_STRGP]); if (strgp) ldmsd_strgp_put(strgp); /* `find` reference */ + if (pi) + ldmsd_put_plugin(pi); return rc; } From 6ad5173cafed9baa5ef01cdbfe186ce78a688a6b Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Wed, 6 Dec 2023 12:51:50 -0600 Subject: [PATCH 04/13] Update ldmsd_controller help string & manpage - Add plugin instance. - Update format for consistency. --- ldms/man/ldmsd_controller.man | 467 +++++++++++++++++------------ ldms/python/ldmsd/ldmsd_controller | 11 +- 2 files changed, 276 insertions(+), 202 deletions(-) diff --git a/ldms/man/ldmsd_controller.man b/ldms/man/ldmsd_controller.man index 6f2e06c31..843ec7777 100644 --- a/ldms/man/ldmsd_controller.man +++ b/ldms/man/ldmsd_controller.man @@ -80,91 +80,131 @@ regular expression characters. Please see \fBregex(7)\fR for more information. .SH PLUGIN COMMAND SYNTAX -.SS Load a plugin +.SS Load a plugin instance .BR load -attr= +.BI name\fR= NAME +.BI plugin\fR= PLUGIN .br .RS .TP -.BI name " name" +.BI name\fR= NAME .br -The plugin name +The name of the plugin instance (user-defined, e.g. p0). +.br +.TP +.BI plugin\fR= PLUGIN +.br +The plugin to load (e.g. meminfo, store_sos). .SS List the usage of the loaded plugins .BR usage -.SS unload a plugin +.SS Unload a plugin instance .BR term -attr= +.BI name\fR= NAME .br .RS .TP -.BI name " name" +.BI name\fR= NAME .br -The plugin name +The name of the plugin instance to terminate. +.RE -.SS Send a configuration command to the specified plugin. +.SS Configuring a storage plugin. +.TP .BR config -attr= +.BI name\fR= NAME +.BI path\fR= PATH +.RB [ ... ] + .RS .TP -.BI name " name" +.BI name\fR= NAME .br -The plugin name +The plugin instance name .TP -.BR attr=value +.BI path\fR= PATH .br -Plugin specific attr=value tuples +The path to the storage. Each storage plugin takes \fBpath\fR differently (e.g. +store_kafka that takes path as path to store_kafka JSON configuration file). So, +please consult the storage plugin manual. +.TP +.RB [ ... ] +.br +The storage plugins may have additional parameters. Please consult its +manual/usage for more information. +.RE + +.SS Configuring a sampler plugin. +.TP +.BR config +.BI name\fR= NAME +.BI producer\fR= PRODUCER +.BI instance\fR= INSTANCE +.RB [ component_id\fR=\fICOMPONENT_ID ] +.RB [ schema\fR=\fISCHEMA ] +.RB [ uid\fR=\fIUID ] +.RB [ gid\fR=\fIGID ] +.RB [ perm\fR=\fIPERM ] +.RB [ ... ] .RS -.B Attributes specific for sampler plugins (Some sampler plugins may have additional -attributes) .TP -.BI producer " producer" +.BI name\fR= NAME +.br +The plugin instance name +.TP +.BI producer\fR= PRODUCER .br A unique name for the host providing the data .TP -.BI instance " instance" +.BI instance\fR= INSTANCE .br The set instance name. The name must be unique among all metric sets in all LDMS daemons. .TP -.BI [component_id " component_id"] +.RB [ component_id\fR=\fICOMPONENT_ID ] .br A unique number for the comopnent being monitored. The default is zero. .TP -.BI [schema " schema"] +.RB [ schema\fR=\fISCHEMA ] .br The name of the metric set schema. .TP -.BI [job_set " job_set"] +.RB [ job_set\fR=\fIJOB_SET ] .br The set instance name of the set containing the job data. The default is 'job_info'. .TP -.BI [uid " uid"] +.RB [ uid\fR=\fIUID ] .br The user id of the set's owner. The default is the returned value of geteuid(). .TP -.BI [gid " gid"] +.RB [ gid\fR=\fIGID ] .br The group id of the set's owner. The default is the returned value of getegid(). .TP -.BI [perm " perm"] +.RB [ perm\fR=\fIPERM ] .br The sampler plugin instance access permission. The default is 0440. -.RE +.TP +.RB [ ... ] +.br +Some sampler plugin may have additional parameters. Please consult its +manual/usage for more information. .RE .SS Start a sampler plugin .BR start -attr= +.BI name\fR= NAME +.BI interval\fR= INTERVAL +.RB [ offset\fR=\fIOFFSET ] .RS .TP -.BI name " name" +.BI name\fR= NAME .br The plugin name. .TP -.BI interval " interval" +.BI interval\fR= INTERVAL .br The sample interval, which is a float followed by a unit string. If no unit string is given, the default unit is microseconds. @@ -176,7 +216,7 @@ A unit string is one of the followings: h -- hours d -- days .TP -.BI [offset " offset"] +.RB [ offset\fR=\fIOFFSET ] .br Offset (shift) from the sample mark in the same format as intervals. Offset can be positive or negative with magnitude up to 1/2 @@ -187,12 +227,12 @@ the sample interval. The default offset is 0. Collection is always synchronous. .SS Stop a sampler plugin .BR stop -attr= +.BI name\fR= NAME .RS .TP -.BI name " name" +.BI name\fR= NAME .br -The plugin name. +The plugin instance name to stop. .RE .SH AUTHENTICATION COMMAND SYNTAX @@ -200,7 +240,7 @@ The plugin name. .B auth_add \fBname\fR=\fINAME\fR \fBplugin\fR=\fIPLUGIN\fR -[ ... \fIPLUGIN ATTRIBUTES\fR ... ] +.RB [ ... ] .RS .TP \fBname\fR=\fINAME\fR @@ -212,7 +252,7 @@ The name of the authentication domain. This is the name referred to by .br The LDMS Authentication Plugin for this domain. .TP -[ ... \fIPLUGIN ATTRIBUTES\fR ... ] +.RB [ ... ] .br Arbitrary plugin attributes. Please consult the manual of the authentication plugin for more information. @@ -223,7 +263,7 @@ plugin for more information. .SS Instruct ldmsd to listen to a port .B listen \fBport\fR=\fIPORT\fR -\fBxprt\fR=\fIsock\fR|\fIrdma\fR|\fIugni\fR|\fIfabric\fR +\fBxprt\fR=\fBsock\fR|\fBrdma\fR|\fBugni\fR|\fBfabric\fR [\fBhost\fR=\fIHOST\fR] [\fBauth\fR=\fIAUTH_REF\fR] .RS @@ -233,16 +273,16 @@ plugin for more information. The port to listen to. Also, please be sure not to use ephemeral port (ports in the range of \fB/proc/sys/net/ip4/ip_local_port_range\fR). .TP -\fBxprt\fR=\fIsock\fR|\fIrdma\fR|\fIugni\fR|\fIfabric\fR +\fBxprt\fR=\fBsock\fR|\fBrdma\fR|\fBugni\fR|\fBfabric\fR .br The type of the transport. .TP -\fBhost\fR=\fIHOST\fR +[\fBhost\fR=\fIHOST\fR] .br An optional hostname or IP address to bind. If not given, listen to all addresses (0.0.0.0 or PORT). .TP -\fBauth\fR=\fIAUTH_REF\fR +[\fBauth\fR=\fIAUTH_REF\fR] .br Instruct \fBldmsd\fR to use \fIAUTH_REF\fR (a name reference to \fBauth\fR object created by \fBauth_add\fR command) to authenticate connections on this @@ -252,29 +292,39 @@ the CLI options (see \fBldmsd\fR(8) option \fB-a\fR). .SH PRODUCER COMMAND SYNTAX .SS Add a producer to the aggregator +.TP 10 .BR prdcr_add -attr= +.BI name\fR= NAME +.BI xprt\fR= XPRT +.BI host\fR= HOST +.BR type = active | passive +.BI reconnect\fR= INTERVAL +.RB [ perm\fR=\fIPERMISSION ] +.RB [ auth\fR=\fIAUTH_REF ] +.RB [ rail\fR=\fINUM ] +.RB [ credits\fR=\fIBYTES ] +.RB [ rx_rate\fR=\fIBYTES_PER_SEC ] .br -.RS +.RS 8 .TP -.BI name " name" +.BI name\fR= NAME .br The producer name. The producer name must be unique in an aggregator. It is independent of any attributes specified for the metric sets or hosts. .TP -.BI xprt " xprt" +.BI xprt\fR= XPRT .br The transport name [sock, rdma, ugni] .TP -.BI host " host" +.BI host\fR= HOST .br The hostname of the host .TP -.BI type " conn_type" +.BR type = active | passive .br The connection type [active, passive] .TP -.BI reconnect " interval" +.BI reconnect\fR= INTERVAL .br The connection retry interval, which is a float followed by a unit string. If no unit string is given, the default unit is microseconds. @@ -286,33 +336,33 @@ A unit string is one of the followings: h -- hours d -- days .TP -.BI interval " interval" +.BI interval\fR= INTERVAL " **DEPRECATED**" .br It is being deprecated. Please use 'reconnect'. .TP -.BI [perm " permission"] +.RB [ perm\fR=\fIPERMISSION ] .br The permission to modify the producer in the future .TP -.BI [auth " AUTH_REF"] +.RB [ auth\fR=\fIAUTH_REF ] .br Instruct \fBldmsd\fR to use \fIAUTH_REF\fR (a name reference to \fBauth\fR object created by \fBauth_add\fR command) with the connections to this producer. If not given, the default authentication method specified on the CLI options (see \fBldmsd\fR(8) option \fB-a\fR) is used. .TP -.BI [rail " NUM"] +.RB [ rail\fR=\fINUM ] .br The number of rail endpooints for the prdcr (default: 1). .TP -.BI [credits " BYTES"] +.RB [ credits\fR=\fIBYTES ] .br The send credits our ldmsd (the one we are controlling) advertises to the prdcr (default: value from ldmsd --credits option). This limits how much outstanding data our ldmsd holds for the prdcr. The prdcr drops messages when it does not have enough send credits. .TP -.BI [rx_rate " BYTES_PER_SEC"] +.RB [ rx_rate\fR=\fIBYTES_PER_SEC ] .br The recv rate (bytes/sec) limit for this connection. The default is -1 (unlimited). @@ -323,24 +373,24 @@ The recv rate (bytes/sec) limit for this connection. The default is -1 The producer cannot be in use or running .br .BR prdcr_del -attr= +.BI name\fR= NAME .RS .TP -.BI name " name" +.BI name\fR= NAME .br The producer name .RE .SS Start a producer .BR prdcr_start -attr= +.BI name\fR= NAME .RS .TP -.BI name " name" +.BI name\fR= NAME .br The producer name .TP -.BI [reconnect " interval"] +.RB [ reconnect\fR=\fIINTERVAL ] .br The connection retry interval, which is a float followed by a unit string. If no unit string is given, the default unit is microseconds. @@ -353,21 +403,22 @@ A unit string is one of the followings: d -- days If unspecified, the previously configured value will be used. Optional. .TP -.BI [interval " interval"] +.RB [ interval\fR=\fIINTERVAL ] " **DEPRECATED**" .br It is being deprecated. Please use 'reconnect'. .RE .SS Start all producers matching a regular expression .BR prdcr_start_regex -attr= +.BI regex\fR= REGEX +.RB [ reconnect\fR=\fIINTERVAL ] .RS .TP -.BI regex " regex" +.BI regex\fR= REGEX .br A regular expression .TP -.BI [reconnect " interval"] +.RB [ reconnect\fR=\fIINTERVAL ] .br The connection retry interval, which is a float followed by a unit stirng. If no unit string is given, the default unit is microseconds. @@ -380,67 +431,80 @@ A unit string is one of the followings: d -- days If unspecified, the previously configured value will be used. Optional. .TP -.BI [interval " interval"] +.RB [ interval\fR=\fIINTERVAL ] " **DEPRECATED**" .br It is being deprecated. Please use 'reconnect'. .RE .SS Stop a producer .BR prdcr_stop -attr= +.BI name\fR= NAME .RS .TP -.BI name " name" +.BI name\fR= NAME .br The producer name .RE .SS Stop all producers matching a regular expression .BR prdcr_stop_regex -attr= +.BI regex\fR= REGEX .RS .TP -.BI regex " regex" +.BI regex\fR= REGEX .br A regular expression .RE .SS Query producer status .BR prdcr_status -attr= +.RB [ name\fR=\fINAME ] .RS .TP -.BI [name " name"] +.RB [ name\fR=\fINAME ] .br The producer name. If none is given, the statuses of all producers are reported. .RE .SS Subscribe for stream data from all matching producers -.BR prdcr_subsribe +.BR prdcr_subscribe +.BI regex\fR= REGEX +.BI stream\fR= STREAM +.RB [ rx_rate\fR=\fIBYTES_PER_SECOND ] .RS .TP -.BI regex " regex" +.BI regex\fR= REGEX .br The regular expression matching producer name .TP -.BI stream " stream" +.BI stream\fR= STREAM .br -The stream name +The stream name, or a regular expression matching a name. +.TP +.RB [ rx_rate\fR=\fIBYTES_PER_SECOND ] +.br +The recv rate (bytes/sec) limit for the matching streams. The default is -1 +(unlimited). .RE .SH UPDATER COMMAND SYNTAX .SS Add an updater process that will periodically sample producer metric sets .BR updtr_add -attr= +.BI name\fR= NAME +.BI interval\fR= INTERVAL +.RB [ offset\fR=\fIOFFSET ] +.RB [ push\fR=\fIonchange\fR|\fItrue ] +.RB [ auto_interval\fR=\fItrue\fR|\fIfalse ] +.RB [ perm\fR=\fIpermission ] .RS .TP -.BI name " name" +.BI name\fR= NAME .br The update policy name. The policy name should be unique. It is independent of any attributes specified for the metric sets or hosts. .TP -.BI interval " interval" +.BI interval\fR= INTERVAL .br The update/collect interval, which is a float followed by a unit string. If no unit string is given, the default unit is microseconds. @@ -452,52 +516,54 @@ A unit string is one of the followings: h -- hours d -- days .TP -.BI [offset " offset"] +.RB [ offset\fR=\fIOFFSET ] .br Offset for synchronized aggregation. Optional. .TP -.BI [push " onchange|true" ] +.RB [ push\fR=\fIonchange\fR|\fItrue ] .br Push mode: 'onchange' and 'true'. 'onchange' means the Updater will get an update whenever the set source ends a transaction or pushes the update. 'true' means the Updater will receive an update only when the set source pushes the update. If `push` is used, `auto_interval` cannot be `true`. .TP -.BI [auto_interval " true|false "] +.RB [ auto_interval\fR=\fItrue\fR|\fIfalse ] If true, the updater will schedule set updates according to the update hint. The sets with no hints will not be updated. If false, the updater will schedule the set updates according to the given interval and offset values. If not specified, the value is \fIfalse\fR. .TP -.BI [perm " permission"] +.RB [ perm\fR=\fIpermission ] .br The permission to modify the updater in the future .RE .SS Remove an updater from the configuration .BR updtr_del -attr= +.BI name\fR= NAME .RS .TP -.BI name " name" +.BI name\fR= NAME .br The update policy name .RE .SS Add a match condition that specifies the sets to update. .BR updtr_match_add -attr= +.BI name\fR= NAME +.BI regex\fR= REGEX +.BR match = inst | schema .RS .TP -.BI name " name" +.BI name\fR= NAME .br The update policy name .TP -.BI regex " regex" +.BI regex\fR= REGEX .br The regular expression .TP -.BI match " match (inst|schema)" +.BR match = inst | schema .br The value with which to compare; if match=inst, the expression will match the set's instance name, if @@ -507,18 +573,20 @@ schema name. .SS Remove a match condition from the Updater. .BR updtr_match_del -attr= +.BI name\fR= NAME +.BI regex\fR= REGEX +.BR match = inst | schema .RS .TP -.BI name " name" +.BI name\fR= NAME .br The update policy name .TP -.BI regex " regex" +.BI regex\fR= REGEX .br The regular expression .TP -.BI match " match (inst|schema)" +.BR match = inst | schema .br The value with which to compare; if match=inst, the expression will match the set's instance name, if @@ -530,42 +598,45 @@ schema name. This is required before starting the updater. .BR updtr_prdcr_add -attr= +.BI name\fR= NAME .RS .TP -.BI name " name" +.BI name\fR= NAME .br The update policy name .TP -.BI regex " regex" +.BI regex\fR= REGEX .br A regular expression matching zero or more producers .RE -.SS Remove matching producers to an updater policy +.SS Remove matching producers from an updater .BR updtr_prdcr_del -attr= +.BI name\fR= NAME +.BI regex\fR= REGEX .RS .TP -.BI name " name" +.BI name\fR= NAME .br The update policy name .TP -.BI regex " regex" +.BI regex\fR= REGEX .br A regular expression matching zero or more producers .RE .SS Start updaters. .BR updtr_start -attr= +.BI name\fR= NAME +.RB [ interval\fR=\fIINTERVAL ] +.RB [ offset\fR=\fIOFFSET ] .RS .TP -.BI name " name" +.BI name\fR= NAME .br The update policy name .TP -.BI [interval " interval"] +.RB [ interval\fR=\fIINTERVAL ] .br The update interval, which is a float followed by a unit string. If no unit string is given, the default unit is microseconds. @@ -578,7 +649,7 @@ A unit string is one of the followings: d -- days If this is not specified, the previously configured value will be used. Optional. .TP -.BI [offset " offset"] +.RB [ offset\fR=\fIOFFSET ] .br Offset for synchronized aggregation. Optional. .RE @@ -587,25 +658,26 @@ Offset for synchronized aggregation. Optional. The Updater must be stopped in order to change it's configuration. .BR updtr_stop -attr= +.BI name\fR= NAME .RS .TP -.BI name " name" +.BI name\fR= NAME .br The update policy name .RE .SS Query the updater status .BR updtr_status -attr= +.RB [ name\fR=\fIname ] +.RB [ reset = true | false ] .RS .TP -.BI [name " name"] +.RB [ name\fR=\fIname ] .br The updater name. If none is given, the statuses of all updaters are reported. .TP -.BI [reset " value"] +.RB [ reset = true | false ] .br If true, reset the updater's counters after returning the values. The default is false. @@ -613,40 +685,45 @@ The default is false. .SS Query the updaters' list of regular expressions to match set names or set schemas .BR updtr_match_list -attr= +.RB [ name\fR=\fINAME ] .RS .TP -.BI [name " name"] +.RB [ name\fR=\fINAME ] .br The Updater name. If none is given, all updaters' regular expression lists will be returned. .SH STORE COMMAND SYNTAX .SS Create a Storage Policy and open/create the storage instance. .BR strgp_add -attr= +.BI name\fR= NAME +.BI plugin\fR= PLUGIN +.BI container\fR= CONTAINER +.RB [ schema\fR=\fISCHEMA ] +.RB [ regex\fR=\fIREGEX ] +.RB [ perm\fR=\fIPERMISSION ] .RS .TP -.BI name " name" +.BI name\fR= NAME .br The unique storage policy name. .TP -.BI plugin " plugin" +.BI plugin\fR= PLUGIN .br The name of the storage backend. .TP -.BI container " container" +.BI container\fR= CONTAINER .br The storage backend container name. .TP -.BI [schema " schema"] +.RB [ schema\fR=\fISCHEMA ] .br The schema name of the metric set to store. If 'schema' is given, 'regex' is ignored. Either 'schema' or 'regex' must be given. .TP -.BI [regex " regex"] +.RB [ regex\fR=\fIREGEX ] .br a regular expression matching set schemas. It must be used with decomposition. Either 'schema' or 'regex' must be given. .TP -.BI [perm " permission"] +.RB [ perm\fR=\fIPERMISSION ] .br The permission to modify the storage in the future .RE @@ -655,10 +732,10 @@ The permission to modify the storage in the future All updaters must be stopped in order for a storage policy to be deleted .br .BR strgp_del -attr= +.BI name\fR= NAME .RS .TP -.BI name " name" +.BI name\fR= NAME .br The storage policy name .RE @@ -668,14 +745,15 @@ If no producers are added to the storage policy, the storage policy will apply on all producers. .br .BR strgp_prdcr_add -attr= +.BI name\fR= NAME +.BI regex\fR= REGEX .RS .TP -.BI name " name" +.BI name\fR= NAME .br The storage policy name .TP -.BI regex " name" +.BI regex\fR= REGEX .br A regular expression matching metric set producers. .RE @@ -683,13 +761,14 @@ A regular expression matching metric set producers. .SS Remove a regular expression from the producer match list .BR strgp_prdcr_del -attr= +.BI name\fR= NAME +.BI regex\fR= REGEX .RS -.BI name " name" +.BI name\fR= NAME .br The storage policy name .TP -.BI regex " regex" +.BI regex\fR= REGEX .br The regex of the producer to remove. .RE @@ -697,35 +776,37 @@ The regex of the producer to remove. .SS Add the name of a metric to store .BR strgp_metric_add -attr= +.BI name\fR= NAME +.BI metric\fR= METRIC .RS -.BI name " name" +.BI name\fR= NAME .br The storage policy name .TP -.BI metric " metric" +.BI metric\fR= METRIC .br The metric name. If the metric list is NULL, all metrics in the metric set will be stored. .RE .SS Remove a metric from the set of stored metrics. .BR strgp_metric_del -attr= +.BI name\fR= NAME +.BI metric\fR= METRIC .RS -.BI name " name" +.BI name\fR= NAME .br The storage policy name .TP -.BI metric " metric" +.BI metric\fR= METRIC .br The metric to remove .RE .SS Start a storage policy. .BR strgp_start -attr= +.BI name\fR= NAME .RS -.BI name " name" +.BI name\fR= NAME .br The storage policy name .RE @@ -735,19 +816,19 @@ The storage policy name A storage policy must be stopped in order to change its configuration. .BR strgp_stop -attr= +.BI name\fR= NAME .RS -.BI name " name" +.BI name\fR= NAME .br The storage policy name .RE .SS Query the storage policy status .BR strgp_status -attr= +.RB [ name\fR=\fINAME ] .RS .TP -.BI [name " name"] +.RB [ name\fR=\fINAME ] .br The storage policy name. If none is given, the statuses of all storage policies are reported. @@ -765,65 +846,48 @@ Please see \fBldmsd_setgroup\fR(7). .SH STREAM COMMAND SYNTAX .SS Publish data to the named stream .BR plublish -attr= +.BI name\fR= NAME +.BI data\fR= DATA .RS .TP -.BI name " name" +.BI name\fR= NAME .br The stream name .TP -.BI data " data" +.BI data\fR= DATA .br The data to publish .RE -.SS Subscribe to a stream on matching producers -.BR prdcr_subscribe -attr= -.RS -.TP -.BI regex " PRDCR_REGEX" -.br -A regular expression matching PRODUCER names -.TP -.BI stream " STREAM_NAME_OR_REGEX" -.br -The stream name or regular expression -.TP -.BI [rx_rate " BYTES_PER_SECOND"] -.br -The recv rate (bytes/sec) limit for the matching streams. The default is -1 -(unlimited). -.RE - .SH LDMS DAEMON COMMAND SYNTAX .SS Changing the log levels of LDMSD infrastructures -.BR loglevel -attr= (deprecated) - -.BR log_level -attr= +.TP 19 +.BR loglevel | log_level +.BI level\fR= STRING +.RB [ name\fR=\fINAME ] +.RB [ regex\fR=\fIREGEX ] +.RS 8 .TP -.BI level " string" +.BI level\fR= STRING .br A string specifying the log levels to be enabled The valid string are "default", "quiet", and a comma-separated list of DEBUG, INFO, WARN, ERROR, and CRITICAL. It is case insensitive. "default" means to set the log level to the defaul log level. "quiet" means disable the log messages. We note that "," and "" give different results. "" -- a single level name -- sets the log level to the given level and all the higher severity levels. In contrast, "," -- a level name followed by a comma -- sets the log level to only the given level. .TP -.BI [name " name"] +.RB [ name\fR=\fINAME ] .br A logger name .TP -.BI [regex " regex"] +.RB [ regex\fR=\fIREGEX ] .br A regular expression matching logger names. If neither 'name' or 'regex' is given, the command sets the default log level to the given level. For example, 'regex=xprt.*' will change the transport-related log levels. Use log_status to query the available log infrastructures. .RE .SS Query LDMSD's log information .BR log_status -attr= +.RB [ name\fR=\fIVALUE ] .RS -.BI [name " value"] +.RB [ name\fR=\fIVALUE ] .br A logger name .RE @@ -837,10 +901,11 @@ A logger name .SS Tell the daemon to dump it's internal state to the log file. .BR status - [name=] +.RI [ TYPES ] +.RB [ name\fR=\fINAME ] .RS -.BI [ type] -.br +.TP 9 +.RI [ TYPES ] Reports only the specified objects. The choices are prdcr, updtr and strgp. .RS prdcr: list the state of all producers. @@ -850,7 +915,7 @@ updtr: list the state of all update policies. strgp: list the state of all storage policies. .RE .TP -.RI [name " value"] +.RB [ name\fR=\fINAME ] The object name of which the status will be reported. .RE @@ -859,18 +924,20 @@ The object name of which the status will be reported. .SS Set the user data value for a metric in a metric set. .br .BR udata -attr= +.BI set\fR= SET +.BI metric\fR= METRIC +.BI udata\fR= UDATA .RS .TP -.BI set " set" +.BI set\fR= SET .br The sampler plugin name .TP -.BI metric " metric" +.BI metric\fR= METRIC .br The metric name .TP -.BI udata " udata" +.BI udata\fR= UDATA .br The desired user-data. This is a 64b unsigned integer. .RE @@ -881,22 +948,25 @@ The base value is incremented by the given 'incr' value and then sets to the user data of the consecutive matched metric and so on. .br .BR udata_regex -attr= +.BI set\fR= SET +.BI regex\fR= REGEX +.BI base\fR= BASE +.RB [ incr\fR=\fIINCR ] .RS .TP -.BI set " set" +.BI set\fR= SET .br The metric set name. .TP -.BI regex " regex" +.BI regex\fR= REGEX .br A regular expression to match metric names to be set .TP -.BI base " base" +.BI base\fR= BASE .br The base value of user data (uint64) .TP -.BI [incr " incr"] +.RB [ incr\fR=\fIINCR ] .br Increment value (int). The default is 0. If incr is 0, the user data of all matched metrics are set @@ -918,24 +988,27 @@ second-level aggregator’s permission with the set security after successfully looking up the set. The second-level aggregator will be able to look up the set if it has permission to do so. The process continues on the higher-level aggregators automatically. -.br +.TP 12 .BR set_sec_mod -attr= -.RS +.BI regex " regex +.RB [ uid\fR=\fIUID ] +.RB [ gid\fR=\fIGID ] +.RB [ perm\fR=\fIPERM ] +.RS 8 .TP .BI regex " regex .br A regular expression to match set instance names .TP -.BI [uid " uid"] +.RB [ uid\fR=\fIUID ] .br An existing user name string or a UID. Optional .TP -.BI [gid " gid"] +.RB [ gid\fR=\fIGID ] .br A GID. Optional .TP -.BI [perm " perm"] +.RB [ perm\fR=\fIPERM ] .br An octal number representing the permission bits. Optional .RE @@ -945,10 +1018,10 @@ An octal number representing the permission bits. Optional .SS Display the IO thread statistics .br .BR thread_stats -attr= +.RB [ reset\fR=\fITRUE\fR|\fIFALSE ] .RS .TP -.BI [reset " true|false"] +.RB [ reset\fR=\fITRUE\fR|\fIFALSE ] .br If true, reset the thread statistics after returning the values. The default is false. .RE @@ -956,10 +1029,10 @@ If true, reset the thread statistics after returning the values. The default is .SS Display the transport operation statistics .br .BR xprt_stats -attr= +.RB [ reset\fR=\fITRUE\fR|\fIFALSE ] .RS .TP -.BI [reset " true|false"] +.RB [ reset\fR=\fITRUE\fR|\fIFALSE ] .br If true, reset the statistics after returning the values. The default is false. .RE @@ -967,14 +1040,14 @@ If true, reset the statistics after returning the values. The default is false. .SS Display the statistics of updaters' update time per set .br .BR update_time_stats -attr= +.RB [ reset\fR=\fITRUE\fR|\fIFALSE ] .RS .TP -.BI [reset " true|false"] +.RB [ reset\fR=\fITRUE\fR|\fIFALSE ] .br If true, reset the update time statistics after returning the values. The default is false. .TP -.BI [name " name"] +.RB [ name\fR=\fINAME ] .br An updater name. Only the statistics of the given updater will be reported and reset if reset is true. .RE @@ -982,14 +1055,14 @@ An updater name. Only the statistics of the given updater will be reported and r .SS Display the statistics of storage policy's store time per set .br .BR store_time_stats -attr= +.RB [ reset\fR=\fITRUE\fR|\fIFALSE ] .RS .TP -.BI [reset " true|false"] +.RB [ reset\fR=\fITRUE\fR|\fIFALSE ] .br If true, reset the store time statistics after returning the values. The default is false. .TP -.BI [name " name"] +.RB [ name\fR=\fINAME ] .br A storage policy name. Only the statistics of the given storage policy will be reported and reset if reset is true. .RE @@ -999,10 +1072,10 @@ A storage policy name. Only the statistics of the given storage policy will be r .SS Display the list of available commands .br .BR help - +.RI [ COMMAND ] .RS -.RI [ command] -.br +.TP 10 +.RI [ COMMAND ] If a command is given, the help of the command will be printed. Otherwise, only the available command names are printed. .RE diff --git a/ldms/python/ldmsd/ldmsd_controller b/ldms/python/ldmsd/ldmsd_controller index 19c086e5d..edc47a57f 100755 --- a/ldms/python/ldmsd/ldmsd_controller +++ b/ldms/python/ldmsd/ldmsd_controller @@ -325,7 +325,8 @@ class LdmsdCmdParser(cmd.Cmd): """ Load a plugin at the Aggregator/Producer Parameters: - name= The plugin name + name= The name of the plugin instance (user-defined, e.g. p0). + plugin= The plugin to load (e.g. meminfo, store_sos). """ arg = self.handle_args('load', arg) if arg: @@ -1628,7 +1629,7 @@ class LdmsdCmdParser(cmd.Cmd): """ Unload the plugin Parameters: - name= The plugin name + name= The name of the plugin instance to terminate. """ arg = self.handle_args('term', arg) if arg: @@ -1643,7 +1644,7 @@ class LdmsdCmdParser(cmd.Cmd): """ Send a configuration command to the specified plugin. Parameters: - name= The plugin name + name= The plugin instance name ... Plugin specific attr=value tuples """ arg = self.handle_args('config', arg) @@ -1659,7 +1660,7 @@ class LdmsdCmdParser(cmd.Cmd): """ Start a sampler plugin Parameters: - name= The plugin name + name= The sampler plugin instance name interval= The sample interval in microseconds [offset=] Optional offset (shift) from the sample mark in microseconds. Offset can be positive or negative with magnitude up to 1/2 @@ -1680,7 +1681,7 @@ class LdmsdCmdParser(cmd.Cmd): """ Stop a sampler plugin Parameters: - name= The plugin name + name= The sampler plugin instance name """ arg = self.handle_args('stop', arg) if arg: From 12dad7745772082e97430d650ad79d97a3235eff Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Wed, 6 Dec 2023 16:06:13 -0600 Subject: [PATCH 05/13] Apply `ovis_ref` to ldmsd cfgobj --- ldms/src/ldmsd/ldmsd.c | 27 +-- ldms/src/ldmsd/ldmsd.h | 52 ++--- ldms/src/ldmsd/ldmsd_auth.c | 8 +- ldms/src/ldmsd/ldmsd_cfgobj.c | 159 ++++++++++----- ldms/src/ldmsd/ldmsd_config.c | 193 +++++++----------- ldms/src/ldmsd/ldmsd_failover.c | 18 +- ldms/src/ldmsd/ldmsd_prdcr.c | 24 ++- ldms/src/ldmsd/ldmsd_request.c | 47 ++--- ldms/src/ldmsd/ldmsd_strgp.c | 21 +- ldms/src/ldmsd/ldmsd_updtr.c | 29 +-- .../examples/test_sampler/test_sampler.c | 3 +- 11 files changed, 305 insertions(+), 276 deletions(-) diff --git a/ldms/src/ldmsd/ldmsd.c b/ldms/src/ldmsd/ldmsd.c index b8033e590..ac831d5ab 100644 --- a/ldms/src/ldmsd/ldmsd.c +++ b/ldms/src/ldmsd/ldmsd.c @@ -544,14 +544,14 @@ static void stop_sampler(struct ldmsd_sampler *samp) release_ovis_scheduler(samp->thread_id); samp->os = NULL; samp->thread_id = -1; - ldmsd_cfgobj_put(&samp->base.cfgobj); + ldmsd_cfgobj_put(&samp->base.cfgobj, "start"); } void plugin_sampler_cb(ovis_event_t oev) { struct ldmsd_plugin *pi = oev->param.ctxt; struct ldmsd_sampler *samp = (void*)pi; - ldmsd_cfgobj_get(&samp->base.cfgobj); + ldmsd_cfgobj_get(&samp->base.cfgobj, "cb"); ldmsd_cfgobj_lock(&pi->cfgobj); assert(pi->type == LDMSD_PLUGIN_SAMPLER); int rc = samp->sample(samp); @@ -566,7 +566,7 @@ void plugin_sampler_cb(ovis_event_t oev) stop_sampler(samp); } ldmsd_cfgobj_unlock(&pi->cfgobj); - ldmsd_cfgobj_put(&samp->base.cfgobj); + ldmsd_cfgobj_put(&samp->base.cfgobj, "cb"); } void ldmsd_set_tree_lock() @@ -995,7 +995,8 @@ ldmsd_set_info_t ldmsd_set_info_get(const char *inst_name) info->interval_us = LDMSD_SAMPLER(pi)->sample_interval_us; info->offset_us = LDMSD_SAMPLER(pi)->sample_offset_us; info->sync = 1; /* Sampling is always synchronous. */ - info->pi = pi; /* pi ref already taken in ldmsd_get_plugin */ + info->pi = ldmsd_plugin_get(pi, "info");; + ldmsd_put_plugin(pi); } info->origin_name = strdup(plugn_set->plugin_name); info->origin_type = LDMSD_SET_ORIGIN_SAMP_PI; @@ -1074,7 +1075,7 @@ void ldmsd_set_info_delete(ldmsd_set_info_t info) info->prd_set = NULL; } if (info->pi) { - ldmsd_cfgobj_put(&info->pi->cfgobj); + ldmsd_cfgobj_put(&info->pi->cfgobj, "info"); info->pi = NULL; } free(info); @@ -1161,7 +1162,7 @@ int ldmsd_start_sampler(char *plugin_name, char *interval, char *offset) samp->os = get_ovis_scheduler(samp->thread_id); rc = ovis_scheduler_event_add(samp->os, &samp->oev); if (0 == rc) { - ldmsd_cfgobj_get(&pi->cfgobj); + ldmsd_plugin_get(pi, "start_sampler"); /* this ref will be put down in ldmsd_stop_sampler() */ } else { samp->os = NULL; @@ -1193,7 +1194,7 @@ void oneshot_sample_cb(ovis_event_t ev) free(os); ldmsd_cfgobj_unlock(&pi->cfgobj); - ldmsd_cfgobj_put(&pi->cfgobj); + ldmsd_plugin_put(pi, "oneshot"); } int ldmsd_oneshot_sample(const char *plugin_name, const char *ts, @@ -1251,8 +1252,8 @@ int ldmsd_oneshot_sample(const char *plugin_name, const char *ts, "The specified plugin is not a sampler."); goto err; } - ldmsd_cfgobj_get(&pi->cfgobj); - ossample->pi = pi; + + ossample->pi = ldmsd_plugin_get(pi, "oneshot"); if (samp->thread_id < 0) { snprintf(errstr, errlen, "Sampler '%s' not started yet.", plugin_name); @@ -1300,7 +1301,7 @@ int ldmsd_stop_sampler(char *plugin_name) samp->os = NULL; release_ovis_scheduler(samp->thread_id); samp->thread_id = -1; - ldmsd_cfgobj_put(&samp->base.cfgobj); + ldmsd_plugin_put(&samp->base, "start_sampler"); } else { rc = EBUSY; } @@ -1499,15 +1500,15 @@ ldmsd_listen_t ldmsd_listen_new(char *xprt, char *port, char *host, char *auth) } } if (auth_dom) - ldmsd_cfgobj_put(&auth_dom->obj); + ldmsd_cfgobj_put(&auth_dom->obj, "find"); } ldmsd_cfgobj_unlock(&listen->obj); return listen; err: if (auth_dom) - ldmsd_cfgobj_put(&auth_dom->obj); + ldmsd_cfgobj_put(&auth_dom->obj, "find"); ldmsd_cfgobj_unlock(&listen->obj); - ldmsd_cfgobj_put(&listen->obj); + ldmsd_cfgobj_put(&listen->obj, "init"); return NULL; } diff --git a/ldms/src/ldmsd/ldmsd.h b/ldms/src/ldmsd/ldmsd.h index 8115187c1..849709779 100644 --- a/ldms/src/ldmsd/ldmsd.h +++ b/ldms/src/ldmsd/ldmsd.h @@ -63,6 +63,7 @@ #include #include +#include #include "ovis_log/ovis_log.h" #include "ldms.h" @@ -182,7 +183,10 @@ typedef void (*ldmsd_cfgobj_del_fn_t)(struct ldmsd_cfgobj *); typedef struct ldmsd_cfgobj { char *name; /* Unique cfgobj name */ + struct ref_s ref; +#if 0 uint32_t ref_count; +#endif ldmsd_cfgobj_type_t type; ldmsd_cfgobj_del_fn_t __del; struct rbn rbn; @@ -768,6 +772,12 @@ struct ldmsd_plugin { LDMSD_PLUGIN_STORE } type; + int multi_instance:1; /* 0 if this is not a multi-instance plugin */ + long reserve0:63; + long reserve1; + + struct rbn __plugin1_rbn; /* For internal use */ + char *libpath; enum ldmsd_plugin_type (*get_type)(struct ldmsd_plugin *self); @@ -775,6 +785,7 @@ struct ldmsd_plugin { void (*term)(struct ldmsd_plugin *self); const char *(*usage)(struct ldmsd_plugin *self); }; +typedef struct ldmsd_plugin *ldmsd_plugin_t; struct ldmsd_sampler { struct ldmsd_plugin base; @@ -801,6 +812,9 @@ struct ldmsd_sampler { struct ldmsd_plugin *ldmsd_get_plugin(const char *name); void ldmsd_put_plugin(struct ldmsd_plugin *pi); +#define ldmsd_plugin_get(p, name) ((ldmsd_plugin_t)ldmsd_cfgobj_get(&(p)->cfgobj, name)) +#define ldmsd_plugin_put(p, name) ldmsd_cfgobj_put(&(p)->cfgobj, name) + struct ldmsd_sampler *ldmsd_sampler_alloc(const char *name, size_t sz, ldmsd_cfgobj_del_fn_t __del, uid_t uid, gid_t gid, int perm); @@ -1000,8 +1014,12 @@ ldmsd_cfgobj_t ldmsd_cfgobj_new_with_auth(const char *name, uid_t uid, gid_t gid, int perm); -ldmsd_cfgobj_t ldmsd_cfgobj_get(ldmsd_cfgobj_t obj); -void ldmsd_cfgobj_put(ldmsd_cfgobj_t obj); +#define ldmsd_cfgobj_get(o, name) ({ \ + if (o) \ + ref_get(&(o)->ref, name); \ + (o); \ + }) +#define ldmsd_cfgobj_put(obj, ref_name) ref_put(&(obj)->ref, ref_name) int ldmsd_cfgobj_refcount(ldmsd_cfgobj_t obj); ldmsd_cfgobj_t ldmsd_cfgobj_find(const char *name, ldmsd_cfgobj_type_t type); void ldmsd_cfgobj_del(const char *name, ldmsd_cfgobj_type_t type); @@ -1045,13 +1063,8 @@ static inline void ldmsd_prdcr_lock(ldmsd_prdcr_t prdcr) { static inline void ldmsd_prdcr_unlock(ldmsd_prdcr_t prdcr) { ldmsd_cfgobj_unlock(&prdcr->obj); } -static inline ldmsd_prdcr_t ldmsd_prdcr_get(ldmsd_prdcr_t prdcr) { - ldmsd_cfgobj_get(&prdcr->obj); - return prdcr; -} -static inline void ldmsd_prdcr_put(ldmsd_prdcr_t prdcr) { - ldmsd_cfgobj_put(&prdcr->obj); -} +#define ldmsd_prdcr_get(p, name) ((ldmsd_prdcr_t)ldmsd_cfgobj_get(&(p)->obj, name)) +#define ldmsd_prdcr_put(p, name) ldmsd_cfgobj_put(&(p)->obj, name) static inline ldmsd_prdcr_t ldmsd_prdcr_find(const char *name) { return (ldmsd_prdcr_t)ldmsd_cfgobj_find(name, LDMSD_CFGOBJ_PRDCR); @@ -1108,13 +1121,8 @@ ldmsd_name_match_t ldmsd_updtr_match_first(ldmsd_updtr_t updtr); ldmsd_name_match_t ldmsd_updtr_match_next(ldmsd_name_match_t match); ldmsd_prdcr_ref_t ldmsd_updtr_prdcr_first(ldmsd_updtr_t updtr); ldmsd_prdcr_ref_t ldmsd_updtr_prdcr_next(ldmsd_prdcr_ref_t ref); -static inline ldmsd_updtr_t ldmsd_updtr_get(ldmsd_updtr_t updtr) { - ldmsd_cfgobj_get(&updtr->obj); - return updtr; -} -static inline void ldmsd_updtr_put(ldmsd_updtr_t updtr) { - ldmsd_cfgobj_put(&updtr->obj); -} +#define ldmsd_updtr_get(u, name) ((ldmsd_updtr_t)ldmsd_cfgobj_get(&(u)->obj, name)) +#define ldmsd_updtr_put(u, name) ldmsd_cfgobj_put(&(u)->obj, name) static inline void ldmsd_updtr_lock(ldmsd_updtr_t updtr) { ldmsd_cfgobj_lock(&updtr->obj); } @@ -1160,13 +1168,8 @@ ldmsd_name_match_t ldmsd_strgp_prdcr_first(ldmsd_strgp_t strgp); ldmsd_name_match_t ldmsd_strgp_prdcr_next(ldmsd_name_match_t match); ldmsd_strgp_metric_t ldmsd_strgp_metric_first(ldmsd_strgp_t strgp); ldmsd_strgp_metric_t ldmsd_strgp_metric_next(ldmsd_strgp_metric_t metric); -static inline ldmsd_strgp_t ldmsd_strgp_get(ldmsd_strgp_t strgp) { - ldmsd_cfgobj_get(&strgp->obj); - return strgp; -} -static inline void ldmsd_strgp_put(ldmsd_strgp_t strgp) { - ldmsd_cfgobj_put(&strgp->obj); -} +#define ldmsd_strgp_get(s, name) ((ldmsd_strgp_t)ldmsd_cfgobj_get(&(s)->obj, name)) +#define ldmsd_strgp_put(s, name) ldmsd_cfgobj_put(&(s)->obj, name) static inline void ldmsd_strgp_lock(ldmsd_strgp_t strgp) { ldmsd_cfgobj_lock(&strgp->obj); } @@ -1414,6 +1417,9 @@ int ldmsd_auth_del(const char *name, ldmsd_sec_ctxt_t ctxt); ldmsd_auth_t ldmsd_auth_default_get(); int ldmsd_auth_default_set(const char *plugin, struct attr_value_list *attrs); +#define ldmsd_auth_get(p, name) ((ldmsd_auth_t)ldmsd_cfgobj_get(&(p)->obj, name)) +#define ldmsd_auth_put(p, name) ldmsd_cfgobj_put(&(p)->obj, name) + static inline ldmsd_auth_t ldmsd_auth_find(const char *name) { diff --git a/ldms/src/ldmsd/ldmsd_auth.c b/ldms/src/ldmsd/ldmsd_auth.c index 0e98c9f7f..342cd9f5e 100644 --- a/ldms/src/ldmsd/ldmsd_auth.c +++ b/ldms/src/ldmsd/ldmsd_auth.c @@ -104,7 +104,7 @@ ldmsd_auth_new_with_auth(const char *name, const char *plugin, err: if (auth) { ldmsd_cfgobj_unlock(&auth->obj); - ldmsd_cfgobj_put(&auth->obj); + ldmsd_auth_put(auth, "init"); } return NULL; } @@ -127,11 +127,11 @@ int ldmsd_auth_del(const char *name, ldmsd_sec_ctxt_t ctxt) goto out; /* remove from the tree */ rbt_del(cfgobj_trees[LDMSD_CFGOBJ_AUTH], &auth->obj.rbn); - ldmsd_cfgobj_put(&auth->obj); /* correspond to `new` */ + ldmsd_auth_put(auth, "cfgobj_tree"); /* correspond to `new` */ out: ldmsd_cfg_unlock(LDMSD_CFGOBJ_AUTH); if (auth) /* put ref back from `find` */ - ldmsd_cfgobj_put(&auth->obj); + ldmsd_auth_put(auth, "find"); return rc; } @@ -175,6 +175,6 @@ int ldmsd_auth_default_set(const char *plugin, struct attr_value_list *attrs) } rc = 0; out: - ldmsd_cfgobj_put(&d->obj); + ldmsd_auth_put(d, "find"); return rc; } diff --git a/ldms/src/ldmsd/ldmsd_cfgobj.c b/ldms/src/ldmsd/ldmsd_cfgobj.c index db8ca2ad3..9fd2f71f6 100644 --- a/ldms/src/ldmsd/ldmsd_cfgobj.c +++ b/ldms/src/ldmsd/ldmsd_cfgobj.c @@ -131,6 +131,30 @@ void ldmsd_cfgobj_unlock(ldmsd_cfgobj_t obj) pthread_mutex_unlock(&obj->lock); } +/* + * Add the obj to the corresponding CFGOBJ tree. + * + * The caller must hold appropriate CFGOBJ lock. + * This function does not check existing entry. + */ +void __cfgobj_add(ldmsd_cfgobj_t obj) +{ + rbn_init(&obj->rbn, obj->name); + rbt_ins(cfgobj_trees[obj->type], &obj->rbn); + ldmsd_cfgobj_get(obj, "cfgobj_tree"); /* put in `rm` */ +} + +/* + * Remove the obj from the corresponding CFGOBJ tree. + * + * The caller must hold appropriate CFGOBJ lock. + */ +void __cfgobj_rm(ldmsd_cfgobj_t obj) +{ + rbt_del(cfgobj_trees[obj->type], &obj->rbn); + ldmsd_cfgobj_put(obj, "cfgobj_tree"); /* from `add` */ +} + int ldmsd_cfgobj_add(ldmsd_cfgobj_t obj) { int rc = EEXIST; @@ -141,10 +165,8 @@ int ldmsd_cfgobj_add(ldmsd_cfgobj_t obj) n = rbt_find(cfgobj_trees[obj->type], obj->name); if (n) goto out; - rbn_init(&obj->rbn, obj->name); - rbt_ins(cfgobj_trees[obj->type], &obj->rbn); rc = 0; - ldmsd_cfgobj_get(obj); /* put in `rm` */ + __cfgobj_add(obj); out: pthread_mutex_unlock(cfgobj_locks[obj->type]); return rc; @@ -155,7 +177,67 @@ void ldmsd_cfgobj_rm(ldmsd_cfgobj_t obj) pthread_mutex_lock(cfgobj_locks[obj->type]); rbt_del(cfgobj_trees[obj->type], &obj->rbn); pthread_mutex_unlock(cfgobj_locks[obj->type]); - ldmsd_cfgobj_put(obj); /* from `add` */ + ldmsd_cfgobj_put(obj, "cfgobj_tree"); /* from `add` */ +} + +/* an interposer to call obj->__del() */ +static void __cfgobj_ref_free(void *arg) +{ + ldmsd_cfgobj_t obj = arg; + obj->__del(obj); +} + +void ldmsd_cfgobj_plugin_cleanup(struct ldmsd_cfgobj *obj) +{ + struct ldmsd_plugin *pi = container_of(obj, struct ldmsd_plugin, cfgobj); + struct avl_q_item *avl; + struct avl_q_item *kwl; + + free(pi->cfgobj.name); + + free(pi->libpath); + pi->libpath = NULL; + + while ((avl = TAILQ_FIRST(&pi->avl_q))) { + TAILQ_REMOVE(&pi->avl_q, avl, entry); + free(avl->av_list); + free(avl); + } + + while ((kwl = TAILQ_FIRST(&pi->kwl_q))) { + TAILQ_REMOVE(&pi->kwl_q, kwl, entry); + free(kwl->av_list); + free(kwl); + } +} + +static int __cfgobj_init(ldmsd_cfgobj_t obj, const char *name, + ldmsd_cfgobj_type_t type, ldmsd_cfgobj_del_fn_t __del, + uid_t uid, gid_t gid, int perm) +{ + obj->name = strdup(name); + if (!obj->name) + return ENOMEM; + + obj->gid = gid; + obj->uid = uid; + obj->perm = perm; + + pthread_mutex_init(&obj->lock, NULL); + ref_init(&obj->ref, "init", __cfgobj_ref_free, obj); + obj->type = type; + + obj->__del = __del; + return 0; +} + +/* + * For old-style plugin that does not initalize cfgobj + */ +int ldmsd_plugin_cfgobj_init(struct ldmsd_plugin *pi, const char *inst_name) +{ + return __cfgobj_init(&pi->cfgobj, inst_name, LDMSD_CFGOBJ_PLUGIN, + ldmsd_cfgobj_plugin_cleanup, getegid(), geteuid(), 0770); } ldmsd_cfgobj_t ldmsd_cfgobj_new_with_auth(const char *name, @@ -167,36 +249,30 @@ ldmsd_cfgobj_t ldmsd_cfgobj_new_with_auth(const char *name, int perm) { ldmsd_cfgobj_t obj = NULL; + int rc; + struct rbn *n; pthread_mutex_lock(cfgobj_locks[type]); - errno = EEXIST; - struct rbn *n = rbt_find(cfgobj_trees[type], name); - if (n) + n = rbt_find(cfgobj_trees[type], name); + if (n) { + errno = EEXIST; goto out_1; + } - errno = ENOMEM; obj = calloc(1, obj_size); if (!obj) goto out_1; - obj->name = strdup(name); - if (!obj->name) + if (!__del) + __del = ldmsd_cfgobj___del; + rc = __cfgobj_init(obj, name, type, __del, uid, gid, perm); + if (rc) { + errno = rc; goto out_2; - - obj->type = type; - obj->ref_count = 1; /* for obj->rbn inserting into the tree */ - if (__del) - obj->__del = __del; - else - obj->__del = ldmsd_cfgobj___del; - obj->uid = uid; - obj->gid = gid; - obj->perm = perm; - - pthread_mutex_init(&obj->lock, NULL); + } + __cfgobj_add(obj); pthread_mutex_lock(&obj->lock); - rbn_init(&obj->rbn, obj->name); - rbt_ins(cfgobj_trees[type], &obj->rbn); + goto out_1; out_2: @@ -220,29 +296,10 @@ ldmsd_cfgobj_t ldmsd_cfgobj_new(const char *name, ldmsd_cfgobj_type_t type, getuid(), getgid(), 0777); } -ldmsd_cfgobj_t ldmsd_cfgobj_get(ldmsd_cfgobj_t obj) -{ - uint32_t ref_count; - if (obj) { - ref_count = __sync_fetch_and_add(&obj->ref_count, 1); - assert(ref_count >= 1); - } - - return obj; -} - -void ldmsd_cfgobj_put(ldmsd_cfgobj_t obj) -{ - if (!obj) - return; - if (0 == __sync_sub_and_fetch(&obj->ref_count, 1)) - obj->__del(obj); -} - /** This function is only useful if the cfgobj lock is held when the function is called. */ int ldmsd_cfgobj_refcount(ldmsd_cfgobj_t obj) { - return obj->ref_count; + return obj->ref.ref_count; } /* @@ -256,7 +313,7 @@ ldmsd_cfgobj_t __cfgobj_find(const char *name, ldmsd_cfgobj_type_t type) goto out; obj = container_of(n, struct ldmsd_cfgobj, rbn); out: - return ldmsd_cfgobj_get(obj); + return ldmsd_cfgobj_get(obj, "find"); } ldmsd_cfgobj_t ldmsd_cfgobj_find(const char *name, ldmsd_cfgobj_type_t type) @@ -275,9 +332,13 @@ void ldmsd_cfgobj_del(const char *name, ldmsd_cfgobj_type_t type) obj = __cfgobj_find(name, type); if (obj) { rbt_del(cfgobj_trees[type], &obj->rbn); - ldmsd_cfgobj_put(obj); + ldmsd_cfgobj_put(obj, "cfgobj_tree"); } pthread_mutex_unlock(cfgobj_locks[type]); + if (obj) { + ldmsd_cfgobj_put(obj, "find"); + ldmsd_cfgobj_put(obj, "init"); + } } /** @@ -290,7 +351,7 @@ ldmsd_cfgobj_t ldmsd_cfgobj_first(ldmsd_cfgobj_type_t type) struct rbn *n; n = rbt_min(cfgobj_trees[type]); if (n) - return ldmsd_cfgobj_get(container_of(n, struct ldmsd_cfgobj, rbn)); + return ldmsd_cfgobj_get(container_of(n, struct ldmsd_cfgobj, rbn), "iter"); return NULL; } @@ -307,8 +368,8 @@ ldmsd_cfgobj_t ldmsd_cfgobj_next(ldmsd_cfgobj_t obj) n = rbn_succ(&obj->rbn); if (!n) goto out; - nobj = ldmsd_cfgobj_get(container_of(n, struct ldmsd_cfgobj, rbn)); + nobj = ldmsd_cfgobj_get(container_of(n, struct ldmsd_cfgobj, rbn), "iter"); out: - ldmsd_cfgobj_put(obj); /* Drop the next reference */ + ldmsd_cfgobj_put(obj, "iter"); /* Drop the next reference */ return nobj; } diff --git a/ldms/src/ldmsd/ldmsd_config.c b/ldms/src/ldmsd/ldmsd_config.c index c8212a824..967559bde 100644 --- a/ldms/src/ldmsd/ldmsd_config.c +++ b/ldms/src/ldmsd/ldmsd_config.c @@ -91,6 +91,11 @@ pthread_mutex_t sp_list_lock = PTHREAD_MUTEX_INITIALIZER; #define LDMSD_PLUGIN_LIBPATH_MAX 1024 +/* implementation in ldmsd_cfgobj.c */ +ldmsd_cfgobj_t __cfgobj_find(const char *name, ldmsd_cfgobj_type_t type); +void __cfgobj_add(ldmsd_cfgobj_t obj); +void __cfgobj_rm(ldmsd_cfgobj_t obj); + void ldmsd_cfg_ldms_xprt_cleanup(ldmsd_cfg_xprt_t xprt) { /* nothing to do */ @@ -104,32 +109,12 @@ struct ldmsd_plugin *ldmsd_get_plugin(const char *inst_name) void ldmsd_put_plugin(struct ldmsd_plugin *pi) { - ldmsd_cfgobj_put(&pi->cfgobj); + ldmsd_cfgobj_put(&pi->cfgobj, "find"); } -void ldmsd_cfgobj_plugin_cleanup(struct ldmsd_cfgobj *obj) -{ - struct ldmsd_plugin *pi = container_of(obj, struct ldmsd_plugin, cfgobj); - struct avl_q_item *avl; - struct avl_q_item *kwl; - - free(pi->cfgobj.name); - - free(pi->libpath); - pi->libpath = NULL; - - while ((avl = TAILQ_FIRST(&pi->avl_q))) { - TAILQ_REMOVE(&pi->avl_q, avl, entry); - free(avl->av_list); - free(avl); - } - - while ((kwl = TAILQ_FIRST(&pi->kwl_q))) { - TAILQ_REMOVE(&pi->kwl_q, kwl, entry); - free(kwl->av_list); - free(kwl); - } -} +/* implementation in ldmsd_cfgobj.c */ +void ldmsd_cfgobj_plugin_cleanup(struct ldmsd_cfgobj *obj); +int ldmsd_plugin_cfgobj_init(struct ldmsd_plugin *pi, const char *inst_name); void ldmsd_sampler_cleanup(struct ldmsd_sampler *samp) { @@ -141,29 +126,6 @@ void ldmsd_store_cleanup(struct ldmsd_store *store) ldmsd_cfgobj_plugin_cleanup(&store->base.cfgobj); } -/* - * For old-style plugin that does not initalize cfgobj - */ -int ldmsd_plugin_cfgobj_init(struct ldmsd_plugin *pi, const char *inst_name) -{ - struct ldmsd_cfgobj *obj = &pi->cfgobj; - - obj->name = strdup(inst_name); - if (!obj->name) - return ENOMEM; - - obj->gid = getegid(); - obj->uid = geteuid(); - obj->perm = 0770; - - pthread_mutex_init(&obj->lock, NULL); - obj->ref_count = 1; - obj->type = LDMSD_CFGOBJ_PLUGIN; - - obj->__del = ldmsd_cfgobj_plugin_cleanup; - return 0; -} - void ldmsd_plugin_init(struct ldmsd_plugin *pi) { /* Initialize only the plugin part, leave the cfgobj alone */ @@ -202,6 +164,7 @@ struct ldmsd_sampler *ldmsd_sampler_alloc(const char *name, size_t sz, if (!samp) goto out; + samp->base.multi_instance = 1; ldmsd_plugin_init(&samp->base); ldmsd_sampler_init(samp); @@ -225,6 +188,7 @@ struct ldmsd_store *ldmsd_store_alloc(const char *name, size_t sz, if (!st) goto out; + st->base.multi_instance = 1; ldmsd_plugin_init(&st->base); ldmsd_store_init(st); @@ -238,43 +202,23 @@ static int __plugin1_ent_cmp(void *tree_key, const void *key) { return strcmp(tree_key, key); } + +/* protected by ldmsd_cfg_lock(LDMSD_CFGOBJ_PLUGIN) */ static struct rbt __plugin1_rbt = RBT_INITIALIZER(__plugin1_ent_cmp); struct plugin1_ent { struct rbn rbn; struct ldmsd_plugin *p; }; -static pthread_mutex_t __plugin1_rbt_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define PLUGIN1_TREE_LOCK() pthread_mutex_lock(&__plugin1_rbt_mutex) -#define PLUGIN1_TREE_UNLOCK() pthread_mutex_unlock(&__plugin1_rbt_mutex) -static struct plugin1_ent *__plugin1_find(const char *name) +static ldmsd_plugin_t __plugin1_find(const char *name) { - return (void*)rbt_find(&__plugin1_rbt, name); -} - -static struct plugin1_ent *__plugin1_ins(struct ldmsd_plugin *pi) -{ - struct plugin1_ent *ent; - - ent = calloc(1, sizeof(*ent)); - if (!ent) - return NULL; - ent->p = pi; - ldmsd_cfgobj_get(&pi->cfgobj); - rbn_init(&ent->rbn, pi->name); - rbt_ins(&__plugin1_rbt, &ent->rbn); - return ent; -} - -static void __plugin1_del(struct plugin1_ent *ent) -{ - rbt_del(&__plugin1_rbt, &ent->rbn); - ldmsd_cfgobj_put(&ent->p->cfgobj); - free(ent); + struct rbn *rbn = rbt_find(&__plugin1_rbt, name); + if (rbn) + return container_of(rbn, struct ldmsd_plugin, __plugin1_rbn); + return NULL; } -struct ldmsd_plugin *new_plugin(const char *inst_name, +static struct ldmsd_plugin *__new_plugin(const char *inst_name, char *plugin_name, char *errstr, size_t errlen) { @@ -327,12 +271,13 @@ struct ldmsd_plugin *new_plugin(const char *inst_name, pi_get = dlsym(d, "get_plugin_instance"); if (pi_get) { /* Plugin instance */ - /* TODO uid, gid, perm ... */ + /* TODO should uid, gid, perm be of xprt? ... */ pi = pi_get(inst_name, geteuid(), getegid(), 0770); if (!pi) { snprintf(errstr, errlen, - "The plugin '%s' (%s) could not be loaded.", - inst_name, plugin_name); + "The plugin '%s' (%s) could not be loaded, " + "errno: %d", + inst_name, plugin_name, errno); goto err_0; } /* The returned `pi` is locked, and it is already in the @@ -343,17 +288,25 @@ struct ldmsd_plugin *new_plugin(const char *inst_name, /* else, let through */ - struct plugin1_ent *ent; - - /* Old style plugin */ - PLUGIN1_TREE_LOCK(); - /* make sure that the single-instance plugins are not loaded multiple - * times. */ - ent = __plugin1_find(plugin_name); - if (ent) { + /* Old style plugin: + * Make sure that the single-instance plugins are not loaded multiple + * times. */ + ldmsd_cfg_lock(LDMSD_CFGOBJ_PLUGIN); + pi = (void*)__cfgobj_find(inst_name, LDMSD_CFGOBJ_PLUGIN); + if (pi) { + errno = EEXIST; + snprintf(errstr, errlen, + "Plugin instance '%s' already existed" , inst_name); + ldmsd_put_plugin(pi); + pi = NULL; + goto err_1; + } + pi = __plugin1_find(plugin_name); + if (pi) { errno = EEXIST; snprintf(errstr, errlen, "The library, '%s' does not support " "multiple instances." , plugin_name); + pi = NULL; goto err_1; } @@ -397,31 +350,23 @@ struct ldmsd_plugin *new_plugin(const char *inst_name, } TAILQ_INIT(&pi->avl_q); TAILQ_INIT(&pi->kwl_q); - ent = __plugin1_ins(pi); - if (!ent) { - snprintf(errstr, errlen, - "Cannot add plugin '%s', error: %d", - plugin_name, errno); - goto err_1; - } - rc = ldmsd_cfgobj_add(&pi->cfgobj); - if (rc) { - snprintf(errstr, errlen, - "Cannot add plugin instance '%s' (%s), error: %d", - inst_name, plugin_name, rc); - goto err_2; - } - PLUGIN1_TREE_UNLOCK(); + + pi->multi_instance = 0; + rbn_init(&pi->__plugin1_rbn, pi->name); + rbt_ins(&__plugin1_rbt, &pi->__plugin1_rbn); + ldmsd_plugin_get(pi, "__plugin1_rbt"); + + __cfgobj_add(&pi->cfgobj); + ldmsd_cfg_unlock(LDMSD_CFGOBJ_PLUGIN); out: return pi; -err_2: - __plugin1_del(ent); err_1: - PLUGIN1_TREE_UNLOCK(); + /* unlock ... */ + ldmsd_cfg_unlock(LDMSD_CFGOBJ_PLUGIN); err_0: if (pi) { - ldmsd_cfgobj_put(&pi->cfgobj); + ldmsd_cfgobj_put(&pi->cfgobj, "init"); } if (d) dlclose(d); @@ -479,17 +424,19 @@ int ldmsd_compile_regex(regex_t *regex, const char *regex_str, int ldmsd_load_plugin(const char *inst_name, char *plugin_name, char *errstr, size_t errlen) { - struct ldmsd_plugin *pi = ldmsd_get_plugin(inst_name); + struct ldmsd_plugin *pi; + + pi = ldmsd_get_plugin(inst_name); if (pi) { snprintf(errstr, errlen, "Plugin '%s' (%s) already loaded", inst_name, plugin_name); ldmsd_put_plugin(pi); return EEXIST; } - pi = new_plugin(inst_name, plugin_name, errstr, errlen); - if (!pi) - return -1; - return 0; + pi = __new_plugin(inst_name, plugin_name, errstr, errlen); + if (pi) + return 0; + return errno; } /* @@ -498,22 +445,33 @@ int ldmsd_load_plugin(const char *inst_name, char *plugin_name, int ldmsd_term_plugin(char *inst_name) { int rc = 0; - struct ldmsd_plugin *pi; + ldmsd_plugin_t pi; - pi = ldmsd_get_plugin(inst_name); - if (!pi) - return ENOENT; + ldmsd_cfg_lock(LDMSD_CFGOBJ_PLUGIN); + pi = (ldmsd_plugin_t)__cfgobj_find(inst_name, LDMSD_CFGOBJ_PLUGIN); + if (!pi) { + rc = ENOENT; + ldmsd_cfg_unlock(LDMSD_CFGOBJ_PLUGIN); + goto out_0; + } ldmsd_cfgobj_lock(&pi->cfgobj); if (pi->type == LDMSD_PLUGIN_SAMPLER && LDMSD_SAMPLER(pi)->thread_id >= 0) { rc = EBUSY; goto out; } + __cfgobj_rm(&pi->cfgobj); pi->term(pi); - ldmsd_cfgobj_rm(&pi->cfgobj); + ldmsd_plugin_put(pi, "init"); /* correspond to init */ + if (0 == pi->multi_instance) { + rbt_del(&__plugin1_rbt, &pi->__plugin1_rbn); + ldmsd_plugin_put(pi, "__plugin1_rbt"); /* correspond to rbt_ins */ + } out: ldmsd_cfgobj_unlock(&pi->cfgobj); - ldmsd_put_plugin(pi); + ldmsd_cfg_unlock(LDMSD_CFGOBJ_PLUGIN); + ldmsd_put_plugin(pi); /* find */ +out_0: return rc; } @@ -1035,7 +993,7 @@ int __req_deferred_start(ldmsd_req_hdr_t req, ldmsd_cfgobj_type_t type) } free(name); obj->perm |= LDMSD_PERM_DSTART; - ldmsd_cfgobj_put(obj); + ldmsd_cfgobj_put(obj, "find"); return 0; } @@ -1395,7 +1353,6 @@ int ldmsd_plugins_usage(const char *plugname) return rc; } - static int ldmsd_plugins_usage_dir(const char *path, const char *plugname) { assert( path || "null dir name in ldmsd_plugins_usage" == NULL); diff --git a/ldms/src/ldmsd/ldmsd_failover.c b/ldms/src/ldmsd/ldmsd_failover.c index 7bd587a71..4fd31cb61 100644 --- a/ldms/src/ldmsd/ldmsd_failover.c +++ b/ldms/src/ldmsd/ldmsd_failover.c @@ -847,7 +847,7 @@ int __failover_send_cfgobjs(ldmsd_failover_t f, ldms_t x) continue; rc = __failover_send_prdcr(f, x, p); if (rc) { - ldmsd_prdcr_put(p); + ldmsd_prdcr_put(p, "iter"); ldmsd_cfg_unlock(LDMSD_CFGOBJ_PRDCR); goto out; } @@ -861,7 +861,7 @@ int __failover_send_cfgobjs(ldmsd_failover_t f, ldms_t x) continue; rc = __failover_send_updtr(f, x, u); if (rc) { - ldmsd_updtr_put(u); + ldmsd_updtr_put(u, "iter"); ldmsd_cfg_unlock(LDMSD_CFGOBJ_UPDTR); goto out; } @@ -874,7 +874,7 @@ int __failover_send_cfgobjs(ldmsd_failover_t f, ldms_t x) continue; rc = __failover_send_strgp(f, x, s); if (rc) { - ldmsd_strgp_put(s); + ldmsd_strgp_put(s, "iter"); ldmsd_cfg_unlock(LDMSD_CFGOBJ_STRGP); goto out; } @@ -2094,7 +2094,7 @@ int failover_cfgprdcr_handler(ldmsd_req_ctxt_t req) /* add stream */ if (stream) rc = ldmsd_prdcr_subscribe(p, stream); - ldmsd_prdcr_put(p); + ldmsd_prdcr_put(p, "find"); goto out; } /* create */ @@ -2221,7 +2221,7 @@ int failover_cfgupdtr_handler(ldmsd_req_ctxt_t req) goto out; } rbt_ins(&f->updtr_rbt, &srbn->rbn); - ldmsd_updtr_get(u); /* so that we can `put` without del */ + ldmsd_updtr_get(u, "find"); /* so that we can `put` without deleting */ } else { /* update by parameters */ if (interval) { @@ -2243,7 +2243,7 @@ int failover_cfgupdtr_handler(ldmsd_req_ctxt_t req) goto updtr_put; } rc = __ldmsd_updtr_prdcr_add(u, p); - ldmsd_prdcr_put(p); + ldmsd_prdcr_put(p, "find"); } if (match && regex) { /* add matching condition */ @@ -2251,7 +2251,7 @@ int failover_cfgupdtr_handler(ldmsd_req_ctxt_t req) req->line_len, &sctxt); } updtr_put: - ldmsd_updtr_put(u); + ldmsd_updtr_put(u, "find"); out: __failover_unlock(f); if (name) @@ -2336,7 +2336,7 @@ int failover_cfgstrgp_handler(ldmsd_req_ctxt_t req) goto out; } rbt_ins(&f->strgp_rbt, &srbn->rbn); - ldmsd_strgp_get(s); /* so that we can `put` without del */ + ldmsd_strgp_get(s, "find"); /* so that we can `put` without del */ s->plugin_name = plugin; plugin = NULL; /* give plugin to s */ s->schema = schema; @@ -2361,7 +2361,7 @@ int failover_cfgstrgp_handler(ldmsd_req_ctxt_t req) } put: - ldmsd_strgp_put(s); + ldmsd_strgp_put(s, "find"); out: __failover_unlock(f); if (name) diff --git a/ldms/src/ldmsd/ldmsd_prdcr.c b/ldms/src/ldmsd/ldmsd_prdcr.c index 11e14c8e4..cf8d989f8 100644 --- a/ldms/src/ldmsd/ldmsd_prdcr.c +++ b/ldms/src/ldmsd/ldmsd_prdcr.c @@ -137,7 +137,7 @@ void __prdcr_set_del(ldmsd_prdcr_set_t set) while (strgp_ref) { LIST_REMOVE(strgp_ref, entry); __atomic_fetch_sub(&strgp_ref->strgp->prdset_cnt, 1, __ATOMIC_SEQ_CST); - ldmsd_strgp_put(strgp_ref->strgp); + ldmsd_strgp_put(strgp_ref->strgp, "strgp_ref"); free(strgp_ref); strgp_ref = LIST_FIRST(&set->strgp_list); } @@ -918,7 +918,7 @@ ldmsd_prdcr_new_with_auth(const char *name, const char *xprt_name, out: rbt_del(cfgobj_trees[LDMSD_CFGOBJ_PRDCR], &prdcr->obj.rbn); ldmsd_cfgobj_unlock(&prdcr->obj); - ldmsd_cfgobj_put(&prdcr->obj); + ldmsd_prdcr_put(prdcr, "init"); return NULL; } @@ -956,14 +956,24 @@ int ldmsd_prdcr_del(const char *prdcr_name, ldmsd_sec_ctxt_t ctxt) rc = EBUSY; goto out_1; } - if (ldmsd_cfgobj_refcount(&prdcr->obj) > 2) { + /* + * Rationale: + * 3 references that it can have: + * - find (from the find above) + * - init (from when it is created) + * - cfgobj_tree (being in the tree) + * + * If it has more than 3 references, it is being used by something else. + */ + if (ldmsd_cfgobj_refcount(&prdcr->obj) > 3) { rc = EBUSY; goto out_1; } /* removing from the tree */ rbt_del(cfgobj_trees[LDMSD_CFGOBJ_PRDCR], &prdcr->obj.rbn); - ldmsd_prdcr_put(prdcr); /* putting down reference from the tree */ + ldmsd_prdcr_put(prdcr, "cfgobj_tree"); /* putting down reference from the tree */ + ldmsd_prdcr_put(prdcr, "init"); rc = 0; /* let-through */ @@ -972,7 +982,7 @@ int ldmsd_prdcr_del(const char *prdcr_name, ldmsd_sec_ctxt_t ctxt) out_0: pthread_mutex_unlock(cfgobj_locks[LDMSD_CFGOBJ_PRDCR]); if (prdcr) - ldmsd_prdcr_put(prdcr); /* `find` reference */ + ldmsd_prdcr_put(prdcr, "find"); /* `find` reference */ return rc; } @@ -1027,7 +1037,7 @@ int ldmsd_prdcr_start(const char *name, const char *interval_str, prdcr->conn_intrvl_us = reconnect; } rc = __ldmsd_prdcr_start(prdcr, ctxt); - ldmsd_prdcr_put(prdcr); + ldmsd_prdcr_put(prdcr, "find"); return rc; } @@ -1073,7 +1083,7 @@ int ldmsd_prdcr_stop(const char *name, ldmsd_sec_ctxt_t ctxt) if (!prdcr) return ENOENT; rc = __ldmsd_prdcr_stop(prdcr, ctxt); - ldmsd_prdcr_put(prdcr); + ldmsd_prdcr_put(prdcr, "find"); return rc; } diff --git a/ldms/src/ldmsd/ldmsd_request.c b/ldms/src/ldmsd/ldmsd_request.c index 7b17dbf70..9e98ef6b3 100644 --- a/ldms/src/ldmsd/ldmsd_request.c +++ b/ldms/src/ldmsd/ldmsd_request.c @@ -2366,7 +2366,7 @@ static int prdcr_status_handler(ldmsd_req_ctxt_t reqc) out: free(name); if (prdcr) - ldmsd_prdcr_put(prdcr); + ldmsd_prdcr_put(prdcr, "find"); return rc; } @@ -3205,11 +3205,11 @@ static int strgp_status_handler(ldmsd_req_ctxt_t reqc) free(name); return 0; } - } - - /* Construct the json object of the strgp(s) */ - if (strgp) { rc = __strgp_status_json_obj(reqc, strgp, 0); + ldmsd_strgp_put(strgp, "find"); + strgp = NULL; + if (rc) + goto out; } else { strgp_cnt = 0; ldmsd_cfg_lock(LDMSD_CFGOBJ_STRGP); @@ -3254,7 +3254,6 @@ static int strgp_status_handler(ldmsd_req_ctxt_t reqc) LDMSD_REQ_EOM_F); out: free(name); - ldmsd_strgp_put(strgp); return rc; } @@ -3841,10 +3840,9 @@ static int updtr_match_list_handler(ldmsd_req_ctxt_t reqc) free(name); return 0; } - } - - if (updtr) { rc = __updtr_match_list_json_obj(reqc, updtr, 0); + ldmsd_updtr_put(updtr, "find"); + updtr = NULL; if (rc) goto out; } else { @@ -3888,8 +3886,6 @@ static int updtr_match_list_handler(ldmsd_req_ctxt_t reqc) LDMSD_REQ_EOM_F); out: free(name); - if (updtr) - ldmsd_updtr_put(updtr); return rc; } @@ -4196,6 +4192,12 @@ static int updtr_status_handler(ldmsd_req_ctxt_t reqc) name = ldmsd_req_attr_str_value_get_by_id(reqc, LDMSD_ATTR_NAME); __dlog(DLOG_QUERY, "updtr_status %s%s\n", name ? " name=" : "", name ? name : ""); + reset_s = ldmsd_req_attr_str_value_get_by_id(reqc, LDMSD_ATTR_RESET); + if (reset_s) { + if (0 != strcasecmp(reset_s, "false")) + reset = 1; + free(reset_s); + } if (name) { updtr = ldmsd_updtr_find(name); if (!updtr) { @@ -4207,18 +4209,9 @@ static int updtr_status_handler(ldmsd_req_ctxt_t reqc) free(name); return 0; } - } - - reset_s = ldmsd_req_attr_str_value_get_by_id(reqc, LDMSD_ATTR_RESET); - if (reset_s) { - if (0 != strcasecmp(reset_s, "false")) - reset = 1; - free(reset_s); - } - - /* Construct the json object of the updater(s) */ - if (updtr) { rc = __updtr_status_json_obj(reqc, updtr, 0, reset); + ldmsd_updtr_put(updtr, "find"); + updtr = NULL; if (rc) goto out; } else { @@ -4265,8 +4258,6 @@ static int updtr_status_handler(ldmsd_req_ctxt_t reqc) LDMSD_REQ_EOM_F); out: free(name); - if (updtr) - ldmsd_updtr_put(updtr); return rc; } @@ -4348,6 +4339,8 @@ static int updtr_task_status_handler(ldmsd_req_ctxt_t reqc) return 0; } rc = __updtr_task_tree_json_obj(reqc, updtr); + ldmsd_updtr_put(updtr, "find"); + updtr = NULL; if (rc) goto err; } else { @@ -4400,8 +4393,6 @@ static int updtr_task_status_handler(ldmsd_req_ctxt_t reqc) "internal error", 15); out: free(name); - if (updtr) - ldmsd_updtr_put(updtr); return rc; } @@ -4508,6 +4499,8 @@ static int prdcr_hint_tree_status_handler(ldmsd_req_ctxt_t reqc) ldmsd_prdcr_lock(prdcr); rc = __prdcr_hint_set_tree_json_obj(reqc, prdcr); ldmsd_prdcr_unlock(prdcr); + ldmsd_prdcr_put(prdcr, "find"); + prdcr = NULL; if (rc) goto intr_err; } else { @@ -4561,8 +4554,6 @@ static int prdcr_hint_tree_status_handler(ldmsd_req_ctxt_t reqc) "interval error", 14); out: free(name); - if (prdcr) - ldmsd_prdcr_put(prdcr); return rc; } diff --git a/ldms/src/ldmsd/ldmsd_strgp.c b/ldms/src/ldmsd/ldmsd_strgp.c index 36934c50a..a3c4cf063 100644 --- a/ldms/src/ldmsd/ldmsd_strgp.c +++ b/ldms/src/ldmsd/ldmsd_strgp.c @@ -365,7 +365,7 @@ int ldmsd_strgp_prdcr_add(const char *strgp_name, const char *regex_str, free(match); out_1: ldmsd_strgp_unlock(strgp); - ldmsd_strgp_put(strgp); + ldmsd_strgp_put(strgp, "find"); return rc; } @@ -396,7 +396,7 @@ int ldmsd_strgp_prdcr_del(const char *strgp_name, const char *regex_str, free(match); out_1: ldmsd_strgp_unlock(strgp); - ldmsd_strgp_put(strgp); + ldmsd_strgp_put(strgp, "find"); return rc; } @@ -451,7 +451,7 @@ int ldmsd_strgp_metric_add(const char *strgp_name, const char *metric_name, TAILQ_INSERT_TAIL(&strgp->metric_list, metric, entry); out_1: ldmsd_strgp_unlock(strgp); - ldmsd_strgp_put(strgp); + ldmsd_strgp_put(strgp, "find"); return rc; } @@ -480,7 +480,7 @@ int ldmsd_strgp_metric_del(const char *strgp_name, const char *metric_name, free(metric); out_1: ldmsd_strgp_unlock(strgp); - ldmsd_strgp_put(strgp); + ldmsd_strgp_put(strgp, "find"); return rc; } @@ -488,7 +488,7 @@ static ldmsd_strgp_ref_t strgp_ref_new(ldmsd_strgp_t strgp) { ldmsd_strgp_ref_t ref = calloc(1, sizeof *ref); if (ref) - ref->strgp = ldmsd_strgp_get(strgp); + ref->strgp = ldmsd_strgp_get(strgp, "strgp_ref"); return ref; } @@ -642,7 +642,7 @@ int ldmsd_strgp_update_prdcr_set(ldmsd_strgp_t strgp, ldmsd_prdcr_set_t prd_set) case LDMSD_STRGP_STATE_STOPPED: if (ref) { LIST_REMOVE(ref, entry); - ldmsd_strgp_put(ref->strgp); + ldmsd_strgp_put(ref->strgp, "strgp_ref"); ref->strgp = NULL; free(ref); } @@ -750,7 +750,7 @@ int ldmsd_strgp_start(const char *name, ldmsd_sec_ctxt_t ctxt) return ENOENT; } rc = __ldmsd_strgp_start(strgp, ctxt); - ldmsd_strgp_put(strgp); + ldmsd_strgp_put(strgp, "find"); return rc; } @@ -783,7 +783,7 @@ int ldmsd_strgp_stop(const char *strgp_name, ldmsd_sec_ctxt_t ctxt) if (!strgp) return ENOENT; rc = __ldmsd_strgp_stop(strgp, ctxt); - ldmsd_strgp_put(strgp); + ldmsd_strgp_put(strgp, "find"); return rc; } @@ -821,7 +821,8 @@ int ldmsd_strgp_del(const char *strgp_name, ldmsd_sec_ctxt_t ctxt) pi = &strgp->store->base; rbt_del(cfgobj_trees[LDMSD_CFGOBJ_STRGP], &strgp->obj.rbn); - ldmsd_strgp_put(strgp); /* tree reference */ + ldmsd_strgp_put(strgp, "cfgobj_tree"); /* tree reference */ + ldmsd_strgp_put(strgp, "init"); if (strgp->decomp) { strgp->decomp->release_decomp(strgp); @@ -833,7 +834,7 @@ int ldmsd_strgp_del(const char *strgp_name, ldmsd_sec_ctxt_t ctxt) out_0: pthread_mutex_unlock(cfgobj_locks[LDMSD_CFGOBJ_STRGP]); if (strgp) - ldmsd_strgp_put(strgp); /* `find` reference */ + ldmsd_strgp_put(strgp, "find"); /* `find` reference */ if (pi) ldmsd_put_plugin(pi); return rc; diff --git a/ldms/src/ldmsd/ldmsd_updtr.c b/ldms/src/ldmsd/ldmsd_updtr.c index 8b60a4cdb..538afadb6 100644 --- a/ldms/src/ldmsd/ldmsd_updtr.c +++ b/ldms/src/ldmsd/ldmsd_updtr.c @@ -81,7 +81,7 @@ void ldmsd_updtr___del(ldmsd_cfgobj_t obj) rbn = rbt_min(&updtr->prdcr_tree); rbt_del(&updtr->prdcr_tree, rbn); prdcr_ref = container_of(rbn, struct ldmsd_prdcr_ref, rbn); - ldmsd_cfgobj_put(&prdcr_ref->prdcr->obj); + ldmsd_prdcr_put(prdcr_ref->prdcr, "prdcr_ref"); free(prdcr_ref); } ldmsd_cfgobj___del(obj); @@ -941,7 +941,7 @@ ldmsd_updtr_new_with_auth(const char *name, char *interval_str, char *offset_str return updtr; einval: ldmsd_cfgobj_unlock(&updtr->obj); - ldmsd_updtr_put(updtr); + ldmsd_updtr_put(updtr, "init"); errno = EINVAL; return NULL; } @@ -984,7 +984,8 @@ int ldmsd_updtr_del(const char *updtr_name, ldmsd_sec_ctxt_t ctxt) } rbt_del(cfgobj_trees[LDMSD_CFGOBJ_UPDTR], &updtr->obj.rbn); - ldmsd_updtr_put(updtr); /* tree reference */ + ldmsd_updtr_put(updtr, "cfgobj_tree"); /* tree reference */ + ldmsd_updtr_put(updtr, "init"); rc = 0; /* let-through */ out_1: @@ -992,7 +993,7 @@ int ldmsd_updtr_del(const char *updtr_name, ldmsd_sec_ctxt_t ctxt) out_0: pthread_mutex_unlock(cfgobj_locks[LDMSD_CFGOBJ_UPDTR]); if (updtr) - ldmsd_updtr_put(updtr); /* `find` reference */ + ldmsd_updtr_put(updtr, "find"); /* `find` reference */ return rc; } @@ -1098,12 +1099,12 @@ int ldmsd_updtr_start(const char *updtr_name, const char *interval_str, updtr_task_init(&updtr->default_task, updtr, 1, interval_us, offset_us); ldmsd_updtr_unlock(updtr); rc = __ldmsd_updtr_start(updtr, ctxt); - ldmsd_updtr_put(updtr); + ldmsd_updtr_put(updtr, "find"); return rc; err: ldmsd_updtr_unlock(updtr); - ldmsd_updtr_put(updtr); + ldmsd_updtr_put(updtr, "find"); return rc; } @@ -1168,7 +1169,7 @@ int ldmsd_updtr_stop(const char *updtr_name, ldmsd_sec_ctxt_t ctxt) if (!updtr) return ENOENT; rc = __ldmsd_updtr_stop(updtr, ctxt); - ldmsd_updtr_put(updtr); + ldmsd_updtr_put(updtr, "find"); return rc; } @@ -1288,7 +1289,7 @@ int ldmsd_updtr_match_add(const char *updtr_name, const char *regex_str, free(match); out_1: ldmsd_updtr_unlock(updtr); - ldmsd_updtr_put(updtr); + ldmsd_updtr_put(updtr, "find"); return rc; } @@ -1330,7 +1331,7 @@ int ldmsd_updtr_match_del(const char *updtr_name, const char *regex_str, free(match); out_1: ldmsd_updtr_unlock(updtr); - ldmsd_updtr_put(updtr); + ldmsd_updtr_put(updtr, "find"); return rc; } @@ -1338,7 +1339,7 @@ ldmsd_prdcr_ref_t prdcr_ref_new(ldmsd_prdcr_t prdcr) { ldmsd_prdcr_ref_t ref = calloc(1, sizeof *ref); if (ref) { - ref->prdcr = ldmsd_prdcr_get(prdcr); + ref->prdcr = ldmsd_prdcr_get(prdcr, "prdcr_ref"); rbn_init(&ref->rbn, prdcr->obj.name); } return ref; @@ -1439,7 +1440,7 @@ int ldmsd_updtr_prdcr_add(const char *updtr_name, const char *prdcr_regex, if (!ref) { rc = ENOMEM; sprintf(rep_buf, "%dMemory allocation failure.\n", ENOMEM); - ldmsd_prdcr_put(prdcr); + ldmsd_prdcr_put(prdcr, "iter"); ldmsd_cfg_unlock(LDMSD_CFGOBJ_PRDCR); goto out_1; } @@ -1450,7 +1451,7 @@ int ldmsd_updtr_prdcr_add(const char *updtr_name, const char *prdcr_regex, out_1: regfree(®ex); ldmsd_updtr_unlock(updtr); - ldmsd_updtr_put(updtr); + ldmsd_updtr_put(updtr, "find"); return rc; } @@ -1484,13 +1485,13 @@ int ldmsd_updtr_prdcr_del(const char *updtr_name, const char *prdcr_regex, for (ref = prdcr_ref_find_regex(updtr, ®ex); ref; ref = prdcr_ref_find_regex(updtr, ®ex)) { rbt_del(&updtr->prdcr_tree, &ref->rbn); - ldmsd_prdcr_put(ref->prdcr); + ldmsd_prdcr_put(ref->prdcr, "prdcr_ref"); free(ref); } out_1: regfree(®ex); ldmsd_updtr_unlock(updtr); - ldmsd_updtr_put(updtr); + ldmsd_updtr_put(updtr, "find"); out_0: return rc; } diff --git a/ldms/src/sampler/examples/test_sampler/test_sampler.c b/ldms/src/sampler/examples/test_sampler/test_sampler.c index 356c30a1a..5cf59039d 100644 --- a/ldms/src/sampler/examples/test_sampler/test_sampler.c +++ b/ldms/src/sampler/examples/test_sampler/test_sampler.c @@ -207,7 +207,8 @@ static void __stream_client_free(struct test_sampler_stream_client *c) if (c->ldms) { ldms_xprt_close(c->ldms); } - ldmsd_cfgobj_put(&c->auth_dom->obj); + if (c->auth_dom) + ldmsd_auth_put(c->auth_dom, "find"); free(c); } From c81a9f4ea60a927a9ec5a0021ec0f2b5af2e1e27 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Tue, 17 Oct 2023 22:26:39 +0000 Subject: [PATCH 06/13] Add multi-instance support in store_sos NOTE: This passed `ldms-test/multi_store_sos_test`, which tests both multi-instance loading and traditional loading. --- ldms/src/store/store_sos.c | 161 ++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 66 deletions(-) diff --git a/ldms/src/store/store_sos.c b/ldms/src/store/store_sos.c index 4384b1fe6..9d46ccd0c 100644 --- a/ldms/src/store/store_sos.c +++ b/ldms/src/store/store_sos.c @@ -85,6 +85,7 @@ typedef struct sos_handle_s { LIST_ENTRY(sos_handle_s) entry; } *sos_handle_t; +pthread_mutex_t sh_lock; /* sos_handle lock */ static LIST_HEAD(sos_handle_list, sos_handle_s) sos_handle_list; struct sos_single_list_records { @@ -108,12 +109,14 @@ struct sos_list_ctxt { struct sos_list *list; }; +typedef struct store_sos_s *store_sos_t; + /* * NOTE: * = / */ struct sos_instance { - struct ldmsd_store *store; + store_sos_t store; /* the plugin instance */ char *container; char *schema_name; char *path; /**< / */ @@ -145,11 +148,15 @@ struct sos_instance { struct rbt schema_rbt; }; -static pthread_mutex_t cfg_lock; -LIST_HEAD(sos_inst_list, sos_instance) inst_list; -static char root_path[PATH_MAX]; /**< store root path */ -time_t timeout = 5; /* Default is 5 seconds */ +struct store_sos_s { + struct ldmsd_store store; + pthread_mutex_t cfg_lock; + LIST_HEAD(sos_inst_list, sos_instance) inst_list; + char root_path[PATH_MAX]; /**< store root path */ + time_t timeout; /* Default is 5 seconds */ +}; + struct row_schema_key_s { const struct ldms_digest_s *digest; @@ -395,7 +402,7 @@ sos_mval_set(sos_value_t v, ldms_mval_t mval, enum ldms_value_type type) fn(v, mval); } -/* caller must hold cfg_lock */ +/* caller must hold sh_lock */ static sos_handle_t __create_handle(const char *path, sos_t sos) { sos_handle_t h = calloc(1, sizeof(*h)); @@ -414,7 +421,7 @@ static sos_handle_t __create_handle(const char *path, sos_t sos) return h; } -/* caller must hold cfg_lock */ +/* caller must hold sh_lock */ static sos_handle_t __create_container(const char *path) { int rc = 0; @@ -492,12 +499,12 @@ static void put_container_no_lock(sos_handle_t h) static void put_container(sos_handle_t h) { - pthread_mutex_lock(&cfg_lock); + pthread_mutex_lock(&sh_lock); put_container_no_lock(h); - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&sh_lock); } -/* caller must hold cfg_lock */ +/* caller must hold sh_lock */ static sos_handle_t __find_container(const char *path) { sos_handle_t h = NULL; @@ -517,7 +524,7 @@ static sos_handle_t get_container(const char *path) sos_handle_t h = NULL; sos_t sos; - pthread_mutex_lock(&cfg_lock); + pthread_mutex_lock(&sh_lock); h = __find_container(path); if (h) goto out; @@ -535,7 +542,7 @@ static sos_handle_t get_container(const char *path) /* the container does not exist, must create it */ h = __create_container(path); out: - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&sh_lock); return h; } @@ -548,10 +555,11 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct int rc = 0; int len; char *value; + store_sos_t ss = (store_sos_t)self; value = av_value(avl, "timeout"); if (value) - timeout = strtol(value, NULL, 0); + ss->timeout = strtol(value, NULL, 0); value = av_value(avl, "path"); if (!value) { @@ -560,8 +568,8 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct __func__, __LINE__); return EINVAL; } - pthread_mutex_lock(&cfg_lock); - if (0 == strcmp(value, root_path)) + pthread_mutex_lock(&ss->cfg_lock); + if (0 == strcmp(value, ss->root_path)) /* Ignore the call if the root_path is unchanged */ goto out; len = strlen(value); @@ -572,20 +580,20 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct rc = ENAMETOOLONG; goto out; } - strcpy(root_path, value); + strcpy(ss->root_path, value); /* Run through all open containers and close them. They will * get re-opened when store() is next called */ rc = ENOMEM; - LIST_FOREACH(si, &inst_list, entry) { + LIST_FOREACH(si, &ss->inst_list, entry) { pthread_mutex_lock(&si->lock); if (si->sos_handle) { - put_container_no_lock(si->sos_handle); + put_container(si->sos_handle); si->sos_handle = NULL; } size_t pathlen = - strlen(root_path) + strlen(si->container) + 4; + strlen(ss->root_path) + strlen(si->container) + 4; if (si->path) free(si->path); si->path = malloc(pathlen); @@ -594,24 +602,23 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct pthread_mutex_unlock(&si->lock); goto out; } - sprintf(si->path, "%s/%s", root_path, si->container); + sprintf(si->path, "%s/%s", ss->root_path, si->container); pthread_mutex_unlock(&si->lock); } rc = 0; out: - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&ss->cfg_lock); return rc; } static void term(struct ldmsd_plugin *self) { - if (mylog) - ovis_log_destroy(mylog); + /* no-op */ } static const char *usage(struct ldmsd_plugin *self) { - return " config name=store_sos path=\n" + return " config name= plugin=store_sos path=\n" " path The path to primary storage\n"; } @@ -626,10 +633,12 @@ open_store(struct ldmsd_store *s, const char *container, const char *schema, struct ldmsd_strgp_metric_list *metric_list, void *ucontext) { struct sos_instance *si = NULL; + store_sos_t ss = (store_sos_t)s; si = calloc(1, sizeof(*si)); if (!si) goto out; + si->store = ss; rbt_init(&si->schema_rbt, row_schema_rbn_cmp); si->ucontext = ucontext; si->container = strdup(container); @@ -639,15 +648,15 @@ open_store(struct ldmsd_store *s, const char *container, const char *schema, if (!si->schema_name) goto err2; size_t pathlen = - strlen(root_path) + strlen(si->container) + 4; + strlen(ss->root_path) + strlen(si->container) + 4; si->path = malloc(pathlen); if (!si->path) goto err3; - sprintf(si->path, "%s/%s", root_path, container); + sprintf(si->path, "%s/%s", ss->root_path, container); pthread_mutex_init(&si->lock, NULL); - pthread_mutex_lock(&cfg_lock); - LIST_INSERT_HEAD(&inst_list, si, entry); - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_lock(&ss->cfg_lock); + LIST_INSERT_HEAD(&ss->inst_list, si, entry); + pthread_mutex_unlock(&ss->cfg_lock); return si; err3: free(si->schema_name); @@ -1324,6 +1333,7 @@ store(ldmsd_store_handle_t _sh, ldms_set_t set, int *metric_arry, size_t metric_count) { struct sos_instance *si = _sh; + store_sos_t ss = si->store; struct ldms_timestamp timestamp; struct timespec now; SOS_VALUE(value); @@ -1366,9 +1376,9 @@ store(ldmsd_store_handle_t _sh, ldms_set_t set, "The job_id is missing from the metric set/schema.\n"); assert(si->ts_attr); } - if (timeout > 0) { + if (ss->timeout > 0) { clock_gettime(CLOCK_REALTIME, &now); - now.tv_sec += timeout; + now.tv_sec += ss->timeout; if (sos_begin_x_wait(si->sos_handle->sos, &now)) { LOG_(OVIS_LERROR, "Timeout attempting to open a transaction on the container '%s'.\n", @@ -1443,14 +1453,15 @@ static int flush_store(ldmsd_store_handle_t _sh) static void close_store(ldmsd_store_handle_t _sh) { struct sos_instance *si = _sh; + store_sos_t ss = si->store; struct row_schema_rbn_s *rrbn; if (!si) return; - pthread_mutex_lock(&cfg_lock); + pthread_mutex_lock(&ss->cfg_lock); LIST_REMOVE(si, entry); - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&ss->cfg_lock); while ((rrbn = (struct row_schema_rbn_s *)rbt_min(&si->schema_rbt))) { rbt_del(&si->schema_rbt, &rrbn->rbn); @@ -1469,6 +1480,7 @@ static void close_store(ldmsd_store_handle_t _sh) static int init_store_instance(ldmsd_strgp_t strgp) { struct sos_instance *si; + store_sos_t ss = (store_sos_t)strgp->store; int len, rc; si = calloc(1, sizeof(*si)); @@ -1476,8 +1488,9 @@ static int init_store_instance(ldmsd_strgp_t strgp) rc = errno; goto err_0; } + si->store = ss; rbt_init(&si->schema_rbt, row_schema_rbn_cmp); - len = asprintf(&si->path, "%s/%s", root_path, strgp->container); + len = asprintf(&si->path, "%s/%s", ss->root_path, strgp->container); if (len < 0) { rc = errno; goto err_1; @@ -1489,9 +1502,9 @@ static int init_store_instance(ldmsd_strgp_t strgp) } strgp->store_handle = si; pthread_mutex_init(&si->lock, NULL); - pthread_mutex_lock(&cfg_lock); - LIST_INSERT_HEAD(&inst_list, si, entry); - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_lock(&ss->cfg_lock); + LIST_INSERT_HEAD(&ss->inst_list, si, entry); + pthread_mutex_unlock(&ss->cfg_lock); return 0; err_2: @@ -1625,6 +1638,7 @@ commit_rows(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, int { int rc = 0; struct sos_instance *si; + store_sos_t ss; ldmsd_row_t row; ldmsd_col_t col; struct row_schema_rbn_s *rrbn; @@ -1649,10 +1663,11 @@ commit_rows(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, int goto out; } } - if (timeout > 0) { + ss = si->store; + if (ss->timeout > 0) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); - now.tv_sec += timeout; + now.tv_sec += ss->timeout; if (sos_begin_x_wait(si->sos_handle->sos, &now)) { LOG_(OVIS_LERROR, "Timeout attempting to open a transaction on the container '%s'.\n", @@ -1741,44 +1756,58 @@ commit_rows(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, int return rc; } -static struct ldmsd_store store_sos = { - .base = { - .name = "sos", - .term = term, - .config = config, - .usage = usage, - .type = LDMSD_PLUGIN_STORE, - }, - .open = open_store, - .get_context = get_ucontext, - .store = store, - .flush = flush_store, - .close = close_store, - .commit = commit_rows, -}; +void store_sos_del(struct ldmsd_cfgobj *obj) +{ + store_sos_t ss = (void*)obj; + ldmsd_store_cleanup(&ss->store); + free(ss); +} -struct ldmsd_plugin *get_plugin() +struct ldmsd_plugin *get_plugin_instance(const char *name, + uid_t uid, gid_t gid, int perm) { - int rc; - mylog = ovis_log_register("store.store_sos", "Log susbsystem of 'store_sos' plugin"); - if (!mylog) { - rc = errno; - ovis_log(NULL, OVIS_LWARN, "Failed to create the log subsystem " - "of 'store_sos' plugin. Error %d\n", rc); - } - return &store_sos.base; + store_sos_t ss; + + ss = (void*)ldmsd_store_alloc(name, sizeof(*ss), store_sos_del, uid, gid, perm); + + if (!ss) + goto out; + + ss->store.base.term = term; + ss->store.base.config = config; + ss->store.base.usage = usage; + + snprintf(ss->store.base.name, sizeof(ss->store.base.name), "store_sos"); + + ss->store.open = open_store; + ss->store.get_context = get_ucontext; + ss->store.store = store; + ss->store.flush = flush_store; + ss->store.close = close_store; + ss->store.commit = commit_rows; + + ss->timeout = 5; + + LIST_INIT(&ss->inst_list); + + out: + return &ss->store.base; } static void __attribute__ ((constructor)) store_sos_init(); static void store_sos_init() { - pthread_mutex_init(&cfg_lock, NULL); + pthread_mutex_init(&sh_lock, NULL); LIST_INIT(&sos_handle_list); + mylog = ovis_log_register("store.store_sos", "Log subsystem of 'store_sos' plugin"); + if (!mylog) { + ovis_log(NULL, OVIS_LWARN, "Failed to create the log subsystem " + "of 'store_sos' plugin. Error %d\n", errno); + } } static void __attribute__ ((destructor)) store_sos_fini(void); static void store_sos_fini() { - pthread_mutex_destroy(&cfg_lock); - /* TODO: clean up container and metric trees */ + pthread_mutex_destroy(&sh_lock); } From 8e6c7d7550eae5e55b345d20dfe3666a6d637a24 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Fri, 1 Dec 2023 11:10:12 -0600 Subject: [PATCH 07/13] Add multi-instance support in json_stream_sampler NOTE: This passed `ldms-test/multi_json_stream_sampler_test` which tests both multi-instance loading and traditional loading. It also tests terminating the plugin instance. --- ldms/src/sampler/json/json_stream_sampler.c | 275 +++++++++++++------- 1 file changed, 179 insertions(+), 96 deletions(-) diff --git a/ldms/src/sampler/json/json_stream_sampler.c b/ldms/src/sampler/json/json_stream_sampler.c index 6bb701616..d5dc413d4 100644 --- a/ldms/src/sampler/json/json_stream_sampler.c +++ b/ldms/src/sampler/json/json_stream_sampler.c @@ -54,15 +54,41 @@ #include #include #include +#include "ovis_ref/ref.h" #include "coll/rbt.h" #include "ovis_json/ovis_json.h" #include "ldms.h" #include "ldmsd.h" #include "ldmsd_stream.h" -#include "ovis_log.h" +#include "ovis_log/ovis_log.h" #define SAMP "json_stream" +typedef struct js_entry_s { + LIST_ENTRY(js_entry_s) entry; + ldms_set_t set; +} *js_entry_t; + +typedef struct json_stream_sampler_s *json_stream_sampler_t; +struct json_stream_sampler_s { + union { + struct ldmsd_sampler sampler; /* sampler interface */ + struct ldmsd_plugin plugin; /* plugin interface */ + }; + char *stream_name; + size_t heap_sz; + char *producer_name; + char *instance_name; + uint64_t comp_id; + uid_t uid; + gid_t gid; + uint32_t perm; + ldms_stream_client_t stream_client; + LIST_HEAD(, js_entry_s) set_list; + pthread_mutex_t lock; +}; + + static ovis_log_t __log = NULL; #define LOG(_level_, _fmt_, ...) ovis_log(__log, _level_, "[%d] " _fmt_, __LINE__, ##__VA_ARGS__) #define LCRITICAL(_fmt_, ...) ovis_log(__log, OVIS_LCRIT, "[%d]" _fmt_, __LINE__, ##__VA_ARGS__) @@ -774,22 +800,6 @@ static int get_schema_for_json(char *name, json_entity_t e, ldms_schema_t *sch) static int json_recv_cb(ldms_stream_event_t ev, void *arg); -pthread_mutex_t cfg_tree_lock = PTHREAD_MUTEX_INITIALIZER; -static struct rbt cfg_tree = RBT_INITIALIZER(str_cmp); -struct json_cfg_inst { - struct ldmsd_plugin *plugin; - char *stream_name; - size_t heap_sz; - char *producer_name; - char *instance_name; - uint64_t comp_id; - uid_t uid; - gid_t gid; - uint32_t perm; - pthread_mutex_t lock; - struct rbn rbn; /* Key is stream_name */ -}; - #define DEFAULT_HEAP_SZ 512 /* * instance=FMT The FMT string is a format specifier for generating @@ -799,15 +809,22 @@ struct json_cfg_inst { static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct attr_value_list *avl) { - struct json_cfg_inst *inst; char *value; int rc; + json_stream_sampler_t p = (json_stream_sampler_t)self; + + pthread_mutex_lock(&p->lock); + if (p->stream_client) { + LERROR("The plugin instance '%s' has been configured to process " + "stream '%s'. Use `term name=%s to terminate the plugin " + "and remove all associated sets and stream clients.\n" , + ldmsd_plugin_inst_name(self), + p->stream_name, + ldmsd_plugin_inst_name(self)); + pthread_mutex_unlock(&p->lock); + return EEXIST; + } - inst = calloc(1, sizeof(*inst)); - if (!inst) - return ENOMEM; - - pthread_mutex_init(&inst->lock, NULL); /* stream name */ value = av_value(avl, "stream"); @@ -816,8 +833,8 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, LERROR("The 'stream' configuration parameter is required.\n"); goto err_0; } - inst->stream_name = strdup(value); - if (!inst->stream_name) { + p->stream_name = strdup(value); + if (!p->stream_name) { rc = ENOMEM; goto err_0; } @@ -829,8 +846,8 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, rc = EINVAL; goto err_0; } - inst->producer_name = strdup(value); - if (!inst->producer_name) { + p->producer_name = strdup(value); + if (!p->producer_name) { rc = ENOMEM; goto err_0; } @@ -838,8 +855,8 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, /* instance name */ value = av_value(avl, "instance"); if (value) { - inst->instance_name = strdup(value); - if (!inst->instance_name) { + p->instance_name = strdup(value); + if (!p->instance_name) { rc = ENOMEM; goto err_0; } @@ -847,21 +864,21 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, /* component_id */ value = av_value(avl, "component_id"); - inst->comp_id = 0; + p->comp_id = 0; if (value) { /* Skip non isdigit prefix */ while (*value != '\0' && !isdigit(*value)) value++; if (*value != '\0') - inst->comp_id = (uint64_t)(atoi(value)); + p->comp_id = (uint64_t)(atoi(value)); } /* heap_sz */ - inst->heap_sz = DEFAULT_HEAP_SZ; + p->heap_sz = DEFAULT_HEAP_SZ; value = av_value(avl, "heap_sz"); if (value) - inst->heap_sz = strtol(value, NULL, 0); + p->heap_sz = strtol(value, NULL, 0); /* Set uid, gid, perm to the default values */ - ldms_set_default_authz(&inst->uid, &inst->gid, &inst->perm, DEFAULT_AUTHZ_READONLY); + ldms_set_default_authz(&p->uid, &p->gid, &p->perm, DEFAULT_AUTHZ_READONLY); /* uid */ value = av_value(avl, "uid"); if (value) { @@ -873,9 +890,9 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, rc = EINVAL; goto err_0; } - inst->uid = pwd->pw_uid; + p->uid = pwd->pw_uid; } else { - inst->uid = strtol(value, NULL, 0); + p->uid = strtol(value, NULL, 0); } } @@ -890,9 +907,9 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, rc = EINVAL; goto err_0; } - inst->gid = grp->gr_gid; + p->gid = grp->gr_gid; } else { - inst->gid = strtol(value, NULL, 0); + p->gid = strtol(value, NULL, 0); } } @@ -904,35 +921,31 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, "as an Octal number.\n", value); } - inst->perm = strtol(value, NULL, 0); + p->perm = strtol(value, NULL, 0); } - rbn_init(&inst->rbn, inst->stream_name); - pthread_mutex_lock(&cfg_tree_lock); - struct rbn *rbn = rbt_find(&cfg_tree, inst->stream_name); - if (rbn) { - rc = EBUSY; - LERROR("The stream name '%s' has already been registered for " - "this sampler.\n", inst->stream_name); - goto err_1; + p->stream_client = ldms_stream_subscribe(p->stream_name, 0, json_recv_cb, p, "json_stream_sampler"); + if (!p->stream_client) { + LERROR("Cannot create stream client.\n"); + rc = errno; + goto err_0; } - rbt_ins(&cfg_tree, &inst->rbn); - pthread_mutex_unlock(&cfg_tree_lock); - ldms_stream_subscribe(inst->stream_name, 0, json_recv_cb, inst, "json_stream_sampler"); + ldmsd_plugin_get(&p->plugin, "subscribe"); /* put in json_recv_cb, CLOSE event */ + pthread_mutex_unlock(&p->lock); return 0; - err_1: - pthread_mutex_unlock(&cfg_tree_lock); + err_0: - if (inst) { - free(inst->stream_name); - free(inst->producer_name); - free(inst->instance_name); - free(inst); - } + free(p->stream_name); + p->stream_name = NULL; + free(p->producer_name); + p->producer_name = NULL; + free(p->instance_name); + p->instance_name = NULL; + pthread_mutex_unlock(&p->lock); return rc; } -static void update_set_data(struct json_cfg_inst *inst, +static void update_set_data(json_stream_sampler_t p, ldms_set_t set, json_entity_t entity) { @@ -988,24 +1001,27 @@ static void update_set_data(struct json_cfg_inst *inst, } } -static int json_recv_cb(ldms_stream_event_t ev, void *arg) +static int __stream_close(ldms_stream_event_t ev, json_stream_sampler_t p) +{ + ldmsd_plugin_put(&p->plugin, "subscribe"); /* from subscribe */ + return 0; +} + +static int __stream_recv(ldms_stream_event_t ev, json_stream_sampler_t p) { const char *msg; json_entity_t entity; int rc = EINVAL; ldms_schema_t schema; - struct json_cfg_inst *inst = arg; json_entity_t schema_name; - if (ev->type != LDMS_STREAM_EVENT_RECV) - return 0; LDEBUG("thread: %lu, stream: '%s', msg: '%s'\n", pthread_self(), ev->recv.name, ev->recv.data); if (ev->recv.type != LDMS_STREAM_JSON) { LERROR("Unexpected stream type data...ignoring\n"); return 0; } - pthread_mutex_lock(&inst->lock); + pthread_mutex_lock(&p->lock); msg = ev->recv.data; entity = ev->recv.json; @@ -1032,10 +1048,10 @@ static int json_recv_cb(ldms_stream_event_t ev, void *arg) goto err_0; } char *set_name; - if (inst->instance_name) { - set_name = strdup(inst->instance_name); + if (p->instance_name) { + set_name = strdup(p->instance_name); } else { - rc = asprintf(&set_name, "%s_%s", inst->producer_name, + rc = asprintf(&set_name, "%s_%s", p->producer_name, json_value_str(schema_name)->str); if (rc < 0) set_name = NULL; @@ -1046,18 +1062,28 @@ static int json_recv_cb(ldms_stream_event_t ev, void *arg) } ldms_set_t set = ldms_set_by_name(set_name); if (!set) { - set = ldms_set_create(set_name, schema, inst->uid, inst->gid, - inst->perm, inst->heap_sz); + js_entry_t ent = calloc(1, sizeof(*ent)); + if (!ent) { + LERROR("Out of memory\n"); + rc = ENOMEM; + goto err_1; + } + set = ldms_set_create(set_name, schema, p->uid, p->gid, + p->perm, p->heap_sz); if (set) { LINFO("Created the set '%s' with schema '%s'\n", set_name, ldms_schema_name_get(schema)); + ldmsd_set_register(set, p->plugin.cfgobj.name); ldms_set_publish(set); } else { + free(ent); LERROR("Error %d creating the set '%s' with schema '%s'\n", errno, set_name, ldms_schema_name_get(schema)); rc = errno; goto err_1; } + ent->set = set; + LIST_INSERT_HEAD(&p->set_list, ent, entry); } free(set_name); @@ -1066,57 +1092,114 @@ static int json_recv_cb(ldms_stream_event_t ev, void *arg) ldms_metric_set_s32(set, 0, ev->recv.cred.uid); ldms_metric_set_s32(set, 1, ev->recv.cred.gid); ldms_metric_set_s32(set, 2, ev->recv.perm); - update_set_data(inst, set, entity); + update_set_data(p, set, entity); ldms_transaction_end(set); - pthread_mutex_unlock(&inst->lock); + pthread_mutex_unlock(&p->lock); return 0; err_1: free(set_name); err_0: - pthread_mutex_unlock(&inst->lock); + pthread_mutex_unlock(&p->lock); return rc; } +static int json_recv_cb(ldms_stream_event_t ev, void *arg) +{ + json_stream_sampler_t p = arg; + + switch (ev->type) { + case LDMS_STREAM_EVENT_CLOSE: + return __stream_close(ev, p); + case LDMS_STREAM_EVENT_RECV: + return __stream_recv(ev, p); + default: + /* ignore other events */ + return 0; + } +} + static void term(struct ldmsd_plugin *self) { - /* TODO: Cleanup ... maybe ... */ + json_stream_sampler_t p = (json_stream_sampler_t)self; + if (p->stream_client) { + ldms_stream_close(p->stream_client); /* CLOSE event will clean up `p` */ + } } static int sample(struct ldmsd_sampler *self) { - /* no opt */ + /* no ops */ return 0; } static ldms_set_t get_set(struct ldmsd_sampler *self) { - /* no opt */ + /* no ops */ return NULL; } -static struct ldmsd_sampler json_plugin = { - .base = { - .name = SAMP, - .type = LDMSD_PLUGIN_SAMPLER, - .term = term, - .config = config, - .usage = usage, - }, - .get_set = get_set, - .sample = sample, -}; +static void __once() +{ + static int once = 0; + if (once) + return; + /* Log initialization errors are quiet and will result in + * messages going to the application log instead of our subsystem + * specific log */ + __log = ovis_log_register("sampler.json_stream", + "JSON sampler that creates LDMS metric sets from JSON messages." + ); + once = 1; +} + -struct ldmsd_plugin *get_plugin() +/* This function is called when obj->ref_count reaches 0 */ +void __jss_del(struct ldmsd_cfgobj *obj) { - if (!__log) { - /* Log initialization errors are quiet and will result in - * messages going to the application log instead of our subsystem - * specific log */ - __log = ovis_log_register - ("sampler.json_stream", - "JSON sampler that creates LDMS metric sets from JSON messages." - ); + json_stream_sampler_t jss = (void*)obj; + js_entry_t ent; + + /* clean up resources in ldmsd_sampler structure */ + ldmsd_sampler_cleanup(&jss->sampler); + + /* strings from `config()` */ + free(jss->stream_name); + free(jss->instance_name); + free(jss->producer_name); + + /* free the sets */ + while ((ent = LIST_FIRST(&jss->set_list))) { + LIST_REMOVE(ent, entry); + ldmsd_set_deregister(ldms_set_name_get(ent->set), obj->name); + ldms_set_delete(ent->set); + free(ent); } - return &json_plugin.base; + + free(jss); +} + +struct ldmsd_plugin *get_plugin_instance(const char *name, + uid_t uid, gid_t gid, int perm) +{ + json_stream_sampler_t jss; + __once(); + + jss = (void*)ldmsd_sampler_alloc(name, sizeof(*jss), __jss_del, uid, gid, perm); + if (!jss) + goto out; + + jss->plugin.term = term; + jss->plugin.config = config; + jss->plugin.usage = usage; + + snprintf(jss->plugin.name, sizeof(jss->plugin.name), "json_stream_sampler_s"); + + jss->sampler.sample = sample; + jss->sampler.get_set = get_set; + + pthread_mutex_init(&jss->lock, NULL); + + out: + return &jss->plugin; } From e71f834deca6027b33924e2bb12aeeabb11f2ca8 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Tue, 12 Dec 2023 15:23:06 -0600 Subject: [PATCH 08/13] Fix zap log warning When ldmsd daemonized (fork + exec), `zap_init()` function is called again from the pthread atfork call chain. Because the logger has already been registered before daemonizing, `ovis_log_register()` failed to register the same logger after ldmsd was daemonized. This patch adds a checking condition to not register the logger if we alread have the log handle. --- lib/src/zap/zap.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/src/zap/zap.c b/lib/src/zap/zap.c index f2f3ea938..d0bd4c343 100644 --- a/lib/src/zap/zap.c +++ b/lib/src/zap/zap.c @@ -1197,9 +1197,15 @@ static void zap_init(void) TAILQ_INIT(&zap_ep_list); pthread_mutex_init(&zap_ep_list_lock, 0); #endif /* _ZAP_EP_TRACK_ */ - zlog = ovis_log_register("xprt.zap", "Messages for Zap"); - if (!zlog) - ovis_log(zlog, OVIS_LWARN, "Failed to create Zap's log subsystem.\n"); + if (!zlog) { + zlog = ovis_log_register("xprt.zap", "Messages for Zap"); + if (!zlog) { + /* This will log through default logger */ + ovis_log(zlog, OVIS_LWARN, + "Failed to create Zap's log subsystem, " + "errno: %d.\n", errno); + } + } zap_io_busy = ZAP_ENV_DBL(ZAP_IO_BUSY); if (zap_io_busy < 0.0 || zap_io_busy > 1.0) { /* bad value, set to default */ From a891b8743c7c50d4dddc1721ce983b59490a29e7 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Tue, 12 Dec 2023 15:54:58 -0600 Subject: [PATCH 09/13] Add cfgobj debug ref logging --- ldms/src/ldmsd/ldmsd_cfgobj.c | 48 +++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/ldms/src/ldmsd/ldmsd_cfgobj.c b/ldms/src/ldmsd/ldmsd_cfgobj.c index 9fd2f71f6..3bf3e0230 100644 --- a/ldms/src/ldmsd/ldmsd_cfgobj.c +++ b/ldms/src/ldmsd/ldmsd_cfgobj.c @@ -1,8 +1,8 @@ /* -*- c-basic-offset: 8 -*- - * Copyright (c) 2015,2018 National Technology & Engineering Solutions + * Copyright (c) 2015,2018,2023 National Technology & Engineering Solutions * of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with * NTESS, the U.S. Government retains certain rights in this software. - * Copyright (c) 2015,2018 Open Grid Computing, Inc. All rights reserved. + * Copyright (c) 2015,2018,2023 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -58,6 +58,29 @@ #include #include "ldmsd.h" +static int __cfgobj_ref_debug = 0; +static ovis_log_t __cfgobj_log; + +#define __CFGOBJ_REF_DEBUG(FMT, ...) do {\ + if (__cfgobj_ref_debug) \ + ovis_log(__cfgobj_log, OVIS_LDEBUG, FMT, ##__VA_ARGS__); \ + } while (0) + + +__attribute__((constructor)) +static void __cfgobj_once() +{ + const char *v; + v = getenv("LDMSD_CFGOBJ_REF_DEBUG"); + if (!v) + return; + __cfgobj_ref_debug = atoi(v); + if (!__cfgobj_ref_debug) + return; + __cfgobj_log = ovis_log_register("ldmsd_cfgobj_ref_debug", "cfgobj reference debug log"); + ovis_log_set_level(__cfgobj_log, OVIS_LDEBUG); +} + int cfgobj_cmp(void *a, const void *b) { return strcmp(a, b); @@ -185,6 +208,7 @@ static void __cfgobj_ref_free(void *arg) { ldmsd_cfgobj_t obj = arg; obj->__del(obj); + __CFGOBJ_REF_DEBUG("cfgobj ref_free: %p\n", obj); } void ldmsd_cfgobj_plugin_cleanup(struct ldmsd_cfgobj *obj) @@ -211,6 +235,22 @@ void ldmsd_cfgobj_plugin_cleanup(struct ldmsd_cfgobj *obj) } } +static const char *__cfgobj_type_str[] = { + [LDMSD_CFGOBJ_PRDCR] = "prdcr", + [LDMSD_CFGOBJ_UPDTR] = "updtr", + [LDMSD_CFGOBJ_STRGP] = "strgp", + [LDMSD_CFGOBJ_LISTEN] = "listen", + [LDMSD_CFGOBJ_AUTH] = "auth", + [LDMSD_CFGOBJ_PLUGIN] = "plugin", +}; + +const char *ldmsd_cfgobj_type_str(ldmsd_cfgobj_type_t t) +{ + if (t < LDMSD_CFGOBJ_FIRST || LDMSD_CFGOBJ_LAST < t) + return "UNKNOWN"; + return __cfgobj_type_str[t]; +} + static int __cfgobj_init(ldmsd_cfgobj_t obj, const char *name, ldmsd_cfgobj_type_t type, ldmsd_cfgobj_del_fn_t __del, uid_t uid, gid_t gid, int perm) @@ -228,6 +268,10 @@ static int __cfgobj_init(ldmsd_cfgobj_t obj, const char *name, obj->type = type; obj->__del = __del; + + __CFGOBJ_REF_DEBUG("cfgobj ref_init: %p %s (%s)\n", + obj, ldmsd_cfgobj_type_str(type), name); + return 0; } From 6f7286048f3adb4b979af6127aae2849f295bee0 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Fri, 15 Dec 2023 17:07:48 -0600 Subject: [PATCH 10/13] store_csv multi-instance This passed `ldms-test/multi_store_csv_test`. --- ldms/src/store/store_csv.c | 322 +++++++++++++++++++++---------------- 1 file changed, 184 insertions(+), 138 deletions(-) diff --git a/ldms/src/store/store_csv.c b/ldms/src/store/store_csv.c index dc0651192..809f44595 100644 --- a/ldms/src/store/store_csv.c +++ b/ldms/src/store/store_csv.c @@ -86,16 +86,32 @@ #define PNAME "store_csv" -static idx_t store_idx; /* protected by cfg_lock */ -struct plugattr *pa = NULL; /* plugin attributes from config */ -static int rollover; -static int rollagain; -/** rollempty determines if timed rollovers are performed even - * when no data has been written (producing empty files). - */ -static bool rollempty = true; -/** rolltype determines how to interpret rollover values > 0. */ -static int rolltype = -1; +/* Plugin structure */ +typedef struct store_csv_s { + struct ldmsd_store store; /* base ldmsd store */ + + /* extension */ + + pthread_mutex_t cfg_lock; + + idx_t store_idx; /* protected by cfg_lock */ + struct plugattr *pa; /* plugin attributes from config */ + int rollover; + int rollagain; + + /* rollempty determines if timed rollovers are performed even + * when no data has been written (producing empty files). + */ + bool rollempty; + + /* rolltype determines how to interpret rollover values > 0. */ + int rolltype; + + pthread_t rothread; + int rothread_used; + +} *store_csv_t; + /** ROLLTYPES documents rolltype and is used in help output. Also used for buffering */ #define ROLLTYPES \ " 1: wake approximately every rollover seconds and roll.\n" \ @@ -122,10 +138,6 @@ static int rolltype = -1; /** Interval to check for passing the record or byte count limits. */ #define ROLL_LIMIT_INTERVAL 60 - -static pthread_t rothread; -static int rothread_used = 0; - static ovis_log_t mylog; #define ERR_LOG(FMT, ...) do { \ @@ -137,11 +149,6 @@ static ovis_log_t mylog; ovis_log(mylog, OVIS_LINFO, FMT, ## __VA_ARGS__); \ } while(0) -#define _stringify(_x) #_x -#define stringify(_x) _stringify(_x) - -#define LOGFILE "/var/log/store_csv.log" - struct csv_lent { ldms_mval_t mval; /* list entry value */ uint64_t udata; /* User data */ @@ -175,6 +182,7 @@ struct csv_store_handle { int num_lists; /* Number of list metrics */ struct csv_lent *lents; int ref_count; /* number of strgp using the csv file; protected by cfg_lock */ + store_csv_t sc; CSV_STORE_HANDLE_COMMON; }; @@ -207,8 +215,6 @@ struct csv_row_store_handle { struct rbt row_schema_rbt; }; -static pthread_mutex_t cfg_lock; - static char* allocStoreKey(const char* container, const char* schema){ if ((container == NULL) || (schema == NULL) || @@ -231,6 +237,7 @@ static char* allocStoreKey(const char* container, const char* schema){ struct roll_cb_arg { struct csv_plugin_static *cps; time_t appx; + store_csv_t sc; }; static void roll_cb(void *obj, void *cb_arg) @@ -241,6 +248,7 @@ static void roll_cb(void *obj, void *cb_arg) struct roll_cb_arg * args = (struct roll_cb_arg *)cb_arg; time_t appx = args->appx; struct csv_plugin_static *cps = args->cps; + store_csv_t sc = args->sc; FILE* nhfp = NULL; FILE* nfp = NULL; @@ -252,16 +260,16 @@ static void roll_cb(void *obj, void *cb_arg) //if we've got here then we've called new_store, but it might be closed pthread_mutex_lock(&s_handle->lock); - switch (rolltype) { + switch (sc->rolltype) { case 1: case 2: case 5: - if (!s_handle->store_count && !rollempty) + if (!s_handle->store_count && !sc->rollempty) /* skip rollover of empty files */ goto out; break; case 3: - if (s_handle->store_count < rollover) { + if (s_handle->store_count < sc->rollover) { goto out; } else { s_handle->store_count = 0; @@ -269,7 +277,7 @@ static void roll_cb(void *obj, void *cb_arg) } break; case 4: - if (s_handle->byte_count < rollover) { + if (s_handle->byte_count < sc->rollover) { goto out; } else { s_handle->byte_count = 0; @@ -278,7 +286,7 @@ static void roll_cb(void *obj, void *cb_arg) break; default: ovis_log(mylog, OVIS_LDEBUG, "Error: unexpected rolltype in store(%d)\n", - rolltype); + sc->rolltype); break; } @@ -391,33 +399,35 @@ function is called. Volume-based rolltypes must check and shortcircuit within this function. */ -static int handleRollover(struct csv_plugin_static *cps){ +static int handleRollover(store_csv_t sc){ //get the config lock //for every handle we have, do the rollover struct roll_cb_arg rca; - pthread_mutex_lock(&cfg_lock); + pthread_mutex_lock(&sc->cfg_lock); rca.appx = time(NULL); - rca.cps = cps; - idx_traverse(store_idx, roll_cb, (void *)&rca); + rca.cps = &PG; + rca.sc = sc; + idx_traverse(sc->store_idx, roll_cb, (void *)&rca); - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&sc->cfg_lock); return 0; } static void* rolloverThreadInit(void* m){ + store_csv_t sc = m; //if got here, then rollover requested while(1){ int tsleep; - switch (rolltype) { + switch (sc->rolltype) { case 1: - tsleep = (rollover < MIN_ROLL_1) ? - MIN_ROLL_1 : rollover; + tsleep = (sc->rollover < MIN_ROLL_1) ? + MIN_ROLL_1 : sc->rollover; break; case 2: { time_t rawtime; @@ -427,7 +437,7 @@ static void* rolloverThreadInit(void* m){ localtime_r( &rawtime, &info ); int secSinceMidnight = info.tm_hour*3600 + info.tm_min*60 + info.tm_sec; - tsleep = 86400 - secSinceMidnight + rollover; + tsleep = 86400 - secSinceMidnight + sc->rollover; if (tsleep < MIN_ROLL_1){ /* if we just did a roll then skip this one */ tsleep+=86400; @@ -435,13 +445,13 @@ static void* rolloverThreadInit(void* m){ } break; case 3: - if (rollover < MIN_ROLL_RECORDS) - rollover = MIN_ROLL_RECORDS; + if (sc->rollover < MIN_ROLL_RECORDS) + sc->rollover = MIN_ROLL_RECORDS; tsleep = ROLL_LIMIT_INTERVAL; break; case 4: - if (rollover < MIN_ROLL_BYTES) - rollover = MIN_ROLL_BYTES; + if (sc->rollover < MIN_ROLL_BYTES) + sc->rollover = MIN_ROLL_BYTES; tsleep = ROLL_LIMIT_INTERVAL; break; case 5: { @@ -453,15 +463,15 @@ static void* rolloverThreadInit(void* m){ int secSinceMidnight = info.tm_hour*3600 + info.tm_min*60 + info.tm_sec; - if (secSinceMidnight < rollover) { - tsleep = rollover - secSinceMidnight; + if (secSinceMidnight < sc->rollover) { + tsleep = sc->rollover - secSinceMidnight; } else { - int y = secSinceMidnight - rollover; - int z = y / rollagain; - tsleep = (z + 1)*rollagain + rollover - secSinceMidnight; + int y = secSinceMidnight - sc->rollover; + int z = y / sc->rollagain; + tsleep = (z + 1)*sc->rollagain + sc->rollover - secSinceMidnight; } if (tsleep < MIN_ROLL_1) { - tsleep += rollagain; + tsleep += sc->rollagain; } } break; @@ -472,7 +482,7 @@ static void* rolloverThreadInit(void* m){ sleep(tsleep); int oldstate = 0; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - handleRollover(&PG); + handleRollover(sc); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); } @@ -484,7 +494,8 @@ static void* rolloverThreadInit(void* m){ */ static int config_handle(struct csv_store_handle *s_handle) { - int cic_err = OPEN_STORE_COMMON(pa, s_handle); + store_csv_t sc = s_handle->sc; + int cic_err = OPEN_STORE_COMMON(sc->pa, s_handle); if ( cic_err != 0 ) { return cic_err; } @@ -492,14 +503,14 @@ static int config_handle(struct csv_store_handle *s_handle) char *c; bool r = false; /* default if not in pa */ - int cvt = ldmsd_plugattr_bool(pa, "userdata", k, &r); + int cvt = ldmsd_plugattr_bool(sc->pa, "userdata", k, &r); if (cvt == -1) { ovis_log(mylog, OVIS_LERROR, "improper userdata= input.\n"); return EINVAL; } s_handle->udata = r; r = true; - cvt = ldmsd_plugattr_bool(pa, "expand_array", k, &r); + cvt = ldmsd_plugattr_bool(sc->pa, "expand_array", k, &r); if (cvt == -1) { ovis_log(mylog, OVIS_LERROR, "improper expand_array= input.\n"); return EINVAL; @@ -509,13 +520,13 @@ static int config_handle(struct csv_store_handle *s_handle) s_handle->array_sep = ':'; s_handle->array_lquote = '\"'; s_handle->array_rquote = '\"'; - c = (char *)ldmsd_plugattr_value(pa, "array_sep", k); + c = (char *)ldmsd_plugattr_value(sc->pa, "array_sep", k); if (c) s_handle->array_sep = c[0]; - c = (char *)ldmsd_plugattr_value(pa, "array_lquote", k); + c = (char *)ldmsd_plugattr_value(sc->pa, "array_lquote", k); if (c) s_handle->array_lquote = c[0]; - c = (char *)ldmsd_plugattr_value(pa, "array_rquote", k); + c = (char *)ldmsd_plugattr_value(sc->pa, "array_rquote", k); if (c) s_handle->array_rquote = c[0]; } @@ -587,16 +598,16 @@ static const char *update_blacklist[] = { /* the container/schema value pair is the key for our plugin */ #define KEY_PLUG_ATTR 2, "container", "schema" -static int update_config(struct attr_value_list *kwl, struct attr_value_list *avl, const char *container, const char *schema) +static int update_config(store_csv_t sc, struct attr_value_list *kwl, struct attr_value_list *avl, const char *container, const char *schema) { int rc = 0; char *k = allocStoreKey(container, schema); if (!k) { return ENOMEM; } - pthread_mutex_lock(&cfg_lock); + pthread_mutex_lock(&sc->cfg_lock); struct csv_store_handle *s_handle = NULL; - s_handle = idx_find(store_idx, (void *)k, strlen(k)); + s_handle = idx_find(sc->store_idx, (void *)k, strlen(k)); if (s_handle) { ovis_log(mylog, OVIS_LWARNING, PNAME " updating config on %s not allowed after store is running.\n", k); /* s_handle *could* consult pa again, but that @@ -617,7 +628,7 @@ static int update_config(struct attr_value_list *kwl, struct attr_value_list *av rc = EINVAL; goto out; } - rc = ldmsd_plugattr_add(pa, avl, kwl, update_blacklist, update_blacklist, dep, KEY_PLUG_ATTR); + rc = ldmsd_plugattr_add(sc->pa, avl, kwl, update_blacklist, update_blacklist, dep, KEY_PLUG_ATTR); if (rc == EEXIST) { ovis_log(mylog, OVIS_LERROR, PNAME " cannot repeat config on %s.\n", k); @@ -628,7 +639,7 @@ static int update_config(struct attr_value_list *kwl, struct attr_value_list *av } out: free(k); - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&sc->cfg_lock); return rc; } @@ -637,6 +648,7 @@ static int update_config(struct attr_value_list *kwl, struct attr_value_list *av */ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct attr_value_list *avl) { + store_csv_t sc = (void*)self; int rollmethod = DEFAULT_ROLLTYPE; int rc; @@ -680,28 +692,28 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct " config arguments schema and opt_file must not be used together.\n"); return EINVAL; } - if (cn && !pa) { + if (cn && !sc->pa) { ovis_log(mylog, OVIS_LERROR, PNAME "plugin defaults must be configured before specifics for container=%s schema=%s\n", cn, sn); return EINVAL; } - if (cn && pa) { + if (cn && sc->pa) { ovis_log(mylog, OVIS_LDEBUG, "parsing specific schema option\n"); - return update_config(kwl, avl, cn, sn); + return update_config(sc, kwl, avl, cn, sn); } - if (pa) { + if (sc->pa) { ovis_log(mylog, OVIS_LINFO, "config of defaults cannot be repeated.\n"); return EINVAL; } /* end strgp pseudo-instance region. */ - pthread_mutex_lock(&cfg_lock); + pthread_mutex_lock(&sc->cfg_lock); - pa = ldmsd_plugattr_create(conf, PNAME, avl, kwl, + sc->pa = ldmsd_plugattr_create(conf, PNAME, avl, kwl, init_blacklist, init_blacklist, dep, KEY_PLUG_ATTR); - if (!pa) { + if (!sc->pa) { ovis_log(mylog, OVIS_LERROR, "Reminder: omit 'config name=<>' from lines in opt_file\n"); ovis_log(mylog, OVIS_LERROR, "error parsing %s\n", conf); rc = EINVAL; @@ -710,25 +722,25 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct ovis_log(mylog, OVIS_LDEBUG, "parsed %s\n", conf); - const char *s = ldmsd_plugattr_value(pa, "path", NULL); + const char *s = ldmsd_plugattr_value(sc->pa, "path", NULL); rc = 0; if (!s) { ovis_log(mylog, OVIS_LERROR, PNAME ": config requires path=value be provided in opt_file or arguments.\n"); - ldmsd_plugattr_destroy(pa); - pa = NULL; + ldmsd_plugattr_destroy(sc->pa); + sc->pa = NULL; rc = EINVAL; goto out; } - if (rolltype != -1) { + if (sc->rolltype != -1) { ovis_log(mylog, OVIS_LWARNING, "%s: repeated rollover config is ignored.\n", PNAME); goto out; /* rollover configured exactly once */ } int ragain = 0; int roll = -1; int cvt; - cvt = ldmsd_plugattr_s32(pa, "rollagain", NULL, &ragain); + cvt = ldmsd_plugattr_s32(sc->pa, "rollagain", NULL, &ragain); if (!cvt) { if (ragain < 0) { ovis_log(mylog, OVIS_LERROR, PNAME @@ -743,7 +755,7 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct goto out; } - cvt = ldmsd_plugattr_s32(pa, "rollover", NULL, &roll); + cvt = ldmsd_plugattr_s32(sc->pa, "rollover", NULL, &roll); if (!cvt) { if (roll < 0) { ovis_log(mylog, OVIS_LERROR, PNAME @@ -758,7 +770,7 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct goto out; } - cvt = ldmsd_plugattr_s32(pa, "rolltype", NULL, &rollmethod); + cvt = ldmsd_plugattr_s32(sc->pa, "rolltype", NULL, &rollmethod); if (!cvt) { if (roll < 0) { /* rolltype not valid without rollover also */ @@ -786,36 +798,36 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct rc = EINVAL; goto out; } - cvt = ldmsd_plugattr_bool(pa, "rollempty", NULL, &rollempty); + cvt = ldmsd_plugattr_bool(sc->pa, "rollempty", NULL, &sc->rollempty); if (cvt == -1) { ovis_log(mylog, OVIS_LERROR, "expected boole for rollempty= input.\n"); rc = EINVAL; goto out; } - rollover = roll; - rollagain = ragain; + sc->rollover = roll; + sc->rollagain = ragain; if (rollmethod >= MINROLLTYPE) { - rolltype = rollmethod; - pthread_create(&rothread, NULL, rolloverThreadInit, NULL); - rothread_used = 1; + sc->rolltype = rollmethod; + pthread_create(&sc->rothread, NULL, rolloverThreadInit, self); + sc->rothread_used = 1; } out: - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&sc->cfg_lock); return rc; } static void term(struct ldmsd_plugin *self) { + /* TODO REVISE ME */ /* clean up any allocated globals here that are not handled by store_csv_fini */ - pthread_mutex_lock(&cfg_lock); - ldmsd_plugattr_destroy(pa); - pa = NULL; - pthread_mutex_unlock(&cfg_lock); - if (mylog) - ovis_log_destroy(mylog); + store_csv_t sc = (void*)self; + pthread_mutex_lock(&sc->cfg_lock); + ldmsd_plugattr_destroy(sc->pa); + sc->pa = NULL; + pthread_mutex_unlock(&sc->cfg_lock); } static const char *usage(struct ldmsd_plugin *self) @@ -920,6 +932,7 @@ static int print_header_from_store(struct csv_store_handle *s_handle, ldms_set_t { /* Only called from Store which already has the lock */ FILE* fp; + store_csv_t sc = s_handle->sc; char tmp_path[PATH_MAX]; if (s_handle == NULL){ @@ -935,14 +948,14 @@ static int print_header_from_store(struct csv_store_handle *s_handle, ldms_set_t } int ec; if (s_handle->altheader) { - if (rolltype >= MINROLLTYPE) + if (sc->rolltype >= MINROLLTYPE) ec = snprintf(tmp_path, PATH_MAX, "%s.HEADER.%d", s_handle->path, (int)s_handle->otime); else ec = snprintf(tmp_path, PATH_MAX, "%s.HEADER", s_handle->path); } else { - if (rolltype >= MINROLLTYPE) + if (sc->rolltype >= MINROLLTYPE) ec = snprintf(tmp_path, PATH_MAX, "%s.%d", s_handle->path, (int)s_handle->otime); else @@ -994,7 +1007,7 @@ static int print_header_from_open(struct csv_store_handle *s_handle, #endif static struct csv_store_handle * -csv_store_handle_get(const char *container, const char *schema); +csv_store_handle_get(store_csv_t sc, const char *container, const char *schema); static void csv_store_handle_put(struct csv_store_handle *s_handle); @@ -1003,15 +1016,16 @@ open_store(struct ldmsd_store *s, const char *container, const char* schema, struct ldmsd_strgp_metric_list *list, void *ucontext) { struct csv_store_handle *s_handle = NULL; + store_csv_t sc = (void*)s; - if (!pa) { + if (!sc->pa) { ovis_log(mylog, OVIS_LERROR, "config not called. cannot open.\n"); return NULL; } - pthread_mutex_lock(&cfg_lock); - s_handle = csv_store_handle_get(container, schema); - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_lock(&sc->cfg_lock); + s_handle = csv_store_handle_get(sc, container, schema); + pthread_mutex_unlock(&sc->cfg_lock); return s_handle; } @@ -1685,6 +1699,7 @@ static int flush_store(ldmsd_store_handle_t _s_handle) static void __csv_handle_close(struct csv_store_handle *s_handle) { + store_csv_t sc = s_handle->sc; pthread_mutex_lock(&s_handle->lock); ovis_log(mylog, OVIS_LDEBUG, "Closing with path <%s>\n", s_handle->path); @@ -1701,7 +1716,7 @@ static void __csv_handle_close(struct csv_store_handle *s_handle) s_handle->headerfile = NULL; CLOSE_STORE_COMMON(s_handle); - idx_delete(store_idx, s_handle->store_key, strlen(s_handle->store_key)); + idx_delete(sc->store_idx, s_handle->store_key, strlen(s_handle->store_key)); if (s_handle->store_key) free(s_handle->store_key); @@ -1739,10 +1754,11 @@ static void __csv_row_handle_close(struct csv_row_store_handle *rs_handle) static void close_store(ldmsd_store_handle_t _s_handle) { - pthread_mutex_lock(&cfg_lock); struct csv_store_handle *s_handle = _s_handle; + store_csv_t sc = s_handle->sc; + pthread_mutex_lock(&sc->cfg_lock); if (!s_handle) { - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&sc->cfg_lock); return; } @@ -1762,7 +1778,7 @@ static void close_store(ldmsd_store_handle_t _s_handle) default: ERR_LOG("Unknown csv_handle type: %d\n", s_handle->type); } - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_unlock(&sc->cfg_lock); } static struct csv_row_store_handle * @@ -1782,7 +1798,7 @@ row_store_new() * - `store_key` is "/" */ static struct csv_store_handle * -csv_store_handle_get(const char *container, const char *schema) +csv_store_handle_get(store_csv_t sc, const char *container, const char *schema) { struct csv_store_handle *s_handle; int len, rc; @@ -1793,7 +1809,7 @@ csv_store_handle_get(const char *container, const char *schema) if (!store_key) goto err_0; - s_handle = idx_find(store_idx, (void *)store_key, strlen(store_key)); + s_handle = idx_find(sc->store_idx, (void *)store_key, strlen(store_key)); if (s_handle) { assert(s_handle->ref_count > 0); s_handle->ref_count++; @@ -1807,11 +1823,12 @@ csv_store_handle_get(const char *container, const char *schema) ERR_LOG("Not enough memory (%s:%s():%d)", __FILE__, __func__, __LINE__); goto err_1; } + s_handle->sc = sc; s_handle->num_lists = -1; s_handle->ref_count = 1; s_handle->type = CSV_STORE_HANDLE; s_handle->store_key = store_key; /* give store_key to s_handle */ - const char *root_path = ldmsd_plugattr_value(pa, "path", store_key); + const char *root_path = ldmsd_plugattr_value(sc->pa, "path", store_key); if (!root_path) { ERR_LOG("`path` plugin attribute is not set\n"); errno = EINVAL; @@ -1847,7 +1864,7 @@ csv_store_handle_get(const char *container, const char *schema) time_t appx = time(NULL); /* csv filename */ - if (rolltype >= MINROLLTYPE){ + if (sc->rolltype >= MINROLLTYPE){ //append the files with epoch. assume wont collide to the sec. len = asprintf(&s_handle->filename, "%s.%ld", s_handle->path, appx); } else { @@ -1869,7 +1886,7 @@ csv_store_handle_get(const char *container, const char *schema) /* header file name */ if (s_handle->altheader) { - if (rolltype >= MINROLLTYPE) { + if (sc->rolltype >= MINROLLTYPE) { len = asprintf(&s_handle->headerfilename, "%s.HEADER.%ld", s_handle->path, appx); } else { len = asprintf(&s_handle->headerfilename, "%s.HEADER", s_handle->path); @@ -1903,7 +1920,7 @@ csv_store_handle_get(const char *container, const char *schema) } if (s_handle->typeheader > 0) { - if (rolltype >= MINROLLTYPE){ + if (sc->rolltype >= MINROLLTYPE){ len = asprintf(&s_handle->typefilename, "%s.KIND.%ld", s_handle->path, appx); } else { @@ -1916,7 +1933,7 @@ csv_store_handle_get(const char *container, const char *schema) } } - idx_add(store_idx, s_handle->store_key, strlen(s_handle->store_key), s_handle); + idx_add(sc->store_idx, s_handle->store_key, strlen(s_handle->store_key), s_handle); out: return s_handle; @@ -1950,6 +1967,7 @@ csv_row_schema_get(ldmsd_strgp_t strgp, struct csv_row_store_handle *rs_handle, struct csv_row_schema_key_s *key) { struct csv_row_schema_rbn_s *rrbn; + store_csv_t sc = (void*)strgp->store; rrbn = (void*)rbt_find(&rs_handle->row_schema_rbt, key); if (rrbn) @@ -1965,9 +1983,9 @@ csv_row_schema_get(ldmsd_strgp_t strgp, struct csv_row_store_handle *rs_handle, snprintf(rrbn->name, sizeof(rrbn->name), "%s", key->name); memcpy(&rrbn->digest, key->digest, sizeof(*key->digest)); - pthread_mutex_lock(&cfg_lock); - rrbn->s_handle = csv_store_handle_get(strgp->container, key->name); - pthread_mutex_unlock(&cfg_lock); + pthread_mutex_lock(&sc->cfg_lock); + rrbn->s_handle = csv_store_handle_get((void*)strgp->store, strgp->container, key->name); + pthread_mutex_unlock(&sc->cfg_lock); if (!rrbn->s_handle) goto err_1; @@ -2315,56 +2333,84 @@ commit_rows(ldmsd_strgp_t strgp, ldms_set_t set, return 0; } -static struct ldmsd_store store_csv = { - .base = { - .name = "csv", - .type = LDMSD_PLUGIN_STORE, - .term = term, - .config = config, - .usage = usage, - }, - .open = open_store, - .get_context = get_ucontext, - .store = store, - .flush = flush_store, - .close = close_store, - .commit = commit_rows, -}; +void store_csv_del(struct ldmsd_cfgobj *obj) +{ + store_csv_t sc = (void*)obj; + ldmsd_store_cleanup(&sc->store); + + pthread_mutex_destroy(&sc->cfg_lock); + idx_destroy(sc->store_idx); + ldmsd_plugattr_destroy(sc->pa); + if (sc->rothread_used) { + void * dontcare = NULL; + pthread_cancel(sc->rothread); + pthread_join(sc->rothread, &dontcare); + } + sc->pa = NULL; + sc->store_idx = NULL; + + free(sc); +} -struct ldmsd_plugin *get_plugin() +void __store_csv_once() { - int rc; - mylog = ovis_log_register("store."PNAME, "The log subsystem of '" PNAME "' plugin"); + static int once = 0; + static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&once_mutex); + if (once) + goto out; + once = 1; if (!mylog) { - rc = errno; + mylog = ovis_log_register("store."PNAME, + "The log subsystem of '" PNAME "' plugin"); ovis_log(NULL, OVIS_LWARN, "Failed to create the log subsystem " - "of '" PNAME "' plugin. Error %d\n", rc); + "of '" PNAME "' plugin. Error %d\n", errno); } PG.mylog = mylog; PG.pname = PNAME; - return &store_csv.base; + out: + pthread_mutex_unlock(&once_mutex); +} + +struct ldmsd_plugin *get_plugin_instance(const char *name, + uid_t uid, gid_t gid, int perm) +{ + store_csv_t sc; + + __store_csv_once(); + + sc = (void*)ldmsd_store_alloc(name, sizeof(*sc), store_csv_del, uid, gid, perm); + if (!sc) + return NULL; + + snprintf(sc->store.base.name, sizeof(sc->store.base.name), "store_csv"); + sc->store.base.term = term; + sc->store.base.config = config; + sc->store.base.usage = usage; + + sc->store.open = open_store; + sc->store.get_context = get_ucontext; + sc->store.store = store; + sc->store.flush = flush_store; + sc->store.close = close_store; + sc->store.commit = commit_rows; + + /* TODO COMPLETE ME (just in case) */ + + sc->store_idx = idx_create(); + pthread_mutex_init(&sc->cfg_lock, NULL); + + return &sc->store.base; } static void __attribute__ ((constructor)) store_csv_init(); static void store_csv_init() { - store_idx = idx_create(); - pthread_mutex_init(&cfg_lock, NULL); LIB_CTOR_COMMON(PG); } static void __attribute__ ((destructor)) store_csv_fini(void); static void store_csv_fini() { - pthread_mutex_destroy(&cfg_lock); - idx_destroy(store_idx); - ldmsd_plugattr_destroy(pa); LIB_DTOR_COMMON(PG); - if (rothread_used) { - void * dontcare = NULL; - pthread_cancel(rothread); - pthread_join(rothread, &dontcare); - } - pa = NULL; - store_idx = NULL; } From 5d818c3d171626148a8886599e4d7ecd1efe4792 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Fri, 22 Dec 2023 09:29:14 -0600 Subject: [PATCH 11/13] store_avro_kafka to support multi-instances This passed `ldms-test/multi_store_avro_kafka_test`. --- ldms/src/store/avro_kafka/store_avro_kafka.c | 147 ++++++++++++------- 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/ldms/src/store/avro_kafka/store_avro_kafka.c b/ldms/src/store/avro_kafka/store_avro_kafka.c index e78c25d81..0e3fdd546 100644 --- a/ldms/src/store/avro_kafka/store_avro_kafka.c +++ b/ldms/src/store/avro_kafka/store_avro_kafka.c @@ -1,6 +1,6 @@ /* -*- c-basic-offset: 8 -*- * Copyright 2022 Lawrence Livermore National Security, LLC - * Copyright 2022 Open Grid Computing, Inc. + * Copyright 2022-2023 Open Grid Computing, Inc. * * See the top-level COPYING file for details. * @@ -31,11 +31,28 @@ #include #include "ldms.h" #include "ldmsd.h" -#include "ovis_log.h" +#include "ovis_log/ovis_log.h" #define STORE_AVRO_KAFKA "store_avro_kafka" static ovis_log_t aks_log = NULL; +static int schema_cmp(void *a, const void *b); + +typedef struct store_kafka_s { + struct ldmsd_store store; + + /* extensions */ + pthread_mutex_t schema_rbt_lock; + struct rbt schema_tree; + + pthread_mutex_t sk_lock; + rd_kafka_conf_t *g_rd_conf; + serdes_conf_t *g_serdes_conf; + int g_serdes_encoding; + char *g_topic_fmt; + +} *store_kafka_t; + #define LOG(_level_, _fmt_, ...) ovis_log(aks_log, _level_, _fmt_, ##__VA_ARGS__) #define LOG_ERROR(FMT, ...) LOG(OVIS_LERROR, FMT, ##__VA_ARGS__) @@ -55,6 +72,7 @@ typedef struct aks_handle_s } encoding; char *topic_fmt; /* Format to use to create topic name from row */ char *topic_name; + store_kafka_t sf; } *aks_handle_t; static const char *_help_str = @@ -91,8 +109,6 @@ static int schema_cmp(void *a, const void *b) { return strcmp((char *)a, (char *)b); } -static pthread_mutex_t schema_rbt_lock = PTHREAD_MUTEX_INITIALIZER; -static struct rbt schema_tree = { .comparator = schema_cmp }; struct schema_entry { @@ -107,6 +123,7 @@ serdes_schema_find(aks_handle_t sh, char *schema_name, ldms_schema_t lschema, ldmsd_row_t row) { struct rbn *rbn; + store_kafka_t sf = sh->sf; serdes_schema_t *sschema = NULL; struct schema_entry *entry; char *json_buf = NULL; @@ -114,9 +131,9 @@ serdes_schema_find(aks_handle_t sh, char *schema_name, char errstr[512]; int rc; - pthread_mutex_lock(&schema_rbt_lock); + pthread_mutex_lock(&sf->schema_rbt_lock); /* Check if the schema is already cached */ - rbn = rbt_find(&schema_tree, schema_name); + rbn = rbt_find(&sf->schema_tree, schema_name); if (rbn) { entry = container_of(rbn, struct schema_entry, rbn); sschema = entry->serdes_schema; @@ -152,20 +169,14 @@ serdes_schema_find(aks_handle_t sh, char *schema_name, entry->ldms_schema = lschema; entry->schema_name = strdup(schema_name); rbn_init(&entry->rbn, entry->schema_name); - rbt_ins(&schema_tree, &entry->rbn); + rbt_ins(&sf->schema_tree, &entry->rbn); out: - pthread_mutex_unlock(&schema_rbt_lock); + pthread_mutex_unlock(&sf->schema_rbt_lock); if (json_buf) free(json_buf); return sschema; } -pthread_mutex_t sk_lock = PTHREAD_MUTEX_INITIALIZER; -static rd_kafka_conf_t *g_rd_conf = NULL; -static serdes_conf_t *g_serdes_conf = NULL; -static int g_serdes_encoding = AKS_ENCODING_AVRO; -static char *g_topic_fmt = NULL; - static char *strip_whitespace(char *s) { char *p; @@ -291,12 +302,13 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct attr_value_list *avl) { int rc = 0; + store_kafka_t sk = (void*)self; char *path, *encoding, *topic; char err_str[512]; - pthread_mutex_lock(&sk_lock); + pthread_mutex_lock(&sk->sk_lock); - if (g_rd_conf) { + if (sk->g_rd_conf) { LOG_ERROR("reconfiguration is not supported\n"); rc = EINVAL; goto out; @@ -311,17 +323,17 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, "serialized messages on the Kafka bus."); } - g_rd_conf = rd_kafka_conf_new(); - if (!g_rd_conf) { + sk->g_rd_conf = rd_kafka_conf_new(); + if (!sk->g_rd_conf) { rc = errno; LOG_ERROR("rd_kafka_conf_new() failed %d\n", rc); goto out; } - g_serdes_conf = serdes_conf_new(err_str, sizeof(err_str), + sk->g_serdes_conf = serdes_conf_new(err_str, sizeof(err_str), /* Default URL */ "schema.registry.url", "http://localhost:8081"); - if (!g_serdes_conf) { + if (!sk->g_serdes_conf) { rc = EINVAL; LOG_ERROR("serdes_conf_new failed '%s'\n", err_str); goto out; @@ -329,14 +341,14 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, path = av_value(avl, "kafka_conf"); if (path) { - rc = parse_rd_conf_file(path, g_rd_conf); + rc = parse_rd_conf_file(path, sk->g_rd_conf); if (rc) { LOG_ERROR("Error %d parsing the Kafka configuration file '%s'", rc, path); } } path = av_value(avl, "serdes_conf"); if (path) { - rc = parse_serdes_conf_file(path, g_serdes_conf); + rc = parse_serdes_conf_file(path, sk->g_serdes_conf); if (rc) { LOG_ERROR("Error %d parsing the Kafka configuration file '%s'", rc, path); } @@ -344,9 +356,9 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, encoding = av_value(avl, "encoding"); if (encoding) { if (0 == strcasecmp(encoding, "avro")) { - g_serdes_encoding = AKS_ENCODING_AVRO; + sk->g_serdes_encoding = AKS_ENCODING_AVRO; } else if (0 == strcasecmp(encoding, "json")) { - g_serdes_encoding = AKS_ENCODING_JSON; + sk->g_serdes_encoding = AKS_ENCODING_JSON; } else { LOG_ERROR("Ignoring unrecognized serialization encoding '%s'\n", encoding); } @@ -354,33 +366,34 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, topic = av_value(avl, "topic"); if (topic) { char *tmp; - g_topic_fmt = strdup(topic); + sk->g_topic_fmt = strdup(topic); /* Strip any enclosing \" */ - while (*g_topic_fmt != '\0' && *g_topic_fmt == '\"') - g_topic_fmt++; - tmp = g_topic_fmt; + while (*sk->g_topic_fmt != '\0' && *sk->g_topic_fmt == '\"') + sk->g_topic_fmt++; + tmp = sk->g_topic_fmt; while (*tmp != '\0' && *tmp != '\"') tmp++; if (*tmp == '\"') *tmp = '\0'; } else { /* The default is the schema name */ - topic = strdup("%S"); + sk->g_topic_fmt= strdup("%S"); } out: - pthread_mutex_unlock(&sk_lock); + pthread_mutex_unlock(&sk->sk_lock); return rc; } static void term(struct ldmsd_plugin *self) { - pthread_mutex_lock(&sk_lock); - if (g_rd_conf) + store_kafka_t sk = (void*)self; + pthread_mutex_lock(&sk->sk_lock); + if (sk->g_rd_conf) { - rd_kafka_conf_destroy(g_rd_conf); - g_rd_conf = NULL; + rd_kafka_conf_destroy(sk->g_rd_conf); + sk->g_rd_conf = NULL; } - pthread_mutex_unlock(&sk_lock); + pthread_mutex_unlock(&sk->sk_lock); } static ldmsd_store_handle_t @@ -432,6 +445,7 @@ static aks_handle_t __handle_new(ldmsd_strgp_t strgp) { char err_str[512]; rd_kafka_conf_res_t res; + store_kafka_t sk = (void*)strgp->store; aks_handle_t sh = calloc(1, sizeof(*sh)); if (!sh) { @@ -439,16 +453,16 @@ static aks_handle_t __handle_new(ldmsd_strgp_t strgp) goto err_0; } - sh->encoding = g_serdes_encoding; - sh->topic_fmt = strdup(g_topic_fmt); + sh->encoding = sk->g_serdes_encoding; + sh->topic_fmt = strdup(sk->g_topic_fmt); if (!sh->topic_fmt) goto err_1; - sh->rd_conf = rd_kafka_conf_dup(g_rd_conf); + sh->rd_conf = rd_kafka_conf_dup(sk->g_rd_conf); if (!sh->rd_conf) goto err_1; - sh->serdes_conf = serdes_conf_copy(g_serdes_conf); + sh->serdes_conf = serdes_conf_copy(sk->g_serdes_conf); if (!sh->serdes_conf) { LOG_ERROR("%s creating serdes configuration\n", err_str); goto err_2; @@ -481,6 +495,8 @@ static aks_handle_t __handle_new(ldmsd_strgp_t strgp) } sh->rd_conf = NULL; /* rd_kafka_new consumed and freed the conf */ + sh->sf = (void*)strgp->store; + return sh; err_3: @@ -1031,25 +1047,44 @@ commit_rows(ldmsd_strgp_t strgp, ldms_set_t set, ldmsd_row_list_t row_list, return 0; } -static struct ldmsd_store store_kafka = { - .base = { - .name = "avro_kafka", - .term = term, - .config = config, - .usage = usage, - .type = LDMSD_PLUGIN_STORE, - }, - .open = open_store, - .get_context = get_ucontext, - .store = store, - .flush = flush_store, - .close = close_store, - .commit = commit_rows, -}; +void store_kafka_del(struct ldmsd_cfgobj *obj) +{ + store_kafka_t sf = (void*)obj; + ldmsd_store_cleanup(&sf->store); + free(sf); +} -struct ldmsd_plugin *get_plugin() +struct ldmsd_plugin *get_plugin_instance(const char *name, + uid_t uid, gid_t gid, int perm) { - return &store_kafka.base; + store_kafka_t sf; + + sf = (void*)ldmsd_store_alloc(name, sizeof(*sf), store_kafka_del, uid, gid, perm); + + if (!sf) + goto out; + + pthread_mutex_init(&sf->schema_rbt_lock, NULL); + pthread_mutex_init(&sf->sk_lock, NULL); + rbt_init(&sf->schema_tree, schema_cmp); + + sf->g_serdes_encoding = AKS_ENCODING_AVRO; + + sf->store.base.term = term; + sf->store.base.config = config; + sf->store.base.usage = usage; + + snprintf(sf->store.base.name, sizeof(sf->store.base.name), "store_kafka"); + + sf->store.open = open_store; + sf->store.get_context = get_ucontext; + sf->store.store = store; + sf->store.flush = flush_store; + sf->store.close = close_store; + sf->store.commit = commit_rows; + + out: + return &sf->store.base; } static void __attribute__((constructor)) store_avro_kafka_init(); From 914c2833aa17f012936b6478125b15725b270dd9 Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Mon, 8 Jan 2024 09:56:01 -0600 Subject: [PATCH 12/13] Muitl-instance test_sampler --- .../examples/test_sampler/test_sampler.c | 155 ++++++++++++------ 1 file changed, 102 insertions(+), 53 deletions(-) diff --git a/ldms/src/sampler/examples/test_sampler/test_sampler.c b/ldms/src/sampler/examples/test_sampler/test_sampler.c index 5cf59039d..8034d8512 100644 --- a/ldms/src/sampler/examples/test_sampler/test_sampler.c +++ b/ldms/src/sampler/examples/test_sampler/test_sampler.c @@ -83,6 +83,8 @@ static ovis_log_t mylog; +typedef struct test_sampler_s *test_sampler_t; + struct test_sampler_set { char *name; char *producer; @@ -154,10 +156,16 @@ struct test_sampler_stream { }; LIST_HEAD(test_sampler_stream_list, test_sampler_stream); +struct test_sampler_s { + struct ldmsd_sampler sampler; + + /* extension */ + struct test_sampler_schema_list schema_list; + struct test_sampler_set_list set_list; + struct test_sampler_stream_list stream_list; +}; + static int num_sets; -static struct test_sampler_schema_list schema_list; -static struct test_sampler_set_list set_list; -static struct test_sampler_stream_list stream_list; static struct test_sampler_schema *test_sampler_schema_find( struct test_sampler_schema_list *list, char *name) @@ -495,7 +503,7 @@ static void test_sampler_schema_free(struct test_sampler_schema *ts_schema) } static struct test_sampler_schema * -test_sampler_schema_new(const char *name) +test_sampler_schema_new(test_sampler_t ts, const char *name) { struct test_sampler_schema *ts_schema; ts_schema = calloc(1, sizeof(*ts_schema)); @@ -505,7 +513,7 @@ test_sampler_schema_new(const char *name) if (!ts_schema->name) goto err; LIST_INIT(&ts_schema->set_list); - LIST_INSERT_HEAD(&schema_list, ts_schema, entry); + LIST_INSERT_HEAD(&ts->schema_list, ts_schema, entry); return ts_schema; err: test_sampler_schema_free(ts_schema); @@ -795,7 +803,7 @@ static int __parse_metric_str(char *s, char delim, struct test_sampler_metric_in return rc; } -static int config_add_schema(struct attr_value_list *avl) +static int config_add_schema(test_sampler_t ts, struct attr_value_list *avl) { int rc = 0; int i; @@ -815,7 +823,7 @@ static int config_add_schema(struct attr_value_list *avl) if (tmp) mattr_delim = tmp[0]; - ts_schema = test_sampler_schema_find(&schema_list, schema_name); + ts_schema = test_sampler_schema_find(&ts->schema_list, schema_name); if (ts_schema) { ovis_log(mylog, OVIS_LERROR, "Schema '%s' already " "exists.\n", schema_name); @@ -938,7 +946,7 @@ static int config_add_schema(struct attr_value_list *avl) } } - ts_schema = test_sampler_schema_new(schema_name); + ts_schema = test_sampler_schema_new(ts, schema_name); if (!ts_schema) { ovis_log(mylog, OVIS_LERROR, "Out of memory\n"); return ENOMEM; @@ -1065,7 +1073,7 @@ static int __init_set(struct test_sampler_set *ts_set) return rc; } -static int config_add_set(struct attr_value_list *avl) +static int config_add_set(test_sampler_t ts, struct attr_value_list *avl) { int rc = 0; struct test_sampler_schema *ts_schema; @@ -1083,7 +1091,7 @@ static int config_add_set(struct attr_value_list *avl) ovis_log(mylog, OVIS_LERROR, "Need set name\n"); return EINVAL; } - ts_schema = test_sampler_schema_find(&schema_list, schema_name); + ts_schema = test_sampler_schema_find(&ts->schema_list, schema_name); if (!ts_schema) { ovis_log(mylog, OVIS_LERROR, "Schema '%s' does not " "exist.\n", schema_name); @@ -1161,7 +1169,7 @@ static int config_add_set(struct attr_value_list *avl) #define DEFAULT_SCHEMA_NAME "my_schema" #define DEFAULT_BASE_SET_NAME "my_set" -static int config_add_default(struct attr_value_list *avl) +static int config_add_default(test_sampler_t ts, struct attr_value_list *avl) { char *sname, *s, *base_set_name; int rc, num_metrics; @@ -1229,7 +1237,7 @@ static int config_add_default(struct attr_value_list *avl) m->flags = info->mtype = LDMS_MDESC_F_DATA; } - ts_schema = test_sampler_schema_new(sname); + ts_schema = test_sampler_schema_new(ts, sname); if (!ts_schema) goto err; ts_schema->schema = ldms_schema_from_template(sname, temp, mid); @@ -1299,7 +1307,7 @@ struct ldms_metric_template_s default_rec_contents[] = { #define LIST_NAME "list_" #define LIST_LEN_NAME "list_len_" #define RECORD_TYPE "test_record" -static int config_add_lists(struct attr_value_list *avl) +static int config_add_lists(test_sampler_t ts, struct attr_value_list *avl) { struct test_sampler_list_info *linfo; char *schema_name, *s, *a, *ptr; @@ -1484,7 +1492,7 @@ static int config_add_lists(struct attr_value_list *avl) i++; } - ts_schema = test_sampler_schema_find(&schema_list, schema_name); + ts_schema = test_sampler_schema_find(&ts->schema_list, schema_name); if (ts_schema) { ovis_log(mylog, OVIS_LERROR, "" "Schema '%s' already exists.\n", @@ -1493,7 +1501,7 @@ static int config_add_lists(struct attr_value_list *avl) goto err; } - ts_schema = test_sampler_schema_new(schema_name); + ts_schema = test_sampler_schema_new(ts, schema_name); if (!ts_schema) { ovis_log(mylog, OVIS_LERROR, "Our of memory\n"); rc = ENOMEM; @@ -1567,7 +1575,7 @@ static int config_add_lists(struct attr_value_list *avl) goto out; } -static int config_add_stream(struct attr_value_list *avl) +static int config_add_stream(test_sampler_t ts, struct attr_value_list *avl) { int rc = 0; char *stream_name, *type, *path, *xprt, *host, *port, *auth; @@ -1618,7 +1626,7 @@ static int config_add_stream(struct attr_value_list *avl) if (!auth) auth = DEFAULT_AUTH; - ts_stream = __stream_find(&stream_list, stream_name); + ts_stream = __stream_find(&ts->stream_list, stream_name); if (ts_stream) { if (0 != strcmp(path, ts_stream->path)) { ovis_log(mylog, OVIS_LERROR, "stream '%s' " @@ -1641,7 +1649,7 @@ static int config_add_stream(struct attr_value_list *avl) rc = EINVAL; goto err; } - LIST_INSERT_HEAD(&stream_list, ts_stream, entry); + LIST_INSERT_HEAD(&ts->stream_list, ts_stream, entry); } c = __stream_client_find(ts_stream, host, port, xprt, auth); @@ -1696,6 +1704,7 @@ static int config_add_stream(struct attr_value_list *avl) static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct attr_value_list *avl) { + test_sampler_t ts = (void*)self; char *action; int rc; @@ -1703,15 +1712,15 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct if (action) { rc = 0; if (0 == strcmp(action, "add_schema")) { - rc = config_add_schema(avl); + rc = config_add_schema(ts, avl); } else if (0 == strcmp(action, "add_set")) { - rc = config_add_set(avl); + rc = config_add_set(ts, avl); } else if (0 == strcmp(action, "default")) { - rc = config_add_default(avl); + rc = config_add_default(ts, avl); } else if (0 == strcmp(action, "add_lists")) { - rc = config_add_lists(avl); + rc = config_add_lists(ts, avl); } else if (0 == strcmp(action, "add_stream")) { - rc = config_add_stream(avl); + rc = config_add_stream(ts, avl); } else { ovis_log(mylog, OVIS_LERROR, "Unrecognized " "action '%s'.\n", action); @@ -1722,11 +1731,6 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct return 0; } -static ldms_set_t get_set(struct ldmsd_sampler *self) -{ - assert(0 == "not implemented"); -} - static int __gen_list_len(int min_len, int max_len) { int llen = rand(); @@ -1877,11 +1881,12 @@ __sample_lists(struct test_sampler_set *ts_set) static int sample(struct ldmsd_sampler *self) { + test_sampler_t ts = (void*)self; int rc; struct test_sampler_set *ts_set; struct test_sampler_schema *ts_schema; - LIST_FOREACH(ts_schema, &schema_list, entry) { + LIST_FOREACH(ts_schema, &ts->schema_list, entry) { LIST_FOREACH(ts_set, &ts_schema->set_list, entry) { if (TEST_SAMPLER_SCHEMA_LISTS == ts_schema->type) { rc = __sample_lists(ts_set); @@ -1904,7 +1909,7 @@ static int sample(struct ldmsd_sampler *self) struct test_sampler_stream *ts_stream; struct test_sampler_stream_client *c; - LIST_FOREACH(ts_stream, &stream_list, entry) { + LIST_FOREACH(ts_stream, &ts->stream_list, entry) { LIST_FOREACH(c, &ts_stream->client_list, entry) { rc = ldms_stream_publish_file( c->ldms, @@ -1925,14 +1930,15 @@ static int sample(struct ldmsd_sampler *self) static void term(struct ldmsd_plugin *self) { + test_sampler_t ts = (void*)self; struct test_sampler_schema *tschema; - while ((tschema = LIST_FIRST(&schema_list))) { + while ((tschema = LIST_FIRST(&ts->schema_list))) { LIST_REMOVE(tschema, entry); test_sampler_schema_free(tschema); } struct test_sampler_stream *ts_stream; - while ((ts_stream = LIST_FIRST(&stream_list))) { + while ((ts_stream = LIST_FIRST(&ts->stream_list))) { LIST_REMOVE(ts_stream, entry); __stream_free(ts_stream); } @@ -2006,29 +2012,72 @@ static const char *usage(struct ldmsd_plugin *self) " A authentication domain name\n"; } -static struct ldmsd_sampler test_sampler_plugin = { - .base = { - .name = SAMP, - .type = LDMSD_PLUGIN_SAMPLER, - .term = term, - .config = config, - .usage = usage, - }, - .get_set = get_set, - .sample = sample, -}; +void __ts_del(struct ldmsd_cfgobj *obj) +{ + test_sampler_t ts = (void*)obj; + struct test_sampler_stream *str_ent; + struct test_sampler_set *set_ent; + struct test_sampler_schema *sch_ent; + + /* clean up stream publishing targets */ + while ((str_ent = LIST_FIRST(&ts->stream_list))) { + LIST_REMOVE(str_ent, entry); + __stream_free(str_ent); + } + + while ((set_ent = LIST_FIRST(&ts->set_list))) { + test_sampler_set_free(set_ent); + } + + while ((sch_ent = LIST_FIRST(&ts->schema_list))) { + test_sampler_schema_free(sch_ent); + } -struct ldmsd_plugin *get_plugin() + free(ts); +} + +static void __once() { - int rc; - mylog = ovis_log_register("sampler."SAMP, "Message for the " SAMP " plugin"); + static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + static int once = 0; + pthread_mutex_lock(&mtx); + if (once) + goto out; if (!mylog) { - rc = errno; - ovis_log(NULL, OVIS_LWARN, "Failed to create the log subsystem " - "of '" SAMP "' plugin. Error %d\n", rc); + mylog = ovis_log_register("sampler."SAMP, + "Message for the " SAMP " plugin"); + if (!mylog) { + ovis_log(NULL, OVIS_LWARN, + "Failed to create the log subsystem " + "of '" SAMP "' plugin. Error %d\n", errno); + } } - LIST_INIT(&schema_list); - LIST_INIT(&set_list); - LIST_INIT(&stream_list); - return &test_sampler_plugin.base; + + once = 1; + out: + pthread_mutex_unlock(&mtx); +} + +struct ldmsd_plugin *get_plugin_instance(const char *name, + uid_t uid, gid_t gid, int perm) +{ + test_sampler_t ts; + __once(); + ts = (void*)ldmsd_sampler_alloc(name, sizeof(*ts), __ts_del, uid, gid, perm); + if (!ts) + return NULL; + + ts->sampler.base.term = term; + ts->sampler.base.config = config; + ts->sampler.base.usage = usage; + + ts->sampler.sample = sample; + + snprintf(ts->sampler.base.name, sizeof(ts->sampler.base.name), "test_sampler"); + + LIST_INIT(&ts->schema_list); + LIST_INIT(&ts->set_list); + LIST_INIT(&ts->stream_list); + + return &ts->sampler.base; } From 0b323aba0412d8abcee3b34fdff095db7077c04f Mon Sep 17 00:00:00 2001 From: Narate Taerat Date: Tue, 9 Jan 2024 16:43:43 -0600 Subject: [PATCH 13/13] multi-instance procnetdev2 - Make procnetdev2 multiple instances - Remove the 32 device list limitation - Add `excludes` option to exclude the listed interfaces - For example, excludes=lo,eth0 NOTE: passed 'ldms-test/multi_procnetdev2_test' --- .../procnetdev2/Plugin_procnetdev2.man | 57 ++- ldms/src/sampler/procnetdev2/procnetdev2.c | 335 +++++++++++------- 2 files changed, 252 insertions(+), 140 deletions(-) diff --git a/ldms/src/sampler/procnetdev2/Plugin_procnetdev2.man b/ldms/src/sampler/procnetdev2/Plugin_procnetdev2.man index 12636ea2a..936fb1754 100644 --- a/ldms/src/sampler/procnetdev2/Plugin_procnetdev2.man +++ b/ldms/src/sampler/procnetdev2/Plugin_procnetdev2.man @@ -6,9 +6,15 @@ Plugin_procnetdev2 - man page for the LDMS procnetdev2 plugin .SH SYNOPSIS -Within ldmsd_controller or a configuration file: +.B load +.RI name= INST_NAME +.RB plugin= procnetdev2 .br -config name=procnetdev2 [ = ] +.B config +.RI name= INST_NAME +.RI [ = ] +.RI [ifaces= CSV_STR ] +.RI [excludes= CSV_STR ] .SH DESCRIPTION With LDMS (Lightweight Distributed Metric Service), plugins for the ldmsd (ldms @@ -18,43 +24,56 @@ from /proc/net/dev. .SH CONFIGURATION ATTRIBUTE SYNTAX The procnetdev2 plugin uses the sampler_base base class. This man page covers -only the configuration attributes, or those with default values, specific to the +only the configuration attributes, or those with default values, specific to this plugin; see ldms_sampler_base.man for the attributes of the base class. .TP .BR config -name= [ifaces=] +.RI name= INST_NAME +.RI [ifaces= CSV_STR ] +.RI [excludes= CSV_STR ] .br configuration line .RS .TP -name= +.RI name= INST_NAME .br -This MUST be procnetdev2. +The name given at \fBload\fR command. .TP -ifaces= +.RI [ifaces= CSV_STR ] .br -(Optional) A CSV list of interfaces to sample. If not specified, all available -interfaces in /proc/net/dev will be reported. It is OK to specify non-existing -interfaces in the ifaces list. +(Optional) A comma-separated list of interface names (e.g. eth0,eth1) to be +collected. If NOT specified, all interfaces are included (unluss the `excludes` +option exclude them). .TP -schema= +.RI [excludes= CSV_STR ] .br -Optional schema name. It is intended that the same sampler on different nodes -with different metrics or ifaces have a different schema. If not specified, will -default to `procnetdev`. +(Optional) A comma-separated list of interface names (e.g. lo,eth0) to be +excluded from the collection. .RE -.SH BUGS -The maximum number of interfaces is limited to 32. + +If \fBifaces\fR and \fBexcludes\fR are NOT specified, the sampler collects data +from all interfaces. If only \fBifaces\fR is specified, the sampler only +collects data from interfaces in the ifaces list. If only \fBexcludes\fR is +specified, the sampler collects data from all interfaces EXCEPT those in the +\fBexcludes\fR option. If both \fBifaces\fR and \fBexcludes\fR are specified, +the sampler collects data from all interfaces that are in \fBifaces\fR option +but are NOT in the \fBexcludes\fR option. .SH EXAMPLES .PP Within ldmsd_controller or a configuration file: .nf -load name=procnetdev -config name=procnetdev producer=vm1_1 instance=vm1_1/procnetdev2 ifaces=eth0,eth1 -start name=procnetdev interval=1000000 offset=0 + + load name=samp plugin=procnetdev + config name=samp producer=vm1_1 instance=vm1_1/procnetdev2 ifaces=eth0,eth1 + start name=samp interval=1000000 offset=0 + + load name=samp2 plugin=procnetdev + config name=samp2 producer=vm1_1 instance=vm1_1/nolo excludes=lo + start name=samp2 interval=1000000 offset=0 + .fi .SH SEE ALSO diff --git a/ldms/src/sampler/procnetdev2/procnetdev2.c b/ldms/src/sampler/procnetdev2/procnetdev2.c index a99c4f2ee..302ad4fcb 100644 --- a/ldms/src/sampler/procnetdev2/procnetdev2.c +++ b/ldms/src/sampler/procnetdev2/procnetdev2.c @@ -1,8 +1,8 @@ /* -*- c-basic-offset: 8 -*- - * Copyright (c) 2010-2016,2018,2022 National Technology & Engineering Solutions + * Copyright (c) 2010-2016,2018,2022,2024 National Technology & Engineering Solutions * of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with * NTESS, the U.S. Government retains certain rights in this software. - * Copyright (c) 2010-2016,2018,2022 Open Grid Computing, Inc. All rights + * Copyright (c) 2010-2016,2018,2022,2024 Open Grid Computing, Inc. All rights * reserved. * * This software is available to you under a choice of one of two @@ -75,6 +75,28 @@ #define PROC_FILE "/proc/net/dev" static char *procfile = PROC_FILE; +typedef struct procnetdev2_s *procnetdev2_t; +struct procnetdev2_s { + union { + struct ldmsd_sampler sampler; /* sampler interface */ + struct ldmsd_plugin plugin; /* plugin interface */ + }; + FILE *mf; + base_data_t base_data; + + int rec_def_idx; + int netdev_list_mid; + size_t rec_heap_sz; + + int niface; + char **iface; + char *iface_str; + + int nexcludes; + char **excludes; + char *excludes_str; +}; + struct rec_metric_info { int mid; const char *name; @@ -119,75 +141,52 @@ static int rec_metric_ids[REC_METRICS_LEN]; */ -static int rec_def_idx; -static int netdev_list_mid; -static size_t rec_heap_sz; - -static int niface = 0; -//max number of interfaces we can include. -static char iface[MAXIFACE][20]; - #define SAMP "procnetdev2" -static FILE *mf = NULL; -static base_data_t base; static ovis_log_t mylog; -static ldms_set_t get_set(struct ldmsd_sampler *self) +static int create_metric_set(procnetdev2_t p) { - if (base) - return base->set; - return NULL; -} - -static int create_metric_set(base_data_t base) -{ - ldms_schema_t schema; + static ldms_schema_t schema; ldms_record_t rec_def; size_t heap_sz; int rc; - mf = fopen(procfile, "r"); - if (!mf) { - ovis_log(mylog, OVIS_LERROR, "Could not open " SAMP " file " - "'%s'...exiting\n", - procfile); - return ENOENT; - } - /* Create a metric set of the required size */ - schema = base_schema_new(base); + schema = base_schema_new(p->base_data); if (!schema) { ovis_log(mylog, OVIS_LERROR, "%s: The schema '%s' could not be created, errno=%d.\n", - __FILE__, base->schema_name, errno); + __FILE__, p->base_data->schema_name, errno); rc = EINVAL; goto err1; } /* Create netdev record definition */ rec_def = ldms_record_from_template("netdev", rec_metrics, rec_metric_ids); - if (!rec_def) + if (!rec_def) { + rc = errno; goto err2; - rec_heap_sz = ldms_record_heap_size_get(rec_def); + } + p->rec_heap_sz = ldms_record_heap_size_get(rec_def); heap_sz = MAXIFACE * ldms_record_heap_size_get(rec_def); /* Add record definition into the schema */ - rec_def_idx = ldms_schema_record_add(schema, rec_def); - if (rec_def_idx < 0) { - rc = -rec_def_idx; + p->rec_def_idx = ldms_schema_record_add(schema, rec_def); + if (p->rec_def_idx < 0) { + rc = -p->rec_def_idx; goto err3; } /* Add a list (of records) */ - netdev_list_mid = ldms_schema_metric_list_add(schema, "netdev_list", NULL, heap_sz); - if (netdev_list_mid < 0) { - rc = -netdev_list_mid; + p->netdev_list_mid = ldms_schema_metric_list_add(schema, "netdev_list", NULL, heap_sz); + if (p->netdev_list_mid < 0) { + rc = -p->netdev_list_mid; goto err2; } - base_set_new(base); - if (!base->set) { + base_set_new(p->base_data); + if (!p->base_data->set) { rc = errno; goto err2; } @@ -198,12 +197,9 @@ static int create_metric_set(base_data_t base) to the schema */ ldms_record_delete(rec_def); err2: - base_schema_delete(base); - base = NULL; + base_schema_delete(p->base_data); + p->base_data = NULL; err1: - if (mf) - fclose(mf); - mf = NULL; return rc; } @@ -233,18 +229,65 @@ static int config_check(struct attr_value_list *kwl, struct attr_value_list *avl static const char *usage(struct ldmsd_plugin *self) { - return "config name=" SAMP " ifaces=\n" \ + return "config name=" SAMP " [ifaces=] [excludes=]\n" \ BASE_CONFIG_USAGE \ - " A comma-separated list of interface names (e.g. eth0,eth1)\n" - " Order matters. All ifaces will be included\n" - " whether they exist of not up to a total of MAXIFACE\n"; + " ifaces A comma-separated list of interface names (e.g. eth0,eth1)\n" + " to be collected. If NOT specified, all interfaces are\n" + " included (unluss the `excludes` option exclude them).\n" + " excludes A comma-separated list of interface names (e.g. lo,eth0)\n" + " to be excluded from the collection.\n" + "\n" + "If `ifaces` and `excludes` are NOT specified, the sampler collects data from\n" + "all interfaces. If only `ifaces` is specified, the sampler only collects data\n" + "from interfaces in the ifaces list. If only `excludes` is specified,\n" + "the sampler collects data from all interfaces EXCEPT those in the `excludes`\n" + "option. If both `ifaces` and `excludes` are specified, the sampler collects\n" + "data from all interfaces that are in `ifaces` option but are NOT in the\n" + "`excludes` option.\n" + ; +} + +int strpcmp(const void *a, const void *b) +{ + return strcmp(*(char**)a, *(char**)b); +} + +char **strarray(char *s, int *n_out) +{ + char **arr = NULL; + char *pch, *saveptr; + int i, n; + + n = 1; + for (pch = s; *pch; pch++) { + if (*pch == ',') + n++; + } + + arr = malloc(n*sizeof(arr[0])); + if (!arr) + goto out; + /* fill */ + i = 0; + pch = strtok_r(s, ",", &saveptr); + while (pch != NULL){ + arr[i++] = pch; + pch = strtok_r(NULL, ",", &saveptr); + } + + /* sort iface array by strcmp */ + qsort(arr, n, sizeof(arr[0]), strpcmp); + *n_out = n; + + out: + return arr; } static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct attr_value_list *avl) { + procnetdev2_t p = (void*)self; char* ifacelist = NULL; - char* pch = NULL; - char *saveptr = NULL; + char* excludes_str = NULL; char *ivalue = NULL; void *arg = NULL; int rc; @@ -254,7 +297,7 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct return rc; } - if (base) { + if (p->base_data) { ovis_log(mylog, OVIS_LERROR, "Set already created.\n"); return EINVAL; } @@ -262,39 +305,44 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct /* process ifaces */ ivalue = av_value(avl, "ifaces"); if (!ivalue) - goto cfg; + goto excludes; ifacelist = strdup(ivalue); if (!ifacelist) { ovis_log(mylog, OVIS_LCRIT, "out of memory\n"); goto err; } - pch = strtok_r(ifacelist, ",", &saveptr); - while (pch != NULL){ - if (niface >= (MAXIFACE-1)) { - ovis_log(mylog, OVIS_LERROR, "too many ifaces: <%s>\n", - pch); - goto err; - } - snprintf(iface[niface], 20, "%s", pch); - ovis_log(mylog, OVIS_LDEBUG, "added iface <%s>\n", iface[niface]); - niface++; - pch = strtok_r(NULL, ",", &saveptr); + + p->iface = strarray(ifacelist, &p->niface); + if (!p->iface) { + goto err; } - free(ifacelist); - ifacelist = NULL; + p->iface_str = ifacelist; - if (niface == 0) + excludes: + /* process excludes */ + ivalue = av_value(avl, "excludes"); + if (!ivalue) + goto cfg; + excludes_str = strdup(ivalue); + if (!excludes_str) { + ovis_log(mylog, OVIS_LCRIT, "out of memory\n"); + goto err; + } + p->excludes = strarray(excludes_str, &p->nexcludes); + if (!p->excludes) { goto err; + } + p->excludes_str = excludes_str; cfg: - base = base_config(avl, SAMP, SAMP, mylog); - if (!base){ + p->base_data = base_config(avl, SAMP, SAMP, mylog); + if (!p->base_data){ rc = EINVAL; goto err; } - rc = create_metric_set(base); + rc = create_metric_set(p); if (rc) { ovis_log(mylog, OVIS_LERROR, "failed to create a metric set.\n"); goto err; @@ -303,51 +351,61 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct return 0; err: + p->iface_str = NULL; + p->niface = 0; + if (p->iface) { + free(p->iface); + p->iface = NULL; + } if (ifacelist) free(ifacelist); - base_del(base); + if (excludes_str) + free(excludes_str); + base_del(p->base_data); return rc; } static int sample(struct ldmsd_sampler *self) { + procnetdev2_t p = (void*)self; int rc; char *s; char lbuf[256]; - char curriface[IFNAMSIZ]; + char _curriface[IFNAMSIZ]; + char *curriface = _curriface; union ldms_value v[REC_METRICS_LEN]; int i; ldms_mval_t lh, rec_inst, name_mval; size_t heap_sz; - if (!base) { + if (!p->base_data) { ovis_log(mylog, OVIS_LDEBUG, "plugin not initialized\n"); return EINVAL; } - if (!mf) - mf = fopen(procfile, "r"); - if (!mf) { + if (!p->mf) + p->mf = fopen(procfile, "r"); + if (!p->mf) { ovis_log(mylog, OVIS_LERROR, "Could not open /proc/net/dev file " "'%s'...exiting\n", procfile); return ENOENT; } begin: - base_sample_begin(base); + base_sample_begin(p->base_data); - lh = ldms_metric_get(base->set, netdev_list_mid); + lh = ldms_metric_get(p->base_data->set, p->netdev_list_mid); /* reset device data */ - ldms_list_purge(base->set, lh); + ldms_list_purge(p->base_data->set, lh); - fseek(mf, 0, SEEK_SET); //seek should work if get to EOF - s = fgets(lbuf, sizeof(lbuf), mf); - s = fgets(lbuf, sizeof(lbuf), mf); + fseek(p->mf, 0, SEEK_SET); //seek should work if get to EOF + s = fgets(lbuf, sizeof(lbuf), p->mf); + s = fgets(lbuf, sizeof(lbuf), p->mf); /* data */ do { - s = fgets(lbuf, sizeof(lbuf), mf); + s = fgets(lbuf, sizeof(lbuf), p->mf); if (!s) break; @@ -372,17 +430,23 @@ static int sample(struct ldmsd_sampler *self) continue; } - if (niface) { - /* ifaces list was given in config */ - for (i = 0; i < niface; i++) { - if (strcmp(curriface, iface[i]) == 0) - goto rec; + if (p->niface) { /* ifaces list was given in config */ + if (bsearch(&curriface, p->iface, p->niface, + sizeof(p->iface[0]), strpcmp)) { + goto rec; } /* not in the ifaces list */ continue; } rec: - rec_inst = ldms_record_alloc(base->set, rec_def_idx); + /* must check if `curriface` is excluded */ + if (p->nexcludes) { + if (bsearch(&curriface, p->excludes, p->nexcludes, + sizeof(p->excludes[0]), strpcmp)) { + continue; + } + } + rec_inst = ldms_record_alloc(p->base_data->set, p->rec_def_idx); if (!rec_inst) goto resize; /* iface name */ @@ -392,20 +456,20 @@ static int sample(struct ldmsd_sampler *self) for (i = 1; i < REC_METRICS_LEN; i++) { ldms_record_set_u64(rec_inst, rec_metric_ids[i], v[i].v_u64); } - ldms_list_append_record(base->set, lh, rec_inst); + ldms_list_append_record(p->base_data->set, lh, rec_inst); } while (s); - base_sample_end(base); + base_sample_end(p->base_data); return 0; resize: /* * We intend to leave the set in the inconsistent state so that * the aggregators are aware that some metrics have not been newly sampled. */ - heap_sz = ldms_set_heap_size_get(base->set) + 2*rec_heap_sz; - base_set_delete(base); - base_set_new_heap(base, heap_sz); - if (!base->set) { + heap_sz = ldms_set_heap_size_get(p->base_data->set) + 2*p->rec_heap_sz; + base_set_delete(p->base_data); + base_set_new_heap(p->base_data, heap_sz); + if (!p->base_data->set) { rc = errno; ovis_log(mylog, OVIS_LCRITICAL, SAMP " : Failed to create a set with " "a bigger heap. Error %d\n", rc); @@ -417,38 +481,67 @@ static int sample(struct ldmsd_sampler *self) static void term(struct ldmsd_plugin *self) { - if (mf) - fclose(mf); - mf = NULL; - base_set_delete(base); - base_del(base); - base = NULL; - if (mylog) - ovis_log_destroy(mylog); + procnetdev2_t p = (void*)self; + if (p->mf) { + fclose(p->mf); + p->mf = NULL; + } + p->mf = NULL; + base_set_delete(p->base_data); + base_del(p->base_data); + p->base_data = NULL; + + if (p->iface) { + free(p->iface); + p->iface = NULL; + } + if (p->iface_str) { + free(p->iface_str); + p->iface_str = NULL; + } + p->niface = 0; } -static struct ldmsd_sampler procnetdev2_plugin = { - .base = { - .name = SAMP, - .type = LDMSD_PLUGIN_SAMPLER, - .term = term, - .config = config, - .usage = usage, - }, - .get_set = get_set, - .sample = sample, -}; +static void __procnetdev2_del(struct ldmsd_cfgobj *self) +{ + procnetdev2_t p = (void*)self; + free(p); +} -struct ldmsd_plugin *get_plugin() +static void __once() { - int rc; - base = NULL; + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&mutex); + if (mylog) + goto out; mylog = ovis_log_register("sampler."SAMP, "Message for the " SAMP " plugin"); if (!mylog) { - rc = errno; ovis_log(NULL, OVIS_LWARN, "Failed to create the log subsystem " - "of '" SAMP "' plugin. Error %d\n", rc); + "of '" SAMP "' plugin. Error %d\n", errno); } - return &procnetdev2_plugin.base; + out: + pthread_mutex_unlock(&mutex); +} + +struct ldmsd_plugin *get_plugin_instance(const char *name, + uid_t uid, gid_t gid, int perm) +{ + procnetdev2_t p; + + __once(); + + p = (void*)ldmsd_sampler_alloc(name, sizeof(*p), __procnetdev2_del, + uid, gid, perm); + if (!p) + return NULL; + + snprintf(p->plugin.name, sizeof(p->plugin.name), "%s", SAMP); + p->plugin.term = term; + p->plugin.config = config; + p->plugin.usage = usage; + + p->sampler.sample = sample; + + return &p->plugin; }