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