1/* 2 ************************************************************************** 3 * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all copies. 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 ************************************************************************** 15 */ 16 17#include <linux/version.h> 18#include <linux/types.h> 19#include <linux/ip.h> 20#include <linux/tcp.h> 21#include <linux/module.h> 22#include <linux/skbuff.h> 23#include <linux/icmp.h> 24#include <linux/kthread.h> 25#include <linux/debugfs.h> 26#include <linux/pkt_sched.h> 27#include <linux/string.h> 28#include <net/route.h> 29#include <net/ip.h> 30#include <net/tcp.h> 31#include <asm/unaligned.h> 32#include <asm/uaccess.h> /* for put_user */ 33#include <net/ipv6.h> 34#include <linux/inet.h> 35#include <linux/in.h> 36#include <linux/udp.h> 37#include <linux/tcp.h> 38 39#include <linux/netfilter_ipv4.h> 40#include <linux/netfilter_bridge.h> 41#include <net/netfilter/nf_conntrack.h> 42#include <net/netfilter/nf_conntrack_helper.h> 43#include <net/netfilter/nf_conntrack_l4proto.h> 44#include <net/netfilter/nf_conntrack_l3proto.h> 45#include <net/netfilter/nf_conntrack_core.h> 46#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> 47#include <net/netfilter/ipv4/nf_defrag_ipv4.h> 48 49#include <hyfi_ecm.h> 50#include <hyfi_hash.h> 51 52/* 53 * Debug output levels 54 * 0 = OFF 55 * 1 = ASSERTS / ERRORS 56 * 2 = 1 + WARN 57 * 3 = 2 + INFO 58 * 4 = 3 + TRACE 59 */ 60#define DEBUG_LEVEL ECM_CLASSIFIER_HYFI_DEBUG_LEVEL 61 62#include "ecm_types.h" 63#include "ecm_db_types.h" 64#include "ecm_state.h" 65#include "ecm_tracker.h" 66#include "ecm_classifier.h" 67#include "ecm_front_end_types.h" 68#include "ecm_tracker_udp.h" 69#include "ecm_tracker_tcp.h" 70#include "ecm_db.h" 71#include "ecm_classifier_hyfi.h" 72 73/* 74 * Magic numbers 75 */ 76#define ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC 0xFE25 77 78/* 79 * Definitions 80 */ 81#define ECM_CLASSIFIER_HYFI_STATE_INIT ( 1 << 0 ) 82#define ECM_CLASSIFIER_HYFI_STATE_REGISTERED ( 1 << 1 ) 83#define ECM_CLASSIFIER_HYFI_STATE_IGNORE ( 1 << 2 ) 84 85/* 86 * struct ecm_classifier_hyfi_instance 87 * State to allow tracking of dynamic qos for a connection 88 */ 89struct ecm_classifier_hyfi_instance { 90 struct ecm_classifier_instance base; /* Base type */ 91 92 struct ecm_classifier_hyfi_instance *next; /* Next classifier state instance (for accouting and reporting purposes) */ 93 struct ecm_classifier_hyfi_instance *prev; /* Next classifier state instance (for accouting and reporting purposes) */ 94 95 uint32_t ci_serial; /* RO: Serial of the connection */ 96 struct ecm_classifier_process_response process_response;/* Last process response computed */ 97 98 uint32_t hyfi_state; 99 struct hyfi_ecm_flow_data_t flow; 100 101 int refs; /* Integer to trap we never go negative */ 102#if (DEBUG_LEVEL > 0) 103 uint16_t magic; 104#endif 105}; 106 107/* 108 * Listener for db events 109 */ 110struct ecm_db_listener_instance *ecm_classifier_hyfi_li = NULL; 111 112/* 113 * Operational control 114 */ 115static bool ecm_classifier_hyfi_enabled = true; /* Operational behaviour */ 116 117/* 118 * Management thread control 119 */ 120static bool ecm_classifier_hyfi_terminate_pending = false; /* True when the user wants us to terminate */ 121 122/* 123 * Debugfs dentry object. 124 */ 125static struct dentry *ecm_classifier_hyfi_dentry; 126 127/* 128 * Locking of the classifier structures 129 */ 130static DEFINE_SPINLOCK(ecm_classifier_hyfi_lock); /* Protect SMP access. */ 131 132/* 133 * List of our classifier instances 134 */ 135static struct ecm_classifier_hyfi_instance *ecm_classifier_hyfi_instances = NULL; 136 /* list of all active instances */ 137static int ecm_classifier_hyfi_count = 0; /* Tracks number of instances allocated */ 138 139/* 140 * ecm_classifier_hyfi_ref() 141 * Ref 142 */ 143static void ecm_classifier_hyfi_ref(struct ecm_classifier_instance *ci) 144{ 145 struct ecm_classifier_hyfi_instance *chfi; 146 chfi = (struct ecm_classifier_hyfi_instance *)ci; 147 148 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi); 149 spin_lock_bh(&ecm_classifier_hyfi_lock); 150 chfi->refs++; 151 DEBUG_TRACE("%p: chfi ref %d\n", chfi, chfi->refs); 152 DEBUG_ASSERT(chfi->refs > 0, "%p: ref wrap\n", chfi); 153 spin_unlock_bh(&ecm_classifier_hyfi_lock); 154} 155 156/* 157 * ecm_classifier_hyfi_deref() 158 * Deref 159 */ 160static int ecm_classifier_hyfi_deref(struct ecm_classifier_instance *ci) 161{ 162 struct ecm_classifier_hyfi_instance *chfi; 163 chfi = (struct ecm_classifier_hyfi_instance *)ci; 164 165 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi); 166 spin_lock_bh(&ecm_classifier_hyfi_lock); 167 chfi->refs--; 168 DEBUG_ASSERT(chfi->refs >= 0, "%p: refs wrapped\n", chfi); 169 DEBUG_TRACE("%p: HyFi classifier deref %d\n", chfi, chfi->refs); 170 if (chfi->refs) { 171 int refs = chfi->refs; 172 spin_unlock_bh(&ecm_classifier_hyfi_lock); 173 return refs; 174 } 175 176 /* 177 * Object to be destroyed 178 */ 179 ecm_classifier_hyfi_count--; 180 DEBUG_ASSERT(ecm_classifier_hyfi_count >= 0, "%p: ecm_classifier_hyfi_count wrap\n", chfi); 181 182 /* 183 * UnLink the instance from our list 184 */ 185 if (chfi->next) { 186 chfi->next->prev = chfi->prev; 187 } 188 if (chfi->prev) { 189 chfi->prev->next = chfi->next; 190 } else { 191 DEBUG_ASSERT(ecm_classifier_hyfi_instances == chfi, "%p: list bad %p\n", chfi, ecm_classifier_hyfi_instances); 192 ecm_classifier_hyfi_instances = chfi->next; 193 } 194 chfi->next = NULL; 195 chfi->prev = NULL; 196 197 spin_unlock_bh(&ecm_classifier_hyfi_lock); 198 199 /* 200 * Final 201 */ 202 DEBUG_INFO("%p: Final HyFi classifier instance\n", chfi); 203 kfree(chfi); 204 205 return 0; 206} 207 208/* 209 * ecm_classifier_hyfi_process() 210 * Process new data for connection 211 */ 212static void ecm_classifier_hyfi_process(struct ecm_classifier_instance *aci, ecm_tracker_sender_type_t sender, 213 struct ecm_tracker_ip_header *ip_hdr, struct sk_buff *skb, 214 struct ecm_classifier_process_response *process_response) 215{ 216 struct ecm_classifier_hyfi_instance *chfi; 217 ecm_classifier_relevence_t relevance; 218 bool enabled; 219 struct ecm_db_connection_instance *ci; 220 struct ecm_front_end_connection_instance *feci; 221 ecm_front_end_acceleration_mode_t accel_mode; 222 uint32_t became_relevant = 0; 223 224 chfi = (struct ecm_classifier_hyfi_instance *)aci; 225 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi); 226 227 /* 228 * Are we yet to decide if this instance is relevant to the connection? 229 */ 230 spin_lock_bh(&ecm_classifier_hyfi_lock); 231 relevance = chfi->process_response.relevance; 232 if (relevance != ECM_CLASSIFIER_RELEVANCE_MAYBE) { 233 /* 234 * We already know 235 * NOTE: Lock still held 236 */ 237 goto hyfi_classifier_out; 238 } 239 240 enabled = ecm_classifier_hyfi_enabled; 241 spin_unlock_bh(&ecm_classifier_hyfi_lock); 242 243 /* 244 * Need to decide our relevance to this connection. 245 * If classifier is enabled and the front end says it can accel then we are "relevant". 246 * Any other condition and we are not and will stop analysing this connection. 247 */ 248 relevance = ECM_CLASSIFIER_RELEVANCE_NO; 249 ci = ecm_db_connection_serial_find_and_ref(chfi->ci_serial); 250 if (!ci) { 251 DEBUG_TRACE("%p: No ci found for %u\n", chfi, chfi->ci_serial); 252 accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DENIED; 253 } else { 254 feci = ecm_db_connection_front_end_get_and_ref(ci); 255 accel_mode = feci->accel_state_get(feci); 256 feci->deref(feci); 257 ecm_db_connection_deref(ci); 258 } 259 260 if (enabled && ECM_FRONT_END_ACCELERATION_POSSIBLE(accel_mode)) { 261 relevance = ECM_CLASSIFIER_RELEVANCE_YES; 262 became_relevant = ecm_db_time_get(); 263 } 264 265 spin_lock_bh(&ecm_classifier_hyfi_lock); 266 chfi->process_response.relevance = relevance; 267 chfi->process_response.became_relevant = became_relevant; 268 269hyfi_classifier_out: 270 ; 271 272 /* 273 * Return our process response 274 */ 275 *process_response = chfi->process_response; 276 if (relevance == ECM_CLASSIFIER_RELEVANCE_NO) { 277 goto hyfi_classifier_done; 278 } 279 280 /* 281 * Fast path, already accelerated or ignored 282 */ 283 if (chfi->hyfi_state & (ECM_CLASSIFIER_HYFI_STATE_REGISTERED | ECM_CLASSIFIER_HYFI_STATE_IGNORE)) { 284 goto hyfi_classifier_done; 285 } 286 287 /* 288 * Compute the hash 289 */ 290 if (unlikely(hyfi_hash_skbuf(skb, &chfi->flow.hash, &chfi->flow.flag, &chfi->flow.priority, &chfi->flow.seq))) { 291 goto hyfi_classifier_done; 292 } 293 294 chfi->flow.ecm_serial = chfi->ci_serial; 295 memcpy(&chfi->flow.sa, eth_hdr(skb)->h_source, ETH_ALEN); 296 memcpy(&chfi->flow.da, eth_hdr(skb)->h_dest, ETH_ALEN); 297 298 DEBUG_INFO("Flow serial: %d\nFlow hash: 0x%02x, priority 0x%08x, flag: %d\nSA: %pM\nDA: %pM\n\n", 299 chfi->flow.ecm_serial, chfi->flow.hash, chfi->flow.priority, chfi->flow.flag, 300 chfi->flow.sa, chfi->flow.da); 301 302 chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_REGISTERED; 303 304hyfi_classifier_done: 305 ; 306 307 spin_unlock_bh(&ecm_classifier_hyfi_lock); 308} 309 310/* 311 * ecm_classifier_hyfi_sync_to_v4() 312 * Front end is pushing accel engine state to us 313 */ 314static void ecm_classifier_hyfi_sync_to_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync) 315{ 316 struct ecm_classifier_hyfi_instance *chfi; 317 struct ecm_db_connection_instance *ci; 318 uint64_t num_packets = 0; 319 uint64_t num_bytes = 0; 320 int32_t ret; 321 322 chfi = (struct ecm_classifier_hyfi_instance *)aci; 323 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi); 324 325 if (chfi->hyfi_state & 326 (ECM_CLASSIFIER_HYFI_STATE_IGNORE)) { 327 return; 328 } 329 330 ci = ecm_db_connection_serial_find_and_ref(chfi->ci_serial); 331 if (!ci) { 332 DEBUG_TRACE("%p: No ci found for %u\n", chfi, chfi->ci_serial); 333 return; 334 } 335 336 /* 337 * Update the stats on Hy-Fi side 338 */ 339 ecm_db_connection_data_stats_get(ci, NULL, &num_bytes, 340 NULL, &num_packets, 341 NULL, NULL, 342 NULL, NULL ); 343 ecm_db_connection_deref(ci); 344 345 DEBUG_INFO("UPDATE STATS: Flow serial: %d\nFlow hash: 0x%02x, priority 0x%08x, flag: %d\nSA: %pM\nDA: %pM\n\n", 346 chfi->flow.ecm_serial, chfi->flow.hash, chfi->flow.priority, chfi->flow.flag, 347 chfi->flow.sa, chfi->flow.da); 348 349 DEBUG_INFO("num_bytes = %lld, num_packets = %lld\n", num_bytes, num_packets); 350 351 ret = hyfi_ecm_update_stats(&chfi->flow, num_bytes, num_packets); 352 353 if (ret < 0) { 354 printk("%s: Fatal error\n", __func__); 355 return; 356 } 357 358 switch(ret) 359 { 360 case 0: /* OK */ 361 chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_REGISTERED; 362 break; 363 364 case 1: /* Not interested */ 365 chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_IGNORE; 366 break; 367 368 case 2: /* Not attached, may be interested in the future */ 369 default: 370 chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_INIT; 371 break; 372 } 373} 374 375/* 376 * ecm_classifier_hyfi_sync_from_v4() 377 * Front end is retrieving accel engine state from us 378 */ 379static void ecm_classifier_hyfi_sync_from_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc) 380{ 381 struct ecm_classifier_hyfi_instance *chfi; 382 383 chfi = (struct ecm_classifier_hyfi_instance *)aci; 384 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi); 385} 386 387/* 388 * ecm_classifier_hyfi_sync_to_v6() 389 * Front end is pushing accel engine state to us 390 */ 391static void ecm_classifier_hyfi_sync_to_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync) 392{ 393 struct ecm_classifier_hyfi_instance *chfi; 394 struct ecm_db_connection_instance *ci; 395 uint64_t num_packets = 0; 396 uint64_t num_bytes = 0; 397 int32_t ret; 398 399 chfi = (struct ecm_classifier_hyfi_instance *)aci; 400 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi); 401 402 if (chfi->hyfi_state & 403 (ECM_CLASSIFIER_HYFI_STATE_IGNORE)) { 404 return; 405 } 406 407 ci = ecm_db_connection_serial_find_and_ref(chfi->ci_serial); 408 if (!ci) { 409 DEBUG_TRACE("%p: No ci found for %u\n", chfi, chfi->ci_serial); 410 return; 411 } 412 413 /* 414 * Update the stats on Hy-Fi side 415 */ 416 ecm_db_connection_data_stats_get(ci, NULL, &num_bytes, 417 NULL, &num_packets, 418 NULL, NULL, 419 NULL, NULL ); 420 ecm_db_connection_deref(ci); 421 422 ret = hyfi_ecm_update_stats(&chfi->flow, num_bytes, num_packets); 423 424 if (ret < 0) { 425 DEBUG_ERROR("%s: Fatal error\n", __func__); 426 return; 427 } 428 429 switch(ret) 430 { 431 case 0: /* OK */ 432 chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_REGISTERED; 433 break; 434 435 case 1: /* Not interested */ 436 chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_IGNORE; 437 break; 438 439 case 2: /* Not attached, may be interested in the future */ 440 default: 441 chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_INIT; 442 break; 443 } 444} 445 446/* 447 * ecm_classifier_hyfi_sync_from_v6() 448 * Front end is retrieving accel engine state from us 449 */ 450static void ecm_classifier_hyfi_sync_from_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc) 451{ 452 struct ecm_classifier_hyfi_instance *chfi; 453 454 chfi = (struct ecm_classifier_hyfi_instance *)aci; 455 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi); 456 457} 458 459/* 460 * ecm_classifier_hyfi_type_get() 461 * Get type of classifier this is 462 */ 463static ecm_classifier_type_t ecm_classifier_hyfi_type_get(struct ecm_classifier_instance *ci) 464{ 465 struct ecm_classifier_hyfi_instance *chfi; 466 chfi = (struct ecm_classifier_hyfi_instance *)ci; 467 468 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi); 469 return ECM_CLASSIFIER_TYPE_HYFI; 470} 471 472/* 473 * ecm_classifier_hyfi_last_process_response_get() 474 * Get result code returned by the last process call 475 */ 476static void ecm_classifier_hyfi_last_process_response_get(struct ecm_classifier_instance *ci, 477 struct ecm_classifier_process_response *process_response) 478{ 479 struct ecm_classifier_hyfi_instance *chfi; 480 481 chfi = (struct ecm_classifier_hyfi_instance *)ci; 482 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi); 483 484 spin_lock_bh(&ecm_classifier_hyfi_lock); 485 *process_response = chfi->process_response; 486 spin_unlock_bh(&ecm_classifier_hyfi_lock); 487} 488 489/* 490 * ecm_classifier_hyfi_reclassify_allowed() 491 * Indicate if reclassify is allowed 492 */ 493static bool ecm_classifier_hyfi_reclassify_allowed(struct ecm_classifier_instance *ci) 494{ 495 struct ecm_classifier_hyfi_instance *chfi; 496 chfi = (struct ecm_classifier_hyfi_instance *)ci; 497 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi); 498 499 return true; 500} 501 502/* 503 * ecm_classifier_hyfi_reclassify() 504 * Reclassify 505 */ 506static void ecm_classifier_hyfi_reclassify(struct ecm_classifier_instance *ci) 507{ 508 struct ecm_classifier_hyfi_instance *chfi; 509 chfi = (struct ecm_classifier_hyfi_instance *)ci; 510 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi); 511} 512 513#ifdef ECM_STATE_OUTPUT_ENABLE 514/* 515 * ecm_classifier_hyfi_state_get() 516 * Return state 517 */ 518static int ecm_classifier_hyfi_state_get(struct ecm_classifier_instance *ci, char *buf, int buf_sz) 519{ 520 struct ecm_classifier_hyfi_instance *chfi; 521 struct ecm_classifier_process_response process_response; 522 523 chfi = (struct ecm_classifier_hyfi_instance *)ci; 524 DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi); 525 526 if ((result = ecm_state_prefix_add(sfi, "hyfi"))) { 527 return result; 528 } 529 530 spin_lock_bh(&ecm_classifier_hyfi_lock); 531 process_response = chfi->process_response; 532 spin_unlock_bh(&ecm_classifier_hyfi_lock); 533 534 /* 535 * Output our last process response 536 */ 537 if ((result = ecm_classifier_process_response_state_get(sfi, &process_response))) { 538 return result; 539 } 540 541 return ecm_state_prefix_remove(sfi); 542} 543#endif 544 545/* 546 * ecm_classifier_hyfi_instance_alloc() 547 * Allocate an instance of the HyFi classifier 548 */ 549struct ecm_classifier_hyfi_instance *ecm_classifier_hyfi_instance_alloc(struct ecm_db_connection_instance *ci) 550{ 551 struct ecm_classifier_hyfi_instance *chfi; 552 553 /* 554 * Allocate the instance 555 */ 556 chfi = (struct ecm_classifier_hyfi_instance *)kzalloc(sizeof(struct ecm_classifier_hyfi_instance), GFP_ATOMIC | __GFP_NOWARN); 557 if (!chfi) { 558 DEBUG_WARN("Failed to allocate HyFi instance\n"); 559 return NULL; 560 } 561 562 DEBUG_SET_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC); 563 chfi->refs = 1; 564 chfi->base.process = ecm_classifier_hyfi_process; 565 chfi->base.sync_from_v4 = ecm_classifier_hyfi_sync_from_v4; 566 chfi->base.sync_to_v4 = ecm_classifier_hyfi_sync_to_v4; 567 chfi->base.sync_from_v6 = ecm_classifier_hyfi_sync_from_v6; 568 chfi->base.sync_to_v6 = ecm_classifier_hyfi_sync_to_v6; 569 chfi->base.type_get = ecm_classifier_hyfi_type_get; 570 chfi->base.last_process_response_get = ecm_classifier_hyfi_last_process_response_get; 571 chfi->base.reclassify_allowed = ecm_classifier_hyfi_reclassify_allowed; 572 chfi->base.reclassify = ecm_classifier_hyfi_reclassify; 573#ifdef ECM_STATE_OUTPUT_ENABLE 574 chfi->base.state_get = ecm_classifier_hyfi_state_get; 575#endif 576 chfi->base.ref = ecm_classifier_hyfi_ref; 577 chfi->base.deref = ecm_classifier_hyfi_deref; 578 chfi->ci_serial = ecm_db_connection_serial_get(ci); 579 chfi->process_response.process_actions = 0; 580 chfi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE; 581 582 /* 583 * Init Hy-Fi state 584 */ 585 chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_INIT; 586 587 spin_lock_bh(&ecm_classifier_hyfi_lock); 588 589 /* 590 * Final check if we are pending termination 591 */ 592 if (ecm_classifier_hyfi_terminate_pending) { 593 spin_unlock_bh(&ecm_classifier_hyfi_lock); 594 DEBUG_INFO("%p: Terminating\n", ci); 595 kfree(chfi); 596 return NULL; 597 } 598 599 /* 600 * Link the new instance into our list at the head 601 */ 602 chfi->next = ecm_classifier_hyfi_instances; 603 if (ecm_classifier_hyfi_instances) { 604 ecm_classifier_hyfi_instances->prev = chfi; 605 } 606 ecm_classifier_hyfi_instances = chfi; 607 608 /* 609 * Increment stats 610 */ 611 ecm_classifier_hyfi_count++; 612 DEBUG_ASSERT(ecm_classifier_hyfi_count > 0, "%p: ecm_classifier_hyfi_count wrap\n", chfi); 613 spin_unlock_bh(&ecm_classifier_hyfi_lock); 614 615 DEBUG_INFO("HyFi instance alloc: %p\n", chfi); 616 return chfi; 617} 618EXPORT_SYMBOL(ecm_classifier_hyfi_instance_alloc); 619 620/* 621 * ecm_classifier_hyfi_connection_added() 622 * Invoked when a connection is added to the DB 623 */ 624static void ecm_classifier_hyfi_connection_added(void *arg, struct ecm_db_connection_instance *ci) 625{ 626#if (DEBUG_LEVEL > 2) 627 uint32_t serial = ecm_db_connection_serial_get(ci); 628 DEBUG_INFO("%p: HYFI LISTENER: added conn with serial: %u\n", ci, serial); 629#endif 630// Add your own code here 631} 632 633/* 634 * ecm_classifier_hyfi_connection_removed() 635 * Invoked when a connection is removed from the DB 636 */ 637static void ecm_classifier_hyfi_connection_removed(void *arg, struct ecm_db_connection_instance *ci) 638{ 639#if (DEBUG_LEVEL > 2) 640 uint32_t serial = ecm_db_connection_serial_get(ci); 641 DEBUG_INFO("%p: HYFI LISTENER: removed conn with serial: %u\n", ci, serial); 642#endif 643// Add your own code here 644} 645 646/* 647 * ecm_classifier_hyfi_set_set_command() 648 * Set hyfi command to accel/decel connection. 649 */ 650static ssize_t ecm_classifier_hyfi_set_command(struct file *file, 651 const char __user *user_buf, 652 size_t sz, loff_t *ppos) 653{ 654#define ECM_CLASSIFIER_HYFI_SET_IP_COMMAND_FIELDS 2 655 char *cmd_buf; 656 int field_count; 657 char *field_ptr; 658 char *fields[ECM_CLASSIFIER_HYFI_SET_IP_COMMAND_FIELDS]; 659 char cmd; 660 uint32_t serial; 661 struct ecm_db_connection_instance *ci; 662 struct ecm_front_end_connection_instance *feci; 663 664 /* 665 * Check if we are enabled 666 */ 667 spin_lock_bh(&ecm_classifier_hyfi_lock); 668 if (!ecm_classifier_hyfi_enabled) { 669 spin_unlock_bh(&ecm_classifier_hyfi_lock); 670 return -EINVAL; 671 } 672 spin_unlock_bh(&ecm_classifier_hyfi_lock); 673 674 /* 675 * buf is formed as: 676 * [0] [1] 677 * <CMD>/<SERIAL> 678 * CMD: 679 * s = Decelerate based on <SERIAL> number given. 680 */ 681 cmd_buf = (char *)kzalloc(sz + 1, GFP_ATOMIC); 682 if (!cmd_buf) { 683 return -ENOMEM; 684 } 685 686 sz = simple_write_to_buffer(cmd_buf, sz, ppos, user_buf, sz); 687 688 /* 689 * Split the buffer into its fields 690 */ 691 field_count = 0; 692 field_ptr = cmd_buf; 693 fields[field_count] = strsep(&field_ptr, "/"); 694 while (fields[field_count] != NULL) { 695 DEBUG_TRACE("FIELD %d: %s\n", field_count, fields[field_count]); 696 field_count++; 697 if (field_count == ECM_CLASSIFIER_HYFI_SET_IP_COMMAND_FIELDS) { 698 break; 699 } 700 fields[field_count] = strsep(&field_ptr, "/"); 701 } 702 703 if (field_count != ECM_CLASSIFIER_HYFI_SET_IP_COMMAND_FIELDS) { 704 DEBUG_WARN("invalid field count %d\n", field_count); 705 kfree(cmd_buf); 706 return -EINVAL; 707 } 708 709 if (!sscanf(fields[0], "%c", &cmd)) { 710 DEBUG_WARN("invalid cmd\n"); 711 kfree(cmd_buf); 712 return -EINVAL; 713 } 714 if (!sscanf(fields[1], "%u", &serial)) { 715 DEBUG_WARN("invalid serial\n"); 716 kfree(cmd_buf); 717 return -EINVAL; 718 } 719 720 kfree(cmd_buf); 721 722 /* 723 * Locate the connection using the serial or tuple given 724 */ 725 switch (cmd) { 726 case 's': 727 DEBUG_TRACE("Lookup connection using serial: %u\n", serial); 728 ci = ecm_db_connection_serial_find_and_ref(serial); 729 break; 730 default: 731 DEBUG_WARN("invalid cmd %c\n", cmd); 732 return -EINVAL; 733 } 734 735 if (!ci) { 736 DEBUG_WARN("database connection not found\n"); 737 return -ENOMEM; 738 } 739 DEBUG_TRACE("Connection found: %p\n", ci); 740 741 /* 742 * Now action the command 743 */ 744 switch (cmd) { 745 case 's': 746 case 'f': 747 /* 748 * Decelerate the connection 749 */ 750 DEBUG_TRACE("Force decel: %p\n", ci); 751 feci = ecm_db_connection_front_end_get_and_ref(ci); 752 feci->decelerate(feci); 753 feci->deref(feci); 754 break; 755 } 756 ecm_db_connection_deref(ci); 757 758 return sz; 759} 760 761/* 762 * File operations for hyfi classifier command. 763 */ 764static struct file_operations ecm_classifier_hyfi_cmd_fops = { 765 .write = ecm_classifier_hyfi_set_command, 766}; 767 768/* 769 * ecm_classifier_hyfi_rules_init() 770 */ 771int ecm_classifier_hyfi_rules_init(struct dentry *dentry) 772{ 773 DEBUG_INFO("HyFi classifier Module init\n"); 774 775 ecm_classifier_hyfi_dentry = debugfs_create_dir("ecm_classifier_hyfi", dentry); 776 if (!ecm_classifier_hyfi_dentry) { 777 DEBUG_ERROR("Failed to create ecm hyfi classifier directory in debugfs\n"); 778 goto classifier_task_cleanup; 779 } 780 781 if (!debugfs_create_bool("enabled", S_IRUGO | S_IWUSR, ecm_classifier_hyfi_dentry, 782 (u32 *)&ecm_classifier_hyfi_enabled)) { 783 DEBUG_ERROR("Failed to create ecm hyfi classifier enabled file in debugfs\n"); 784 goto classifier_task_cleanup; 785 } 786 787 if (!debugfs_create_file("cmd", S_IWUSR, ecm_classifier_hyfi_dentry, 788 NULL, &ecm_classifier_hyfi_cmd_fops)) { 789 DEBUG_ERROR("Failed to create ecm hyfi classifier cmd file in debugfs\n"); 790 goto classifier_task_cleanup; 791 } 792 793 /* 794 * Allocate listener instance to listen for db events 795 */ 796 ecm_classifier_hyfi_li = ecm_db_listener_alloc(); 797 if (!ecm_classifier_hyfi_li) { 798 DEBUG_ERROR("Failed to allocate listener\n"); 799 goto classifier_task_cleanup; 800 } 801 802 /* 803 * Add the listener into the database 804 * NOTE: Ref the thread count for the listener 805 */ 806 ecm_db_listener_add(ecm_classifier_hyfi_li, 807 NULL /* ecm_classifier_hyfi_iface_added */, 808 NULL /* ecm_classifier_hyfi_iface_removed */, 809 NULL /* ecm_classifier_hyfi_node_added */, 810 NULL /* ecm_classifier_hyfi_node_removed */, 811 NULL /* ecm_classifier_hyfi_host_added */, 812 NULL /* ecm_classifier_hyfi_host_removed */, 813 NULL /* ecm_classifier_hyfi_mapping_added */, 814 NULL /* ecm_classifier_hyfi_mapping_removed */, 815 ecm_classifier_hyfi_connection_added, 816 ecm_classifier_hyfi_connection_removed, 817 NULL /* ecm_classifier_hyfi_listener_final */, 818 ecm_classifier_hyfi_li); 819 820 return 0; 821 822classifier_task_cleanup: 823 824 debugfs_remove_recursive(ecm_classifier_hyfi_dentry); 825 return -1; 826} 827EXPORT_SYMBOL(ecm_classifier_hyfi_rules_init); 828 829/* 830 * ecm_classifier_hyfi_rules_exit() 831 */ 832void ecm_classifier_hyfi_rules_exit(void) 833{ 834 DEBUG_INFO("HyFi classifier Module exit\n"); 835 836 spin_lock_bh(&ecm_classifier_hyfi_lock); 837 ecm_classifier_hyfi_terminate_pending = true; 838 spin_unlock_bh(&ecm_classifier_hyfi_lock); 839 840 /* 841 * Release our ref to the listener. 842 * This will cause it to be unattached to the db listener list. 843 * NOTE: Our thread refs will be released on final callback when 844 * we know there will be no more callbacks to it. 845 */ 846 if (ecm_classifier_hyfi_li) { 847 ecm_db_listener_deref(ecm_classifier_hyfi_li); 848 ecm_classifier_hyfi_li = NULL; 849 } 850 851 /* 852 * Remove the debugfs files recursively. 853 */ 854 if (ecm_classifier_hyfi_dentry) { 855 debugfs_remove_recursive(ecm_classifier_hyfi_dentry); 856 } 857} 858EXPORT_SYMBOL(ecm_classifier_hyfi_rules_exit); 859