xref: /haiku/docs/develop/file_systems/node_monitoring.rst (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
1Node Monitoring
2===============
3
4Creation Date: January 16, 2003
5Author(s): Axel Dörfler
6
7
8This document describes the feature of the BeOS kernel to monitor nodes.
9First, there is an explanation of what kind of functionality we have to
10reproduce (along with the higher level API), then we will present the
11implementation in OpenBeOS.
12
13Requirements - Exported Functionality in BeOS
14---------------------------------------------
15
16From user-level, BeOS exports the following API as found in the
17storage/NodeMonitor.h header file:
18
19::
20
21       status_t watch_node(const node_ref *node,
22           uint32 flags,
23           BMessenger target);
24
25       status_t watch_node(const node_ref *node,
26           uint32 flags,
27           const BHandler *handler,
28           const BLooper *looper = NULL);
29
30       status_t stop_watching(BMessenger target);
31
32       status_t stop_watching(const BHandler *handler,
33           const BLooper *looper = NULL);
34
35
36The kernel also exports two other functions to be used from file system
37add-ons that causes the kernel to send out notification messages:
38
39::
40
41       int notify_listener(int op, nspace_id nsid,
42           vnode_id vnida, vnode_id vnidb,
43           vnode_id vnidc, const char *name);
44       int send_notification(port_id port, long token,
45           ulong what, long op, nspace_id nsida,
46           nspace_id nsidb, vnode_id vnida,
47           vnode_id vnidb, vnode_id vnidc,
48           const char *name);
49
50
51The latter is only used for live query updates, but is obviously called
52by the former. The port/token pair identify a unique BLooper/BHandler
53pair, and it used internally to address those high-level objects from
54the kernel.
55
56When a file system calls the ``notify_listener()`` function, it will
57have a look if there are monitors for that node which meet the specified
58constraints - and it will call ``send_notification()`` for every single
59message to be send.
60
61Each of the parameters ``vnida - vnidc`` has a dedicated meaning:
62
63-  **vnida:** the parent directory of the "main" node
64-  **vnidb:** the target parent directory for a move
65-  **vnidc:** the node that has triggered the notification to be send
66
67The flags parameter in ``watch_node()`` understands the following
68constants:
69
70-  **B_STOP_WATCHING**
71   watch_node() will stop to watch the specified node.
72-  **B_WATCH_NAME**
73   name changes are notified through a B_ENTRY_MOVED opcode.
74-  **B_WATCH_STAT**
75   changes to the node's stat structure are notified with a
76   B_STAT_CHANGED code.
77-  **B_WATCH_ATTR**
78   attribute changes will cause a B_ATTR_CHANGED to be send.
79-  **B_WATCH_DIRECTORY**
80   notifies on changes made to the specified directory, i.e.
81   B_ENTRY_REMOVED, B_ENTRY_CREATED
82-  **B_WATCH_ALL**
83   is a short-hand for the flags above.
84-  **B_WATCH_MOUNT**
85   causes B_DEVICE_MOUNTED and B_DEVICE_UNMOUNTED to be send.
86
87Node monitors are maintained per team - every team can have up to 4096
88monitors, although there exists a private kernel call to raise this
89limit (for example, Tracker is using it intensively).
90
91The kernel is able to send the BMessages directly to the specified
92BLooper and BHandler; it achieves this using the application kit's token
93mechanism. The message is constructed manually in the kernel, it doesn't
94use any application kit services.
95
96|
97
98Meeting the Requirements in an Optimal Way - Implementation in OpenBeOS
99-----------------------------------------------------------------------
100
101If you assume that every file operation could trigger a notification
102message to be send, it's clear that the node monitoring system must be
103optimized for sending messages. For every call to ``notify_listener()``,
104the kernel must check if there are any monitors for the node that was
105updated.
106
107Those monitors are put into a hash table which has the device number and
108the vnode ID as keys. Each of the monitors maintains a list of listeners
109which specify which port/token pair should be notified for what change.
110Since the vnodes are created/deleted as needed from the kernel, the node
111monitor is maintained independently from them; a simple pointer from a
112vnode to its monitor is not possible.
113
114The main structures that are involved in providing the node monitoring
115functionality look like this:
116
117::
118
119       struct monitor_listener {
120           monitor_listener    *next;
121           monitor_listener    *prev;
122           list_link       monitor_link;
123           port_id         port;
124           int32           token;
125           uint32          flags;
126           node_monitor        *monitor;
127       };
128
129       struct node_monitor {
130           node_monitor        *next;
131           mount_id        device;
132           vnode_id        node;
133           struct list     listeners;
134       };
135
136
137The relevant part of the I/O context structure is this:
138
139::
140
141       struct io_context {
142           ...
143           struct list     node_monitors;
144           uint32          num_monitors;
145           uint32          max_monitors;
146       };
147
148
149If you call ``watch_node()`` on a file with a flags parameter unequal to
150B_STOP_WATCHING, the following will happen in the node monitor:
151
152#. The ``add_node_monitor()`` function does a hash lookup for the
153   device/vnode pair. If there is no ``node_monitor`` yet for this pair,
154   a new one will be created.
155#. The list of listeners is scanned for the provided port/token pair
156   (the BLooper/BHandler pointer will already be translated in
157   user-space), and the new flag is or'd to the old field, or a new
158   ``monitor_listener`` is created if necessary - in the latter case,
159   the team's node monitor counter is incremented.
160
161If it's called with B_STOP_WATCHING defined, the reverse operation take
162effect, and the ``monitor`` field is used to see if this monitor don't
163have any listeners anymore, in which case it will be removed.
164
165Note the presence of the ``max_monitors`` - there is no hard limit the
166kernel exposes to userland applications; the listeners are maintained in
167a doubly-linked list.
168
169If a team is shut down, all listeners from its I/O context will be
170removed - since every listener stores a pointer to its monitor,
171determining the monitors that can be removed because of this operation
172is very cheap.
173
174The ``notify_listener()`` also only does a hash lookup for the
175device/node pair it got from the file system, and sends out as many
176notifications as specified by the listeners of the monitor that belong
177to that node.
178
179If a node is deleted from the disk, the corresponding ``node_monitor``
180and its listeners will be removed as well, to prevent watching a new
181file that accidently happen to have the same device/node pair (as is
182possible with BFS, for example).
183
184Differences Between Both Implementations
185----------------------------------------
186
187Although the aim was to create a completely compatible monitoring
188implementation, there are some notable differences between the two.
189
190BeOS reserves a certain number of slots for calls to ``watch_node()`` -
191each call to that function will use one slot, even if you call it twice
192for the same node. OpenBeOS, however, will always use one slot per node
193- you could call ``watch_node()`` several times, but you would waste
194only one slot.
195
196While this is an implementational detail, it also causes a change in
197behaviour for applications; in BeOS, applications will get one message
198for every ``watch_node()`` call, in OpenBeOS, you'll get only one
199message per node. If an application relies on this strange behaviour of
200the BeOS kernel, it will no longer work correctly.
201
202The other difference is that OpenBeOS exports its node monitoring
203functionality to kernel modules as well, and provides an extra plain C
204API for them to use.
205
206And Beyond?
207-----------
208
209The current implementation directly iterates over all listeners and
210sends out notifications as required synchronously in the context of the
211thread that triggered the notification to be sent.
212
213If a node monitor needs to send out several messages, this could
214theoretically greatly decrease file system performance. To optimize for
215this case, the required data of the notification could be put into a
216queue and be sent by a dedicated worker thread. Since this requires an
217additional copy operation and a reserved address space for this queue,
218this optimization could be more expensive than the current
219implementation, depending on the usage pattern of the node monitoring
220mechanism.
221
222With BFS, it would be possible to introduce the possibility to
223automatically watch all files in a specified directory. While this would
224be very convenient at application level, it comes with several
225disadvantages:
226
227#. This feature might not be easily accomplishable for many file
228   systems; a file system must be able to retrieve a node by ID only -
229   it might not be feasible to find out about the parent directory for
230   many file systems.
231#. Although it could potentially save node monitors, it might cause the
232   kernel to send out a lot more messages to the application than it
233   needs. With the restriction the kernel imposes to the number of
234   watched nodes for a team, the application's designer might try to be
235   much stricter with the number of monitors his application will
236   consume.
237
238While 1.) might be a real show stopper, 2.) is almost invalidated
239because of Tracker's usage of node monitors; it consumes a monitor for
240every entry it displays, which might be several thousands. Implementing
241this feature would not only greatly speed up maintaining this massive
242need of monitors, and cut down memory usage, but also ease the
243implementation at application level.
244
245Even 1.) could be solved if the kernel could query a file system if it
246can support this particular feature; it could then automatically monitor
247all files in that directory without adding complexity to the application
248using this feature. Of course, the effort to provide this functionality
249is much larger then - but for applications like Tracker, the complexity
250would be removed from the application without extra cost.
251
252However, none of the discussed feature extensions have been implemented
253for the currently developed version R1 of OpenBeOS.
254