1 // SPDX-License-Identifier: GPL-2.0
2 // Author: Kirill Smelkov (kirr@nexedi.com)
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.
11 virtual explain // explain decisions in the patch (SPFLAGS="-D explain")
13 // stream-like reader & writer - ones that do not depend on f_pos.
15 identifier readstream, ppos;
16 identifier f, buf, len;
19 ssize_t readstream(struct file *f, char *buf, size_t len, loff_t *ppos)
25 identifier writestream, ppos;
26 identifier f, buf, len;
29 ssize_t writestream(struct file *f, const char *buf, size_t len, loff_t *ppos)
35 // a function that blocks
38 identifier wait =~ "^wait_.*";
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_.*";
62 identifier stream_reader.readstream;
63 identifier blocks.block_f;
72 @ reader_blocks depends on reader_blocks_direct || reader_blocks_1 @
73 identifier stream_reader.readstream;
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)
86 struct file_operations fops = {
91 identifier fops0.fops;
94 struct file_operations fops = {
99 identifier fops0.fops;
100 identifier read_iter_f;
102 struct file_operations fops = {
103 .read_iter = read_iter_f,
107 identifier fops0.fops;
110 struct file_operations fops = {
115 identifier fops0.fops;
116 identifier write_iter_f;
118 struct file_operations fops = {
119 .write_iter = write_iter_f,
123 identifier fops0.fops;
126 struct file_operations fops = {
131 identifier fops0.fops;
133 struct file_operations fops = {
138 identifier fops0.fops;
140 struct file_operations fops = {
141 .llseek = noop_llseek,
145 identifier fops0.fops;
148 struct file_operations fops = {
152 @ has_copy_file_range @
153 identifier fops0.fops;
154 identifier copy_file_range_f;
156 struct file_operations fops = {
157 .copy_file_range = copy_file_range_f,
160 @ has_remap_file_range @
161 identifier fops0.fops;
162 identifier remap_file_range_f;
164 struct file_operations fops = {
165 .remap_file_range = remap_file_range_f,
169 identifier fops0.fops;
170 identifier splice_read_f;
172 struct file_operations fops = {
173 .splice_read = splice_read_f,
177 identifier fops0.fops;
178 identifier splice_write_f;
180 struct file_operations fops = {
181 .splice_write = splice_write_f,
185 // file_operations that is candidate for stream_open conversion - it does not
186 // use mmap and other methods that assume @offset access to file.
188 // XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now.
189 // XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops".
190 @ 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 @
191 identifier fops0.fops;
193 struct file_operations fops = {
197 // ---- conversions ----
199 // XXX .open = nonseekable_open -> .open = stream_open
200 // XXX .open = func -> openfunc -> nonseekable_open
204 // if both are used in the same file_operations together with an opener -
205 // under that conditions we can use stream_open instead of nonseekable_open.
206 @ fops_rw depends on maybe_stream @
207 identifier fops0.fops, openfunc;
208 identifier stream_reader.readstream;
209 identifier stream_writer.writestream;
211 struct file_operations fops = {
214 .write = writestream,
217 @ report_rw depends on report @
218 identifier fops_rw.openfunc;
227 @ script:python depends on report && reader_blocks @
231 coccilib.report.print_report(p[0],
232 "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,))
234 @ script:python depends on report && !reader_blocks @
238 coccilib.report.print_report(p[0],
239 "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
242 @ explain_rw_deadlocked depends on explain && reader_blocks @
243 identifier fops_rw.openfunc;
248 + nonseekable_open /* read & write (was deadlock) */
253 @ explain_rw_nodeadlock depends on explain && !reader_blocks @
254 identifier fops_rw.openfunc;
259 + nonseekable_open /* read & write (no direct deadlock) */
263 @ patch_rw depends on patch @
264 identifier fops_rw.openfunc;
274 // read, but not write
275 @ fops_r depends on maybe_stream && !has_write @
276 identifier fops0.fops, openfunc;
277 identifier stream_reader.readstream;
279 struct file_operations fops = {
284 @ report_r depends on report @
285 identifier fops_r.openfunc;
294 @ script:python depends on report @
298 coccilib.report.print_report(p[0],
299 "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
301 @ explain_r depends on explain @
302 identifier fops_r.openfunc;
307 + nonseekable_open /* read only */
311 @ patch_r depends on patch @
312 identifier fops_r.openfunc;
322 // write, but not read
323 @ fops_w depends on maybe_stream && !has_read @
324 identifier fops0.fops, openfunc;
325 identifier stream_writer.writestream;
327 struct file_operations fops = {
329 .write = writestream,
332 @ report_w depends on report @
333 identifier fops_w.openfunc;
342 @ script:python depends on report @
346 coccilib.report.print_report(p[0],
347 "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
349 @ explain_w depends on explain @
350 identifier fops_w.openfunc;
355 + nonseekable_open /* write only */
359 @ patch_w depends on patch @
360 identifier fops_w.openfunc;
370 // no read, no write - don't change anything