1 #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 2 3 #ifdef CONFIG_PROC_FS 4 #include <linux/errno.h> 5 #include <linux/kernel.h> 6 #include <linux/string.h> 7 #include <linux/mm.h> 8 #include <linux/module.h> 9 #include <linux/proc_fs.h> 10 #include <linux/time.h> 11 #include <linux/seq_file.h> 12 #include <linux/uaccess.h> 13 #include <linux/atmmpc.h> 14 #include <linux/atm.h> 15 #include "mpc.h" 16 #include "mpoa_caches.h" 17 18 /* 19 * mpoa_proc.c: Implementation MPOA client's proc 20 * file system statistics 21 */ 22 23 #if 1 24 #define dprintk(format, args...) printk(KERN_DEBUG format, ##args) /* debug */ 25 #else 26 #define dprintk(format, args...) \ 27 do { if (0) printk(KERN_DEBUG format, ##args); } while (0) 28 #endif 29 30 #define STAT_FILE_NAME "mpc" /* Our statistic file's name */ 31 32 extern struct mpoa_client *mpcs; 33 extern struct proc_dir_entry *atm_proc_root; /* from proc.c. */ 34 35 static int proc_mpc_open(struct inode *inode, struct file *file); 36 static ssize_t proc_mpc_write(struct file *file, const char __user *buff, 37 size_t nbytes, loff_t *ppos); 38 39 static int parse_qos(const char *buff); 40 41 /* 42 * Define allowed FILE OPERATIONS 43 */ 44 static const struct file_operations mpc_file_operations = { 45 .owner = THIS_MODULE, 46 .open = proc_mpc_open, 47 .read = seq_read, 48 .llseek = seq_lseek, 49 .write = proc_mpc_write, 50 .release = seq_release, 51 }; 52 53 /* 54 * Returns the state of an ingress cache entry as a string 55 */ 56 static const char *ingress_state_string(int state) 57 { 58 switch (state) { 59 case INGRESS_RESOLVING: 60 return "resolving "; 61 case INGRESS_RESOLVED: 62 return "resolved "; 63 case INGRESS_INVALID: 64 return "invalid "; 65 case INGRESS_REFRESHING: 66 return "refreshing "; 67 } 68 69 return ""; 70 } 71 72 /* 73 * Returns the state of an egress cache entry as a string 74 */ 75 static const char *egress_state_string(int state) 76 { 77 switch (state) { 78 case EGRESS_RESOLVED: 79 return "resolved "; 80 case EGRESS_PURGE: 81 return "purge "; 82 case EGRESS_INVALID: 83 return "invalid "; 84 } 85 86 return ""; 87 } 88 89 /* 90 * FIXME: mpcs (and per-mpc lists) have no locking whatsoever. 91 */ 92 93 static void *mpc_start(struct seq_file *m, loff_t *pos) 94 { 95 loff_t l = *pos; 96 struct mpoa_client *mpc; 97 98 if (!l--) 99 return SEQ_START_TOKEN; 100 for (mpc = mpcs; mpc; mpc = mpc->next) 101 if (!l--) 102 return mpc; 103 return NULL; 104 } 105 106 static void *mpc_next(struct seq_file *m, void *v, loff_t *pos) 107 { 108 struct mpoa_client *p = v; 109 (*pos)++; 110 return v == SEQ_START_TOKEN ? mpcs : p->next; 111 } 112 113 static void mpc_stop(struct seq_file *m, void *v) 114 { 115 } 116 117 /* 118 * READING function - called when the /proc/atm/mpoa file is read from. 119 */ 120 static int mpc_show(struct seq_file *m, void *v) 121 { 122 struct mpoa_client *mpc = v; 123 int i; 124 in_cache_entry *in_entry; 125 eg_cache_entry *eg_entry; 126 struct timeval now; 127 unsigned char ip_string[16]; 128 129 if (v == SEQ_START_TOKEN) { 130 atm_mpoa_disp_qos(m); 131 return 0; 132 } 133 134 seq_printf(m, "\nInterface %d:\n\n", mpc->dev_num); 135 seq_printf(m, "Ingress Entries:\nIP address State Holding time Packets fwded VPI VCI\n"); 136 do_gettimeofday(&now); 137 138 for (in_entry = mpc->in_cache; in_entry; in_entry = in_entry->next) { 139 sprintf(ip_string, "%pI4", &in_entry->ctrl_info.in_dst_ip); 140 seq_printf(m, "%-16s%s%-14lu%-12u", 141 ip_string, 142 ingress_state_string(in_entry->entry_state), 143 in_entry->ctrl_info.holding_time - 144 (now.tv_sec-in_entry->tv.tv_sec), 145 in_entry->packets_fwded); 146 if (in_entry->shortcut) 147 seq_printf(m, " %-3d %-3d", 148 in_entry->shortcut->vpi, 149 in_entry->shortcut->vci); 150 seq_printf(m, "\n"); 151 } 152 153 seq_printf(m, "\n"); 154 seq_printf(m, "Egress Entries:\nIngress MPC ATM addr\nCache-id State Holding time Packets recvd Latest IP addr VPI VCI\n"); 155 for (eg_entry = mpc->eg_cache; eg_entry; eg_entry = eg_entry->next) { 156 unsigned char *p = eg_entry->ctrl_info.in_MPC_data_ATM_addr; 157 for (i = 0; i < ATM_ESA_LEN; i++) 158 seq_printf(m, "%02x", p[i]); 159 seq_printf(m, "\n%-16lu%s%-14lu%-15u", 160 (unsigned long)ntohl(eg_entry->ctrl_info.cache_id), 161 egress_state_string(eg_entry->entry_state), 162 (eg_entry->ctrl_info.holding_time - 163 (now.tv_sec-eg_entry->tv.tv_sec)), 164 eg_entry->packets_rcvd); 165 166 /* latest IP address */ 167 sprintf(ip_string, "%pI4", &eg_entry->latest_ip_addr); 168 seq_printf(m, "%-16s", ip_string); 169 170 if (eg_entry->shortcut) 171 seq_printf(m, " %-3d %-3d", 172 eg_entry->shortcut->vpi, 173 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 pr_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 #endif /* CONFIG_PROC_FS */ 296 297 298 299 300 301 302