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

Configure Feed

Select the types of activity you want to include in your feed.

fanotify: mix event info and pid into merge key hash

Improve the merge key hash by mixing more values relevant for merge.

For example, all FAN_CREATE name events in the same dir used to have the
same merge key based on the dir inode. With this change the created
file name is mixed into the merge key.

The object id that was used as merge key is redundant to the event info
so it is no longer mixed into the hash.

Permission events are not hashed, so no need to hash their info.

Link: https://lore.kernel.org/r/20210304104826.3993892-4-amir73il@gmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Amir Goldstein and committed by
Jan Kara
7e3e5c69 8988f11a

+66 -26
+61 -26
fs/notify/fanotify/fanotify.c
··· 14 14 #include <linux/audit.h> 15 15 #include <linux/sched/mm.h> 16 16 #include <linux/statfs.h> 17 + #include <linux/stringhash.h> 17 18 18 19 #include "fanotify.h" 19 20 ··· 23 22 return p1->mnt == p2->mnt && p1->dentry == p2->dentry; 24 23 } 25 24 25 + static unsigned int fanotify_hash_path(const struct path *path) 26 + { 27 + return hash_ptr(path->dentry, FANOTIFY_EVENT_HASH_BITS) ^ 28 + hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS); 29 + } 30 + 26 31 static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1, 27 32 __kernel_fsid_t *fsid2) 28 33 { 29 34 return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1]; 35 + } 36 + 37 + static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid) 38 + { 39 + return hash_32(fsid->val[0], FANOTIFY_EVENT_HASH_BITS) ^ 40 + hash_32(fsid->val[1], FANOTIFY_EVENT_HASH_BITS); 30 41 } 31 42 32 43 static bool fanotify_fh_equal(struct fanotify_fh *fh1, ··· 49 36 50 37 return !fh1->len || 51 38 !memcmp(fanotify_fh_buf(fh1), fanotify_fh_buf(fh2), fh1->len); 39 + } 40 + 41 + static unsigned int fanotify_hash_fh(struct fanotify_fh *fh) 42 + { 43 + long salt = (long)fh->type | (long)fh->len << 8; 44 + 45 + /* 46 + * full_name_hash() works long by long, so it handles fh buf optimally. 47 + */ 48 + return full_name_hash((void *)salt, fanotify_fh_buf(fh), fh->len); 52 49 } 53 50 54 51 static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1, ··· 348 325 * Return 0 on failure to encode. 349 326 */ 350 327 static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, 351 - unsigned int fh_len, gfp_t gfp) 328 + unsigned int fh_len, unsigned int *hash, 329 + gfp_t gfp) 352 330 { 353 331 int dwords, type = 0; 354 332 char *ext_buf = NULL; ··· 391 367 392 368 fh->type = type; 393 369 fh->len = fh_len; 370 + 371 + /* Mix fh into event merge key */ 372 + *hash ^= fanotify_hash_fh(fh); 394 373 395 374 return FANOTIFY_FH_HDR_LEN + fh_len; 396 375 ··· 448 421 } 449 422 450 423 static struct fanotify_event *fanotify_alloc_path_event(const struct path *path, 424 + unsigned int *hash, 451 425 gfp_t gfp) 452 426 { 453 427 struct fanotify_path_event *pevent; ··· 459 431 460 432 pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH; 461 433 pevent->path = *path; 434 + *hash ^= fanotify_hash_path(path); 462 435 path_get(path); 463 436 464 437 return &pevent->fae; ··· 485 456 486 457 static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id, 487 458 __kernel_fsid_t *fsid, 459 + unsigned int *hash, 488 460 gfp_t gfp) 489 461 { 490 462 struct fanotify_fid_event *ffe; ··· 496 466 497 467 ffe->fae.type = FANOTIFY_EVENT_TYPE_FID; 498 468 ffe->fsid = *fsid; 469 + *hash ^= fanotify_hash_fsid(fsid); 499 470 fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id), 500 - gfp); 471 + hash, gfp); 501 472 502 473 return &ffe->fae; 503 474 } 504 475 505 476 static struct fanotify_event *fanotify_alloc_name_event(struct inode *id, 506 477 __kernel_fsid_t *fsid, 507 - const struct qstr *file_name, 478 + const struct qstr *name, 508 479 struct inode *child, 480 + unsigned int *hash, 509 481 gfp_t gfp) 510 482 { 511 483 struct fanotify_name_event *fne; ··· 520 488 size = sizeof(*fne) + FANOTIFY_FH_HDR_LEN + dir_fh_len; 521 489 if (child_fh_len) 522 490 size += FANOTIFY_FH_HDR_LEN + child_fh_len; 523 - if (file_name) 524 - size += file_name->len + 1; 491 + if (name) 492 + size += name->len + 1; 525 493 fne = kmalloc(size, gfp); 526 494 if (!fne) 527 495 return NULL; 528 496 529 497 fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME; 530 498 fne->fsid = *fsid; 499 + *hash ^= fanotify_hash_fsid(fsid); 531 500 info = &fne->info; 532 501 fanotify_info_init(info); 533 502 dfh = fanotify_info_dir_fh(info); 534 - info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, 0); 503 + info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, hash, 0); 535 504 if (child_fh_len) { 536 505 ffh = fanotify_info_file_fh(info); 537 - info->file_fh_totlen = fanotify_encode_fh(ffh, child, child_fh_len, 0); 506 + info->file_fh_totlen = fanotify_encode_fh(ffh, child, 507 + child_fh_len, hash, 0); 538 508 } 539 - if (file_name) 540 - fanotify_info_copy_name(info, file_name); 509 + if (name) { 510 + long salt = name->len; 511 + 512 + fanotify_info_copy_name(info, name); 513 + *hash ^= full_name_hash((void *)salt, name->name, name->len); 514 + } 541 515 542 516 pr_debug("%s: ino=%lu size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n", 543 517 __func__, id->i_ino, size, dir_fh_len, child_fh_len, ··· 568 530 struct inode *child = NULL; 569 531 bool name_event = false; 570 532 unsigned int hash = 0; 533 + bool ondir = mask & FAN_ONDIR; 534 + struct pid *pid; 571 535 572 536 if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) { 573 537 /* ··· 577 537 * report the child fid for events reported on a non-dir child 578 538 * in addition to reporting the parent fid and maybe child name. 579 539 */ 580 - if ((fid_mode & FAN_REPORT_FID) && 581 - id != dirid && !(mask & FAN_ONDIR)) 540 + if ((fid_mode & FAN_REPORT_FID) && id != dirid && !ondir) 582 541 child = id; 583 542 584 543 id = dirid; ··· 598 559 if (!(fid_mode & FAN_REPORT_NAME)) { 599 560 name_event = !!child; 600 561 file_name = NULL; 601 - } else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) || 602 - !(mask & FAN_ONDIR)) { 562 + } else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) || !ondir) { 603 563 name_event = true; 604 564 } 605 565 } ··· 621 583 event = fanotify_alloc_perm_event(path, gfp); 622 584 } else if (name_event && (file_name || child)) { 623 585 event = fanotify_alloc_name_event(id, fsid, file_name, child, 624 - gfp); 586 + &hash, gfp); 625 587 } else if (fid_mode) { 626 - event = fanotify_alloc_fid_event(id, fsid, gfp); 588 + event = fanotify_alloc_fid_event(id, fsid, &hash, gfp); 627 589 } else { 628 - event = fanotify_alloc_path_event(path, gfp); 590 + event = fanotify_alloc_path_event(path, &hash, gfp); 629 591 } 630 592 631 593 if (!event) 632 594 goto out; 633 595 634 - /* 635 - * Use the victim inode instead of the watching inode as the id for 636 - * event queue, so event reported on parent is merged with event 637 - * reported on child when both directory and child watches exist. 638 - * Hash object id for queue merge. 639 - */ 640 - hash = hash_ptr(id, FANOTIFY_EVENT_HASH_BITS); 641 - fanotify_init_event(event, hash, mask); 642 596 if (FAN_GROUP_FLAG(group, FAN_REPORT_TID)) 643 - event->pid = get_pid(task_pid(current)); 597 + pid = get_pid(task_pid(current)); 644 598 else 645 - event->pid = get_pid(task_tgid(current)); 599 + pid = get_pid(task_tgid(current)); 600 + 601 + /* Mix event info, FAN_ONDIR flag and pid into event merge key */ 602 + hash ^= hash_long((unsigned long)pid | ondir, FANOTIFY_EVENT_HASH_BITS); 603 + fanotify_init_event(event, hash, mask); 604 + event->pid = pid; 646 605 647 606 out: 648 607 set_active_memcg(old_memcg);
+5
fs/notify/fanotify/fanotify.h
··· 115 115 info->name_len = 0; 116 116 } 117 117 118 + static inline unsigned int fanotify_info_len(struct fanotify_info *info) 119 + { 120 + return info->dir_fh_totlen + info->file_fh_totlen + info->name_len; 121 + } 122 + 118 123 static inline void fanotify_info_copy_name(struct fanotify_info *info, 119 124 const struct qstr *name) 120 125 {