Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / scripts / coccinelle / api / stream_open.cocci
blob50ab60c81f13dbb1fd870ef335c5c5ed7def284b
1 // SPDX-License-Identifier: GPL-2.0
2 // Author: Kirill Smelkov (kirr@nexedi.com)
3 //
4 // Search for stream-like files that are using nonseekable_open and convert
5 // them to stream_open. A stream-like file is a file that does not use ppos in
6 // its read and write. Rationale for the conversion is to avoid deadlock in
7 // between read and write.
9 virtual report
10 virtual patch
11 virtual explain  // explain decisions in the patch (SPFLAGS="-D explain")
13 // stream-like reader & writer - ones that do not depend on f_pos.
14 @ stream_reader @
15 identifier readstream, ppos;
16 identifier f, buf, len;
17 type loff_t;
19   ssize_t readstream(struct file *f, char *buf, size_t len, loff_t *ppos)
20   {
21     ... when != ppos
22   }
24 @ stream_writer @
25 identifier writestream, ppos;
26 identifier f, buf, len;
27 type loff_t;
29   ssize_t writestream(struct file *f, const char *buf, size_t len, loff_t *ppos)
30   {
31     ... when != ppos
32   }
35 // a function that blocks
36 @ blocks @
37 identifier block_f;
38 identifier wait =~ "^wait_.*";
40   block_f(...) {
41     ... when exists
42     wait(...)
43     ... when exists
44   }
46 // stream_reader that can block inside.
48 // XXX wait_* can be called not directly from current function (e.g. func -> f -> g -> wait())
49 // XXX currently reader_blocks supports only direct and 1-level indirect cases.
50 @ reader_blocks_direct @
51 identifier stream_reader.readstream;
52 identifier wait =~ "^wait_.*";
54   readstream(...)
55   {
56     ... when exists
57     wait(...)
58     ... when exists
59   }
61 @ reader_blocks_1 @
62 identifier stream_reader.readstream;
63 identifier blocks.block_f;
65   readstream(...)
66   {
67     ... when exists
68     block_f(...)
69     ... when exists
70   }
72 @ reader_blocks depends on reader_blocks_direct || reader_blocks_1 @
73 identifier stream_reader.readstream;
75   readstream(...) {
76     ...
77   }
80 // file_operations + whether they have _any_ .read, .write, .llseek ... at all.
82 // XXX add support for file_operations xxx[N] = ...     (sound/core/pcm_native.c)
83 @ fops0 @
84 identifier fops;
86   struct file_operations fops = {
87     ...
88   };
90 @ has_read @
91 identifier fops0.fops;
92 identifier read_f;
94   struct file_operations fops = {
95     .read = read_f,
96   };
98 @ has_read_iter @
99 identifier fops0.fops;
100 identifier read_iter_f;
102   struct file_operations fops = {
103     .read_iter = read_iter_f,
104   };
106 @ has_write @
107 identifier fops0.fops;
108 identifier write_f;
110   struct file_operations fops = {
111     .write = write_f,
112   };
114 @ has_write_iter @
115 identifier fops0.fops;
116 identifier write_iter_f;
118   struct file_operations fops = {
119     .write_iter = write_iter_f,
120   };
122 @ has_llseek @
123 identifier fops0.fops;
124 identifier llseek_f;
126   struct file_operations fops = {
127     .llseek = llseek_f,
128   };
130 @ has_no_llseek @
131 identifier fops0.fops;
133   struct file_operations fops = {
134   };
136 @ has_noop_llseek @
137 identifier fops0.fops;
139   struct file_operations fops = {
140     .llseek = noop_llseek,
141   };
143 @ has_mmap @
144 identifier fops0.fops;
145 identifier mmap_f;
147   struct file_operations fops = {
148     .mmap = mmap_f,
149   };
151 @ has_copy_file_range @
152 identifier fops0.fops;
153 identifier copy_file_range_f;
155   struct file_operations fops = {
156     .copy_file_range = copy_file_range_f,
157   };
159 @ has_remap_file_range @
160 identifier fops0.fops;
161 identifier remap_file_range_f;
163   struct file_operations fops = {
164     .remap_file_range = remap_file_range_f,
165   };
167 @ has_splice_read @
168 identifier fops0.fops;
169 identifier splice_read_f;
171   struct file_operations fops = {
172     .splice_read = splice_read_f,
173   };
175 @ has_splice_write @
176 identifier fops0.fops;
177 identifier splice_write_f;
179   struct file_operations fops = {
180     .splice_write = splice_write_f,
181   };
184 // file_operations that is candidate for stream_open conversion - it does not
185 // use mmap and other methods that assume @offset access to file.
187 // XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now.
188 // XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops".
189 @ maybe_stream depends on (!has_llseek || has_no_llseek || has_noop_llseek) && !has_mmap && !has_copy_file_range && !has_remap_file_range && !has_read_iter && !has_write_iter && !has_splice_read && !has_splice_write @
190 identifier fops0.fops;
192   struct file_operations fops = {
193   };
196 // ---- conversions ----
198 // XXX .open = nonseekable_open -> .open = stream_open
199 // XXX .open = func -> openfunc -> nonseekable_open
201 // read & write
203 // if both are used in the same file_operations together with an opener -
204 // under that conditions we can use stream_open instead of nonseekable_open.
205 @ fops_rw depends on maybe_stream @
206 identifier fops0.fops, openfunc;
207 identifier stream_reader.readstream;
208 identifier stream_writer.writestream;
210   struct file_operations fops = {
211       .open  = openfunc,
212       .read  = readstream,
213       .write = writestream,
214   };
216 @ report_rw depends on report @
217 identifier fops_rw.openfunc;
218 position p1;
220   openfunc(...) {
221     <...
222      nonseekable_open@p1
223     ...>
224   }
226 @ script:python depends on report && reader_blocks @
227 fops << fops0.fops;
228 p << report_rw.p1;
230 coccilib.report.print_report(p[0],
231   "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,))
233 @ script:python depends on report && !reader_blocks @
234 fops << fops0.fops;
235 p << report_rw.p1;
237 coccilib.report.print_report(p[0],
238   "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
241 @ explain_rw_deadlocked depends on explain && reader_blocks @
242 identifier fops_rw.openfunc;
244   openfunc(...) {
245     <...
246 -    nonseekable_open
247 +    nonseekable_open /* read & write (was deadlock) */
248     ...>
249   }
252 @ explain_rw_nodeadlock depends on explain && !reader_blocks @
253 identifier fops_rw.openfunc;
255   openfunc(...) {
256     <...
257 -    nonseekable_open
258 +    nonseekable_open /* read & write (no direct deadlock) */
259     ...>
260   }
262 @ patch_rw depends on patch @
263 identifier fops_rw.openfunc;
265   openfunc(...) {
266     <...
267 -   nonseekable_open
268 +   stream_open
269     ...>
270   }
273 // read, but not write
274 @ fops_r depends on maybe_stream && !has_write @
275 identifier fops0.fops, openfunc;
276 identifier stream_reader.readstream;
278   struct file_operations fops = {
279       .open  = openfunc,
280       .read  = readstream,
281   };
283 @ report_r depends on report @
284 identifier fops_r.openfunc;
285 position p1;
287   openfunc(...) {
288     <...
289     nonseekable_open@p1
290     ...>
291   }
293 @ script:python depends on report @
294 fops << fops0.fops;
295 p << report_r.p1;
297 coccilib.report.print_report(p[0],
298   "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
300 @ explain_r depends on explain @
301 identifier fops_r.openfunc;
303   openfunc(...) {
304     <...
305 -   nonseekable_open
306 +   nonseekable_open /* read only */
307     ...>
308   }
310 @ patch_r depends on patch @
311 identifier fops_r.openfunc;
313   openfunc(...) {
314     <...
315 -   nonseekable_open
316 +   stream_open
317     ...>
318   }
321 // write, but not read
322 @ fops_w depends on maybe_stream && !has_read @
323 identifier fops0.fops, openfunc;
324 identifier stream_writer.writestream;
326   struct file_operations fops = {
327       .open  = openfunc,
328       .write = writestream,
329   };
331 @ report_w depends on report @
332 identifier fops_w.openfunc;
333 position p1;
335   openfunc(...) {
336     <...
337     nonseekable_open@p1
338     ...>
339   }
341 @ script:python depends on report @
342 fops << fops0.fops;
343 p << report_w.p1;
345 coccilib.report.print_report(p[0],
346   "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
348 @ explain_w depends on explain @
349 identifier fops_w.openfunc;
351   openfunc(...) {
352     <...
353 -   nonseekable_open
354 +   nonseekable_open /* write only */
355     ...>
356   }
358 @ patch_w depends on patch @
359 identifier fops_w.openfunc;
361   openfunc(...) {
362     <...
363 -   nonseekable_open
364 +   stream_open
365     ...>
366   }
369 // no read, no write - don't change anything