Skip to content

Commit 7e3e5c6

Browse files
amir73iljankara
authored andcommitted
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/[email protected] Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]>
1 parent 8988f11 commit 7e3e5c6

File tree

2 files changed

+66
-26
lines changed

2 files changed

+66
-26
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/audit.h>
1515
#include <linux/sched/mm.h>
1616
#include <linux/statfs.h>
17+
#include <linux/stringhash.h>
1718

1819
#include "fanotify.h"
1920

@@ -22,12 +23,24 @@ static bool fanotify_path_equal(struct path *p1, struct path *p2)
2223
return p1->mnt == p2->mnt && p1->dentry == p2->dentry;
2324
}
2425

26+
static unsigned int fanotify_hash_path(const struct path *path)
27+
{
28+
return hash_ptr(path->dentry, FANOTIFY_EVENT_HASH_BITS) ^
29+
hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS);
30+
}
31+
2532
static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
2633
__kernel_fsid_t *fsid2)
2734
{
2835
return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
2936
}
3037

38+
static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
39+
{
40+
return hash_32(fsid->val[0], FANOTIFY_EVENT_HASH_BITS) ^
41+
hash_32(fsid->val[1], FANOTIFY_EVENT_HASH_BITS);
42+
}
43+
3144
static bool fanotify_fh_equal(struct fanotify_fh *fh1,
3245
struct fanotify_fh *fh2)
3346
{
@@ -38,6 +51,16 @@ static bool fanotify_fh_equal(struct fanotify_fh *fh1,
3851
!memcmp(fanotify_fh_buf(fh1), fanotify_fh_buf(fh2), fh1->len);
3952
}
4053

54+
static unsigned int fanotify_hash_fh(struct fanotify_fh *fh)
55+
{
56+
long salt = (long)fh->type | (long)fh->len << 8;
57+
58+
/*
59+
* full_name_hash() works long by long, so it handles fh buf optimally.
60+
*/
61+
return full_name_hash((void *)salt, fanotify_fh_buf(fh), fh->len);
62+
}
63+
4164
static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1,
4265
struct fanotify_fid_event *ffe2)
4366
{
@@ -325,7 +348,8 @@ static int fanotify_encode_fh_len(struct inode *inode)
325348
* Return 0 on failure to encode.
326349
*/
327350
static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
328-
unsigned int fh_len, gfp_t gfp)
351+
unsigned int fh_len, unsigned int *hash,
352+
gfp_t gfp)
329353
{
330354
int dwords, type = 0;
331355
char *ext_buf = NULL;
@@ -368,6 +392,9 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
368392
fh->type = type;
369393
fh->len = fh_len;
370394

395+
/* Mix fh into event merge key */
396+
*hash ^= fanotify_hash_fh(fh);
397+
371398
return FANOTIFY_FH_HDR_LEN + fh_len;
372399

373400
out_err:
@@ -421,6 +448,7 @@ static struct inode *fanotify_dfid_inode(u32 event_mask, const void *data,
421448
}
422449

423450
static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
451+
unsigned int *hash,
424452
gfp_t gfp)
425453
{
426454
struct fanotify_path_event *pevent;
@@ -431,6 +459,7 @@ static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
431459

432460
pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH;
433461
pevent->path = *path;
462+
*hash ^= fanotify_hash_path(path);
434463
path_get(path);
435464

436465
return &pevent->fae;
@@ -456,6 +485,7 @@ static struct fanotify_event *fanotify_alloc_perm_event(const struct path *path,
456485

457486
static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
458487
__kernel_fsid_t *fsid,
488+
unsigned int *hash,
459489
gfp_t gfp)
460490
{
461491
struct fanotify_fid_event *ffe;
@@ -466,16 +496,18 @@ static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
466496

467497
ffe->fae.type = FANOTIFY_EVENT_TYPE_FID;
468498
ffe->fsid = *fsid;
499+
*hash ^= fanotify_hash_fsid(fsid);
469500
fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id),
470-
gfp);
501+
hash, gfp);
471502

472503
return &ffe->fae;
473504
}
474505

475506
static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
476507
__kernel_fsid_t *fsid,
477-
const struct qstr *file_name,
508+
const struct qstr *name,
478509
struct inode *child,
510+
unsigned int *hash,
479511
gfp_t gfp)
480512
{
481513
struct fanotify_name_event *fne;
@@ -488,24 +520,30 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
488520
size = sizeof(*fne) + FANOTIFY_FH_HDR_LEN + dir_fh_len;
489521
if (child_fh_len)
490522
size += FANOTIFY_FH_HDR_LEN + child_fh_len;
491-
if (file_name)
492-
size += file_name->len + 1;
523+
if (name)
524+
size += name->len + 1;
493525
fne = kmalloc(size, gfp);
494526
if (!fne)
495527
return NULL;
496528

497529
fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME;
498530
fne->fsid = *fsid;
531+
*hash ^= fanotify_hash_fsid(fsid);
499532
info = &fne->info;
500533
fanotify_info_init(info);
501534
dfh = fanotify_info_dir_fh(info);
502-
info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, 0);
535+
info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, hash, 0);
503536
if (child_fh_len) {
504537
ffh = fanotify_info_file_fh(info);
505-
info->file_fh_totlen = fanotify_encode_fh(ffh, child, child_fh_len, 0);
538+
info->file_fh_totlen = fanotify_encode_fh(ffh, child,
539+
child_fh_len, hash, 0);
540+
}
541+
if (name) {
542+
long salt = name->len;
543+
544+
fanotify_info_copy_name(info, name);
545+
*hash ^= full_name_hash((void *)salt, name->name, name->len);
506546
}
507-
if (file_name)
508-
fanotify_info_copy_name(info, file_name);
509547

510548
pr_debug("%s: ino=%lu size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n",
511549
__func__, id->i_ino, size, dir_fh_len, child_fh_len,
@@ -530,15 +568,16 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
530568
struct inode *child = NULL;
531569
bool name_event = false;
532570
unsigned int hash = 0;
571+
bool ondir = mask & FAN_ONDIR;
572+
struct pid *pid;
533573

534574
if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) {
535575
/*
536576
* With both flags FAN_REPORT_DIR_FID and FAN_REPORT_FID, we
537577
* report the child fid for events reported on a non-dir child
538578
* in addition to reporting the parent fid and maybe child name.
539579
*/
540-
if ((fid_mode & FAN_REPORT_FID) &&
541-
id != dirid && !(mask & FAN_ONDIR))
580+
if ((fid_mode & FAN_REPORT_FID) && id != dirid && !ondir)
542581
child = id;
543582

544583
id = dirid;
@@ -559,8 +598,7 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
559598
if (!(fid_mode & FAN_REPORT_NAME)) {
560599
name_event = !!child;
561600
file_name = NULL;
562-
} else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) ||
563-
!(mask & FAN_ONDIR)) {
601+
} else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) || !ondir) {
564602
name_event = true;
565603
}
566604
}
@@ -583,28 +621,25 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
583621
event = fanotify_alloc_perm_event(path, gfp);
584622
} else if (name_event && (file_name || child)) {
585623
event = fanotify_alloc_name_event(id, fsid, file_name, child,
586-
gfp);
624+
&hash, gfp);
587625
} else if (fid_mode) {
588-
event = fanotify_alloc_fid_event(id, fsid, gfp);
626+
event = fanotify_alloc_fid_event(id, fsid, &hash, gfp);
589627
} else {
590-
event = fanotify_alloc_path_event(path, gfp);
628+
event = fanotify_alloc_path_event(path, &hash, gfp);
591629
}
592630

593631
if (!event)
594632
goto out;
595633

596-
/*
597-
* Use the victim inode instead of the watching inode as the id for
598-
* event queue, so event reported on parent is merged with event
599-
* reported on child when both directory and child watches exist.
600-
* Hash object id for queue merge.
601-
*/
602-
hash = hash_ptr(id, FANOTIFY_EVENT_HASH_BITS);
603-
fanotify_init_event(event, hash, mask);
604634
if (FAN_GROUP_FLAG(group, FAN_REPORT_TID))
605-
event->pid = get_pid(task_pid(current));
635+
pid = get_pid(task_pid(current));
606636
else
607-
event->pid = get_pid(task_tgid(current));
637+
pid = get_pid(task_tgid(current));
638+
639+
/* Mix event info, FAN_ONDIR flag and pid into event merge key */
640+
hash ^= hash_long((unsigned long)pid | ondir, FANOTIFY_EVENT_HASH_BITS);
641+
fanotify_init_event(event, hash, mask);
642+
event->pid = pid;
608643

609644
out:
610645
set_active_memcg(old_memcg);

fs/notify/fanotify/fanotify.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ static inline void fanotify_info_init(struct fanotify_info *info)
115115
info->name_len = 0;
116116
}
117117

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+
118123
static inline void fanotify_info_copy_name(struct fanotify_info *info,
119124
const struct qstr *name)
120125
{

0 commit comments

Comments
 (0)