18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Author: Kirill Smelkov (kirr@nexedi.com)
38c2ecf20Sopenharmony_ci//
48c2ecf20Sopenharmony_ci// Search for stream-like files that are using nonseekable_open and convert
58c2ecf20Sopenharmony_ci// them to stream_open. A stream-like file is a file that does not use ppos in
68c2ecf20Sopenharmony_ci// its read and write. Rationale for the conversion is to avoid deadlock in
78c2ecf20Sopenharmony_ci// between read and write.
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_civirtual report
108c2ecf20Sopenharmony_civirtual patch
118c2ecf20Sopenharmony_civirtual explain  // explain decisions in the patch (SPFLAGS="-D explain")
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci// stream-like reader & writer - ones that do not depend on f_pos.
148c2ecf20Sopenharmony_ci@ stream_reader @
158c2ecf20Sopenharmony_ciidentifier readstream, ppos;
168c2ecf20Sopenharmony_ciidentifier f, buf, len;
178c2ecf20Sopenharmony_citype loff_t;
188c2ecf20Sopenharmony_ci@@
198c2ecf20Sopenharmony_ci  ssize_t readstream(struct file *f, char *buf, size_t len, loff_t *ppos)
208c2ecf20Sopenharmony_ci  {
218c2ecf20Sopenharmony_ci    ... when != ppos
228c2ecf20Sopenharmony_ci  }
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci@ stream_writer @
258c2ecf20Sopenharmony_ciidentifier writestream, ppos;
268c2ecf20Sopenharmony_ciidentifier f, buf, len;
278c2ecf20Sopenharmony_citype loff_t;
288c2ecf20Sopenharmony_ci@@
298c2ecf20Sopenharmony_ci  ssize_t writestream(struct file *f, const char *buf, size_t len, loff_t *ppos)
308c2ecf20Sopenharmony_ci  {
318c2ecf20Sopenharmony_ci    ... when != ppos
328c2ecf20Sopenharmony_ci  }
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci// a function that blocks
368c2ecf20Sopenharmony_ci@ blocks @
378c2ecf20Sopenharmony_ciidentifier block_f;
388c2ecf20Sopenharmony_ciidentifier wait =~ "^wait_.*";
398c2ecf20Sopenharmony_ci@@
408c2ecf20Sopenharmony_ci  block_f(...) {
418c2ecf20Sopenharmony_ci    ... when exists
428c2ecf20Sopenharmony_ci    wait(...)
438c2ecf20Sopenharmony_ci    ... when exists
448c2ecf20Sopenharmony_ci  }
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci// stream_reader that can block inside.
478c2ecf20Sopenharmony_ci//
488c2ecf20Sopenharmony_ci// XXX wait_* can be called not directly from current function (e.g. func -> f -> g -> wait())
498c2ecf20Sopenharmony_ci// XXX currently reader_blocks supports only direct and 1-level indirect cases.
508c2ecf20Sopenharmony_ci@ reader_blocks_direct @
518c2ecf20Sopenharmony_ciidentifier stream_reader.readstream;
528c2ecf20Sopenharmony_ciidentifier wait =~ "^wait_.*";
538c2ecf20Sopenharmony_ci@@
548c2ecf20Sopenharmony_ci  readstream(...)
558c2ecf20Sopenharmony_ci  {
568c2ecf20Sopenharmony_ci    ... when exists
578c2ecf20Sopenharmony_ci    wait(...)
588c2ecf20Sopenharmony_ci    ... when exists
598c2ecf20Sopenharmony_ci  }
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci@ reader_blocks_1 @
628c2ecf20Sopenharmony_ciidentifier stream_reader.readstream;
638c2ecf20Sopenharmony_ciidentifier blocks.block_f;
648c2ecf20Sopenharmony_ci@@
658c2ecf20Sopenharmony_ci  readstream(...)
668c2ecf20Sopenharmony_ci  {
678c2ecf20Sopenharmony_ci    ... when exists
688c2ecf20Sopenharmony_ci    block_f(...)
698c2ecf20Sopenharmony_ci    ... when exists
708c2ecf20Sopenharmony_ci  }
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci@ reader_blocks depends on reader_blocks_direct || reader_blocks_1 @
738c2ecf20Sopenharmony_ciidentifier stream_reader.readstream;
748c2ecf20Sopenharmony_ci@@
758c2ecf20Sopenharmony_ci  readstream(...) {
768c2ecf20Sopenharmony_ci    ...
778c2ecf20Sopenharmony_ci  }
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci// file_operations + whether they have _any_ .read, .write, .llseek ... at all.
818c2ecf20Sopenharmony_ci//
828c2ecf20Sopenharmony_ci// XXX add support for file_operations xxx[N] = ...	(sound/core/pcm_native.c)
838c2ecf20Sopenharmony_ci@ fops0 @
848c2ecf20Sopenharmony_ciidentifier fops;
858c2ecf20Sopenharmony_ci@@
868c2ecf20Sopenharmony_ci  struct file_operations fops = {
878c2ecf20Sopenharmony_ci    ...
888c2ecf20Sopenharmony_ci  };
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci@ has_read @
918c2ecf20Sopenharmony_ciidentifier fops0.fops;
928c2ecf20Sopenharmony_ciidentifier read_f;
938c2ecf20Sopenharmony_ci@@
948c2ecf20Sopenharmony_ci  struct file_operations fops = {
958c2ecf20Sopenharmony_ci    .read = read_f,
968c2ecf20Sopenharmony_ci  };
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci@ has_read_iter @
998c2ecf20Sopenharmony_ciidentifier fops0.fops;
1008c2ecf20Sopenharmony_ciidentifier read_iter_f;
1018c2ecf20Sopenharmony_ci@@
1028c2ecf20Sopenharmony_ci  struct file_operations fops = {
1038c2ecf20Sopenharmony_ci    .read_iter = read_iter_f,
1048c2ecf20Sopenharmony_ci  };
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci@ has_write @
1078c2ecf20Sopenharmony_ciidentifier fops0.fops;
1088c2ecf20Sopenharmony_ciidentifier write_f;
1098c2ecf20Sopenharmony_ci@@
1108c2ecf20Sopenharmony_ci  struct file_operations fops = {
1118c2ecf20Sopenharmony_ci    .write = write_f,
1128c2ecf20Sopenharmony_ci  };
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci@ has_write_iter @
1158c2ecf20Sopenharmony_ciidentifier fops0.fops;
1168c2ecf20Sopenharmony_ciidentifier write_iter_f;
1178c2ecf20Sopenharmony_ci@@
1188c2ecf20Sopenharmony_ci  struct file_operations fops = {
1198c2ecf20Sopenharmony_ci    .write_iter = write_iter_f,
1208c2ecf20Sopenharmony_ci  };
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci@ has_llseek @
1238c2ecf20Sopenharmony_ciidentifier fops0.fops;
1248c2ecf20Sopenharmony_ciidentifier llseek_f;
1258c2ecf20Sopenharmony_ci@@
1268c2ecf20Sopenharmony_ci  struct file_operations fops = {
1278c2ecf20Sopenharmony_ci    .llseek = llseek_f,
1288c2ecf20Sopenharmony_ci  };
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci@ has_no_llseek @
1318c2ecf20Sopenharmony_ciidentifier fops0.fops;
1328c2ecf20Sopenharmony_ci@@
1338c2ecf20Sopenharmony_ci  struct file_operations fops = {
1348c2ecf20Sopenharmony_ci    .llseek = no_llseek,
1358c2ecf20Sopenharmony_ci  };
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci@ has_noop_llseek @
1388c2ecf20Sopenharmony_ciidentifier fops0.fops;
1398c2ecf20Sopenharmony_ci@@
1408c2ecf20Sopenharmony_ci  struct file_operations fops = {
1418c2ecf20Sopenharmony_ci    .llseek = noop_llseek,
1428c2ecf20Sopenharmony_ci  };
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci@ has_mmap @
1458c2ecf20Sopenharmony_ciidentifier fops0.fops;
1468c2ecf20Sopenharmony_ciidentifier mmap_f;
1478c2ecf20Sopenharmony_ci@@
1488c2ecf20Sopenharmony_ci  struct file_operations fops = {
1498c2ecf20Sopenharmony_ci    .mmap = mmap_f,
1508c2ecf20Sopenharmony_ci  };
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci@ has_copy_file_range @
1538c2ecf20Sopenharmony_ciidentifier fops0.fops;
1548c2ecf20Sopenharmony_ciidentifier copy_file_range_f;
1558c2ecf20Sopenharmony_ci@@
1568c2ecf20Sopenharmony_ci  struct file_operations fops = {
1578c2ecf20Sopenharmony_ci    .copy_file_range = copy_file_range_f,
1588c2ecf20Sopenharmony_ci  };
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci@ has_remap_file_range @
1618c2ecf20Sopenharmony_ciidentifier fops0.fops;
1628c2ecf20Sopenharmony_ciidentifier remap_file_range_f;
1638c2ecf20Sopenharmony_ci@@
1648c2ecf20Sopenharmony_ci  struct file_operations fops = {
1658c2ecf20Sopenharmony_ci    .remap_file_range = remap_file_range_f,
1668c2ecf20Sopenharmony_ci  };
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci@ has_splice_read @
1698c2ecf20Sopenharmony_ciidentifier fops0.fops;
1708c2ecf20Sopenharmony_ciidentifier splice_read_f;
1718c2ecf20Sopenharmony_ci@@
1728c2ecf20Sopenharmony_ci  struct file_operations fops = {
1738c2ecf20Sopenharmony_ci    .splice_read = splice_read_f,
1748c2ecf20Sopenharmony_ci  };
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci@ has_splice_write @
1778c2ecf20Sopenharmony_ciidentifier fops0.fops;
1788c2ecf20Sopenharmony_ciidentifier splice_write_f;
1798c2ecf20Sopenharmony_ci@@
1808c2ecf20Sopenharmony_ci  struct file_operations fops = {
1818c2ecf20Sopenharmony_ci    .splice_write = splice_write_f,
1828c2ecf20Sopenharmony_ci  };
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci// file_operations that is candidate for stream_open conversion - it does not
1868c2ecf20Sopenharmony_ci// use mmap and other methods that assume @offset access to file.
1878c2ecf20Sopenharmony_ci//
1888c2ecf20Sopenharmony_ci// XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now.
1898c2ecf20Sopenharmony_ci// XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops".
1908c2ecf20Sopenharmony_ci@ 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 @
1918c2ecf20Sopenharmony_ciidentifier fops0.fops;
1928c2ecf20Sopenharmony_ci@@
1938c2ecf20Sopenharmony_ci  struct file_operations fops = {
1948c2ecf20Sopenharmony_ci  };
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci// ---- conversions ----
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci// XXX .open = nonseekable_open -> .open = stream_open
2008c2ecf20Sopenharmony_ci// XXX .open = func -> openfunc -> nonseekable_open
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci// read & write
2038c2ecf20Sopenharmony_ci//
2048c2ecf20Sopenharmony_ci// if both are used in the same file_operations together with an opener -
2058c2ecf20Sopenharmony_ci// under that conditions we can use stream_open instead of nonseekable_open.
2068c2ecf20Sopenharmony_ci@ fops_rw depends on maybe_stream @
2078c2ecf20Sopenharmony_ciidentifier fops0.fops, openfunc;
2088c2ecf20Sopenharmony_ciidentifier stream_reader.readstream;
2098c2ecf20Sopenharmony_ciidentifier stream_writer.writestream;
2108c2ecf20Sopenharmony_ci@@
2118c2ecf20Sopenharmony_ci  struct file_operations fops = {
2128c2ecf20Sopenharmony_ci      .open  = openfunc,
2138c2ecf20Sopenharmony_ci      .read  = readstream,
2148c2ecf20Sopenharmony_ci      .write = writestream,
2158c2ecf20Sopenharmony_ci  };
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci@ report_rw depends on report @
2188c2ecf20Sopenharmony_ciidentifier fops_rw.openfunc;
2198c2ecf20Sopenharmony_ciposition p1;
2208c2ecf20Sopenharmony_ci@@
2218c2ecf20Sopenharmony_ci  openfunc(...) {
2228c2ecf20Sopenharmony_ci    <...
2238c2ecf20Sopenharmony_ci     nonseekable_open@p1
2248c2ecf20Sopenharmony_ci    ...>
2258c2ecf20Sopenharmony_ci  }
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci@ script:python depends on report && reader_blocks @
2288c2ecf20Sopenharmony_cifops << fops0.fops;
2298c2ecf20Sopenharmony_cip << report_rw.p1;
2308c2ecf20Sopenharmony_ci@@
2318c2ecf20Sopenharmony_cicoccilib.report.print_report(p[0],
2328c2ecf20Sopenharmony_ci  "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,))
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci@ script:python depends on report && !reader_blocks @
2358c2ecf20Sopenharmony_cifops << fops0.fops;
2368c2ecf20Sopenharmony_cip << report_rw.p1;
2378c2ecf20Sopenharmony_ci@@
2388c2ecf20Sopenharmony_cicoccilib.report.print_report(p[0],
2398c2ecf20Sopenharmony_ci  "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci@ explain_rw_deadlocked depends on explain && reader_blocks @
2438c2ecf20Sopenharmony_ciidentifier fops_rw.openfunc;
2448c2ecf20Sopenharmony_ci@@
2458c2ecf20Sopenharmony_ci  openfunc(...) {
2468c2ecf20Sopenharmony_ci    <...
2478c2ecf20Sopenharmony_ci-    nonseekable_open
2488c2ecf20Sopenharmony_ci+    nonseekable_open /* read & write (was deadlock) */
2498c2ecf20Sopenharmony_ci    ...>
2508c2ecf20Sopenharmony_ci  }
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci@ explain_rw_nodeadlock depends on explain && !reader_blocks @
2548c2ecf20Sopenharmony_ciidentifier fops_rw.openfunc;
2558c2ecf20Sopenharmony_ci@@
2568c2ecf20Sopenharmony_ci  openfunc(...) {
2578c2ecf20Sopenharmony_ci    <...
2588c2ecf20Sopenharmony_ci-    nonseekable_open
2598c2ecf20Sopenharmony_ci+    nonseekable_open /* read & write (no direct deadlock) */
2608c2ecf20Sopenharmony_ci    ...>
2618c2ecf20Sopenharmony_ci  }
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci@ patch_rw depends on patch @
2648c2ecf20Sopenharmony_ciidentifier fops_rw.openfunc;
2658c2ecf20Sopenharmony_ci@@
2668c2ecf20Sopenharmony_ci  openfunc(...) {
2678c2ecf20Sopenharmony_ci    <...
2688c2ecf20Sopenharmony_ci-   nonseekable_open
2698c2ecf20Sopenharmony_ci+   stream_open
2708c2ecf20Sopenharmony_ci    ...>
2718c2ecf20Sopenharmony_ci  }
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci// read, but not write
2758c2ecf20Sopenharmony_ci@ fops_r depends on maybe_stream && !has_write @
2768c2ecf20Sopenharmony_ciidentifier fops0.fops, openfunc;
2778c2ecf20Sopenharmony_ciidentifier stream_reader.readstream;
2788c2ecf20Sopenharmony_ci@@
2798c2ecf20Sopenharmony_ci  struct file_operations fops = {
2808c2ecf20Sopenharmony_ci      .open  = openfunc,
2818c2ecf20Sopenharmony_ci      .read  = readstream,
2828c2ecf20Sopenharmony_ci  };
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci@ report_r depends on report @
2858c2ecf20Sopenharmony_ciidentifier fops_r.openfunc;
2868c2ecf20Sopenharmony_ciposition p1;
2878c2ecf20Sopenharmony_ci@@
2888c2ecf20Sopenharmony_ci  openfunc(...) {
2898c2ecf20Sopenharmony_ci    <...
2908c2ecf20Sopenharmony_ci    nonseekable_open@p1
2918c2ecf20Sopenharmony_ci    ...>
2928c2ecf20Sopenharmony_ci  }
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci@ script:python depends on report @
2958c2ecf20Sopenharmony_cifops << fops0.fops;
2968c2ecf20Sopenharmony_cip << report_r.p1;
2978c2ecf20Sopenharmony_ci@@
2988c2ecf20Sopenharmony_cicoccilib.report.print_report(p[0],
2998c2ecf20Sopenharmony_ci  "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci@ explain_r depends on explain @
3028c2ecf20Sopenharmony_ciidentifier fops_r.openfunc;
3038c2ecf20Sopenharmony_ci@@
3048c2ecf20Sopenharmony_ci  openfunc(...) {
3058c2ecf20Sopenharmony_ci    <...
3068c2ecf20Sopenharmony_ci-   nonseekable_open
3078c2ecf20Sopenharmony_ci+   nonseekable_open /* read only */
3088c2ecf20Sopenharmony_ci    ...>
3098c2ecf20Sopenharmony_ci  }
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci@ patch_r depends on patch @
3128c2ecf20Sopenharmony_ciidentifier fops_r.openfunc;
3138c2ecf20Sopenharmony_ci@@
3148c2ecf20Sopenharmony_ci  openfunc(...) {
3158c2ecf20Sopenharmony_ci    <...
3168c2ecf20Sopenharmony_ci-   nonseekable_open
3178c2ecf20Sopenharmony_ci+   stream_open
3188c2ecf20Sopenharmony_ci    ...>
3198c2ecf20Sopenharmony_ci  }
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci// write, but not read
3238c2ecf20Sopenharmony_ci@ fops_w depends on maybe_stream && !has_read @
3248c2ecf20Sopenharmony_ciidentifier fops0.fops, openfunc;
3258c2ecf20Sopenharmony_ciidentifier stream_writer.writestream;
3268c2ecf20Sopenharmony_ci@@
3278c2ecf20Sopenharmony_ci  struct file_operations fops = {
3288c2ecf20Sopenharmony_ci      .open  = openfunc,
3298c2ecf20Sopenharmony_ci      .write = writestream,
3308c2ecf20Sopenharmony_ci  };
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci@ report_w depends on report @
3338c2ecf20Sopenharmony_ciidentifier fops_w.openfunc;
3348c2ecf20Sopenharmony_ciposition p1;
3358c2ecf20Sopenharmony_ci@@
3368c2ecf20Sopenharmony_ci  openfunc(...) {
3378c2ecf20Sopenharmony_ci    <...
3388c2ecf20Sopenharmony_ci    nonseekable_open@p1
3398c2ecf20Sopenharmony_ci    ...>
3408c2ecf20Sopenharmony_ci  }
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci@ script:python depends on report @
3438c2ecf20Sopenharmony_cifops << fops0.fops;
3448c2ecf20Sopenharmony_cip << report_w.p1;
3458c2ecf20Sopenharmony_ci@@
3468c2ecf20Sopenharmony_cicoccilib.report.print_report(p[0],
3478c2ecf20Sopenharmony_ci  "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci@ explain_w depends on explain @
3508c2ecf20Sopenharmony_ciidentifier fops_w.openfunc;
3518c2ecf20Sopenharmony_ci@@
3528c2ecf20Sopenharmony_ci  openfunc(...) {
3538c2ecf20Sopenharmony_ci    <...
3548c2ecf20Sopenharmony_ci-   nonseekable_open
3558c2ecf20Sopenharmony_ci+   nonseekable_open /* write only */
3568c2ecf20Sopenharmony_ci    ...>
3578c2ecf20Sopenharmony_ci  }
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci@ patch_w depends on patch @
3608c2ecf20Sopenharmony_ciidentifier fops_w.openfunc;
3618c2ecf20Sopenharmony_ci@@
3628c2ecf20Sopenharmony_ci  openfunc(...) {
3638c2ecf20Sopenharmony_ci    <...
3648c2ecf20Sopenharmony_ci-   nonseekable_open
3658c2ecf20Sopenharmony_ci+   stream_open
3668c2ecf20Sopenharmony_ci    ...>
3678c2ecf20Sopenharmony_ci  }
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci// no read, no write - don't change anything
371