Loading...
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); |