Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Author: Kirill Smelkov (kirr@nexedi.com)
0003 //
0004 // Search for stream-like files that are using nonseekable_open and convert
0005 // them to stream_open. A stream-like file is a file that does not use ppos in
0006 // its read and write. Rationale for the conversion is to avoid deadlock in
0007 // between read and write.
0008 
0009 virtual report
0010 virtual patch
0011 virtual explain  // explain decisions in the patch (SPFLAGS="-D explain")
0012 
0013 // stream-like reader & writer - ones that do not depend on f_pos.
0014 @ stream_reader @
0015 identifier readstream, ppos;
0016 identifier f, buf, len;
0017 type loff_t;
0018 @@
0019   ssize_t readstream(struct file *f, char *buf, size_t len, loff_t *ppos)
0020   {
0021     ... when != ppos
0022   }
0023 
0024 @ stream_writer @
0025 identifier writestream, ppos;
0026 identifier f, buf, len;
0027 type loff_t;
0028 @@
0029   ssize_t writestream(struct file *f, const char *buf, size_t len, loff_t *ppos)
0030   {
0031     ... when != ppos
0032   }
0033 
0034 
0035 // a function that blocks
0036 @ blocks @
0037 identifier block_f;
0038 identifier wait =~ "^wait_.*";
0039 @@
0040   block_f(...) {
0041     ... when exists
0042     wait(...)
0043     ... when exists
0044   }
0045 
0046 // stream_reader that can block inside.
0047 //
0048 // XXX wait_* can be called not directly from current function (e.g. func -> f -> g -> wait())
0049 // XXX currently reader_blocks supports only direct and 1-level indirect cases.
0050 @ reader_blocks_direct @
0051 identifier stream_reader.readstream;
0052 identifier wait =~ "^wait_.*";
0053 @@
0054   readstream(...)
0055   {
0056     ... when exists
0057     wait(...)
0058     ... when exists
0059   }
0060 
0061 @ reader_blocks_1 @
0062 identifier stream_reader.readstream;
0063 identifier blocks.block_f;
0064 @@
0065   readstream(...)
0066   {
0067     ... when exists
0068     block_f(...)
0069     ... when exists
0070   }
0071 
0072 @ reader_blocks depends on reader_blocks_direct || reader_blocks_1 @
0073 identifier stream_reader.readstream;
0074 @@
0075   readstream(...) {
0076     ...
0077   }
0078 
0079 
0080 // file_operations + whether they have _any_ .read, .write, .llseek ... at all.
0081 //
0082 // XXX add support for file_operations xxx[N] = ...     (sound/core/pcm_native.c)
0083 @ fops0 @
0084 identifier fops;
0085 @@
0086   struct file_operations fops = {
0087     ...
0088   };
0089 
0090 @ has_read @
0091 identifier fops0.fops;
0092 identifier read_f;
0093 @@
0094   struct file_operations fops = {
0095     .read = read_f,
0096   };
0097 
0098 @ has_read_iter @
0099 identifier fops0.fops;
0100 identifier read_iter_f;
0101 @@
0102   struct file_operations fops = {
0103     .read_iter = read_iter_f,
0104   };
0105 
0106 @ has_write @
0107 identifier fops0.fops;
0108 identifier write_f;
0109 @@
0110   struct file_operations fops = {
0111     .write = write_f,
0112   };
0113 
0114 @ has_write_iter @
0115 identifier fops0.fops;
0116 identifier write_iter_f;
0117 @@
0118   struct file_operations fops = {
0119     .write_iter = write_iter_f,
0120   };
0121 
0122 @ has_llseek @
0123 identifier fops0.fops;
0124 identifier llseek_f;
0125 @@
0126   struct file_operations fops = {
0127     .llseek = llseek_f,
0128   };
0129 
0130 @ has_no_llseek @
0131 identifier fops0.fops;
0132 @@
0133   struct file_operations fops = {
0134     .llseek = no_llseek,
0135   };
0136 
0137 @ has_noop_llseek @
0138 identifier fops0.fops;
0139 @@
0140   struct file_operations fops = {
0141     .llseek = noop_llseek,
0142   };
0143 
0144 @ has_mmap @
0145 identifier fops0.fops;
0146 identifier mmap_f;
0147 @@
0148   struct file_operations fops = {
0149     .mmap = mmap_f,
0150   };
0151 
0152 @ has_copy_file_range @
0153 identifier fops0.fops;
0154 identifier copy_file_range_f;
0155 @@
0156   struct file_operations fops = {
0157     .copy_file_range = copy_file_range_f,
0158   };
0159 
0160 @ has_remap_file_range @
0161 identifier fops0.fops;
0162 identifier remap_file_range_f;
0163 @@
0164   struct file_operations fops = {
0165     .remap_file_range = remap_file_range_f,
0166   };
0167 
0168 @ has_splice_read @
0169 identifier fops0.fops;
0170 identifier splice_read_f;
0171 @@
0172   struct file_operations fops = {
0173     .splice_read = splice_read_f,
0174   };
0175 
0176 @ has_splice_write @
0177 identifier fops0.fops;
0178 identifier splice_write_f;
0179 @@
0180   struct file_operations fops = {
0181     .splice_write = splice_write_f,
0182   };
0183 
0184 
0185 // file_operations that is candidate for stream_open conversion - it does not
0186 // use mmap and other methods that assume @offset access to file.
0187 //
0188 // XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now.
0189 // XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops".
0190 @ 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 @
0191 identifier fops0.fops;
0192 @@
0193   struct file_operations fops = {
0194   };
0195 
0196 
0197 // ---- conversions ----
0198 
0199 // XXX .open = nonseekable_open -> .open = stream_open
0200 // XXX .open = func -> openfunc -> nonseekable_open
0201 
0202 // read & write
0203 //
0204 // if both are used in the same file_operations together with an opener -
0205 // under that conditions we can use stream_open instead of nonseekable_open.
0206 @ fops_rw depends on maybe_stream @
0207 identifier fops0.fops, openfunc;
0208 identifier stream_reader.readstream;
0209 identifier stream_writer.writestream;
0210 @@
0211   struct file_operations fops = {
0212       .open  = openfunc,
0213       .read  = readstream,
0214       .write = writestream,
0215   };
0216 
0217 @ report_rw depends on report @
0218 identifier fops_rw.openfunc;
0219 position p1;
0220 @@
0221   openfunc(...) {
0222     <...
0223      nonseekable_open@p1
0224     ...>
0225   }
0226 
0227 @ script:python depends on report && reader_blocks @
0228 fops << fops0.fops;
0229 p << report_rw.p1;
0230 @@
0231 coccilib.report.print_report(p[0],
0232   "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,))
0233 
0234 @ script:python depends on report && !reader_blocks @
0235 fops << fops0.fops;
0236 p << report_rw.p1;
0237 @@
0238 coccilib.report.print_report(p[0],
0239   "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
0240 
0241 
0242 @ explain_rw_deadlocked depends on explain && reader_blocks @
0243 identifier fops_rw.openfunc;
0244 @@
0245   openfunc(...) {
0246     <...
0247 -    nonseekable_open
0248 +    nonseekable_open /* read & write (was deadlock) */
0249     ...>
0250   }
0251 
0252 
0253 @ explain_rw_nodeadlock depends on explain && !reader_blocks @
0254 identifier fops_rw.openfunc;
0255 @@
0256   openfunc(...) {
0257     <...
0258 -    nonseekable_open
0259 +    nonseekable_open /* read & write (no direct deadlock) */
0260     ...>
0261   }
0262 
0263 @ patch_rw depends on patch @
0264 identifier fops_rw.openfunc;
0265 @@
0266   openfunc(...) {
0267     <...
0268 -   nonseekable_open
0269 +   stream_open
0270     ...>
0271   }
0272 
0273 
0274 // read, but not write
0275 @ fops_r depends on maybe_stream && !has_write @
0276 identifier fops0.fops, openfunc;
0277 identifier stream_reader.readstream;
0278 @@
0279   struct file_operations fops = {
0280       .open  = openfunc,
0281       .read  = readstream,
0282   };
0283 
0284 @ report_r depends on report @
0285 identifier fops_r.openfunc;
0286 position p1;
0287 @@
0288   openfunc(...) {
0289     <...
0290     nonseekable_open@p1
0291     ...>
0292   }
0293 
0294 @ script:python depends on report @
0295 fops << fops0.fops;
0296 p << report_r.p1;
0297 @@
0298 coccilib.report.print_report(p[0],
0299   "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
0300 
0301 @ explain_r depends on explain @
0302 identifier fops_r.openfunc;
0303 @@
0304   openfunc(...) {
0305     <...
0306 -   nonseekable_open
0307 +   nonseekable_open /* read only */
0308     ...>
0309   }
0310 
0311 @ patch_r depends on patch @
0312 identifier fops_r.openfunc;
0313 @@
0314   openfunc(...) {
0315     <...
0316 -   nonseekable_open
0317 +   stream_open
0318     ...>
0319   }
0320 
0321 
0322 // write, but not read
0323 @ fops_w depends on maybe_stream && !has_read @
0324 identifier fops0.fops, openfunc;
0325 identifier stream_writer.writestream;
0326 @@
0327   struct file_operations fops = {
0328       .open  = openfunc,
0329       .write = writestream,
0330   };
0331 
0332 @ report_w depends on report @
0333 identifier fops_w.openfunc;
0334 position p1;
0335 @@
0336   openfunc(...) {
0337     <...
0338     nonseekable_open@p1
0339     ...>
0340   }
0341 
0342 @ script:python depends on report @
0343 fops << fops0.fops;
0344 p << report_w.p1;
0345 @@
0346 coccilib.report.print_report(p[0],
0347   "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
0348 
0349 @ explain_w depends on explain @
0350 identifier fops_w.openfunc;
0351 @@
0352   openfunc(...) {
0353     <...
0354 -   nonseekable_open
0355 +   nonseekable_open /* write only */
0356     ...>
0357   }
0358 
0359 @ patch_w depends on patch @
0360 identifier fops_w.openfunc;
0361 @@
0362   openfunc(...) {
0363     <...
0364 -   nonseekable_open
0365 +   stream_open
0366     ...>
0367   }
0368 
0369 
0370 // no read, no write - don't change anything