New upstream version 17.11.4
[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         for (idx_name = 0; idx_name < cnt_names; idx_name++)
128                 if (names[idx_name] == NULL)
129                         return -EINVAL;
130
131         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
132         if (memzone == NULL)
133                 return -EIO;
134         stats = memzone->addr;
135
136         if (stats->cnt_stats + cnt_names >= RTE_METRICS_MAX_METRICS)
137                 return -ENOMEM;
138
139         rte_spinlock_lock(&stats->lock);
140
141         /* Overwritten later if this is actually first set.. */
142         stats->metadata[stats->idx_last_set].idx_next_set = stats->cnt_stats;
143
144         stats->idx_last_set = idx_base = stats->cnt_stats;
145
146         for (idx_name = 0; idx_name < cnt_names; idx_name++) {
147                 entry = &stats->metadata[idx_name + stats->cnt_stats];
148                 strncpy(entry->name, names[idx_name],
149                         RTE_METRICS_MAX_NAME_LEN);
150                 /* Enforce NULL-termination */
151                 entry->name[RTE_METRICS_MAX_NAME_LEN - 1] = '\0';
152                 memset(entry->value, 0, sizeof(entry->value));
153                 entry->idx_next_stat = idx_name + stats->cnt_stats + 1;
154         }
155         entry->idx_next_stat = 0;
156         entry->idx_next_set = 0;
157         stats->cnt_stats += cnt_names;
158
159         rte_spinlock_unlock(&stats->lock);
160
161         return idx_base;
162 }
163
164 int
165 rte_metrics_update_value(int port_id, uint16_t key, const uint64_t value)
166 {
167         return rte_metrics_update_values(port_id, key, &value, 1);
168 }
169
170 int
171 rte_metrics_update_values(int port_id,
172         uint16_t key,
173         const uint64_t *values,
174         uint32_t count)
175 {
176         struct rte_metrics_meta_s *entry;
177         struct rte_metrics_data_s *stats;
178         const struct rte_memzone *memzone;
179         uint16_t idx_metric;
180         uint16_t idx_value;
181         uint16_t cnt_setsize;
182
183         if (port_id != RTE_METRICS_GLOBAL &&
184                         (port_id < 0 || port_id >= RTE_MAX_ETHPORTS))
185                 return -EINVAL;
186
187         if (values == NULL)
188                 return -EINVAL;
189
190         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
191         if (memzone == NULL)
192                 return -EIO;
193         stats = memzone->addr;
194
195         rte_spinlock_lock(&stats->lock);
196
197         if (key >= stats->cnt_stats) {
198                 rte_spinlock_unlock(&stats->lock);
199                 return -EINVAL;
200         }
201         idx_metric = key;
202         cnt_setsize = 1;
203         while (idx_metric < stats->cnt_stats) {
204                 entry = &stats->metadata[idx_metric];
205                 if (entry->idx_next_stat == 0)
206                         break;
207                 cnt_setsize++;
208                 idx_metric++;
209         }
210         /* Check update does not cross set border */
211         if (count > cnt_setsize) {
212                 rte_spinlock_unlock(&stats->lock);
213                 return -ERANGE;
214         }
215
216         if (port_id == RTE_METRICS_GLOBAL)
217                 for (idx_value = 0; idx_value < count; idx_value++) {
218                         idx_metric = key + idx_value;
219                         stats->metadata[idx_metric].global_value =
220                                 values[idx_value];
221                 }
222         else
223                 for (idx_value = 0; idx_value < count; idx_value++) {
224                         idx_metric = key + idx_value;
225                         stats->metadata[idx_metric].value[port_id] =
226                                 values[idx_value];
227                 }
228         rte_spinlock_unlock(&stats->lock);
229         return 0;
230 }
231
232 int
233 rte_metrics_get_names(struct rte_metric_name *names,
234         uint16_t capacity)
235 {
236         struct rte_metrics_data_s *stats;
237         const struct rte_memzone *memzone;
238         uint16_t idx_name;
239         int return_value;
240
241         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
242         if (memzone == NULL)
243                 return -EIO;
244
245         stats = memzone->addr;
246         rte_spinlock_lock(&stats->lock);
247         if (names != NULL) {
248                 if (capacity < stats->cnt_stats) {
249                         return_value = stats->cnt_stats;
250                         rte_spinlock_unlock(&stats->lock);
251                         return return_value;
252                 }
253                 for (idx_name = 0; idx_name < stats->cnt_stats; idx_name++)
254                         strncpy(names[idx_name].name,
255                                 stats->metadata[idx_name].name,
256                                 RTE_METRICS_MAX_NAME_LEN);
257         }
258         return_value = stats->cnt_stats;
259         rte_spinlock_unlock(&stats->lock);
260         return return_value;
261 }
262
263 int
264 rte_metrics_get_values(int port_id,
265         struct rte_metric_value *values,
266         uint16_t capacity)
267 {
268         struct rte_metrics_meta_s *entry;
269         struct rte_metrics_data_s *stats;
270         const struct rte_memzone *memzone;
271         uint16_t idx_name;
272         int return_value;
273
274         if (port_id != RTE_METRICS_GLOBAL &&
275                         (port_id < 0 || port_id >= RTE_MAX_ETHPORTS))
276                 return -EINVAL;
277
278         memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME);
279         if (memzone == NULL)
280                 return -EIO;
281
282         stats = memzone->addr;
283         rte_spinlock_lock(&stats->lock);
284
285         if (values != NULL) {
286                 if (capacity < stats->cnt_stats) {
287                         return_value = stats->cnt_stats;
288                         rte_spinlock_unlock(&stats->lock);
289                         return return_value;
290                 }
291                 if (port_id == RTE_METRICS_GLOBAL)
292                         for (idx_name = 0;
293                                         idx_name < stats->cnt_stats;
294                                         idx_name++) {
295                                 entry = &stats->metadata[idx_name];
296                                 values[idx_name].key = idx_name;
297                                 values[idx_name].value = entry->global_value;
298                         }
299                 else
300                         for (idx_name = 0;
301                                         idx_name < stats->cnt_stats;
302                                         idx_name++) {
303                                 entry = &stats->metadata[idx_name];
304                                 values[idx_name].key = idx_name;
305                                 values[idx_name].value = entry->value[port_id];
306                         }
307         }
308         return_value = stats->cnt_stats;
309         rte_spinlock_unlock(&stats->lock);
310         return return_value;
311 }