wprobe: move measurement task to the kernel, add some configurability (work in progress)
[openwrt/svn-archive/archive.git] / package / wprobe / src / kernel / wprobe-core.c
1 /*
2 * wprobe-core.c: Wireless probe interface core
3 * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16 #include <linux/kernel.h>
17 #include <linux/version.h>
18 #include <linux/module.h>
19 #include <linux/types.h>
20 #include <linux/spinlock.h>
21 #include <linux/rcupdate.h>
22 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
23 #include <linux/rculist.h>
24 #else
25 #include <linux/list.h>
26 #endif
27 #include <linux/skbuff.h>
28 #include <linux/wprobe.h>
29 #include <linux/math64.h>
30
31 #define static
32
33 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
34 #define list_for_each_rcu __list_for_each_rcu
35 #endif
36
37 #define WPROBE_MIN_INTERVAL 100 /* minimum measurement interval in msecs */
38
39 static struct list_head wprobe_if;
40 static spinlock_t wprobe_lock;
41
42 static struct genl_family wprobe_fam = {
43 .id = GENL_ID_GENERATE,
44 .name = "wprobe",
45 .hdrsize = 0,
46 .version = 1,
47 /* only the first set of attributes is used for queries */
48 .maxattr = WPROBE_ATTR_LAST,
49 };
50
51 static void wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l);
52 static int wprobe_sync_data(struct wprobe_iface *dev, struct wprobe_link *l, bool query);
53
54 int
55 wprobe_add_link(struct wprobe_iface *s, struct wprobe_link *l, const char *addr)
56 {
57 unsigned long flags;
58
59 INIT_LIST_HEAD(&l->list);
60 l->val = kzalloc(sizeof(struct wprobe_value) * s->n_link_items, GFP_ATOMIC);
61 if (!l->val)
62 return -ENOMEM;
63
64 l->iface = s;
65 memcpy(&l->addr, addr, ETH_ALEN);
66 spin_lock_irqsave(&wprobe_lock, flags);
67 list_add_tail_rcu(&l->list, &s->links);
68 spin_unlock_irqrestore(&wprobe_lock, flags);
69
70 return 0;
71 }
72 EXPORT_SYMBOL(wprobe_add_link);
73
74 void
75 wprobe_remove_link(struct wprobe_iface *s, struct wprobe_link *l)
76 {
77 unsigned long flags;
78
79 spin_lock_irqsave(&wprobe_lock, flags);
80 list_del_rcu(&l->list);
81 spin_unlock_irqrestore(&wprobe_lock, flags);
82 synchronize_rcu();
83 kfree(l->val);
84 }
85 EXPORT_SYMBOL(wprobe_remove_link);
86
87 static void
88 wprobe_measure_timer(unsigned long data)
89 {
90 struct wprobe_iface *dev = (struct wprobe_iface *) data;
91
92 /* set next measurement interval */
93 mod_timer(&dev->measure_timer, jiffies +
94 msecs_to_jiffies(dev->measure_interval));
95
96 /* perform measurement */
97 wprobe_sync_data(dev, NULL, false);
98 }
99
100 int
101 wprobe_add_iface(struct wprobe_iface *s)
102 {
103 unsigned long flags;
104 int vsize;
105
106 /* reset only wprobe private area */
107 memset(&s->list, 0, sizeof(struct wprobe_iface) - offsetof(struct wprobe_iface, list));
108
109 BUG_ON(!s->name);
110 INIT_LIST_HEAD(&s->list);
111 INIT_LIST_HEAD(&s->links);
112 setup_timer(&s->measure_timer, wprobe_measure_timer, (unsigned long) s);
113
114 vsize = max(s->n_link_items, s->n_global_items);
115 s->val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC);
116 if (!s->val)
117 goto error;
118
119 s->query_val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC);
120 if (!s->query_val)
121 goto error;
122
123 /* initialize defaults to be able to handle overflow,
124 * user space will need to handle this if it keeps an
125 * internal histogram */
126 s->scale_min = 20;
127 s->scale_max = (1 << 31);
128
129 s->scale_m = 1;
130 s->scale_d = 10;
131
132 spin_lock_irqsave(&wprobe_lock, flags);
133 list_add_rcu(&s->list, &wprobe_if);
134 spin_unlock_irqrestore(&wprobe_lock, flags);
135
136 return 0;
137
138 error:
139 if (s->val)
140 kfree(s->val);
141 return -ENOMEM;
142 }
143 EXPORT_SYMBOL(wprobe_add_iface);
144
145 void
146 wprobe_remove_iface(struct wprobe_iface *s)
147 {
148 unsigned long flags;
149
150 BUG_ON(!list_empty(&s->links));
151
152 del_timer_sync(&s->measure_timer);
153 spin_lock_irqsave(&wprobe_lock, flags);
154 list_del_rcu(&s->list);
155 spin_unlock_irqrestore(&wprobe_lock, flags);
156
157 /* wait for all queries to finish before freeing the
158 * temporary value storage buffer */
159 synchronize_rcu();
160
161 kfree(s->val);
162 kfree(s->query_val);
163 }
164 EXPORT_SYMBOL(wprobe_remove_iface);
165
166 static struct wprobe_iface *
167 wprobe_get_dev(struct nlattr *attr)
168 {
169 struct wprobe_iface *dev = NULL;
170 struct wprobe_iface *p;
171 const char *name;
172 int i = 0;
173
174 if (!attr)
175 return NULL;
176
177 name = nla_data(attr);
178 list_for_each_entry_rcu(p, &wprobe_if, list) {
179 i++;
180 if (strcmp(name, p->name) != 0)
181 continue;
182
183 dev = p;
184 break;
185 }
186
187 return dev;
188 }
189
190 static int
191 wprobe_sync_data(struct wprobe_iface *dev, struct wprobe_link *l, bool query)
192 {
193 struct wprobe_value *val;
194 unsigned long flags;
195 int n, err;
196
197 if (l) {
198 n = dev->n_link_items;
199 val = l->val;
200 } else {
201 n = dev->n_global_items;
202 val = dev->val;
203 }
204
205 spin_lock_irqsave(&dev->lock, flags);
206 err = dev->sync_data(dev, l, val, !query);
207 if (err)
208 goto done;
209
210 if (query)
211 memcpy(dev->query_val, val, sizeof(struct wprobe_value) * n);
212
213 wprobe_update_stats(dev, l);
214 done:
215 spin_unlock_irqrestore(&dev->lock, flags);
216 return 0;
217 }
218 EXPORT_SYMBOL(wprobe_sync_data);
219
220 static void
221 wprobe_scale_stats(struct wprobe_iface *dev, const struct wprobe_item *item,
222 struct wprobe_value *val, int n)
223 {
224 u64 scale_ts = jiffies_64;
225 int i;
226
227 for (i = 0; i < n; i++) {
228 if (!(item[i].flags & WPROBE_F_KEEPSTAT))
229 continue;
230
231 if (val[i].n <= dev->scale_min)
232 continue;
233
234 /* FIXME: div_s64 seems to be very imprecise here, even when
235 * the values are scaled up */
236 val[i].s *= dev->scale_m;
237 val[i].s = div_s64(val[i].s, dev->scale_d);
238
239 val[i].ss *= dev->scale_m;
240 val[i].ss = div_s64(val[i].ss, dev->scale_d);
241
242 val[i].n = (val[i].n * dev->scale_m) / dev->scale_d;
243 val[i].scale_timestamp = scale_ts;
244 }
245 }
246
247
248 void
249 wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l)
250 {
251 const struct wprobe_item *item;
252 struct wprobe_value *val;
253 bool scale_stats = false;
254 int i, n;
255
256 if (l) {
257 n = dev->n_link_items;
258 item = dev->link_items;
259 val = l->val;
260 } else {
261 n = dev->n_global_items;
262 item = dev->global_items;
263 val = dev->val;
264 }
265
266 /* process statistics */
267 for (i = 0; i < n; i++) {
268 s64 v;
269
270 if (!val[i].pending)
271 continue;
272
273 val[i].n++;
274 if ((item[i].flags & WPROBE_F_KEEPSTAT) &&
275 (dev->scale_max > 0) && (val[i].n > dev->scale_max)) {
276 scale_stats = true;
277 }
278
279 switch(item[i].type) {
280 case WPROBE_VAL_S8:
281 v = val[i].S8;
282 break;
283 case WPROBE_VAL_S16:
284 v = val[i].S16;
285 break;
286 case WPROBE_VAL_S32:
287 v = val[i].S32;
288 break;
289 case WPROBE_VAL_S64:
290 v = val[i].S64;
291 break;
292 case WPROBE_VAL_U8:
293 v = val[i].U8;
294 break;
295 case WPROBE_VAL_U16:
296 v = val[i].U16;
297 break;
298 case WPROBE_VAL_U32:
299 v = val[i].U32;
300 break;
301 case WPROBE_VAL_U64:
302 v = val[i].U64;
303 break;
304 default:
305 continue;
306 }
307
308 val[i].s += v;
309 val[i].ss += v * v;
310 val[i].pending = false;
311 }
312 if (scale_stats)
313 wprobe_scale_stats(dev, item, val, n);
314 }
315 EXPORT_SYMBOL(wprobe_update_stats);
316
317 static const struct nla_policy wprobe_policy[WPROBE_ATTR_LAST+1] = {
318 [WPROBE_ATTR_INTERFACE] = { .type = NLA_NUL_STRING },
319 [WPROBE_ATTR_MAC] = { .type = NLA_STRING },
320 [WPROBE_ATTR_FLAGS] = { .type = NLA_U32 },
321
322 /* config */
323 [WPROBE_ATTR_INTERVAL] = { .type = NLA_MSECS },
324 [WPROBE_ATTR_SAMPLES_MIN] = { .type = NLA_U32 },
325 [WPROBE_ATTR_SAMPLES_MAX] = { .type = NLA_U32 },
326 [WPROBE_ATTR_SAMPLES_SCALE_M] = { .type = NLA_U32 },
327 [WPROBE_ATTR_SAMPLES_SCALE_D] = { .type = NLA_U32 },
328 };
329
330 static bool
331 wprobe_check_ptr(struct list_head *list, struct list_head *ptr)
332 {
333 struct list_head *p;
334
335 list_for_each_rcu(p, list) {
336 if (ptr == p)
337 return true;
338 }
339 return false;
340 }
341
342 static bool
343 wprobe_send_item_value(struct sk_buff *msg, struct netlink_callback *cb,
344 struct wprobe_iface *dev, struct wprobe_link *l,
345 const struct wprobe_item *item,
346 int i, u32 flags)
347 {
348 struct genlmsghdr *hdr;
349 struct wprobe_value *val = dev->query_val;
350 u64 time = val[i].last - val[i].first;
351
352 hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
353 &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_INFO);
354
355 NLA_PUT_U32(msg, WPROBE_ATTR_ID, i);
356 NLA_PUT_U32(msg, WPROBE_ATTR_FLAGS, flags);
357 NLA_PUT_U8(msg, WPROBE_ATTR_TYPE, item[i].type);
358 NLA_PUT_U64(msg, WPROBE_ATTR_DURATION, time);
359
360 switch(item[i].type) {
361 case WPROBE_VAL_S8:
362 case WPROBE_VAL_U8:
363 NLA_PUT_U8(msg, item[i].type, val[i].U8);
364 break;
365 case WPROBE_VAL_S16:
366 case WPROBE_VAL_U16:
367 NLA_PUT_U16(msg, item[i].type, val[i].U16);
368 break;
369 case WPROBE_VAL_S32:
370 case WPROBE_VAL_U32:
371 NLA_PUT_U32(msg, item[i].type, val[i].U32);
372 break;
373 case WPROBE_VAL_S64:
374 case WPROBE_VAL_U64:
375 NLA_PUT_U64(msg, item[i].type, val[i].U64);
376 break;
377 case WPROBE_VAL_STRING:
378 if (val[i].STRING)
379 NLA_PUT_STRING(msg, item[i].type, val[i].STRING);
380 else
381 NLA_PUT_STRING(msg, item[i].type, "");
382 /* bypass avg/stdev */
383 goto done;
384 default:
385 /* skip unknown values */
386 goto done;
387 }
388 if (item[i].flags & WPROBE_F_KEEPSTAT) {
389 NLA_PUT_U64(msg, WPROBE_VAL_SUM, val[i].s);
390 NLA_PUT_U64(msg, WPROBE_VAL_SUM_SQ, val[i].ss);
391 NLA_PUT_U32(msg, WPROBE_VAL_SAMPLES, (u32) val[i].n);
392 NLA_PUT_MSECS(msg, WPROBE_VAL_SCALE_TIME, val[i].scale_timestamp);
393 }
394 done:
395 genlmsg_end(msg, hdr);
396 return true;
397
398 nla_put_failure:
399 genlmsg_cancel(msg, hdr);
400 return false;
401 }
402
403 static bool
404 wprobe_send_item_info(struct sk_buff *msg, struct netlink_callback *cb,
405 struct wprobe_iface *dev,
406 const struct wprobe_item *item, int i)
407 {
408 struct genlmsghdr *hdr;
409
410 hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
411 &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_LIST);
412
413 if ((i == 0) && (dev->addr != NULL))
414 NLA_PUT(msg, WPROBE_ATTR_MAC, 6, dev->addr);
415 NLA_PUT_U32(msg, WPROBE_ATTR_ID, (u32) i);
416 NLA_PUT_STRING(msg, WPROBE_ATTR_NAME, item[i].name);
417 NLA_PUT_U8(msg, WPROBE_ATTR_TYPE, item[i].type);
418 NLA_PUT_U32(msg, WPROBE_ATTR_FLAGS, item[i].flags);
419 genlmsg_end(msg, hdr);
420 return true;
421
422 nla_put_failure:
423 genlmsg_cancel(msg, hdr);
424 return false;
425 }
426
427
428 static struct wprobe_link *
429 wprobe_find_link(struct wprobe_iface *dev, const char *mac)
430 {
431 struct wprobe_link *l;
432
433 list_for_each_entry_rcu(l, &dev->links, list) {
434 if (!memcmp(l->addr, mac, 6))
435 return l;
436 }
437 return NULL;
438 }
439
440 static bool
441 wprobe_dump_link(struct sk_buff *msg, struct wprobe_link *l, struct netlink_callback *cb)
442 {
443 struct genlmsghdr *hdr;
444
445 hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
446 &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_LINKS);
447 if (!hdr)
448 return false;
449
450 NLA_PUT(msg, WPROBE_ATTR_MAC, 6, l->addr);
451 genlmsg_end(msg, hdr);
452 return true;
453
454 nla_put_failure:
455 genlmsg_cancel(msg, hdr);
456 return false;
457 }
458
459 static int
460 wprobe_dump_links(struct sk_buff *skb, struct netlink_callback *cb)
461 {
462 struct wprobe_iface *dev = (struct wprobe_iface *)cb->args[0];
463 struct wprobe_link *l;
464 int err = 0;
465 int i = 0;
466
467 if (!dev) {
468 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + wprobe_fam.hdrsize,
469 wprobe_fam.attrbuf, wprobe_fam.maxattr, wprobe_policy);
470 if (err)
471 goto done;
472
473 dev = wprobe_get_dev(wprobe_fam.attrbuf[WPROBE_ATTR_INTERFACE]);
474 if (!dev) {
475 err = -ENODEV;
476 goto done;
477 }
478
479 cb->args[0] = (long) dev;
480 } else {
481 if (!wprobe_check_ptr(&wprobe_if, &dev->list)) {
482 err = -ENODEV;
483 goto done;
484 }
485 }
486
487 rcu_read_lock();
488 list_for_each_entry_rcu(l, &dev->links, list) {
489 if (i < cb->args[1])
490 continue;
491
492 if (unlikely(!wprobe_dump_link(skb, l, cb)))
493 break;
494
495 i++;
496 }
497 cb->args[1] = i;
498 rcu_read_unlock();
499 err = skb->len;
500 done:
501 return err;
502 }
503
504 #define WPROBE_F_LINK (1 << 31) /* for internal use */
505 static int
506 wprobe_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
507 {
508 struct wprobe_iface *dev = (struct wprobe_iface *)cb->args[0];
509 struct wprobe_link *l = (struct wprobe_link *)cb->args[1];
510 struct wprobe_value *val;
511 const struct wprobe_item *item;
512 struct genlmsghdr *hdr;
513 unsigned long flags;
514 int cmd, n, i = cb->args[3];
515 u32 vflags = cb->args[2];
516 int err = 0;
517
518 hdr = (struct genlmsghdr *)nlmsg_data(cb->nlh);
519 cmd = hdr->cmd;
520
521 /* since the attribute value list might be too big for a single netlink
522 * message, the device, link and offset get stored in the netlink callback.
523 * if this is the first request, we need to do the full lookup for the device.
524 *
525 * access to the device and link structure is synchronized through rcu.
526 */
527 rcu_read_lock();
528 if (!dev) {
529 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + wprobe_fam.hdrsize,
530 wprobe_fam.attrbuf, wprobe_fam.maxattr, wprobe_policy);
531 if (err)
532 goto done;
533
534 err = -ENOENT;
535 dev = wprobe_get_dev(wprobe_fam.attrbuf[WPROBE_ATTR_INTERFACE]);
536 if (!dev)
537 goto done;
538
539 if (cmd == WPROBE_CMD_GET_INFO) {
540 if (wprobe_fam.attrbuf[WPROBE_ATTR_MAC]) {
541 l = wprobe_find_link(dev, nla_data(wprobe_fam.attrbuf[WPROBE_ATTR_MAC]));
542 if (!l)
543 goto done;
544
545 vflags = l->flags;
546 }
547
548 if (l) {
549 item = dev->link_items;
550 n = dev->n_link_items;
551 val = l->val;
552 } else {
553 item = dev->global_items;
554 n = dev->n_global_items;
555 val = dev->val;
556 }
557
558 /* sync data and move to temp storage for the query */
559 spin_lock_irqsave(&dev->lock, flags);
560 err = wprobe_sync_data(dev, l, true);
561 if (!err)
562 memcpy(dev->query_val, val, n * sizeof(struct wprobe_value));
563 spin_unlock_irqrestore(&dev->lock, flags);
564
565 if (err)
566 goto done;
567 }
568
569 if (wprobe_fam.attrbuf[WPROBE_ATTR_FLAGS])
570 vflags |= nla_get_u32(wprobe_fam.attrbuf[WPROBE_ATTR_FLAGS]);
571
572 if (wprobe_fam.attrbuf[WPROBE_ATTR_MAC])
573 vflags |= WPROBE_F_LINK;
574
575 cb->args[0] = (long) dev;
576 cb->args[1] = (long) l;
577 cb->args[2] = vflags;
578 cb->args[3] = 0;
579 } else {
580 /* when pulling pointers from the callback, validate them
581 * against the list using rcu to make sure that we won't
582 * dereference pointers to free'd memory after the last
583 * grace period */
584 err = -ENOENT;
585 if (!wprobe_check_ptr(&wprobe_if, &dev->list))
586 goto done;
587
588 if (l && !wprobe_check_ptr(&dev->links, &l->list))
589 goto done;
590 }
591
592 if (vflags & WPROBE_F_LINK) {
593 item = dev->link_items;
594 n = dev->n_link_items;
595 } else {
596 item = dev->global_items;
597 n = dev->n_global_items;
598 }
599
600 err = 0;
601 switch(cmd) {
602 case WPROBE_CMD_GET_INFO:
603 while (i < n) {
604 if (!wprobe_send_item_value(skb, cb, dev, l, item, i, vflags))
605 break;
606 i++;
607 }
608 break;
609 case WPROBE_CMD_GET_LIST:
610 while (i < n) {
611 if (!wprobe_send_item_info(skb, cb, dev, item, i))
612 break;
613 i++;
614 }
615 break;
616 default:
617 err = -EINVAL;
618 goto done;
619 }
620 cb->args[3] = i;
621 err = skb->len;
622
623 done:
624 rcu_read_unlock();
625 return err;
626 }
627 #undef WPROBE_F_LINK
628
629 static int
630 wprobe_update_auto_measurement(struct wprobe_iface *dev, u32 interval)
631 {
632 if (interval && (interval < WPROBE_MIN_INTERVAL))
633 return -EINVAL;
634
635 if (!interval && dev->measure_interval)
636 del_timer_sync(&dev->measure_timer);
637
638 dev->measure_interval = interval;
639 if (!interval)
640 return 0;
641
642 /* kick of a new measurement immediately */
643 mod_timer(&dev->measure_timer, jiffies + 1);
644
645 return 0;
646 }
647
648 static int
649 wprobe_measure(struct sk_buff *skb, struct genl_info *info)
650 {
651 struct wprobe_iface *dev;
652 struct wprobe_link *l = NULL;
653 int err = -ENOENT;
654
655 rcu_read_lock();
656 dev = wprobe_get_dev(info->attrs[WPROBE_ATTR_INTERFACE]);
657 if (!dev)
658 goto done;
659
660 if (info->attrs[WPROBE_ATTR_MAC]) {
661 l = wprobe_find_link(dev, nla_data(wprobe_fam.attrbuf[WPROBE_ATTR_MAC]));
662 if (!l)
663 goto done;
664 }
665
666 err = wprobe_sync_data(dev, l, false);
667
668 done:
669 rcu_read_unlock();
670 return err;
671 }
672
673 static int
674 wprobe_set_config(struct sk_buff *skb, struct genl_info *info)
675 {
676 struct wprobe_iface *dev;
677 unsigned long flags;
678 int err = -ENOENT;
679 u32 scale_min, scale_max;
680 u32 scale_m, scale_d;
681
682 rcu_read_lock();
683 dev = wprobe_get_dev(info->attrs[WPROBE_ATTR_INTERFACE]);
684 if (!dev)
685 goto done_unlocked;
686
687 err = -EINVAL;
688 spin_lock_irqsave(&dev->lock, flags);
689 if (info->attrs[WPROBE_ATTR_MAC]) {
690 /* not supported yet */
691 goto done;
692 }
693
694 if (info->attrs[WPROBE_ATTR_SAMPLES_MIN] ||
695 info->attrs[WPROBE_ATTR_SAMPLES_MAX]) {
696 if (info->attrs[WPROBE_ATTR_SAMPLES_MIN])
697 scale_min = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_MIN]);
698 else
699 scale_min = dev->scale_min;
700
701 if (info->attrs[WPROBE_ATTR_SAMPLES_MAX])
702 scale_max = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_MAX]);
703 else
704 scale_max = dev->scale_max;
705
706 if ((!scale_min && !scale_max) ||
707 (scale_min && scale_max && (scale_min < scale_max))) {
708 dev->scale_min = scale_min;
709 dev->scale_max = scale_max;
710 } else {
711 goto done;
712 }
713 }
714
715 if (info->attrs[WPROBE_ATTR_SAMPLES_SCALE_M] &&
716 info->attrs[WPROBE_ATTR_SAMPLES_SCALE_D]) {
717
718 scale_m = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_SCALE_M]);
719 scale_d = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_SCALE_D]);
720
721 if (!scale_d || (scale_m > scale_d))
722 goto done;
723
724 dev->scale_m = scale_m;
725 dev->scale_d = scale_d;
726 }
727
728 err = 0;
729 if (info->attrs[WPROBE_ATTR_INTERVAL]) {
730 /* change of measurement interval requested */
731 err = wprobe_update_auto_measurement(dev,
732 (u32) nla_get_u64(info->attrs[WPROBE_ATTR_INTERVAL]));
733 }
734
735 done:
736 spin_unlock_irqrestore(&dev->lock, flags);
737 done_unlocked:
738 rcu_read_unlock();
739 return err;
740 }
741
742 static struct genl_ops wprobe_ops[] = {
743 {
744 .cmd = WPROBE_CMD_GET_INFO,
745 .dumpit = wprobe_dump_info,
746 .policy = wprobe_policy,
747 },
748 {
749 .cmd = WPROBE_CMD_GET_LIST,
750 .dumpit = wprobe_dump_info,
751 .policy = wprobe_policy,
752 },
753 {
754 .cmd = WPROBE_CMD_MEASURE,
755 .doit = wprobe_measure,
756 .policy = wprobe_policy,
757 },
758 {
759 .cmd = WPROBE_CMD_GET_LINKS,
760 .dumpit = wprobe_dump_links,
761 .policy = wprobe_policy,
762 },
763 {
764 .cmd = WPROBE_CMD_CONFIG,
765 .doit = wprobe_set_config,
766 },
767 };
768
769 static void __exit
770 wprobe_exit(void)
771 {
772 BUG_ON(!list_empty(&wprobe_if));
773 genl_unregister_family(&wprobe_fam);
774 }
775
776
777 static int __init
778 wprobe_init(void)
779 {
780 int i, err;
781
782 spin_lock_init(&wprobe_lock);
783 INIT_LIST_HEAD(&wprobe_if);
784
785 err = genl_register_family(&wprobe_fam);
786 if (err)
787 return err;
788
789 for (i = 0; i < ARRAY_SIZE(wprobe_ops); i++) {
790 err = genl_register_ops(&wprobe_fam, &wprobe_ops[i]);
791 if (err)
792 goto error;
793 }
794
795 return 0;
796
797 error:
798 genl_unregister_family(&wprobe_fam);
799 return err;
800 }
801
802 module_init(wprobe_init);
803 module_exit(wprobe_exit);
804 MODULE_LICENSE("GPL");
805