ethernetgmii: update for Linux with MMU
[ana-net.git] / src / fb_counter.c
blobff421f6bd05a667a0f5f5e4b81e9be2d62973d1a
1 /*
2 * Lightweight Autonomic Network Architecture
4 * Packet counter module.
6 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,
7 * Swiss federal institute of technology (ETH Zurich)
8 * Subject to the GPL.
9 */
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/spinlock.h>
14 #include <linux/notifier.h>
15 #include <linux/rcupdate.h>
16 #include <linux/seqlock.h>
17 #include <linux/percpu.h>
18 #include <linux/prefetch.h>
19 #include <linux/u64_stats_sync.h>
20 #include <linux/proc_fs.h>
21 #include <linux/seq_file.h>
23 #include "xt_fblock.h"
24 #include "xt_builder.h"
25 #include "xt_idp.h"
26 #include "xt_skb.h"
27 #include "xt_engine.h"
28 #include "xt_builder.h"
30 struct fb_counter_priv {
31 idp_t port[2];
32 seqlock_t lock;
33 u64 packets;
34 u64 bytes;
35 struct u64_stats_sync syncp;
38 static int fb_counter_netrx(const struct fblock * const fb,
39 struct sk_buff * const skb,
40 enum path_type * const dir)
42 int drop = 0;
43 unsigned int seq;
44 struct fb_counter_priv __percpu *fb_priv_cpu;
46 fb_priv_cpu = this_cpu_ptr(rcu_dereference_raw(fb->private_data));
47 prefetchw(skb->cb);
48 do {
49 seq = read_seqbegin(&fb_priv_cpu->lock);
50 write_next_idp_to_skb(skb, fb->idp, fb_priv_cpu->port[*dir]);
51 if (fb_priv_cpu->port[*dir] == IDP_UNKNOWN)
52 drop = 1;
53 } while (read_seqretry(&fb_priv_cpu->lock, seq));
55 u64_stats_update_begin(&fb_priv_cpu->syncp);
56 fb_priv_cpu->packets++;
57 fb_priv_cpu->bytes += skb->len;
58 u64_stats_update_end(&fb_priv_cpu->syncp);
60 if (drop) {
61 kfree_skb(skb);
62 return PPE_DROPPED;
64 return PPE_SUCCESS;
67 static int fb_counter_event(struct notifier_block *self, unsigned long cmd,
68 void *args)
70 int ret = NOTIFY_OK;
71 unsigned int cpu;
72 struct fblock *fb;
73 struct fb_counter_priv __percpu *fb_priv;
75 rcu_read_lock();
76 fb = rcu_dereference_raw(container_of(self, struct fblock_notifier, nb)->self);
77 fb_priv = (struct fb_counter_priv __percpu *) rcu_dereference_raw(fb->private_data);
78 rcu_read_unlock();
80 switch (cmd) {
81 case FBLOCK_BIND_IDP: {
82 int bound = 0;
83 struct fblock_bind_msg *msg = args;
84 get_online_cpus();
85 for_each_online_cpu(cpu) {
86 struct fb_counter_priv *fb_priv_cpu;
87 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
88 if (fb_priv_cpu->port[msg->dir] == IDP_UNKNOWN) {
89 write_seqlock(&fb_priv_cpu->lock);
90 fb_priv_cpu->port[msg->dir] = msg->idp;
91 write_sequnlock(&fb_priv_cpu->lock);
92 bound = 1;
93 } else {
94 ret = NOTIFY_BAD;
95 break;
98 put_online_cpus();
99 if (bound)
100 printk(KERN_INFO "[%s::%s] port %s bound to IDP%u\n",
101 fb->name, fb->factory->type,
102 path_names[msg->dir], msg->idp);
103 } break;
104 case FBLOCK_UNBIND_IDP: {
105 int unbound = 0;
106 struct fblock_bind_msg *msg = args;
107 get_online_cpus();
108 for_each_online_cpu(cpu) {
109 struct fb_counter_priv *fb_priv_cpu;
110 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
111 if (fb_priv_cpu->port[msg->dir] == msg->idp) {
112 write_seqlock(&fb_priv_cpu->lock);
113 fb_priv_cpu->port[msg->dir] = IDP_UNKNOWN;
114 write_sequnlock(&fb_priv_cpu->lock);
115 unbound = 1;
116 } else {
117 ret = NOTIFY_BAD;
118 break;
121 put_online_cpus();
122 if (unbound)
123 printk(KERN_INFO "[%s::%s] port %s unbound\n",
124 fb->name, fb->factory->type,
125 path_names[msg->dir]);
126 } break;
127 case FBLOCK_SET_OPT: {
128 struct fblock_opt_msg *msg = args;
129 printk("Set option %s to %s!\n", msg->key, msg->val);
130 } break;
131 default:
132 break;
135 return ret;
138 static int fb_counter_proc_show(struct seq_file *m, void *v)
140 u64 pkts_sum = 0, bytes_sum = 0;
141 unsigned int cpu;
142 char sline[256];
143 struct fblock *fb = (struct fblock *) m->private;
144 struct fb_counter_priv __percpu *fb_priv;
146 rcu_read_lock();
147 fb_priv = (struct fb_counter_priv __percpu *) rcu_dereference_raw(fb->private_data);
148 rcu_read_unlock();
150 get_online_cpus();
151 for_each_online_cpu(cpu) {
152 unsigned int start;
153 struct fb_counter_priv *fb_priv_cpu;
154 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
155 do {
156 start = u64_stats_fetch_begin(&fb_priv_cpu->syncp);
157 pkts_sum += fb_priv_cpu->packets;
158 bytes_sum += fb_priv_cpu->bytes;
159 } while (u64_stats_fetch_retry(&fb_priv_cpu->syncp, start));
161 put_online_cpus();
163 memset(sline, 0, sizeof(sline));
164 snprintf(sline, sizeof(sline), "%llu %llu\n", pkts_sum, bytes_sum);
165 seq_puts(m, sline);
167 return 0;
170 static int fb_counter_proc_open(struct inode *inode, struct file *file)
172 return single_open(file, fb_counter_proc_show, PDE(inode)->data);
175 static const struct file_operations fb_counter_proc_fops = {
176 .owner = THIS_MODULE,
177 .open = fb_counter_proc_open,
178 .read = seq_read,
179 .llseek = seq_lseek,
180 .release = single_release,
183 static struct fblock *fb_counter_ctor(char *name)
185 int ret = 0;
186 unsigned int cpu;
187 struct fblock *fb;
188 struct fb_counter_priv __percpu *fb_priv;
189 struct proc_dir_entry *fb_proc;
191 fb = alloc_fblock(GFP_ATOMIC);
192 if (!fb)
193 return NULL;
195 fb_priv = alloc_percpu(struct fb_counter_priv);
196 if (!fb_priv)
197 goto err;
199 get_online_cpus();
200 for_each_online_cpu(cpu) {
201 struct fb_counter_priv *fb_priv_cpu;
202 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
203 seqlock_init(&fb_priv_cpu->lock);
204 fb_priv_cpu->port[0] = IDP_UNKNOWN;
205 fb_priv_cpu->port[1] = IDP_UNKNOWN;
206 fb_priv_cpu->packets = 0;
207 fb_priv_cpu->bytes = 0;
209 put_online_cpus();
211 ret = init_fblock(fb, name, fb_priv);
212 if (ret)
213 goto err2;
214 fb->netfb_rx = fb_counter_netrx;
215 fb->event_rx = fb_counter_event;
217 fb_proc = proc_create_data(fb->name, 0444, fblock_proc_dir,
218 &fb_counter_proc_fops,
219 (void *)(long) fb);
220 if (!fb_proc)
221 goto err3;
223 ret = register_fblock_namespace(fb);
224 if (ret)
225 goto err4;
227 __module_get(THIS_MODULE);
228 return fb;
229 err4:
230 remove_proc_entry(fb->name, fblock_proc_dir);
231 err3:
232 cleanup_fblock_ctor(fb);
233 err2:
234 free_percpu(fb_priv);
235 err:
236 kfree_fblock(fb);
237 return NULL;
240 static void fb_counter_dtor(struct fblock *fb)
242 free_percpu(rcu_dereference_raw(fb->private_data));
243 remove_proc_entry(fb->name, fblock_proc_dir);
244 module_put(THIS_MODULE);
247 static struct fblock_factory fb_counter_factory = {
248 .type = "counter",
249 .mode = MODE_DUAL,
250 .ctor = fb_counter_ctor,
251 .dtor = fb_counter_dtor,
252 .owner = THIS_MODULE,
255 static int __init init_fb_counter_module(void)
257 return register_fblock_type(&fb_counter_factory);
260 static void __exit cleanup_fb_counter_module(void)
262 synchronize_rcu();
263 unregister_fblock_type(&fb_counter_factory);
266 module_init(init_fb_counter_module);
267 module_exit(cleanup_fb_counter_module);
269 MODULE_LICENSE("GPL");
270 MODULE_AUTHOR("Daniel Borkmann <dborkma@tik.ee.ethz.ch>");
271 MODULE_DESCRIPTION("LANA packet counter module");