1 // SPDX-License-Identifier: GPL-2.0 1 // SPDX-License-Identifier: GPL-2.0 2 // Author: Kirill Smelkov (kirr@nexedi.com) 2 // Author: Kirill Smelkov (kirr@nexedi.com) 3 // 3 // 4 // Search for stream-like files that are using 4 // Search for stream-like files that are using nonseekable_open and convert 5 // them to stream_open. A stream-like file is 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 conve 6 // its read and write. Rationale for the conversion is to avoid deadlock in 7 // between read and write. 7 // between read and write. 8 8 9 virtual report 9 virtual report 10 virtual patch 10 virtual patch 11 virtual explain // explain decisions in the p 11 virtual explain // explain decisions in the patch (SPFLAGS="-D explain") 12 12 13 // stream-like reader & writer - ones that do 13 // stream-like reader & writer - ones that do not depend on f_pos. 14 @ stream_reader @ 14 @ stream_reader @ 15 identifier readstream, ppos; 15 identifier readstream, ppos; 16 identifier f, buf, len; 16 identifier f, buf, len; 17 type loff_t; 17 type loff_t; 18 @@ 18 @@ 19 ssize_t readstream(struct file *f, char *buf 19 ssize_t readstream(struct file *f, char *buf, size_t len, loff_t *ppos) 20 { 20 { 21 ... when != ppos 21 ... when != ppos 22 } 22 } 23 23 24 @ stream_writer @ 24 @ stream_writer @ 25 identifier writestream, ppos; 25 identifier writestream, ppos; 26 identifier f, buf, len; 26 identifier f, buf, len; 27 type loff_t; 27 type loff_t; 28 @@ 28 @@ 29 ssize_t writestream(struct file *f, const ch 29 ssize_t writestream(struct file *f, const char *buf, size_t len, loff_t *ppos) 30 { 30 { 31 ... when != ppos 31 ... when != ppos 32 } 32 } 33 33 34 34 35 // a function that blocks 35 // a function that blocks 36 @ blocks @ 36 @ blocks @ 37 identifier block_f; 37 identifier block_f; 38 identifier wait =~ "^wait_.*"; 38 identifier wait =~ "^wait_.*"; 39 @@ 39 @@ 40 block_f(...) { 40 block_f(...) { 41 ... when exists 41 ... when exists 42 wait(...) 42 wait(...) 43 ... when exists 43 ... when exists 44 } 44 } 45 45 46 // stream_reader that can block inside. 46 // stream_reader that can block inside. 47 // 47 // 48 // XXX wait_* can be called not directly from 48 // XXX wait_* can be called not directly from current function (e.g. func -> f -> g -> wait()) 49 // XXX currently reader_blocks supports only d 49 // XXX currently reader_blocks supports only direct and 1-level indirect cases. 50 @ reader_blocks_direct @ 50 @ reader_blocks_direct @ 51 identifier stream_reader.readstream; 51 identifier stream_reader.readstream; 52 identifier wait =~ "^wait_.*"; 52 identifier wait =~ "^wait_.*"; 53 @@ 53 @@ 54 readstream(...) 54 readstream(...) 55 { 55 { 56 ... when exists 56 ... when exists 57 wait(...) 57 wait(...) 58 ... when exists 58 ... when exists 59 } 59 } 60 60 61 @ reader_blocks_1 @ 61 @ reader_blocks_1 @ 62 identifier stream_reader.readstream; 62 identifier stream_reader.readstream; 63 identifier blocks.block_f; 63 identifier blocks.block_f; 64 @@ 64 @@ 65 readstream(...) 65 readstream(...) 66 { 66 { 67 ... when exists 67 ... when exists 68 block_f(...) 68 block_f(...) 69 ... when exists 69 ... when exists 70 } 70 } 71 71 72 @ reader_blocks depends on reader_blocks_direc 72 @ reader_blocks depends on reader_blocks_direct || reader_blocks_1 @ 73 identifier stream_reader.readstream; 73 identifier stream_reader.readstream; 74 @@ 74 @@ 75 readstream(...) { 75 readstream(...) { 76 ... 76 ... 77 } 77 } 78 78 79 79 80 // file_operations + whether they have _any_ . 80 // file_operations + whether they have _any_ .read, .write, .llseek ... at all. 81 // 81 // 82 // XXX add support for file_operations xxx[N] 82 // XXX add support for file_operations xxx[N] = ... (sound/core/pcm_native.c) 83 @ fops0 @ 83 @ fops0 @ 84 identifier fops; 84 identifier fops; 85 @@ 85 @@ 86 struct file_operations fops = { 86 struct file_operations fops = { 87 ... 87 ... 88 }; 88 }; 89 89 90 @ has_read @ 90 @ has_read @ 91 identifier fops0.fops; 91 identifier fops0.fops; 92 identifier read_f; 92 identifier read_f; 93 @@ 93 @@ 94 struct file_operations fops = { 94 struct file_operations fops = { 95 .read = read_f, 95 .read = read_f, 96 }; 96 }; 97 97 98 @ has_read_iter @ 98 @ has_read_iter @ 99 identifier fops0.fops; 99 identifier fops0.fops; 100 identifier read_iter_f; 100 identifier read_iter_f; 101 @@ 101 @@ 102 struct file_operations fops = { 102 struct file_operations fops = { 103 .read_iter = read_iter_f, 103 .read_iter = read_iter_f, 104 }; 104 }; 105 105 106 @ has_write @ 106 @ has_write @ 107 identifier fops0.fops; 107 identifier fops0.fops; 108 identifier write_f; 108 identifier write_f; 109 @@ 109 @@ 110 struct file_operations fops = { 110 struct file_operations fops = { 111 .write = write_f, 111 .write = write_f, 112 }; 112 }; 113 113 114 @ has_write_iter @ 114 @ has_write_iter @ 115 identifier fops0.fops; 115 identifier fops0.fops; 116 identifier write_iter_f; 116 identifier write_iter_f; 117 @@ 117 @@ 118 struct file_operations fops = { 118 struct file_operations fops = { 119 .write_iter = write_iter_f, 119 .write_iter = write_iter_f, 120 }; 120 }; 121 121 122 @ has_llseek @ 122 @ has_llseek @ 123 identifier fops0.fops; 123 identifier fops0.fops; 124 identifier llseek_f; 124 identifier llseek_f; 125 @@ 125 @@ 126 struct file_operations fops = { 126 struct file_operations fops = { 127 .llseek = llseek_f, 127 .llseek = llseek_f, 128 }; 128 }; 129 129 130 @ has_no_llseek @ 130 @ has_no_llseek @ 131 identifier fops0.fops; 131 identifier fops0.fops; 132 @@ 132 @@ 133 struct file_operations fops = { 133 struct file_operations fops = { 134 }; 134 }; 135 135 136 @ has_noop_llseek @ 136 @ has_noop_llseek @ 137 identifier fops0.fops; 137 identifier fops0.fops; 138 @@ 138 @@ 139 struct file_operations fops = { 139 struct file_operations fops = { 140 .llseek = noop_llseek, 140 .llseek = noop_llseek, 141 }; 141 }; 142 142 143 @ has_mmap @ 143 @ has_mmap @ 144 identifier fops0.fops; 144 identifier fops0.fops; 145 identifier mmap_f; 145 identifier mmap_f; 146 @@ 146 @@ 147 struct file_operations fops = { 147 struct file_operations fops = { 148 .mmap = mmap_f, 148 .mmap = mmap_f, 149 }; 149 }; 150 150 151 @ has_copy_file_range @ 151 @ has_copy_file_range @ 152 identifier fops0.fops; 152 identifier fops0.fops; 153 identifier copy_file_range_f; 153 identifier copy_file_range_f; 154 @@ 154 @@ 155 struct file_operations fops = { 155 struct file_operations fops = { 156 .copy_file_range = copy_file_range_f, 156 .copy_file_range = copy_file_range_f, 157 }; 157 }; 158 158 159 @ has_remap_file_range @ 159 @ has_remap_file_range @ 160 identifier fops0.fops; 160 identifier fops0.fops; 161 identifier remap_file_range_f; 161 identifier remap_file_range_f; 162 @@ 162 @@ 163 struct file_operations fops = { 163 struct file_operations fops = { 164 .remap_file_range = remap_file_range_f, 164 .remap_file_range = remap_file_range_f, 165 }; 165 }; 166 166 167 @ has_splice_read @ 167 @ has_splice_read @ 168 identifier fops0.fops; 168 identifier fops0.fops; 169 identifier splice_read_f; 169 identifier splice_read_f; 170 @@ 170 @@ 171 struct file_operations fops = { 171 struct file_operations fops = { 172 .splice_read = splice_read_f, 172 .splice_read = splice_read_f, 173 }; 173 }; 174 174 175 @ has_splice_write @ 175 @ has_splice_write @ 176 identifier fops0.fops; 176 identifier fops0.fops; 177 identifier splice_write_f; 177 identifier splice_write_f; 178 @@ 178 @@ 179 struct file_operations fops = { 179 struct file_operations fops = { 180 .splice_write = splice_write_f, 180 .splice_write = splice_write_f, 181 }; 181 }; 182 182 183 183 184 // file_operations that is candidate for strea 184 // file_operations that is candidate for stream_open conversion - it does not 185 // use mmap and other methods that assume @off 185 // use mmap and other methods that assume @offset access to file. 186 // 186 // 187 // XXX for simplicity require no .{read/write} 187 // XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now. 188 // XXX maybe_steam.fops cannot be used in othe 188 // XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops". 189 @ maybe_stream depends on (!has_llseek || has_ 189 @ 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 @ 190 identifier fops0.fops; 190 identifier fops0.fops; 191 @@ 191 @@ 192 struct file_operations fops = { 192 struct file_operations fops = { 193 }; 193 }; 194 194 195 195 196 // ---- conversions ---- 196 // ---- conversions ---- 197 197 198 // XXX .open = nonseekable_open -> .open = str 198 // XXX .open = nonseekable_open -> .open = stream_open 199 // XXX .open = func -> openfunc -> nonseekable 199 // XXX .open = func -> openfunc -> nonseekable_open 200 200 201 // read & write 201 // read & write 202 // 202 // 203 // if both are used in the same file_operation 203 // if both are used in the same file_operations together with an opener - 204 // under that conditions we can use stream_ope 204 // under that conditions we can use stream_open instead of nonseekable_open. 205 @ fops_rw depends on maybe_stream @ 205 @ fops_rw depends on maybe_stream @ 206 identifier fops0.fops, openfunc; 206 identifier fops0.fops, openfunc; 207 identifier stream_reader.readstream; 207 identifier stream_reader.readstream; 208 identifier stream_writer.writestream; 208 identifier stream_writer.writestream; 209 @@ 209 @@ 210 struct file_operations fops = { 210 struct file_operations fops = { 211 .open = openfunc, 211 .open = openfunc, 212 .read = readstream, 212 .read = readstream, 213 .write = writestream, 213 .write = writestream, 214 }; 214 }; 215 215 216 @ report_rw depends on report @ 216 @ report_rw depends on report @ 217 identifier fops_rw.openfunc; 217 identifier fops_rw.openfunc; 218 position p1; 218 position p1; 219 @@ 219 @@ 220 openfunc(...) { 220 openfunc(...) { 221 <... 221 <... 222 nonseekable_open@p1 222 nonseekable_open@p1 223 ...> 223 ...> 224 } 224 } 225 225 226 @ script:python depends on report && reader_bl 226 @ script:python depends on report && reader_blocks @ 227 fops << fops0.fops; 227 fops << fops0.fops; 228 p << report_rw.p1; 228 p << report_rw.p1; 229 @@ 229 @@ 230 coccilib.report.print_report(p[0], 230 coccilib.report.print_report(p[0], 231 "ERROR: %s: .read() can deadlock .write(); c 231 "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,)) 232 232 233 @ script:python depends on report && !reader_b 233 @ script:python depends on report && !reader_blocks @ 234 fops << fops0.fops; 234 fops << fops0.fops; 235 p << report_rw.p1; 235 p << report_rw.p1; 236 @@ 236 @@ 237 coccilib.report.print_report(p[0], 237 coccilib.report.print_report(p[0], 238 "WARNING: %s: .read() and .write() have stre 238 "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 239 239 240 240 241 @ explain_rw_deadlocked depends on explain && 241 @ explain_rw_deadlocked depends on explain && reader_blocks @ 242 identifier fops_rw.openfunc; 242 identifier fops_rw.openfunc; 243 @@ 243 @@ 244 openfunc(...) { 244 openfunc(...) { 245 <... 245 <... 246 - nonseekable_open 246 - nonseekable_open 247 + nonseekable_open /* read & write (was dea 247 + nonseekable_open /* read & write (was deadlock) */ 248 ...> 248 ...> 249 } 249 } 250 250 251 251 252 @ explain_rw_nodeadlock depends on explain && 252 @ explain_rw_nodeadlock depends on explain && !reader_blocks @ 253 identifier fops_rw.openfunc; 253 identifier fops_rw.openfunc; 254 @@ 254 @@ 255 openfunc(...) { 255 openfunc(...) { 256 <... 256 <... 257 - nonseekable_open 257 - nonseekable_open 258 + nonseekable_open /* read & write (no dire 258 + nonseekable_open /* read & write (no direct deadlock) */ 259 ...> 259 ...> 260 } 260 } 261 261 262 @ patch_rw depends on patch @ 262 @ patch_rw depends on patch @ 263 identifier fops_rw.openfunc; 263 identifier fops_rw.openfunc; 264 @@ 264 @@ 265 openfunc(...) { 265 openfunc(...) { 266 <... 266 <... 267 - nonseekable_open 267 - nonseekable_open 268 + stream_open 268 + stream_open 269 ...> 269 ...> 270 } 270 } 271 271 272 272 273 // read, but not write 273 // read, but not write 274 @ fops_r depends on maybe_stream && !has_write 274 @ fops_r depends on maybe_stream && !has_write @ 275 identifier fops0.fops, openfunc; 275 identifier fops0.fops, openfunc; 276 identifier stream_reader.readstream; 276 identifier stream_reader.readstream; 277 @@ 277 @@ 278 struct file_operations fops = { 278 struct file_operations fops = { 279 .open = openfunc, 279 .open = openfunc, 280 .read = readstream, 280 .read = readstream, 281 }; 281 }; 282 282 283 @ report_r depends on report @ 283 @ report_r depends on report @ 284 identifier fops_r.openfunc; 284 identifier fops_r.openfunc; 285 position p1; 285 position p1; 286 @@ 286 @@ 287 openfunc(...) { 287 openfunc(...) { 288 <... 288 <... 289 nonseekable_open@p1 289 nonseekable_open@p1 290 ...> 290 ...> 291 } 291 } 292 292 293 @ script:python depends on report @ 293 @ script:python depends on report @ 294 fops << fops0.fops; 294 fops << fops0.fops; 295 p << report_r.p1; 295 p << report_r.p1; 296 @@ 296 @@ 297 coccilib.report.print_report(p[0], 297 coccilib.report.print_report(p[0], 298 "WARNING: %s: .read() has stream semantic; s 298 "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 299 299 300 @ explain_r depends on explain @ 300 @ explain_r depends on explain @ 301 identifier fops_r.openfunc; 301 identifier fops_r.openfunc; 302 @@ 302 @@ 303 openfunc(...) { 303 openfunc(...) { 304 <... 304 <... 305 - nonseekable_open 305 - nonseekable_open 306 + nonseekable_open /* read only */ 306 + nonseekable_open /* read only */ 307 ...> 307 ...> 308 } 308 } 309 309 310 @ patch_r depends on patch @ 310 @ patch_r depends on patch @ 311 identifier fops_r.openfunc; 311 identifier fops_r.openfunc; 312 @@ 312 @@ 313 openfunc(...) { 313 openfunc(...) { 314 <... 314 <... 315 - nonseekable_open 315 - nonseekable_open 316 + stream_open 316 + stream_open 317 ...> 317 ...> 318 } 318 } 319 319 320 320 321 // write, but not read 321 // write, but not read 322 @ fops_w depends on maybe_stream && !has_read 322 @ fops_w depends on maybe_stream && !has_read @ 323 identifier fops0.fops, openfunc; 323 identifier fops0.fops, openfunc; 324 identifier stream_writer.writestream; 324 identifier stream_writer.writestream; 325 @@ 325 @@ 326 struct file_operations fops = { 326 struct file_operations fops = { 327 .open = openfunc, 327 .open = openfunc, 328 .write = writestream, 328 .write = writestream, 329 }; 329 }; 330 330 331 @ report_w depends on report @ 331 @ report_w depends on report @ 332 identifier fops_w.openfunc; 332 identifier fops_w.openfunc; 333 position p1; 333 position p1; 334 @@ 334 @@ 335 openfunc(...) { 335 openfunc(...) { 336 <... 336 <... 337 nonseekable_open@p1 337 nonseekable_open@p1 338 ...> 338 ...> 339 } 339 } 340 340 341 @ script:python depends on report @ 341 @ script:python depends on report @ 342 fops << fops0.fops; 342 fops << fops0.fops; 343 p << report_w.p1; 343 p << report_w.p1; 344 @@ 344 @@ 345 coccilib.report.print_report(p[0], 345 coccilib.report.print_report(p[0], 346 "WARNING: %s: .write() has stream semantic; 346 "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 347 347 348 @ explain_w depends on explain @ 348 @ explain_w depends on explain @ 349 identifier fops_w.openfunc; 349 identifier fops_w.openfunc; 350 @@ 350 @@ 351 openfunc(...) { 351 openfunc(...) { 352 <... 352 <... 353 - nonseekable_open 353 - nonseekable_open 354 + nonseekable_open /* write only */ 354 + nonseekable_open /* write only */ 355 ...> 355 ...> 356 } 356 } 357 357 358 @ patch_w depends on patch @ 358 @ patch_w depends on patch @ 359 identifier fops_w.openfunc; 359 identifier fops_w.openfunc; 360 @@ 360 @@ 361 openfunc(...) { 361 openfunc(...) { 362 <... 362 <... 363 - nonseekable_open 363 - nonseekable_open 364 + stream_open 364 + stream_open 365 ...> 365 ...> 366 } 366 } 367 367 368 368 369 // no read, no write - don't change anything 369 // no read, no write - don't change anything
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.