162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Author: Kirill Smelkov (kirr@nexedi.com) 362306a36Sopenharmony_ci// 462306a36Sopenharmony_ci// Search for stream-like files that are using nonseekable_open and convert 562306a36Sopenharmony_ci// them to stream_open. A stream-like file is a file that does not use ppos in 662306a36Sopenharmony_ci// its read and write. Rationale for the conversion is to avoid deadlock in 762306a36Sopenharmony_ci// between read and write. 862306a36Sopenharmony_ci 962306a36Sopenharmony_civirtual report 1062306a36Sopenharmony_civirtual patch 1162306a36Sopenharmony_civirtual explain // explain decisions in the patch (SPFLAGS="-D explain") 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci// stream-like reader & writer - ones that do not depend on f_pos. 1462306a36Sopenharmony_ci@ stream_reader @ 1562306a36Sopenharmony_ciidentifier readstream, ppos; 1662306a36Sopenharmony_ciidentifier f, buf, len; 1762306a36Sopenharmony_citype loff_t; 1862306a36Sopenharmony_ci@@ 1962306a36Sopenharmony_ci ssize_t readstream(struct file *f, char *buf, size_t len, loff_t *ppos) 2062306a36Sopenharmony_ci { 2162306a36Sopenharmony_ci ... when != ppos 2262306a36Sopenharmony_ci } 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci@ stream_writer @ 2562306a36Sopenharmony_ciidentifier writestream, ppos; 2662306a36Sopenharmony_ciidentifier f, buf, len; 2762306a36Sopenharmony_citype loff_t; 2862306a36Sopenharmony_ci@@ 2962306a36Sopenharmony_ci ssize_t writestream(struct file *f, const char *buf, size_t len, loff_t *ppos) 3062306a36Sopenharmony_ci { 3162306a36Sopenharmony_ci ... when != ppos 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci// a function that blocks 3662306a36Sopenharmony_ci@ blocks @ 3762306a36Sopenharmony_ciidentifier block_f; 3862306a36Sopenharmony_ciidentifier wait =~ "^wait_.*"; 3962306a36Sopenharmony_ci@@ 4062306a36Sopenharmony_ci block_f(...) { 4162306a36Sopenharmony_ci ... when exists 4262306a36Sopenharmony_ci wait(...) 4362306a36Sopenharmony_ci ... when exists 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci// stream_reader that can block inside. 4762306a36Sopenharmony_ci// 4862306a36Sopenharmony_ci// XXX wait_* can be called not directly from current function (e.g. func -> f -> g -> wait()) 4962306a36Sopenharmony_ci// XXX currently reader_blocks supports only direct and 1-level indirect cases. 5062306a36Sopenharmony_ci@ reader_blocks_direct @ 5162306a36Sopenharmony_ciidentifier stream_reader.readstream; 5262306a36Sopenharmony_ciidentifier wait =~ "^wait_.*"; 5362306a36Sopenharmony_ci@@ 5462306a36Sopenharmony_ci readstream(...) 5562306a36Sopenharmony_ci { 5662306a36Sopenharmony_ci ... when exists 5762306a36Sopenharmony_ci wait(...) 5862306a36Sopenharmony_ci ... when exists 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci@ reader_blocks_1 @ 6262306a36Sopenharmony_ciidentifier stream_reader.readstream; 6362306a36Sopenharmony_ciidentifier blocks.block_f; 6462306a36Sopenharmony_ci@@ 6562306a36Sopenharmony_ci readstream(...) 6662306a36Sopenharmony_ci { 6762306a36Sopenharmony_ci ... when exists 6862306a36Sopenharmony_ci block_f(...) 6962306a36Sopenharmony_ci ... when exists 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci@ reader_blocks depends on reader_blocks_direct || reader_blocks_1 @ 7362306a36Sopenharmony_ciidentifier stream_reader.readstream; 7462306a36Sopenharmony_ci@@ 7562306a36Sopenharmony_ci readstream(...) { 7662306a36Sopenharmony_ci ... 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci// file_operations + whether they have _any_ .read, .write, .llseek ... at all. 8162306a36Sopenharmony_ci// 8262306a36Sopenharmony_ci// XXX add support for file_operations xxx[N] = ... (sound/core/pcm_native.c) 8362306a36Sopenharmony_ci@ fops0 @ 8462306a36Sopenharmony_ciidentifier fops; 8562306a36Sopenharmony_ci@@ 8662306a36Sopenharmony_ci struct file_operations fops = { 8762306a36Sopenharmony_ci ... 8862306a36Sopenharmony_ci }; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci@ has_read @ 9162306a36Sopenharmony_ciidentifier fops0.fops; 9262306a36Sopenharmony_ciidentifier read_f; 9362306a36Sopenharmony_ci@@ 9462306a36Sopenharmony_ci struct file_operations fops = { 9562306a36Sopenharmony_ci .read = read_f, 9662306a36Sopenharmony_ci }; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci@ has_read_iter @ 9962306a36Sopenharmony_ciidentifier fops0.fops; 10062306a36Sopenharmony_ciidentifier read_iter_f; 10162306a36Sopenharmony_ci@@ 10262306a36Sopenharmony_ci struct file_operations fops = { 10362306a36Sopenharmony_ci .read_iter = read_iter_f, 10462306a36Sopenharmony_ci }; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci@ has_write @ 10762306a36Sopenharmony_ciidentifier fops0.fops; 10862306a36Sopenharmony_ciidentifier write_f; 10962306a36Sopenharmony_ci@@ 11062306a36Sopenharmony_ci struct file_operations fops = { 11162306a36Sopenharmony_ci .write = write_f, 11262306a36Sopenharmony_ci }; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci@ has_write_iter @ 11562306a36Sopenharmony_ciidentifier fops0.fops; 11662306a36Sopenharmony_ciidentifier write_iter_f; 11762306a36Sopenharmony_ci@@ 11862306a36Sopenharmony_ci struct file_operations fops = { 11962306a36Sopenharmony_ci .write_iter = write_iter_f, 12062306a36Sopenharmony_ci }; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci@ has_llseek @ 12362306a36Sopenharmony_ciidentifier fops0.fops; 12462306a36Sopenharmony_ciidentifier llseek_f; 12562306a36Sopenharmony_ci@@ 12662306a36Sopenharmony_ci struct file_operations fops = { 12762306a36Sopenharmony_ci .llseek = llseek_f, 12862306a36Sopenharmony_ci }; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci@ has_no_llseek @ 13162306a36Sopenharmony_ciidentifier fops0.fops; 13262306a36Sopenharmony_ci@@ 13362306a36Sopenharmony_ci struct file_operations fops = { 13462306a36Sopenharmony_ci .llseek = no_llseek, 13562306a36Sopenharmony_ci }; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci@ has_noop_llseek @ 13862306a36Sopenharmony_ciidentifier fops0.fops; 13962306a36Sopenharmony_ci@@ 14062306a36Sopenharmony_ci struct file_operations fops = { 14162306a36Sopenharmony_ci .llseek = noop_llseek, 14262306a36Sopenharmony_ci }; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci@ has_mmap @ 14562306a36Sopenharmony_ciidentifier fops0.fops; 14662306a36Sopenharmony_ciidentifier mmap_f; 14762306a36Sopenharmony_ci@@ 14862306a36Sopenharmony_ci struct file_operations fops = { 14962306a36Sopenharmony_ci .mmap = mmap_f, 15062306a36Sopenharmony_ci }; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci@ has_copy_file_range @ 15362306a36Sopenharmony_ciidentifier fops0.fops; 15462306a36Sopenharmony_ciidentifier copy_file_range_f; 15562306a36Sopenharmony_ci@@ 15662306a36Sopenharmony_ci struct file_operations fops = { 15762306a36Sopenharmony_ci .copy_file_range = copy_file_range_f, 15862306a36Sopenharmony_ci }; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci@ has_remap_file_range @ 16162306a36Sopenharmony_ciidentifier fops0.fops; 16262306a36Sopenharmony_ciidentifier remap_file_range_f; 16362306a36Sopenharmony_ci@@ 16462306a36Sopenharmony_ci struct file_operations fops = { 16562306a36Sopenharmony_ci .remap_file_range = remap_file_range_f, 16662306a36Sopenharmony_ci }; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci@ has_splice_read @ 16962306a36Sopenharmony_ciidentifier fops0.fops; 17062306a36Sopenharmony_ciidentifier splice_read_f; 17162306a36Sopenharmony_ci@@ 17262306a36Sopenharmony_ci struct file_operations fops = { 17362306a36Sopenharmony_ci .splice_read = splice_read_f, 17462306a36Sopenharmony_ci }; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci@ has_splice_write @ 17762306a36Sopenharmony_ciidentifier fops0.fops; 17862306a36Sopenharmony_ciidentifier splice_write_f; 17962306a36Sopenharmony_ci@@ 18062306a36Sopenharmony_ci struct file_operations fops = { 18162306a36Sopenharmony_ci .splice_write = splice_write_f, 18262306a36Sopenharmony_ci }; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci// file_operations that is candidate for stream_open conversion - it does not 18662306a36Sopenharmony_ci// use mmap and other methods that assume @offset access to file. 18762306a36Sopenharmony_ci// 18862306a36Sopenharmony_ci// XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now. 18962306a36Sopenharmony_ci// XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops". 19062306a36Sopenharmony_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 @ 19162306a36Sopenharmony_ciidentifier fops0.fops; 19262306a36Sopenharmony_ci@@ 19362306a36Sopenharmony_ci struct file_operations fops = { 19462306a36Sopenharmony_ci }; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci// ---- conversions ---- 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci// XXX .open = nonseekable_open -> .open = stream_open 20062306a36Sopenharmony_ci// XXX .open = func -> openfunc -> nonseekable_open 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci// read & write 20362306a36Sopenharmony_ci// 20462306a36Sopenharmony_ci// if both are used in the same file_operations together with an opener - 20562306a36Sopenharmony_ci// under that conditions we can use stream_open instead of nonseekable_open. 20662306a36Sopenharmony_ci@ fops_rw depends on maybe_stream @ 20762306a36Sopenharmony_ciidentifier fops0.fops, openfunc; 20862306a36Sopenharmony_ciidentifier stream_reader.readstream; 20962306a36Sopenharmony_ciidentifier stream_writer.writestream; 21062306a36Sopenharmony_ci@@ 21162306a36Sopenharmony_ci struct file_operations fops = { 21262306a36Sopenharmony_ci .open = openfunc, 21362306a36Sopenharmony_ci .read = readstream, 21462306a36Sopenharmony_ci .write = writestream, 21562306a36Sopenharmony_ci }; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci@ report_rw depends on report @ 21862306a36Sopenharmony_ciidentifier fops_rw.openfunc; 21962306a36Sopenharmony_ciposition p1; 22062306a36Sopenharmony_ci@@ 22162306a36Sopenharmony_ci openfunc(...) { 22262306a36Sopenharmony_ci <... 22362306a36Sopenharmony_ci nonseekable_open@p1 22462306a36Sopenharmony_ci ...> 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci@ script:python depends on report && reader_blocks @ 22862306a36Sopenharmony_cifops << fops0.fops; 22962306a36Sopenharmony_cip << report_rw.p1; 23062306a36Sopenharmony_ci@@ 23162306a36Sopenharmony_cicoccilib.report.print_report(p[0], 23262306a36Sopenharmony_ci "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,)) 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci@ script:python depends on report && !reader_blocks @ 23562306a36Sopenharmony_cifops << fops0.fops; 23662306a36Sopenharmony_cip << report_rw.p1; 23762306a36Sopenharmony_ci@@ 23862306a36Sopenharmony_cicoccilib.report.print_report(p[0], 23962306a36Sopenharmony_ci "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci@ explain_rw_deadlocked depends on explain && reader_blocks @ 24362306a36Sopenharmony_ciidentifier fops_rw.openfunc; 24462306a36Sopenharmony_ci@@ 24562306a36Sopenharmony_ci openfunc(...) { 24662306a36Sopenharmony_ci <... 24762306a36Sopenharmony_ci- nonseekable_open 24862306a36Sopenharmony_ci+ nonseekable_open /* read & write (was deadlock) */ 24962306a36Sopenharmony_ci ...> 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci@ explain_rw_nodeadlock depends on explain && !reader_blocks @ 25462306a36Sopenharmony_ciidentifier fops_rw.openfunc; 25562306a36Sopenharmony_ci@@ 25662306a36Sopenharmony_ci openfunc(...) { 25762306a36Sopenharmony_ci <... 25862306a36Sopenharmony_ci- nonseekable_open 25962306a36Sopenharmony_ci+ nonseekable_open /* read & write (no direct deadlock) */ 26062306a36Sopenharmony_ci ...> 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci@ patch_rw depends on patch @ 26462306a36Sopenharmony_ciidentifier fops_rw.openfunc; 26562306a36Sopenharmony_ci@@ 26662306a36Sopenharmony_ci openfunc(...) { 26762306a36Sopenharmony_ci <... 26862306a36Sopenharmony_ci- nonseekable_open 26962306a36Sopenharmony_ci+ stream_open 27062306a36Sopenharmony_ci ...> 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci// read, but not write 27562306a36Sopenharmony_ci@ fops_r depends on maybe_stream && !has_write @ 27662306a36Sopenharmony_ciidentifier fops0.fops, openfunc; 27762306a36Sopenharmony_ciidentifier stream_reader.readstream; 27862306a36Sopenharmony_ci@@ 27962306a36Sopenharmony_ci struct file_operations fops = { 28062306a36Sopenharmony_ci .open = openfunc, 28162306a36Sopenharmony_ci .read = readstream, 28262306a36Sopenharmony_ci }; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci@ report_r depends on report @ 28562306a36Sopenharmony_ciidentifier fops_r.openfunc; 28662306a36Sopenharmony_ciposition p1; 28762306a36Sopenharmony_ci@@ 28862306a36Sopenharmony_ci openfunc(...) { 28962306a36Sopenharmony_ci <... 29062306a36Sopenharmony_ci nonseekable_open@p1 29162306a36Sopenharmony_ci ...> 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci@ script:python depends on report @ 29562306a36Sopenharmony_cifops << fops0.fops; 29662306a36Sopenharmony_cip << report_r.p1; 29762306a36Sopenharmony_ci@@ 29862306a36Sopenharmony_cicoccilib.report.print_report(p[0], 29962306a36Sopenharmony_ci "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci@ explain_r depends on explain @ 30262306a36Sopenharmony_ciidentifier fops_r.openfunc; 30362306a36Sopenharmony_ci@@ 30462306a36Sopenharmony_ci openfunc(...) { 30562306a36Sopenharmony_ci <... 30662306a36Sopenharmony_ci- nonseekable_open 30762306a36Sopenharmony_ci+ nonseekable_open /* read only */ 30862306a36Sopenharmony_ci ...> 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci@ patch_r depends on patch @ 31262306a36Sopenharmony_ciidentifier fops_r.openfunc; 31362306a36Sopenharmony_ci@@ 31462306a36Sopenharmony_ci openfunc(...) { 31562306a36Sopenharmony_ci <... 31662306a36Sopenharmony_ci- nonseekable_open 31762306a36Sopenharmony_ci+ stream_open 31862306a36Sopenharmony_ci ...> 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci// write, but not read 32362306a36Sopenharmony_ci@ fops_w depends on maybe_stream && !has_read @ 32462306a36Sopenharmony_ciidentifier fops0.fops, openfunc; 32562306a36Sopenharmony_ciidentifier stream_writer.writestream; 32662306a36Sopenharmony_ci@@ 32762306a36Sopenharmony_ci struct file_operations fops = { 32862306a36Sopenharmony_ci .open = openfunc, 32962306a36Sopenharmony_ci .write = writestream, 33062306a36Sopenharmony_ci }; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci@ report_w depends on report @ 33362306a36Sopenharmony_ciidentifier fops_w.openfunc; 33462306a36Sopenharmony_ciposition p1; 33562306a36Sopenharmony_ci@@ 33662306a36Sopenharmony_ci openfunc(...) { 33762306a36Sopenharmony_ci <... 33862306a36Sopenharmony_ci nonseekable_open@p1 33962306a36Sopenharmony_ci ...> 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci@ script:python depends on report @ 34362306a36Sopenharmony_cifops << fops0.fops; 34462306a36Sopenharmony_cip << report_w.p1; 34562306a36Sopenharmony_ci@@ 34662306a36Sopenharmony_cicoccilib.report.print_report(p[0], 34762306a36Sopenharmony_ci "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci@ explain_w depends on explain @ 35062306a36Sopenharmony_ciidentifier fops_w.openfunc; 35162306a36Sopenharmony_ci@@ 35262306a36Sopenharmony_ci openfunc(...) { 35362306a36Sopenharmony_ci <... 35462306a36Sopenharmony_ci- nonseekable_open 35562306a36Sopenharmony_ci+ nonseekable_open /* write only */ 35662306a36Sopenharmony_ci ...> 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci@ patch_w depends on patch @ 36062306a36Sopenharmony_ciidentifier fops_w.openfunc; 36162306a36Sopenharmony_ci@@ 36262306a36Sopenharmony_ci openfunc(...) { 36362306a36Sopenharmony_ci <... 36462306a36Sopenharmony_ci- nonseekable_open 36562306a36Sopenharmony_ci+ stream_open 36662306a36Sopenharmony_ci ...> 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci// no read, no write - don't change anything 371