Skip to content

Commit afc894c

Browse files
committed
fanotify: Store fanotify handles differently
Currently, struct fanotify_fid groups fsid and file handle and is unioned together with struct path to save space. Also there is fh_type and fh_len directly in struct fanotify_event to avoid padding overhead. In the follwing patches, we will be adding more event types and this packing makes code difficult to follow. So unpack everything and create struct fanotify_fh which groups members logically related to file handle to make code easier to follow. In the following patch we will pack things again differently to make events smaller. Signed-off-by: Jan Kara <[email protected]>
1 parent a741c2f commit afc894c

File tree

3 files changed

+145
-106
lines changed

3 files changed

+145
-106
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,31 @@
1717

1818
#include "fanotify.h"
1919

20+
static bool fanotify_path_equal(struct path *p1, struct path *p2)
21+
{
22+
return p1->mnt == p2->mnt && p1->dentry == p2->dentry;
23+
}
24+
25+
static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
26+
__kernel_fsid_t *fsid2)
27+
{
28+
return fsid1->val[0] == fsid1->val[0] && fsid2->val[1] == fsid2->val[1];
29+
}
30+
31+
static bool fanotify_fh_equal(struct fanotify_fh *fh1,
32+
struct fanotify_fh *fh2)
33+
{
34+
if (fh1->type != fh2->type || fh1->len != fh2->len)
35+
return false;
36+
37+
/* Do not merge events if we failed to encode fh */
38+
if (fh1->type == FILEID_INVALID)
39+
return false;
40+
41+
return !fh1->len ||
42+
!memcmp(fanotify_fh_buf(fh1), fanotify_fh_buf(fh2), fh1->len);
43+
}
44+
2045
static bool should_merge(struct fsnotify_event *old_fsn,
2146
struct fsnotify_event *new_fsn)
2247
{
@@ -27,12 +52,12 @@ static bool should_merge(struct fsnotify_event *old_fsn,
2752
new = FANOTIFY_E(new_fsn);
2853

2954
if (old_fsn->objectid != new_fsn->objectid || old->pid != new->pid ||
30-
old->fh_type != new->fh_type || old->fh_len != new->fh_len)
55+
old->fh.type != new->fh.type)
3156
return false;
3257

3358
if (fanotify_event_has_path(old)) {
34-
return old->path.mnt == new->path.mnt &&
35-
old->path.dentry == new->path.dentry;
59+
return fanotify_path_equal(fanotify_event_path(old),
60+
fanotify_event_path(new));
3661
} else if (fanotify_event_has_fid(old)) {
3762
/*
3863
* We want to merge many dirent events in the same dir (i.e.
@@ -42,8 +67,11 @@ static bool should_merge(struct fsnotify_event *old_fsn,
4267
* mask FAN_CREATE|FAN_DELETE|FAN_ONDIR if it describes mkdir+
4368
* unlink pair or rmdir+create pair of events.
4469
*/
45-
return (old->mask & FS_ISDIR) == (new->mask & FS_ISDIR) &&
46-
fanotify_fid_equal(&old->fid, &new->fid, old->fh_len);
70+
if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR))
71+
return false;
72+
73+
return fanotify_fsid_equal(&old->fsid, &new->fsid) &&
74+
fanotify_fh_equal(&old->fh, &new->fh);
4775
}
4876

4977
/* Do not merge events if we failed to encode fid */
@@ -213,15 +241,14 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
213241
return test_mask & user_mask;
214242
}
215243

216-
static int fanotify_encode_fid(struct fanotify_event *event,
217-
struct inode *inode, gfp_t gfp,
218-
__kernel_fsid_t *fsid)
244+
static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
245+
gfp_t gfp)
219246
{
220-
struct fanotify_fid *fid = &event->fid;
221-
int dwords, bytes = 0;
222-
int err, type;
247+
int dwords, type, bytes = 0;
248+
char *ext_buf = NULL;
249+
void *buf = fh->buf;
250+
int err;
223251

224-
fid->ext_fh = NULL;
225252
dwords = 0;
226253
err = -ENOENT;
227254
type = exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
@@ -232,31 +259,32 @@ static int fanotify_encode_fid(struct fanotify_event *event,
232259
if (bytes > FANOTIFY_INLINE_FH_LEN) {
233260
/* Treat failure to allocate fh as failure to allocate event */
234261
err = -ENOMEM;
235-
fid->ext_fh = kmalloc(bytes, gfp);
236-
if (!fid->ext_fh)
262+
ext_buf = kmalloc(bytes, gfp);
263+
if (!ext_buf)
237264
goto out_err;
265+
266+
*fanotify_fh_ext_buf_ptr(fh) = ext_buf;
267+
buf = ext_buf;
238268
}
239269

240-
type = exportfs_encode_inode_fh(inode, fanotify_fid_fh(fid, bytes),
241-
&dwords, NULL);
270+
type = exportfs_encode_inode_fh(inode, buf, &dwords, NULL);
242271
err = -EINVAL;
243272
if (!type || type == FILEID_INVALID || bytes != dwords << 2)
244273
goto out_err;
245274

246-
fid->fsid = *fsid;
247-
event->fh_len = bytes;
275+
fh->type = type;
276+
fh->len = bytes;
248277

249-
return type;
278+
return;
250279

251280
out_err:
252-
pr_warn_ratelimited("fanotify: failed to encode fid (fsid=%x.%x, "
253-
"type=%d, bytes=%d, err=%i)\n",
254-
fsid->val[0], fsid->val[1], type, bytes, err);
255-
kfree(fid->ext_fh);
256-
fid->ext_fh = NULL;
257-
event->fh_len = 0;
258-
259-
return FILEID_INVALID;
281+
pr_warn_ratelimited("fanotify: failed to encode fid (type=%d, len=%d, err=%i)\n",
282+
type, bytes, err);
283+
kfree(ext_buf);
284+
*fanotify_fh_ext_buf_ptr(fh) = NULL;
285+
/* Report the event without a file identifier on encode error */
286+
fh->type = FILEID_INVALID;
287+
fh->len = 0;
260288
}
261289

262290
/*
@@ -326,16 +354,17 @@ init: __maybe_unused
326354
event->pid = get_pid(task_pid(current));
327355
else
328356
event->pid = get_pid(task_tgid(current));
329-
event->fh_len = 0;
357+
event->fh.len = 0;
330358
if (id && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
331-
/* Report the event without a file identifier on encode error */
332-
event->fh_type = fanotify_encode_fid(event, id, gfp, fsid);
359+
event->fsid = *fsid;
360+
if (id)
361+
fanotify_encode_fh(&event->fh, id, gfp);
333362
} else if (path) {
334-
event->fh_type = FILEID_ROOT;
363+
event->fh.type = FILEID_ROOT;
335364
event->path = *path;
336365
path_get(path);
337366
} else {
338-
event->fh_type = FILEID_INVALID;
367+
event->fh.type = FILEID_INVALID;
339368
event->path.mnt = NULL;
340369
event->path.dentry = NULL;
341370
}
@@ -483,8 +512,8 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event)
483512
event = FANOTIFY_E(fsn_event);
484513
if (fanotify_event_has_path(event))
485514
path_put(&event->path);
486-
else if (fanotify_event_has_ext_fh(event))
487-
kfree(event->fid.ext_fh);
515+
else if (fanotify_fh_has_ext_buf(&event->fh))
516+
kfree(fanotify_fh_ext_buf(&event->fh));
488517
put_pid(event->pid);
489518
if (fanotify_is_perm_event(event->mask)) {
490519
kmem_cache_free(fanotify_perm_event_cachep,

fs/notify/fanotify/fanotify.h

Lines changed: 62 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,37 @@ enum {
1818

1919
/*
2020
* 3 dwords are sufficient for most local fs (64bit ino, 32bit generation).
21-
* For 32bit arch, fid increases the size of fanotify_event by 12 bytes and
22-
* fh_* fields increase the size of fanotify_event by another 4 bytes.
23-
* For 64bit arch, fid increases the size of fanotify_fid by 8 bytes and
24-
* fh_* fields are packed in a hole after mask.
21+
* fh buf should be dword aligned. On 64bit arch, the ext_buf pointer is
22+
* stored in either the first or last 2 dwords.
2523
*/
26-
#if BITS_PER_LONG == 32
2724
#define FANOTIFY_INLINE_FH_LEN (3 << 2)
28-
#else
29-
#define FANOTIFY_INLINE_FH_LEN (4 << 2)
30-
#endif
3125

32-
struct fanotify_fid {
33-
__kernel_fsid_t fsid;
34-
union {
35-
unsigned char fh[FANOTIFY_INLINE_FH_LEN];
36-
unsigned char *ext_fh;
37-
};
38-
};
26+
struct fanotify_fh {
27+
unsigned char buf[FANOTIFY_INLINE_FH_LEN];
28+
u8 type;
29+
u8 len;
30+
} __aligned(4);
31+
32+
static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh)
33+
{
34+
return fh->len > FANOTIFY_INLINE_FH_LEN;
35+
}
3936

40-
static inline void *fanotify_fid_fh(struct fanotify_fid *fid,
41-
unsigned int fh_len)
37+
static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh)
4238
{
43-
return fh_len <= FANOTIFY_INLINE_FH_LEN ? fid->fh : fid->ext_fh;
39+
BUILD_BUG_ON(__alignof__(char *) - 4 + sizeof(char *) >
40+
FANOTIFY_INLINE_FH_LEN);
41+
return (char **)ALIGN((unsigned long)(fh->buf), __alignof__(char *));
4442
}
4543

46-
static inline bool fanotify_fid_equal(struct fanotify_fid *fid1,
47-
struct fanotify_fid *fid2,
48-
unsigned int fh_len)
44+
static inline void *fanotify_fh_ext_buf(struct fanotify_fh *fh)
4945
{
50-
return fid1->fsid.val[0] == fid2->fsid.val[0] &&
51-
fid1->fsid.val[1] == fid2->fsid.val[1] &&
52-
!memcmp(fanotify_fid_fh(fid1, fh_len),
53-
fanotify_fid_fh(fid2, fh_len), fh_len);
46+
return *fanotify_fh_ext_buf_ptr(fh);
47+
}
48+
49+
static inline void *fanotify_fh_buf(struct fanotify_fh *fh)
50+
{
51+
return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh->buf;
5452
}
5553

5654
/*
@@ -62,50 +60,53 @@ struct fanotify_event {
6260
struct fsnotify_event fse;
6361
u32 mask;
6462
/*
65-
* Those fields are outside fanotify_fid to pack fanotify_event nicely
66-
* on 64bit arch and to use fh_type as an indication of whether path
67-
* or fid are used in the union:
68-
* FILEID_ROOT (0) for path, > 0 for fid, FILEID_INVALID for neither.
63+
* With FAN_REPORT_FID, we do not hold any reference on the
64+
* victim object. Instead we store its NFS file handle and its
65+
* filesystem's fsid as a unique identifier.
66+
*/
67+
__kernel_fsid_t fsid;
68+
struct fanotify_fh fh;
69+
/*
70+
* We hold ref to this path so it may be dereferenced at any
71+
* point during this object's lifetime
6972
*/
70-
u8 fh_type;
71-
u8 fh_len;
72-
u16 pad;
73-
union {
74-
/*
75-
* We hold ref to this path so it may be dereferenced at any
76-
* point during this object's lifetime
77-
*/
78-
struct path path;
79-
/*
80-
* With FAN_REPORT_FID, we do not hold any reference on the
81-
* victim object. Instead we store its NFS file handle and its
82-
* filesystem's fsid as a unique identifier.
83-
*/
84-
struct fanotify_fid fid;
85-
};
73+
struct path path;
8674
struct pid *pid;
8775
};
8876

8977
static inline bool fanotify_event_has_path(struct fanotify_event *event)
9078
{
91-
return event->fh_type == FILEID_ROOT;
79+
return event->fh.type == FILEID_ROOT;
9280
}
9381

9482
static inline bool fanotify_event_has_fid(struct fanotify_event *event)
9583
{
96-
return event->fh_type != FILEID_ROOT &&
97-
event->fh_type != FILEID_INVALID;
84+
return event->fh.type != FILEID_ROOT &&
85+
event->fh.type != FILEID_INVALID;
9886
}
9987

100-
static inline bool fanotify_event_has_ext_fh(struct fanotify_event *event)
88+
static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
10189
{
102-
return fanotify_event_has_fid(event) &&
103-
event->fh_len > FANOTIFY_INLINE_FH_LEN;
90+
if (fanotify_event_has_fid(event))
91+
return &event->fsid;
92+
else
93+
return NULL;
10494
}
10595

106-
static inline void *fanotify_event_fh(struct fanotify_event *event)
96+
static inline struct fanotify_fh *fanotify_event_object_fh(
97+
struct fanotify_event *event)
10798
{
108-
return fanotify_fid_fh(&event->fid, event->fh_len);
99+
if (fanotify_event_has_fid(event))
100+
return &event->fh;
101+
else
102+
return NULL;
103+
}
104+
105+
static inline int fanotify_event_object_fh_len(struct fanotify_event *event)
106+
{
107+
struct fanotify_fh *fh = fanotify_event_object_fh(event);
108+
109+
return fh ? fh->len : 0;
109110
}
110111

111112
/*
@@ -139,6 +140,14 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
139140
return container_of(fse, struct fanotify_event, fse);
140141
}
141142

143+
static inline struct path *fanotify_event_path(struct fanotify_event *event)
144+
{
145+
if (fanotify_event_has_path(event))
146+
return &event->path;
147+
else
148+
return NULL;
149+
}
150+
142151
struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
143152
struct inode *inode, u32 mask,
144153
const void *data, int data_type,

0 commit comments

Comments
 (0)