1 2 #ifdef CONFIG_PROC_FS 3 #include <linux/errno.h> 4 #include <linux/kernel.h> 5 #include <linux/string.h> 6 #include <linux/mm.h> 7 #include <linux/module.h> 8 #include <linux/proc_fs.h> 9 #include <linux/time.h> 10 #include <linux/seq_file.h> 11 #include <asm/uaccess.h> 12 #include <linux/atmmpc.h> 13 #include <linux/atm.h> 14 #include "mpc.h" 15 #include "mpoa_caches.h" 16 17 /* 18 * mpoa_proc.c: Implementation MPOA client's proc 19 * file system statistics 20 */ 21 22 #if 1 23 #define dprintk printk /* debug */ 24 #else 25 #define dprintk(format,args...) 26 #endif 27 28 #define STAT_FILE_NAME "mpc" /* Our statistic file's name */ 29 30 extern struct mpoa_client *mpcs; 31 extern struct proc_dir_entry *atm_proc_root; /* from proc.c. */ 32 33 static int proc_mpc_open(struct inode *inode, struct file *file); 34 static ssize_t proc_mpc_write(struct file *file, const char __user *buff, 35 size_t nbytes, loff_t *ppos); 36 37 static int parse_qos(const char *buff); 38 39 /* 40 * Define allowed FILE OPERATIONS 41 */ 42 static const struct file_operations mpc_file_operations = { 43 .owner = THIS_MODULE, 44 .open = proc_mpc_open, 45 .read = seq_read, 46 .llseek = seq_lseek, 47 .write = proc_mpc_write, 48 .release = seq_release, 49 }; 50 51 /* 52 * Returns the state of an ingress cache entry as a string 53 */ 54 static const char *ingress_state_string(int state){ 55 switch(state) { 56 case INGRESS_RESOLVING: 57 return "resolving "; 58 break; 59 case INGRESS_RESOLVED: 60 return "resolved "; 61 break; 62 case INGRESS_INVALID: 63 return "invalid "; 64 break; 65 case INGRESS_REFRESHING: 66 return "refreshing "; 67 break; 68 default: 69 return ""; 70 } 71 } 72 73 /* 74 * Returns the state of an egress cache entry as a string 75 */ 76 static const char *egress_state_string(int state){ 77 switch(state) { 78 case EGRESS_RESOLVED: 79 return "resolved "; 80 break; 81 case EGRESS_PURGE: 82 return "purge "; 83 break; 84 case EGRESS_INVALID: 85 return "invalid "; 86 break; 87 default: 88 return ""; 89 } 90 } 91 92 /* 93 * FIXME: mpcs (and per-mpc lists) have no locking whatsoever. 94 */ 95 96 static void *mpc_start(struct seq_file *m, loff_t *pos) 97 { 98 loff_t l = *pos; 99 struct mpoa_client *mpc; 100 101 if (!l--) 102 return SEQ_START_TOKEN; 103 for (mpc = mpcs; mpc; mpc = mpc->next) 104 if (!l--) 105 return mpc; 106 return NULL; 107 } 108 109 static void *mpc_next(struct seq_file *m, void *v, loff_t *pos) 110 { 111 struct mpoa_client *p = v; 112 (*pos)++; 113 return v == SEQ_START_TOKEN ? mpcs : p->next; 114 } 115 116 static void mpc_stop(struct seq_file *m, void *v) 117 { 118 } 119 120 /* 121 * READING function - called when the /proc/atm/mpoa file is read from. 122 */ 123 static int mpc_show(struct seq_file *m, void *v) 124 { 125 struct mpoa_client *mpc = v; 126 unsigned char *temp; 127 int i; 128 in_cache_entry *in_entry; 129 eg_cache_entry *eg_entry; 130 struct timeval now; 131 unsigned char ip_string[16]; 132 133 if (v == SEQ_START_TOKEN) { 134 atm_mpoa_disp_qos(m); 135 return 0; 136 } 137 138 seq_printf(m, "\nInterface %d:\n\n", mpc->dev_num); 139 seq_printf(m, "Ingress Entries:\nIP address State Holding time Packets fwded VPI VCI\n"); 140 do_gettimeofday(&now); 141 142 for (in_entry = mpc->in_cache; in_entry; in_entry = in_entry->next) { 143 temp = (unsigned char *)&in_entry->ctrl_info.in_dst_ip; 144 sprintf(ip_string,"%d.%d.%d.%d", temp[0], temp[1], temp[2], temp[3]); 145 seq_printf(m, "%-16s%s%-14lu%-12u", 146 ip_string, 147 ingress_state_string(in_entry->entry_state), 148 in_entry->ctrl_info.holding_time-(now.tv_sec-in_entry->tv.tv_sec), 149 in_entry->packets_fwded); 150 if (in_entry->shortcut) 151 seq_printf(m, " %-3d %-3d",in_entry->shortcut->vpi,in_entry->shortcut->vci); 152 seq_printf(m, "\n"); 153 } 154 155 seq_printf(m, "\n"); 156 seq_printf(m, "Egress Entries:\nIngress MPC ATM addr\nCache-id State Holding time Packets recvd Latest IP addr VPI VCI\n"); 157 for (eg_entry = mpc->eg_cache; eg_entry; eg_entry = eg_entry->next) { 158 unsigned char *p = eg_entry->ctrl_info.in_MPC_data_ATM_addr; 159 for(i = 0; i < ATM_ESA_LEN; i++) 160 seq_printf(m, "%02x", p[i]); 161 seq_printf(m, "\n%-16lu%s%-14lu%-15u", 162 (unsigned long)ntohl(eg_entry->ctrl_info.cache_id), 163 egress_state_string(eg_entry->entry_state), 164 (eg_entry->ctrl_info.holding_time-(now.tv_sec-eg_entry->tv.tv_sec)), 165 eg_entry->packets_rcvd); 166 167 /* latest IP address */ 168 temp = (unsigned char *)&eg_entry->latest_ip_addr; 169 sprintf(ip_string, "%d.%d.%d.%d", temp[0], temp[1], temp[2], temp[3]); 170 seq_printf(m, "%-16s", ip_string); 171 172 if (eg_entry->shortcut) 173 seq_printf(m, " %-3d %-3d",eg_entry->shortcut->vpi,eg_entry->shortcut->vci); 174 seq_printf(m, "\n"); 175 } 176 seq_printf(m, "\n"); 177 return 0; 178 } 179 180 static const struct seq_operations mpc_op = { 181 .start = mpc_start, 182 .next = mpc_next, 183 .stop = mpc_stop, 184 .show = mpc_show 185 }; 186 187 static int proc_mpc_open(struct inode *inode, struct file *file) 188 { 189 return seq_open(file, &mpc_op); 190 } 191 192 static ssize_t proc_mpc_write(struct file *file, const char __user *buff, 193 size_t nbytes, loff_t *ppos) 194 { 195 char *page, *p; 196 unsigned len; 197 198 if (nbytes == 0) 199 return 0; 200 201 if (nbytes >= PAGE_SIZE) 202 nbytes = PAGE_SIZE-1; 203 204 page = (char *)__get_free_page(GFP_KERNEL); 205 if (!page) 206 return -ENOMEM; 207 208 for (p = page, len = 0; len < nbytes; p++, len++) { 209 if (get_user(*p, buff++)) { 210 free_page((unsigned long)page); 211 return -EFAULT; 212 } 213 if (*p == '\0' || *p == '\n') 214 break; 215 } 216 217 *p = '\0'; 218 219 if (!parse_qos(page)) 220 printk("mpoa: proc_mpc_write: could not parse '%s'\n", page); 221 222 free_page((unsigned long)page); 223 224 return len; 225 } 226 227 static int parse_qos(const char *buff) 228 { 229 /* possible lines look like this 230 * add 130.230.54.142 tx=max_pcr,max_sdu rx=max_pcr,max_sdu 231 */ 232 unsigned char ip[4]; 233 int tx_pcr, tx_sdu, rx_pcr, rx_sdu; 234 __be32 ipaddr; 235 struct atm_qos qos; 236 237 memset(&qos, 0, sizeof(struct atm_qos)); 238 239 if (sscanf(buff, "del %hhu.%hhu.%hhu.%hhu", 240 ip, ip+1, ip+2, ip+3) == 4) { 241 ipaddr = *(__be32 *)ip; 242 return atm_mpoa_delete_qos(atm_mpoa_search_qos(ipaddr)); 243 } 244 245 if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=tx", 246 ip, ip+1, ip+2, ip+3, &tx_pcr, &tx_sdu) == 6) { 247 rx_pcr = tx_pcr; 248 rx_sdu = tx_sdu; 249 } else if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=%d,%d", 250 ip, ip+1, ip+2, ip+3, &tx_pcr, &tx_sdu, &rx_pcr, &rx_sdu) != 8) 251 return 0; 252 253 ipaddr = *(__be32 *)ip; 254 qos.txtp.traffic_class = ATM_CBR; 255 qos.txtp.max_pcr = tx_pcr; 256 qos.txtp.max_sdu = tx_sdu; 257 qos.rxtp.traffic_class = ATM_CBR; 258 qos.rxtp.max_pcr = rx_pcr; 259 qos.rxtp.max_sdu = rx_sdu; 260 qos.aal = ATM_AAL5; 261 dprintk("mpoa: mpoa_proc.c: parse_qos(): setting qos paramameters to tx=%d,%d rx=%d,%d\n", 262 qos.txtp.max_pcr, 263 qos.txtp.max_sdu, 264 qos.rxtp.max_pcr, 265 qos.rxtp.max_sdu 266 ); 267 268 atm_mpoa_add_qos(ipaddr, &qos); 269 return 1; 270 } 271 272 /* 273 * INITIALIZATION function - called when module is initialized/loaded. 274 */ 275 int mpc_proc_init(void) 276 { 277 struct proc_dir_entry *p; 278 279 p = proc_create(STAT_FILE_NAME, 0, atm_proc_root, &mpc_file_operations); 280 if (!p) { 281 printk(KERN_ERR "Unable to initialize /proc/atm/%s\n", STAT_FILE_NAME); 282 return -ENOMEM; 283 } 284 return 0; 285 } 286 287 /* 288 * DELETING function - called when module is removed. 289 */ 290 void mpc_proc_clean(void) 291 { 292 remove_proc_entry(STAT_FILE_NAME,atm_proc_root); 293 } 294 295 296 #endif /* CONFIG_PROC_FS */ 297 298 299 300 301 302 303