xref: /linux/include/linux/watch_queue.h (revision c02b872a7ca7842e4cdbbf621f77607d0a655f83)
1c73be61cSDavid Howells // SPDX-License-Identifier: GPL-2.0
2c73be61cSDavid Howells /* User-mappable watch queue
3c73be61cSDavid Howells  *
4c73be61cSDavid Howells  * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
5c73be61cSDavid Howells  * Written by David Howells (dhowells@redhat.com)
6c73be61cSDavid Howells  *
7*c02b872aSMauro Carvalho Chehab  * See Documentation/core-api/watch_queue.rst
8c73be61cSDavid Howells  */
9c73be61cSDavid Howells 
10c73be61cSDavid Howells #ifndef _LINUX_WATCH_QUEUE_H
11c73be61cSDavid Howells #define _LINUX_WATCH_QUEUE_H
12c73be61cSDavid Howells 
13c73be61cSDavid Howells #include <uapi/linux/watch_queue.h>
14c73be61cSDavid Howells #include <linux/kref.h>
15c73be61cSDavid Howells #include <linux/rcupdate.h>
16c73be61cSDavid Howells 
17c73be61cSDavid Howells #ifdef CONFIG_WATCH_QUEUE
18c73be61cSDavid Howells 
19c73be61cSDavid Howells struct cred;
20c73be61cSDavid Howells 
21c73be61cSDavid Howells struct watch_type_filter {
22c73be61cSDavid Howells 	enum watch_notification_type type;
23c73be61cSDavid Howells 	__u32		subtype_filter[1];	/* Bitmask of subtypes to filter on */
24c73be61cSDavid Howells 	__u32		info_filter;		/* Filter on watch_notification::info */
25c73be61cSDavid Howells 	__u32		info_mask;		/* Mask of relevant bits in info_filter */
26c73be61cSDavid Howells };
27c73be61cSDavid Howells 
28c73be61cSDavid Howells struct watch_filter {
29c73be61cSDavid Howells 	union {
30c73be61cSDavid Howells 		struct rcu_head	rcu;
31c993ee0fSDavid Howells 		/* Bitmask of accepted types */
32c993ee0fSDavid Howells 		DECLARE_BITMAP(type_filter, WATCH_TYPE__NR);
33c73be61cSDavid Howells 	};
34c73be61cSDavid Howells 	u32			nr_filters;	/* Number of filters */
35c73be61cSDavid Howells 	struct watch_type_filter filters[];
36c73be61cSDavid Howells };
37c73be61cSDavid Howells 
38c73be61cSDavid Howells struct watch_queue {
39c73be61cSDavid Howells 	struct rcu_head		rcu;
40c73be61cSDavid Howells 	struct watch_filter __rcu *filter;
41c73be61cSDavid Howells 	struct pipe_inode_info	*pipe;		/* The pipe we're using as a buffer */
42c73be61cSDavid Howells 	struct hlist_head	watches;	/* Contributory watches */
43c73be61cSDavid Howells 	struct page		**notes;	/* Preallocated notifications */
44c73be61cSDavid Howells 	unsigned long		*notes_bitmap;	/* Allocation bitmap for notes */
45c73be61cSDavid Howells 	struct kref		usage;		/* Object usage count */
46c73be61cSDavid Howells 	spinlock_t		lock;
47c73be61cSDavid Howells 	unsigned int		nr_notes;	/* Number of notes */
48c73be61cSDavid Howells 	unsigned int		nr_pages;	/* Number of pages in notes[] */
49c73be61cSDavid Howells 	bool			defunct;	/* T when queues closed */
50c73be61cSDavid Howells };
51c73be61cSDavid Howells 
52c73be61cSDavid Howells /*
53c73be61cSDavid Howells  * Representation of a watch on an object.
54c73be61cSDavid Howells  */
55c73be61cSDavid Howells struct watch {
56c73be61cSDavid Howells 	union {
57c73be61cSDavid Howells 		struct rcu_head	rcu;
58c73be61cSDavid Howells 		u32		info_id;	/* ID to be OR'd in to info field */
59c73be61cSDavid Howells 	};
60c73be61cSDavid Howells 	struct watch_queue __rcu *queue;	/* Queue to post events to */
61c73be61cSDavid Howells 	struct hlist_node	queue_node;	/* Link in queue->watches */
62c73be61cSDavid Howells 	struct watch_list __rcu	*watch_list;
63c73be61cSDavid Howells 	struct hlist_node	list_node;	/* Link in watch_list->watchers */
64c73be61cSDavid Howells 	const struct cred	*cred;		/* Creds of the owner of the watch */
65c73be61cSDavid Howells 	void			*private;	/* Private data for the watched object */
66c73be61cSDavid Howells 	u64			id;		/* Internal identifier */
67c73be61cSDavid Howells 	struct kref		usage;		/* Object usage count */
68c73be61cSDavid Howells };
69c73be61cSDavid Howells 
70c73be61cSDavid Howells /*
71c73be61cSDavid Howells  * List of watches on an object.
72c73be61cSDavid Howells  */
73c73be61cSDavid Howells struct watch_list {
74c73be61cSDavid Howells 	struct rcu_head		rcu;
75c73be61cSDavid Howells 	struct hlist_head	watchers;
76c73be61cSDavid Howells 	void (*release_watch)(struct watch *);
77c73be61cSDavid Howells 	spinlock_t		lock;
78c73be61cSDavid Howells };
79c73be61cSDavid Howells 
80c73be61cSDavid Howells extern void __post_watch_notification(struct watch_list *,
81c73be61cSDavid Howells 				      struct watch_notification *,
82c73be61cSDavid Howells 				      const struct cred *,
83c73be61cSDavid Howells 				      u64);
84c73be61cSDavid Howells extern struct watch_queue *get_watch_queue(int);
85c73be61cSDavid Howells extern void put_watch_queue(struct watch_queue *);
86c73be61cSDavid Howells extern void init_watch(struct watch *, struct watch_queue *);
87c73be61cSDavid Howells extern int add_watch_to_object(struct watch *, struct watch_list *);
88c73be61cSDavid Howells extern int remove_watch_from_object(struct watch_list *, struct watch_queue *, u64, bool);
89c73be61cSDavid Howells extern long watch_queue_set_size(struct pipe_inode_info *, unsigned int);
90c73be61cSDavid Howells extern long watch_queue_set_filter(struct pipe_inode_info *,
91c73be61cSDavid Howells 				   struct watch_notification_filter __user *);
92c73be61cSDavid Howells extern int watch_queue_init(struct pipe_inode_info *);
93c73be61cSDavid Howells extern void watch_queue_clear(struct watch_queue *);
94c73be61cSDavid Howells 
95c73be61cSDavid Howells static inline void init_watch_list(struct watch_list *wlist,
96c73be61cSDavid Howells 				   void (*release_watch)(struct watch *))
97c73be61cSDavid Howells {
98c73be61cSDavid Howells 	INIT_HLIST_HEAD(&wlist->watchers);
99c73be61cSDavid Howells 	spin_lock_init(&wlist->lock);
100c73be61cSDavid Howells 	wlist->release_watch = release_watch;
101c73be61cSDavid Howells }
102c73be61cSDavid Howells 
103c73be61cSDavid Howells static inline void post_watch_notification(struct watch_list *wlist,
104c73be61cSDavid Howells 					   struct watch_notification *n,
105c73be61cSDavid Howells 					   const struct cred *cred,
106c73be61cSDavid Howells 					   u64 id)
107c73be61cSDavid Howells {
108c73be61cSDavid Howells 	if (unlikely(wlist))
109c73be61cSDavid Howells 		__post_watch_notification(wlist, n, cred, id);
110c73be61cSDavid Howells }
111c73be61cSDavid Howells 
112c73be61cSDavid Howells static inline void remove_watch_list(struct watch_list *wlist, u64 id)
113c73be61cSDavid Howells {
114c73be61cSDavid Howells 	if (wlist) {
115c73be61cSDavid Howells 		remove_watch_from_object(wlist, NULL, id, true);
116c73be61cSDavid Howells 		kfree_rcu(wlist, rcu);
117c73be61cSDavid Howells 	}
118c73be61cSDavid Howells }
119c73be61cSDavid Howells 
120c73be61cSDavid Howells /**
121c73be61cSDavid Howells  * watch_sizeof - Calculate the information part of the size of a watch record,
122c73be61cSDavid Howells  * given the structure size.
123c73be61cSDavid Howells  */
124c73be61cSDavid Howells #define watch_sizeof(STRUCT) (sizeof(STRUCT) << WATCH_INFO_LENGTH__SHIFT)
125c73be61cSDavid Howells 
1268a018eb5SQian Cai #else
1278a018eb5SQian Cai static inline int watch_queue_init(struct pipe_inode_info *pipe)
1288a018eb5SQian Cai {
1298a018eb5SQian Cai 	return -ENOPKG;
1308a018eb5SQian Cai }
1318a018eb5SQian Cai 
132c73be61cSDavid Howells #endif
133c73be61cSDavid Howells 
134c73be61cSDavid Howells #endif /* _LINUX_WATCH_QUEUE_H */
135