summaryrefslogtreecommitdiff
path: root/include/linux/watch_queue.h
blob: 429c7b6afeada77dc624e139a2a93fe7e47fbde8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// SPDX-License-Identifier: GPL-2.0
/* User-mappable watch queue
 *
 * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * See Documentation/core-api/watch_queue.rst
 */

#ifndef _LINUX_WATCH_QUEUE_H
#define _LINUX_WATCH_QUEUE_H

#include <uapi/linux/watch_queue.h>
#include <linux/kref.h>
#include <linux/rcupdate.h>

#ifdef CONFIG_WATCH_QUEUE

struct cred;

struct watch_type_filter {
	enum watch_notification_type type;
	__u32		subtype_filter[1];	/* Bitmask of subtypes to filter on */
	__u32		info_filter;		/* Filter on watch_notification::info */
	__u32		info_mask;		/* Mask of relevant bits in info_filter */
};

struct watch_filter {
	union {
		struct rcu_head	rcu;
		/* Bitmask of accepted types */
		DECLARE_BITMAP(type_filter, WATCH_TYPE__NR);
	};
	u32			nr_filters;	/* Number of filters */
	struct watch_type_filter filters[] __counted_by(nr_filters);
};

struct watch_queue {
	struct rcu_head		rcu;
	struct watch_filter __rcu *filter;
	struct pipe_inode_info	*pipe;		/* Pipe we use as a buffer, NULL if queue closed */
	struct hlist_head	watches;	/* Contributory watches */
	struct page		**notes;	/* Preallocated notifications */
	unsigned long		*notes_bitmap;	/* Allocation bitmap for notes */
	struct kref		usage;		/* Object usage count */
	spinlock_t		lock;
	unsigned int		nr_notes;	/* Number of notes */
	unsigned int		nr_pages;	/* Number of pages in notes[] */
};

/*
 * Representation of a watch on an object.
 */
struct watch {
	union {
		struct rcu_head	rcu;
		u32		info_id;	/* ID to be OR'd in to info field */
	};
	struct watch_queue __rcu *queue;	/* Queue to post events to */
	struct hlist_node	queue_node;	/* Link in queue->watches */
	struct watch_list __rcu	*watch_list;
	struct hlist_node	list_node;	/* Link in watch_list->watchers */
	const struct cred	*cred;		/* Creds of the owner of the watch */
	void			*private;	/* Private data for the watched object */
	u64			id;		/* Internal identifier */
	struct kref		usage;		/* Object usage count */
};

/*
 * List of watches on an object.
 */
struct watch_list {
	struct rcu_head		rcu;
	struct hlist_head	watchers;
	void (*release_watch)(struct watch *);
	spinlock_t		lock;
};

extern void __post_watch_notification(struct watch_list *,
				      struct watch_notification *,
				      const struct cred *,
				      u64);
extern struct watch_queue *get_watch_queue(int);
extern void put_watch_queue(struct watch_queue *);
extern void init_watch(struct watch *, struct watch_queue *);
extern int add_watch_to_object(struct watch *, struct watch_list *);
extern int remove_watch_from_object(struct watch_list *, struct watch_queue *, u64, bool);
extern long watch_queue_set_size(struct pipe_inode_info *, unsigned int);
extern long watch_queue_set_filter(struct pipe_inode_info *,
				   struct watch_notification_filter __user *);
extern int watch_queue_init(struct pipe_inode_info *);
extern void watch_queue_clear(struct watch_queue *);

static inline void init_watch_list(struct watch_list *wlist,
				   void (*release_watch)(struct watch *))
{
	INIT_HLIST_HEAD(&wlist->watchers);
	spin_lock_init(&wlist->lock);
	wlist->release_watch = release_watch;
}

static inline void post_watch_notification(struct watch_list *wlist,
					   struct watch_notification *n,
					   const struct cred *cred,
					   u64 id)
{
	if (unlikely(wlist))
		__post_watch_notification(wlist, n, cred, id);
}

static inline void remove_watch_list(struct watch_list *wlist, u64 id)
{
	if (wlist) {
		remove_watch_from_object(wlist, NULL, id, true);
		kfree_rcu(wlist, rcu);
	}
}

/**
 * watch_sizeof - Calculate the information part of the size of a watch record,
 * given the structure size.
 */
#define watch_sizeof(STRUCT) (sizeof(STRUCT) << WATCH_INFO_LENGTH__SHIFT)

#else
static inline int watch_queue_init(struct pipe_inode_info *pipe)
{
	return -ENOPKG;
}

#endif

#endif /* _LINUX_WATCH_QUEUE_H */