Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/atm/proc.c
15109 views
1
/* net/atm/proc.c - ATM /proc interface
2
*
3
* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA
4
*
5
* seq_file api usage by [email protected]
6
*
7
* Evaluating the efficiency of the whole thing if left as an exercise to
8
* the reader.
9
*/
10
11
#include <linux/module.h> /* for EXPORT_SYMBOL */
12
#include <linux/string.h>
13
#include <linux/types.h>
14
#include <linux/mm.h>
15
#include <linux/fs.h>
16
#include <linux/stat.h>
17
#include <linux/proc_fs.h>
18
#include <linux/seq_file.h>
19
#include <linux/errno.h>
20
#include <linux/atm.h>
21
#include <linux/atmdev.h>
22
#include <linux/netdevice.h>
23
#include <linux/atmclip.h>
24
#include <linux/init.h> /* for __init */
25
#include <linux/slab.h>
26
#include <net/net_namespace.h>
27
#include <net/atmclip.h>
28
#include <linux/uaccess.h>
29
#include <linux/param.h> /* for HZ */
30
#include <asm/atomic.h>
31
#include "resources.h"
32
#include "common.h" /* atm_proc_init prototype */
33
#include "signaling.h" /* to get sigd - ugly too */
34
35
static ssize_t proc_dev_atm_read(struct file *file, char __user *buf,
36
size_t count, loff_t *pos);
37
38
static const struct file_operations proc_atm_dev_ops = {
39
.owner = THIS_MODULE,
40
.read = proc_dev_atm_read,
41
.llseek = noop_llseek,
42
};
43
44
static void add_stats(struct seq_file *seq, const char *aal,
45
const struct k_atm_aal_stats *stats)
46
{
47
seq_printf(seq, "%s ( %d %d %d %d %d )", aal,
48
atomic_read(&stats->tx), atomic_read(&stats->tx_err),
49
atomic_read(&stats->rx), atomic_read(&stats->rx_err),
50
atomic_read(&stats->rx_drop));
51
}
52
53
static void atm_dev_info(struct seq_file *seq, const struct atm_dev *dev)
54
{
55
int i;
56
57
seq_printf(seq, "%3d %-8s", dev->number, dev->type);
58
for (i = 0; i < ESI_LEN; i++)
59
seq_printf(seq, "%02x", dev->esi[i]);
60
seq_puts(seq, " ");
61
add_stats(seq, "0", &dev->stats.aal0);
62
seq_puts(seq, " ");
63
add_stats(seq, "5", &dev->stats.aal5);
64
seq_printf(seq, "\t[%d]", atomic_read(&dev->refcnt));
65
seq_putc(seq, '\n');
66
}
67
68
struct vcc_state {
69
int bucket;
70
struct sock *sk;
71
int family;
72
};
73
74
static inline int compare_family(struct sock *sk, int family)
75
{
76
return !family || (sk->sk_family == family);
77
}
78
79
static int __vcc_walk(struct sock **sock, int family, int *bucket, loff_t l)
80
{
81
struct sock *sk = *sock;
82
83
if (sk == SEQ_START_TOKEN) {
84
for (*bucket = 0; *bucket < VCC_HTABLE_SIZE; ++*bucket) {
85
struct hlist_head *head = &vcc_hash[*bucket];
86
87
sk = hlist_empty(head) ? NULL : __sk_head(head);
88
if (sk)
89
break;
90
}
91
l--;
92
}
93
try_again:
94
for (; sk; sk = sk_next(sk)) {
95
l -= compare_family(sk, family);
96
if (l < 0)
97
goto out;
98
}
99
if (!sk && ++*bucket < VCC_HTABLE_SIZE) {
100
sk = sk_head(&vcc_hash[*bucket]);
101
goto try_again;
102
}
103
sk = SEQ_START_TOKEN;
104
out:
105
*sock = sk;
106
return (l < 0);
107
}
108
109
static inline void *vcc_walk(struct vcc_state *state, loff_t l)
110
{
111
return __vcc_walk(&state->sk, state->family, &state->bucket, l) ?
112
state : NULL;
113
}
114
115
static int __vcc_seq_open(struct inode *inode, struct file *file,
116
int family, const struct seq_operations *ops)
117
{
118
struct vcc_state *state;
119
120
state = __seq_open_private(file, ops, sizeof(*state));
121
if (state == NULL)
122
return -ENOMEM;
123
124
state->family = family;
125
return 0;
126
}
127
128
static void *vcc_seq_start(struct seq_file *seq, loff_t *pos)
129
__acquires(vcc_sklist_lock)
130
{
131
struct vcc_state *state = seq->private;
132
loff_t left = *pos;
133
134
read_lock(&vcc_sklist_lock);
135
state->sk = SEQ_START_TOKEN;
136
return left ? vcc_walk(state, left) : SEQ_START_TOKEN;
137
}
138
139
static void vcc_seq_stop(struct seq_file *seq, void *v)
140
__releases(vcc_sklist_lock)
141
{
142
read_unlock(&vcc_sklist_lock);
143
}
144
145
static void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
146
{
147
struct vcc_state *state = seq->private;
148
149
v = vcc_walk(state, 1);
150
*pos += !!PTR_ERR(v);
151
return v;
152
}
153
154
static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc)
155
{
156
static const char *const class_name[] = {
157
"off", "UBR", "CBR", "VBR", "ABR"};
158
static const char *const aal_name[] = {
159
"---", "1", "2", "3/4", /* 0- 3 */
160
"???", "5", "???", "???", /* 4- 7 */
161
"???", "???", "???", "???", /* 8-11 */
162
"???", "0", "???", "???"}; /* 12-15 */
163
164
seq_printf(seq, "%3d %3d %5d %-3s %7d %-5s %7d %-6s",
165
vcc->dev->number, vcc->vpi, vcc->vci,
166
vcc->qos.aal >= ARRAY_SIZE(aal_name) ? "err" :
167
aal_name[vcc->qos.aal], vcc->qos.rxtp.min_pcr,
168
class_name[vcc->qos.rxtp.traffic_class],
169
vcc->qos.txtp.min_pcr,
170
class_name[vcc->qos.txtp.traffic_class]);
171
if (test_bit(ATM_VF_IS_CLIP, &vcc->flags)) {
172
struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
173
struct net_device *dev;
174
175
dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL;
176
seq_printf(seq, "CLIP, Itf:%s, Encap:",
177
dev ? dev->name : "none?");
178
seq_printf(seq, "%s", clip_vcc->encap ? "LLC/SNAP" : "None");
179
}
180
seq_putc(seq, '\n');
181
}
182
183
static const char *vcc_state(struct atm_vcc *vcc)
184
{
185
static const char *const map[] = { ATM_VS2TXT_MAP };
186
187
return map[ATM_VF2VS(vcc->flags)];
188
}
189
190
static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc)
191
{
192
struct sock *sk = sk_atm(vcc);
193
194
seq_printf(seq, "%pK ", vcc);
195
if (!vcc->dev)
196
seq_printf(seq, "Unassigned ");
197
else
198
seq_printf(seq, "%3d %3d %5d ", vcc->dev->number, vcc->vpi,
199
vcc->vci);
200
switch (sk->sk_family) {
201
case AF_ATMPVC:
202
seq_printf(seq, "PVC");
203
break;
204
case AF_ATMSVC:
205
seq_printf(seq, "SVC");
206
break;
207
default:
208
seq_printf(seq, "%3d", sk->sk_family);
209
}
210
seq_printf(seq, " %04lx %5d %7d/%7d %7d/%7d [%d]\n",
211
vcc->flags, sk->sk_err,
212
sk_wmem_alloc_get(sk), sk->sk_sndbuf,
213
sk_rmem_alloc_get(sk), sk->sk_rcvbuf,
214
atomic_read(&sk->sk_refcnt));
215
}
216
217
static void svc_info(struct seq_file *seq, struct atm_vcc *vcc)
218
{
219
if (!vcc->dev)
220
seq_printf(seq, sizeof(void *) == 4 ?
221
"N/A@%pK%10s" : "N/A@%pK%2s", vcc, "");
222
else
223
seq_printf(seq, "%3d %3d %5d ",
224
vcc->dev->number, vcc->vpi, vcc->vci);
225
seq_printf(seq, "%-10s ", vcc_state(vcc));
226
seq_printf(seq, "%s%s", vcc->remote.sas_addr.pub,
227
*vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
228
if (*vcc->remote.sas_addr.prv) {
229
int i;
230
231
for (i = 0; i < ATM_ESA_LEN; i++)
232
seq_printf(seq, "%02x", vcc->remote.sas_addr.prv[i]);
233
}
234
seq_putc(seq, '\n');
235
}
236
237
static int atm_dev_seq_show(struct seq_file *seq, void *v)
238
{
239
static char atm_dev_banner[] =
240
"Itf Type ESI/\"MAC\"addr "
241
"AAL(TX,err,RX,err,drop) ... [refcnt]\n";
242
243
if (v == &atm_devs)
244
seq_puts(seq, atm_dev_banner);
245
else {
246
struct atm_dev *dev = list_entry(v, struct atm_dev, dev_list);
247
248
atm_dev_info(seq, dev);
249
}
250
return 0;
251
}
252
253
static const struct seq_operations atm_dev_seq_ops = {
254
.start = atm_dev_seq_start,
255
.next = atm_dev_seq_next,
256
.stop = atm_dev_seq_stop,
257
.show = atm_dev_seq_show,
258
};
259
260
static int atm_dev_seq_open(struct inode *inode, struct file *file)
261
{
262
return seq_open(file, &atm_dev_seq_ops);
263
}
264
265
static const struct file_operations devices_seq_fops = {
266
.open = atm_dev_seq_open,
267
.read = seq_read,
268
.llseek = seq_lseek,
269
.release = seq_release,
270
};
271
272
static int pvc_seq_show(struct seq_file *seq, void *v)
273
{
274
static char atm_pvc_banner[] =
275
"Itf VPI VCI AAL RX(PCR,Class) TX(PCR,Class)\n";
276
277
if (v == SEQ_START_TOKEN)
278
seq_puts(seq, atm_pvc_banner);
279
else {
280
struct vcc_state *state = seq->private;
281
struct atm_vcc *vcc = atm_sk(state->sk);
282
283
pvc_info(seq, vcc);
284
}
285
return 0;
286
}
287
288
static const struct seq_operations pvc_seq_ops = {
289
.start = vcc_seq_start,
290
.next = vcc_seq_next,
291
.stop = vcc_seq_stop,
292
.show = pvc_seq_show,
293
};
294
295
static int pvc_seq_open(struct inode *inode, struct file *file)
296
{
297
return __vcc_seq_open(inode, file, PF_ATMPVC, &pvc_seq_ops);
298
}
299
300
static const struct file_operations pvc_seq_fops = {
301
.open = pvc_seq_open,
302
.read = seq_read,
303
.llseek = seq_lseek,
304
.release = seq_release_private,
305
};
306
307
static int vcc_seq_show(struct seq_file *seq, void *v)
308
{
309
if (v == SEQ_START_TOKEN) {
310
seq_printf(seq, sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
311
"Address ", "Itf VPI VCI Fam Flags Reply "
312
"Send buffer Recv buffer [refcnt]\n");
313
} else {
314
struct vcc_state *state = seq->private;
315
struct atm_vcc *vcc = atm_sk(state->sk);
316
317
vcc_info(seq, vcc);
318
}
319
return 0;
320
}
321
322
static const struct seq_operations vcc_seq_ops = {
323
.start = vcc_seq_start,
324
.next = vcc_seq_next,
325
.stop = vcc_seq_stop,
326
.show = vcc_seq_show,
327
};
328
329
static int vcc_seq_open(struct inode *inode, struct file *file)
330
{
331
return __vcc_seq_open(inode, file, 0, &vcc_seq_ops);
332
}
333
334
static const struct file_operations vcc_seq_fops = {
335
.open = vcc_seq_open,
336
.read = seq_read,
337
.llseek = seq_lseek,
338
.release = seq_release_private,
339
};
340
341
static int svc_seq_show(struct seq_file *seq, void *v)
342
{
343
static const char atm_svc_banner[] =
344
"Itf VPI VCI State Remote\n";
345
346
if (v == SEQ_START_TOKEN)
347
seq_puts(seq, atm_svc_banner);
348
else {
349
struct vcc_state *state = seq->private;
350
struct atm_vcc *vcc = atm_sk(state->sk);
351
352
svc_info(seq, vcc);
353
}
354
return 0;
355
}
356
357
static const struct seq_operations svc_seq_ops = {
358
.start = vcc_seq_start,
359
.next = vcc_seq_next,
360
.stop = vcc_seq_stop,
361
.show = svc_seq_show,
362
};
363
364
static int svc_seq_open(struct inode *inode, struct file *file)
365
{
366
return __vcc_seq_open(inode, file, PF_ATMSVC, &svc_seq_ops);
367
}
368
369
static const struct file_operations svc_seq_fops = {
370
.open = svc_seq_open,
371
.read = seq_read,
372
.llseek = seq_lseek,
373
.release = seq_release_private,
374
};
375
376
static ssize_t proc_dev_atm_read(struct file *file, char __user *buf,
377
size_t count, loff_t *pos)
378
{
379
struct atm_dev *dev;
380
unsigned long page;
381
int length;
382
383
if (count == 0)
384
return 0;
385
page = get_zeroed_page(GFP_KERNEL);
386
if (!page)
387
return -ENOMEM;
388
dev = PDE(file->f_path.dentry->d_inode)->data;
389
if (!dev->ops->proc_read)
390
length = -EINVAL;
391
else {
392
length = dev->ops->proc_read(dev, pos, (char *)page);
393
if (length > count)
394
length = -EINVAL;
395
}
396
if (length >= 0) {
397
if (copy_to_user(buf, (char *)page, length))
398
length = -EFAULT;
399
(*pos)++;
400
}
401
free_page(page);
402
return length;
403
}
404
405
struct proc_dir_entry *atm_proc_root;
406
EXPORT_SYMBOL(atm_proc_root);
407
408
409
int atm_proc_dev_register(struct atm_dev *dev)
410
{
411
int error;
412
413
/* No proc info */
414
if (!dev->ops->proc_read)
415
return 0;
416
417
error = -ENOMEM;
418
dev->proc_name = kasprintf(GFP_KERNEL, "%s:%d", dev->type, dev->number);
419
if (!dev->proc_name)
420
goto err_out;
421
422
dev->proc_entry = proc_create_data(dev->proc_name, 0, atm_proc_root,
423
&proc_atm_dev_ops, dev);
424
if (!dev->proc_entry)
425
goto err_free_name;
426
return 0;
427
428
err_free_name:
429
kfree(dev->proc_name);
430
err_out:
431
return error;
432
}
433
434
void atm_proc_dev_deregister(struct atm_dev *dev)
435
{
436
if (!dev->ops->proc_read)
437
return;
438
439
remove_proc_entry(dev->proc_name, atm_proc_root);
440
kfree(dev->proc_name);
441
}
442
443
static struct atm_proc_entry {
444
char *name;
445
const struct file_operations *proc_fops;
446
struct proc_dir_entry *dirent;
447
} atm_proc_ents[] = {
448
{ .name = "devices", .proc_fops = &devices_seq_fops },
449
{ .name = "pvc", .proc_fops = &pvc_seq_fops },
450
{ .name = "svc", .proc_fops = &svc_seq_fops },
451
{ .name = "vc", .proc_fops = &vcc_seq_fops },
452
{ .name = NULL, .proc_fops = NULL }
453
};
454
455
static void atm_proc_dirs_remove(void)
456
{
457
static struct atm_proc_entry *e;
458
459
for (e = atm_proc_ents; e->name; e++) {
460
if (e->dirent)
461
remove_proc_entry(e->name, atm_proc_root);
462
}
463
proc_net_remove(&init_net, "atm");
464
}
465
466
int __init atm_proc_init(void)
467
{
468
static struct atm_proc_entry *e;
469
int ret;
470
471
atm_proc_root = proc_net_mkdir(&init_net, "atm", init_net.proc_net);
472
if (!atm_proc_root)
473
goto err_out;
474
for (e = atm_proc_ents; e->name; e++) {
475
struct proc_dir_entry *dirent;
476
477
dirent = proc_create(e->name, S_IRUGO,
478
atm_proc_root, e->proc_fops);
479
if (!dirent)
480
goto err_out_remove;
481
e->dirent = dirent;
482
}
483
ret = 0;
484
out:
485
return ret;
486
487
err_out_remove:
488
atm_proc_dirs_remove();
489
err_out:
490
ret = -ENOMEM;
491
goto out;
492
}
493
494
void atm_proc_exit(void)
495
{
496
atm_proc_dirs_remove();
497
}
498
499