summaryrefslogtreecommitdiffstats
path: root/net/atm/atm_misc.c
blob: c8e19d2bcc082bd87c2cd0b4e67f3e5cfb90d765 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* net/atm/atm_misc.c - Various functions for use by ATM drivers */

/* Written 1995-2000 by Werner Almesberger, EPFL ICA */


#include <linux/module.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/skbuff.h>
#include <linux/sonet.h>
#include <linux/bitops.h>
#include <asm/atomic.h>
#include <asm/errno.h>


int atm_charge(struct atm_vcc *vcc,int truesize)
{
	atm_force_charge(vcc,truesize);
	if (atomic_read(&vcc->rx_inuse) <= vcc->sk->rcvbuf) return 1;
	atm_return(vcc,truesize);
	atomic_inc(&vcc->stats->rx_drop);
	return 0;
}


struct sk_buff *atm_alloc_charge(struct atm_vcc *vcc,int pdu_size,
    int gfp_flags)
{
	int guess = atm_guess_pdu2truesize(pdu_size);

	atm_force_charge(vcc,guess);
	if (atomic_read(&vcc->rx_inuse) <= vcc->sk->rcvbuf) {
		struct sk_buff *skb = alloc_skb(pdu_size,gfp_flags);

		if (skb) {
			atomic_add(skb->truesize-guess,&vcc->rx_inuse);
			return skb;
		}
	}
	atm_return(vcc,guess);
	atomic_inc(&vcc->stats->rx_drop);
	return NULL;
}


static int check_ci(struct atm_vcc *vcc,short vpi,int vci)
{
	struct atm_vcc *walk;

	for (walk = vcc->dev->vccs; walk; walk = walk->next)
		if (test_bit(ATM_VF_ADDR,&walk->flags) && walk->vpi == vpi &&
		    walk->vci == vci && ((walk->qos.txtp.traffic_class !=
		    ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) ||
		    (walk->qos.rxtp.traffic_class != ATM_NONE &&
		    vcc->qos.rxtp.traffic_class != ATM_NONE)))
			return -EADDRINUSE;
		/* allow VCCs with same VPI/VCI iff they don't collide on
		   TX/RX (but we may refuse such sharing for other reasons,
		   e.g. if protocol requires to have both channels) */
	return 0;
}


int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci)
{
	static short p = 0; /* poor man's per-device cache */
	static int c = 0;
	short old_p;
	int old_c;

	if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY)
		return check_ci(vcc,*vpi,*vci);
	/* last scan may have left values out of bounds for current device */
	if (*vpi != ATM_VPI_ANY) p = *vpi;
	else if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
	if (*vci != ATM_VCI_ANY) c = *vci;
	else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits)
			c = ATM_NOT_RSV_VCI;
	old_p = p;
	old_c = c;
	do {
		if (!check_ci(vcc,p,c)) {
			*vpi = p;
			*vci = c;
			return 0;
		}
		if (*vci == ATM_VCI_ANY) {
			c++;
			if (c >= 1 << vcc->dev->ci_range.vci_bits)
				c = ATM_NOT_RSV_VCI;
		}
		if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) &&
		    *vpi == ATM_VPI_ANY) {
			p++;
			if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
		}
	}
	while (old_p != p || old_c != c);
	return -EADDRINUSE;
}


/*
 * atm_pcr_goal returns the positive PCR if it should be rounded up, the
 * negative PCR if it should be rounded down, and zero if the maximum available
 * bandwidth should be used.
 *
 * The rules are as follows (* = maximum, - = absent (0), x = value "x",
 * (x+ = x or next value above x, x- = x or next value below):
 *
 *	min max pcr	result		min max pcr	result
 *	-   -   -	* (UBR only)	x   -   -	x+
 *	-   -   *	*		x   -   *	*
 *	-   -   z	z-		x   -   z	z-
 *	-   *   -	*		x   *   -	x+
 *	-   *   *	*		x   *   *	*
 *	-   *   z	z-		x   *   z	z-
 *	-   y   -	y-		x   y   -	x+
 *	-   y   *	y-		x   y   *	y-
 *	-   y   z	z-		x   y   z	z-
 *
 * All non-error cases can be converted with the following simple set of rules:
 *
 *   if pcr == z then z-
 *   else if min == x && pcr == - then x+
 *     else if max == y then y-
 *	 else *
 */


int atm_pcr_goal(struct atm_trafprm *tp)
{
	if (tp->pcr && tp->pcr != ATM_MAX_PCR) return -tp->pcr;
	if (tp->min_pcr && !tp->pcr) return tp->min_pcr;
	if (tp->max_pcr != ATM_MAX_PCR) return -tp->max_pcr;
	return 0;
}


void sonet_copy_stats(struct k_sonet_stats *from,struct sonet_stats *to)
{
#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
	__SONET_ITEMS
#undef __HANDLE_ITEM
}


void sonet_subtract_stats(struct k_sonet_stats *from,struct sonet_stats *to)
{
#define __HANDLE_ITEM(i) atomic_sub(to->i,&from->i)
	__SONET_ITEMS
#undef __HANDLE_ITEM
}


EXPORT_SYMBOL(atm_charge);
EXPORT_SYMBOL(atm_alloc_charge);
EXPORT_SYMBOL(atm_find_ci);
EXPORT_SYMBOL(atm_pcr_goal);
EXPORT_SYMBOL(sonet_copy_stats);
EXPORT_SYMBOL(sonet_subtract_stats);