Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

can: add hash based access to single EFF frame filters

In contrast to the direct access to the single SFF frame filters (which are
indexed by the SFF CAN ID itself) the single EFF frame filters are arranged
in a single linked hlist. To reduce the hlist traversal in the case of many
filter subscriptions a hash based access is introduced for single EFF filters.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>

authored by

Oliver Hartkopp and committed by
Marc Kleine-Budde
45c70029 e3d3917f

+75 -9
+26 -5
net/can/af_can.c
··· 338 338 } 339 339 340 340 /** 341 + * effhash - hash function for 29 bit CAN identifier reduction 342 + * @can_id: 29 bit CAN identifier 343 + * 344 + * Description: 345 + * To reduce the linear traversal in one linked list of _single_ EFF CAN 346 + * frame subscriptions the 29 bit identifier is mapped to 10 bits. 347 + * (see CAN_EFF_RCV_HASH_BITS definition) 348 + * 349 + * Return: 350 + * Hash value from 0x000 - 0x3FF ( enforced by CAN_EFF_RCV_HASH_BITS mask ) 351 + */ 352 + static unsigned int effhash(canid_t can_id) 353 + { 354 + unsigned int hash; 355 + 356 + hash = can_id; 357 + hash ^= can_id >> CAN_EFF_RCV_HASH_BITS; 358 + hash ^= can_id >> (2 * CAN_EFF_RCV_HASH_BITS); 359 + 360 + return hash & ((1 << CAN_EFF_RCV_HASH_BITS) - 1); 361 + } 362 + 363 + /** 341 364 * find_rcv_list - determine optimal filterlist inside device filter struct 342 365 * @can_id: pointer to CAN identifier of a given can_filter 343 366 * @mask: pointer to CAN mask of a given can_filter ··· 423 400 !(*can_id & CAN_RTR_FLAG)) { 424 401 425 402 if (*can_id & CAN_EFF_FLAG) { 426 - if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) { 427 - /* RFC: a future use-case for hash-tables? */ 428 - return &d->rx[RX_EFF]; 429 - } 403 + if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) 404 + return &d->rx_eff[effhash(*can_id)]; 430 405 } else { 431 406 if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS)) 432 407 return &d->rx_sff[*can_id]; ··· 653 632 return matches; 654 633 655 634 if (can_id & CAN_EFF_FLAG) { 656 - hlist_for_each_entry_rcu(r, &d->rx[RX_EFF], list) { 635 + hlist_for_each_entry_rcu(r, &d->rx_eff[effhash(can_id)], list) { 657 636 if (r->can_id == can_id) { 658 637 deliver(skb, r); 659 638 matches++;
+4 -1
net/can/af_can.h
··· 60 60 }; 61 61 62 62 #define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS) 63 + #define CAN_EFF_RCV_HASH_BITS 10 64 + #define CAN_EFF_RCV_ARRAY_SZ (1 << CAN_EFF_RCV_HASH_BITS) 63 65 64 - enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX }; 66 + enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX }; 65 67 66 68 /* per device receive filters linked at dev->ml_priv */ 67 69 struct dev_rcv_lists { 68 70 struct hlist_head rx[RX_MAX]; 69 71 struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ]; 72 + struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ]; 70 73 int remove_on_zero_entries; 71 74 int entries; 72 75 };
+45 -3
net/can/proc.c
··· 80 80 [RX_ALL] = "rx_all", 81 81 [RX_FIL] = "rx_fil", 82 82 [RX_INV] = "rx_inv", 83 - [RX_EFF] = "rx_eff", 84 83 }; 85 84 86 85 /* ··· 455 456 .release = single_release, 456 457 }; 457 458 459 + 460 + static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v) 461 + { 462 + struct net_device *dev; 463 + struct dev_rcv_lists *d; 464 + 465 + /* RX_EFF */ 466 + seq_puts(m, "\nreceive list 'rx_eff':\n"); 467 + 468 + rcu_read_lock(); 469 + 470 + /* eff receive list for 'all' CAN devices (dev == NULL) */ 471 + d = &can_rx_alldev_list; 472 + can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff)); 473 + 474 + /* eff receive list for registered CAN devices */ 475 + for_each_netdev_rcu(&init_net, dev) { 476 + if (dev->type == ARPHRD_CAN && dev->ml_priv) { 477 + d = dev->ml_priv; 478 + can_rcvlist_proc_show_array(m, dev, d->rx_eff, 479 + ARRAY_SIZE(d->rx_eff)); 480 + } 481 + } 482 + 483 + rcu_read_unlock(); 484 + 485 + seq_putc(m, '\n'); 486 + return 0; 487 + } 488 + 489 + static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file) 490 + { 491 + return single_open(file, can_rcvlist_eff_proc_show, NULL); 492 + } 493 + 494 + static const struct file_operations can_rcvlist_eff_proc_fops = { 495 + .owner = THIS_MODULE, 496 + .open = can_rcvlist_eff_proc_open, 497 + .read = seq_read, 498 + .llseek = seq_lseek, 499 + .release = single_release, 500 + }; 501 + 458 502 /* 459 503 * proc utility functions 460 504 */ ··· 537 495 &can_rcvlist_proc_fops, (void *)RX_FIL); 538 496 pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir, 539 497 &can_rcvlist_proc_fops, (void *)RX_INV); 540 - pde_rcvlist_eff = proc_create_data(CAN_PROC_RCVLIST_EFF, 0644, can_dir, 541 - &can_rcvlist_proc_fops, (void *)RX_EFF); 498 + pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir, 499 + &can_rcvlist_eff_proc_fops); 542 500 pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir, 543 501 &can_rcvlist_sff_proc_fops); 544 502 }