New upstream version 17.11-rc3
[deb_dpdk.git] / lib / librte_metrics / rte_metrics.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2017 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <string.h>
35 #include <sys/queue.h>
36
37 #include <rte_common.h>
38 #include <rte_malloc.h>
39 #include <rte_metrics.h>
40 #include <rte_lcore.h>
41 #include <rte_memzone.h>
42 #include <rte_spinlock.h>
43
44 #define RTE_METRICS_MAX_METRICS 256
45 #define RTE_METRICS_MEMZONE_NAME "RTE_METRICS"
46
47 /**
48  * Internal stats metadata and value entry.
49  *
50  * @internal
51  */
52 struct rte_metrics_meta_s {
53         /** Name of metric */
54         char name[RTE_METRICS_MAX_NAME_LEN];
55         /** Current value for metric */
56         uint64_t value[RTE_MAX_ETHPORTS];
57         /** Used for global metrics */
58         uint64_t global_value;
59         /** Index of next root element (zero for none) */
60         uint16_t idx_next_set;
61         /** Index of next metric in set (zero for none) */
62         uint16_t idx_next_stat;
63 };
64
65 /**
66  * Internal stats info structure.
67  *
68  * @internal
69  * Offsets into metadata are used instead of pointers because ASLR
70  * means that having the same physical addresses in different
71  * processes is not guaranteed.
72  */
73 struct rte_metrics_data_s {
74         /**   Index of last metadata entry with valid data.
75          * This value is not valid if cnt_stats is zero.
76          */
77         uint16_t idx_last_set;
78         /**   Number of metrics. */
79         uint16_t cnt_stats;
80         /** Metric data memory block. */
81         struct rte_metrics_meta_s metadata[RTE_METRICS_MAX_METRICS];
82         /** Metric data access lock */
83         rte_spinlock_t lock;
84 };
85
86 void
87 rte_metrics_init(int socket_id)
88 {
89         struct rte_metrics_data_s *stats;
90         const struct rte_memzone *memzone;
91
92         if (rte_eal_process_type() != RTE_PROC_PRIMARY)
93                 return;
94
95         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
96         if (memzone != NULL)
97                 return;
98         memzone = rte_memzone_reserve(RTE_METRICS_MEMZONE_NAME,
99                 sizeof(struct rte_metrics_data_s), socket_id, 0);
100         if (memzone == NULL)
101                 rte_exit(EXIT_FAILURE, "Unable to allocate stats memzone\n");
102         stats = memzone->addr;
103         memset(stats, 0, sizeof(struct rte_metrics_data_s));
104         rte_spinlock_init(&stats->lock);
105 }
106
107 int
108 rte_metrics_reg_name(const char *name)
109 {
110         const char * const list_names[] = {name};
111
112         return rte_metrics_reg_names(list_names, 1);
113 }
114
115 int
116 rte_metrics_reg_names(const char * const *names, uint16_t cnt_names)
117 {
118         struct rte_metrics_meta_s *entry = NULL;
119         struct rte_metrics_data_s *stats;
120         const struct rte_memzone *memzone;
121         uint16_t idx_name;
122         uint16_t idx_base;
123
124         /* Some sanity checks */
125         if (cnt_names < 1 || names == NULL)
126                 return -EINVAL;
127
128         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
129         if (memzone == NULL)
130                 return -EIO;
131         stats = memzone->addr;
132
133         if (stats->cnt_stats + cnt_names >= RTE_METRICS_MAX_METRICS)
134                 return -ENOMEM;
135
136         rte_spinlock_lock(&stats->lock);
137
138         /* Overwritten later if this is actually first set.. */
139         stats->metadata[stats->idx_last_set].idx_next_set = stats->cnt_stats;
140
141         stats->idx_last_set = idx_base = stats->cnt_stats;
142
143         for (idx_name = 0; idx_name < cnt_names; idx_name++) {
144                 entry = &stats->metadata[idx_name + stats->cnt_stats];
145                 strncpy(entry->name, names[idx_name],
146                         RTE_METRICS_MAX_NAME_LEN);
147                 /* Enforce NULL-termination */
148                 entry->name[RTE_METRICS_MAX_NAME_LEN - 1] = '\0';
149                 memset(entry->value, 0, sizeof(entry->value));
150                 entry->idx_next_stat = idx_name + stats->cnt_stats + 1;
151         }
152         entry->idx_next_stat = 0;
153         entry->idx_next_set = 0;
154         stats->cnt_stats += cnt_names;
155
156         rte_spinlock_unlock(&stats->lock);
157
158         return idx_base;
159 }
160
161 int
162 rte_metrics_update_value(int port_id, uint16_t key, const uint64_t value)
163 {
164         return rte_metrics_update_values(port_id, key, &value, 1);
165 }
166
167 int
168 rte_metrics_update_values(int port_id,
169         uint16_t key,
170         const uint64_t *values,
171         uint32_t count)
172 {
173         struct rte_metrics_meta_s *entry;
174         struct rte_metrics_data_s *stats;
175         const struct rte_memzone *memzone;
176         uint16_t idx_metric;
177         uint16_t idx_value;
178         uint16_t cnt_setsize;
179
180         if (port_id != RTE_METRICS_GLOBAL &&
181                         (port_id < 0 || port_id >= RTE_MAX_ETHPORTS))
182                 return -EINVAL;
183
184         if (values == NULL)
185                 return -EINVAL;
186
187         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
188         if (memzone == NULL)
189                 return -EIO;
190         stats = memzone->addr;
191
192         rte_spinlock_lock(&stats->lock);
193         idx_metric = key;
194         cnt_setsize = 1;
195         while (idx_metric < stats->cnt_stats) {
196                 entry = &stats->metadata[idx_metric];
197                 if (entry->idx_next_stat == 0)
198                         break;
199                 cnt_setsize++;
200                 idx_metric++;
201         }
202         /* Check update does not cross set border */
203         if (count > cnt_setsize) {
204                 rte_spinlock_unlock(&stats->lock);
205                 return -ERANGE;
206         }
207
208         if (port_id == RTE_METRICS_GLOBAL)
209                 for (idx_value = 0; idx_value < count; idx_value++) {
210                         idx_metric = key + idx_value;
211                         stats->metadata[idx_metric].global_value =
212                                 values[idx_value];
213                 }
214         else
215                 for (idx_value = 0; idx_value < count; idx_value++) {
216                         idx_metric = key + idx_value;
217                         stats->metadata[idx_metric].value[port_id] =
218                                 values[idx_value];
219                 }
220         rte_spinlock_unlock(&stats->lock);
221         return 0;
222 }
223
224 int
225 rte_metrics_get_names(struct rte_metric_name *names,
226         uint16_t capacity)
227 {
228         struct rte_metrics_data_s *stats;
229         const struct rte_memzone *memzone;
230         uint16_t idx_name;
231         int return_value;
232
233         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
234         /* If not allocated, fail silently */
235         if (memzone == NULL)
236                 return 0;
237
238         stats = memzone->addr;
239         rte_spinlock_lock(&stats->lock);
240         if (names != NULL) {
241                 if (capacity < stats->cnt_stats) {
242                         return_value = stats->cnt_stats;
243                         rte_spinlock_unlock(&stats->lock);
244                         return return_value;
245                 }
246                 for (idx_name = 0; idx_name < stats->cnt_stats; idx_name++)
247                         strncpy(names[idx_name].name,
248                                 stats->metadata[idx_name].name,
249                                 RTE_METRICS_MAX_NAME_LEN);
250         }
251         return_value = stats->cnt_stats;
252         rte_spinlock_unlock(&stats->lock);
253         return return_value;
254 }
255
256 int
257 rte_metrics_get_values(int port_id,
258         struct rte_metric_value *values,
259         uint16_t capacity)
260 {
261         struct rte_metrics_meta_s *entry;
262         struct rte_metrics_data_s *stats;
263         const struct rte_memzone *memzone;
264         uint16_t idx_name;
265         int return_value;
266
267         if (port_id != RTE_METRICS_GLOBAL &&
268                         (port_id < 0 || port_id >= RTE_MAX_ETHPORTS))
269                 return -EINVAL;
270
271         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
272         /* If not allocated, fail silently */
273         if (memzone == NULL)
274                 return 0;
275         stats = memzone->addr;
276         rte_spinlock_lock(&stats->lock);
277
278         if (values != NULL) {
279                 if (capacity < stats->cnt_stats) {
280                         return_value = stats->cnt_stats;
281                         rte_spinlock_unlock(&stats->lock);
282                         return return_value;
283                 }
284                 if (port_id == RTE_METRICS_GLOBAL)
285                         for (idx_name = 0;
286                                         idx_name < stats->cnt_stats;
287                                         idx_name++) {
288                                 entry = &stats->metadata[idx_name];
289                                 values[idx_name].key = idx_name;
290                                 values[idx_name].value = entry->global_value;
291                         }
292                 else
293                         for (idx_name = 0;
294                                         idx_name < stats->cnt_stats;
295                                         idx_name++) {
296                                 entry = &stats->metadata[idx_name];
297                                 values[idx_name].key = idx_name;
298                                 values[idx_name].value = entry->value[port_id];
299                         }
300         }
301         return_value = stats->cnt_stats;
302         rte_spinlock_unlock(&stats->lock);
303         return return_value;
304 }