Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'bcachefs-2024-11-07' of git://evilpiepirate.org/bcachefs

Pull bcachefs fixes from Kent Overstreet:
"Some trivial syzbot fixes, two more serious btree fixes found by
looping single_devices.ktest small_nodes:

- Topology error on split after merge, where we accidentaly picked
the node being deleted for the pivot, resulting in an assertion pop

- New nodes being preallocated were left on the freedlist, unlocked,
resulting in them sometimes being accidentally freed: this dated
from pre-cycle detector, when we could leave them locked. This
should have resulted in more explosions and fireworks, but turned
out to be surprisingly hard to hit because the preallocated nodes
were being used right away.

The fix for this is bigger than we'd like - reworking btree list
handling was a bit invasive - but we've now got more assertions and
it's well tested.

- Also another mishandled transaction restart fix (in
btree_node_prefetch) - we're almost done with those"

* tag 'bcachefs-2024-11-07' of git://evilpiepirate.org/bcachefs:
bcachefs: Fix UAF in __promote_alloc() error path
bcachefs: Change OPT_STR max to be 1 less than the size of choices array
bcachefs: btree_cache.freeable list fixes
bcachefs: check the invalid parameter for perf test
bcachefs: add check NULL return of bio_kmalloc in journal_read_bucket
bcachefs: Ensure BCH_FS_may_go_rw is set before exiting recovery
bcachefs: Fix topology errors on split after merge
bcachefs: Ancient versions with bad bkey_formats are no longer supported
bcachefs: Fix error handling in bch2_btree_node_prefetch()
bcachefs: Fix null ptr deref in bucket_gen_get()

+131 -78
+3 -4
fs/bcachefs/bkey.c
··· 643 643 enum bch_validate_flags flags, 644 644 struct printbuf *err) 645 645 { 646 - unsigned i, bits = KEY_PACKED_BITS_START; 646 + unsigned bits = KEY_PACKED_BITS_START; 647 647 648 648 if (f->nr_fields != BKEY_NR_FIELDS) { 649 649 prt_printf(err, "incorrect number of fields: got %u, should be %u", ··· 655 655 * Verify that the packed format can't represent fields larger than the 656 656 * unpacked format: 657 657 */ 658 - for (i = 0; i < f->nr_fields; i++) { 659 - if ((!c || c->sb.version_min >= bcachefs_metadata_version_snapshot) && 660 - bch2_bkey_format_field_overflows(f, i)) { 658 + for (unsigned i = 0; i < f->nr_fields; i++) { 659 + if (bch2_bkey_format_field_overflows(f, i)) { 661 660 unsigned unpacked_bits = bch2_bkey_format_current.bits_per_field[i]; 662 661 u64 unpacked_max = ~((~0ULL << 1) << (unpacked_bits - 1)); 663 662 unsigned packed_bits = min(64, f->bits_per_field[i]);
+66 -41
fs/bcachefs/btree_cache.c
··· 59 59 60 60 static void btree_node_to_freedlist(struct btree_cache *bc, struct btree *b) 61 61 { 62 + BUG_ON(!list_empty(&b->list)); 63 + 62 64 if (b->c.lock.readers) 63 - list_move(&b->list, &bc->freed_pcpu); 65 + list_add(&b->list, &bc->freed_pcpu); 64 66 else 65 - list_move(&b->list, &bc->freed_nonpcpu); 67 + list_add(&b->list, &bc->freed_nonpcpu); 66 68 } 67 69 68 - static void btree_node_data_free(struct bch_fs *c, struct btree *b) 70 + static void __bch2_btree_node_to_freelist(struct btree_cache *bc, struct btree *b) 71 + { 72 + BUG_ON(!list_empty(&b->list)); 73 + BUG_ON(!b->data); 74 + 75 + bc->nr_freeable++; 76 + list_add(&b->list, &bc->freeable); 77 + } 78 + 79 + void bch2_btree_node_to_freelist(struct bch_fs *c, struct btree *b) 69 80 { 70 81 struct btree_cache *bc = &c->btree_cache; 71 82 83 + mutex_lock(&bc->lock); 84 + __bch2_btree_node_to_freelist(bc, b); 85 + mutex_unlock(&bc->lock); 86 + 87 + six_unlock_write(&b->c.lock); 88 + six_unlock_intent(&b->c.lock); 89 + } 90 + 91 + static void __btree_node_data_free(struct btree_cache *bc, struct btree *b) 92 + { 93 + BUG_ON(!list_empty(&b->list)); 72 94 BUG_ON(btree_node_hashed(b)); 73 95 74 96 /* ··· 116 94 #endif 117 95 b->aux_data = NULL; 118 96 119 - bc->nr_freeable--; 120 - 121 97 btree_node_to_freedlist(bc, b); 98 + } 99 + 100 + static void btree_node_data_free(struct btree_cache *bc, struct btree *b) 101 + { 102 + BUG_ON(list_empty(&b->list)); 103 + list_del_init(&b->list); 104 + --bc->nr_freeable; 105 + __btree_node_data_free(bc, b); 122 106 } 123 107 124 108 static int bch2_btree_cache_cmp_fn(struct rhashtable_compare_arg *arg, ··· 202 174 203 175 bch2_btree_lock_init(&b->c, 0); 204 176 205 - bc->nr_freeable++; 206 - list_add(&b->list, &bc->freeable); 177 + __bch2_btree_node_to_freelist(bc, b); 207 178 return b; 208 - } 209 - 210 - void bch2_btree_node_to_freelist(struct bch_fs *c, struct btree *b) 211 - { 212 - mutex_lock(&c->btree_cache.lock); 213 - list_move(&b->list, &c->btree_cache.freeable); 214 - mutex_unlock(&c->btree_cache.lock); 215 - 216 - six_unlock_write(&b->c.lock); 217 - six_unlock_intent(&b->c.lock); 218 179 } 219 180 220 181 static inline bool __btree_node_pinned(struct btree_cache *bc, struct btree *b) ··· 253 236 254 237 /* Btree in memory cache - hash table */ 255 238 256 - void bch2_btree_node_hash_remove(struct btree_cache *bc, struct btree *b) 239 + void __bch2_btree_node_hash_remove(struct btree_cache *bc, struct btree *b) 257 240 { 258 241 lockdep_assert_held(&bc->lock); 259 - int ret = rhashtable_remove_fast(&bc->table, &b->hash, bch_btree_cache_params); 260 242 243 + int ret = rhashtable_remove_fast(&bc->table, &b->hash, bch_btree_cache_params); 261 244 BUG_ON(ret); 262 245 263 246 /* Cause future lookups for this node to fail: */ ··· 265 248 266 249 if (b->c.btree_id < BTREE_ID_NR) 267 250 --bc->nr_by_btree[b->c.btree_id]; 251 + --bc->live[btree_node_pinned(b)].nr; 252 + list_del_init(&b->list); 253 + } 268 254 269 - bc->live[btree_node_pinned(b)].nr--; 270 - bc->nr_freeable++; 271 - list_move(&b->list, &bc->freeable); 255 + void bch2_btree_node_hash_remove(struct btree_cache *bc, struct btree *b) 256 + { 257 + __bch2_btree_node_hash_remove(bc, b); 258 + __bch2_btree_node_to_freelist(bc, b); 272 259 } 273 260 274 261 int __bch2_btree_node_hash_insert(struct btree_cache *bc, struct btree *b) 275 262 { 263 + BUG_ON(!list_empty(&b->list)); 276 264 BUG_ON(b->hash_val); 277 - b->hash_val = btree_ptr_hash_val(&b->key); 278 265 266 + b->hash_val = btree_ptr_hash_val(&b->key); 279 267 int ret = rhashtable_lookup_insert_fast(&bc->table, &b->hash, 280 268 bch_btree_cache_params); 281 269 if (ret) ··· 292 270 bool p = __btree_node_pinned(bc, b); 293 271 mod_bit(BTREE_NODE_pinned, &b->flags, p); 294 272 295 - list_move_tail(&b->list, &bc->live[p].list); 273 + list_add_tail(&b->list, &bc->live[p].list); 296 274 bc->live[p].nr++; 297 - 298 - bc->nr_freeable--; 299 275 return 0; 300 276 } 301 277 ··· 505 485 goto out; 506 486 507 487 if (!btree_node_reclaim(c, b, true)) { 508 - btree_node_data_free(c, b); 488 + btree_node_data_free(bc, b); 509 489 six_unlock_write(&b->c.lock); 510 490 six_unlock_intent(&b->c.lock); 511 491 freed++; ··· 521 501 bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_access_bit]++; 522 502 --touched;; 523 503 } else if (!btree_node_reclaim(c, b, true)) { 524 - bch2_btree_node_hash_remove(bc, b); 504 + __bch2_btree_node_hash_remove(bc, b); 505 + __btree_node_data_free(bc, b); 525 506 526 507 freed++; 527 - btree_node_data_free(c, b); 528 508 bc->nr_freed++; 529 509 530 510 six_unlock_write(&b->c.lock); ··· 607 587 BUG_ON(btree_node_read_in_flight(b) || 608 588 btree_node_write_in_flight(b)); 609 589 610 - btree_node_data_free(c, b); 590 + btree_node_data_free(bc, b); 611 591 } 612 592 613 593 BUG_ON(!bch2_journal_error(&c->journal) && ··· 806 786 807 787 BUG_ON(!six_trylock_intent(&b->c.lock)); 808 788 BUG_ON(!six_trylock_write(&b->c.lock)); 809 - got_node: 810 789 790 + got_node: 811 791 /* 812 792 * btree_free() doesn't free memory; it sticks the node on the end of 813 793 * the list. Check if there's any freed nodes there: ··· 816 796 if (!btree_node_reclaim(c, b2, false)) { 817 797 swap(b->data, b2->data); 818 798 swap(b->aux_data, b2->aux_data); 799 + 800 + list_del_init(&b2->list); 801 + --bc->nr_freeable; 819 802 btree_node_to_freedlist(bc, b2); 803 + mutex_unlock(&bc->lock); 804 + 820 805 six_unlock_write(&b2->c.lock); 821 806 six_unlock_intent(&b2->c.lock); 822 807 goto got_mem; ··· 835 810 goto err; 836 811 } 837 812 838 - mutex_lock(&bc->lock); 839 - bc->nr_freeable++; 840 813 got_mem: 841 - mutex_unlock(&bc->lock); 842 - 814 + BUG_ON(!list_empty(&b->list)); 843 815 BUG_ON(btree_node_hashed(b)); 844 816 BUG_ON(btree_node_dirty(b)); 845 817 BUG_ON(btree_node_write_in_flight(b)); ··· 867 845 if (bc->alloc_lock == current) { 868 846 b2 = btree_node_cannibalize(c); 869 847 clear_btree_node_just_written(b2); 870 - bch2_btree_node_hash_remove(bc, b2); 848 + __bch2_btree_node_hash_remove(bc, b2); 871 849 872 850 if (b) { 873 851 swap(b->data, b2->data); ··· 877 855 six_unlock_intent(&b2->c.lock); 878 856 } else { 879 857 b = b2; 880 - list_del_init(&b->list); 881 858 } 882 859 860 + BUG_ON(!list_empty(&b->list)); 883 861 mutex_unlock(&bc->lock); 884 862 885 863 trace_and_count(c, btree_cache_cannibalize, trans); ··· 958 936 b->hash_val = 0; 959 937 960 938 mutex_lock(&bc->lock); 961 - list_add(&b->list, &bc->freeable); 939 + __bch2_btree_node_to_freelist(bc, b); 962 940 mutex_unlock(&bc->lock); 963 941 964 942 six_unlock_write(&b->c.lock); ··· 1334 1312 1335 1313 b = bch2_btree_node_fill(trans, path, k, btree_id, 1336 1314 level, SIX_LOCK_read, false); 1337 - if (!IS_ERR_OR_NULL(b)) 1315 + int ret = PTR_ERR_OR_ZERO(b); 1316 + if (ret) 1317 + return ret; 1318 + if (b) 1338 1319 six_unlock_read(&b->c.lock); 1339 - return bch2_trans_relock(trans) ?: PTR_ERR_OR_ZERO(b); 1320 + return 0; 1340 1321 } 1341 1322 1342 1323 void bch2_btree_node_evict(struct btree_trans *trans, const struct bkey_i *k) ··· 1378 1353 1379 1354 mutex_lock(&bc->lock); 1380 1355 bch2_btree_node_hash_remove(bc, b); 1381 - btree_node_data_free(c, b); 1356 + btree_node_data_free(bc, b); 1382 1357 mutex_unlock(&bc->lock); 1383 1358 out: 1384 1359 six_unlock_write(&b->c.lock);
+2
fs/bcachefs/btree_cache.h
··· 14 14 15 15 void bch2_btree_node_to_freelist(struct bch_fs *, struct btree *); 16 16 17 + void __bch2_btree_node_hash_remove(struct btree_cache *, struct btree *); 17 18 void bch2_btree_node_hash_remove(struct btree_cache *, struct btree *); 19 + 18 20 int __bch2_btree_node_hash_insert(struct btree_cache *, struct btree *); 19 21 int bch2_btree_node_hash_insert(struct btree_cache *, struct btree *, 20 22 unsigned, enum btree_id);
+1 -1
fs/bcachefs/btree_node_scan.c
··· 186 186 .ptrs[0].type = 1 << BCH_EXTENT_ENTRY_ptr, 187 187 .ptrs[0].offset = offset, 188 188 .ptrs[0].dev = ca->dev_idx, 189 - .ptrs[0].gen = *bucket_gen(ca, sector_to_bucket(ca, offset)), 189 + .ptrs[0].gen = bucket_gen_get(ca, sector_to_bucket(ca, offset)), 190 190 }; 191 191 rcu_read_unlock(); 192 192
+18 -12
fs/bcachefs/btree_update_interior.c
··· 237 237 BUG_ON(b->will_make_reachable); 238 238 239 239 clear_btree_node_noevict(b); 240 - 241 - mutex_lock(&c->btree_cache.lock); 242 - list_move(&b->list, &c->btree_cache.freeable); 243 - mutex_unlock(&c->btree_cache.lock); 244 240 } 245 241 246 242 static void bch2_btree_node_free_inmem(struct btree_trans *trans, ··· 248 252 249 253 bch2_btree_node_lock_write_nofail(trans, path, &b->c); 250 254 255 + __btree_node_free(trans, b); 256 + 251 257 mutex_lock(&c->btree_cache.lock); 252 258 bch2_btree_node_hash_remove(&c->btree_cache, b); 253 259 mutex_unlock(&c->btree_cache.lock); 254 - 255 - __btree_node_free(trans, b); 256 260 257 261 six_unlock_write(&b->c.lock); 258 262 mark_btree_node_locked_noreset(path, level, BTREE_NODE_INTENT_LOCKED); ··· 285 289 clear_btree_node_need_write(b); 286 290 287 291 mutex_lock(&c->btree_cache.lock); 288 - bch2_btree_node_hash_remove(&c->btree_cache, b); 292 + __bch2_btree_node_hash_remove(&c->btree_cache, b); 289 293 mutex_unlock(&c->btree_cache.lock); 290 294 291 295 BUG_ON(p->nr >= ARRAY_SIZE(p->b)); ··· 517 521 btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_intent); 518 522 btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_write); 519 523 __btree_node_free(trans, b); 520 - six_unlock_write(&b->c.lock); 521 - six_unlock_intent(&b->c.lock); 524 + bch2_btree_node_to_freelist(c, b); 522 525 } 523 526 } 524 527 } ··· 1429 1434 } 1430 1435 } 1431 1436 1437 + static bool key_deleted_in_insert(struct keylist *insert_keys, struct bpos pos) 1438 + { 1439 + if (insert_keys) 1440 + for_each_keylist_key(insert_keys, k) 1441 + if (bkey_deleted(&k->k) && bpos_eq(k->k.p, pos)) 1442 + return true; 1443 + return false; 1444 + } 1445 + 1432 1446 /* 1433 1447 * Move keys from n1 (original replacement node, now lower node) to n2 (higher 1434 1448 * node) ··· 1445 1441 static void __btree_split_node(struct btree_update *as, 1446 1442 struct btree_trans *trans, 1447 1443 struct btree *b, 1448 - struct btree *n[2]) 1444 + struct btree *n[2], 1445 + struct keylist *insert_keys) 1449 1446 { 1450 1447 struct bkey_packed *k; 1451 1448 struct bpos n1_pos = POS_MIN; ··· 1481 1476 if (b->c.level && 1482 1477 u64s < n1_u64s && 1483 1478 u64s + k->u64s >= n1_u64s && 1484 - bch2_key_deleted_in_journal(trans, b->c.btree_id, b->c.level, uk.p)) 1479 + (bch2_key_deleted_in_journal(trans, b->c.btree_id, b->c.level, uk.p) || 1480 + key_deleted_in_insert(insert_keys, uk.p))) 1485 1481 n1_u64s += k->u64s; 1486 1482 1487 1483 i = u64s >= n1_u64s; ··· 1609 1603 n[0] = n1 = bch2_btree_node_alloc(as, trans, b->c.level); 1610 1604 n[1] = n2 = bch2_btree_node_alloc(as, trans, b->c.level); 1611 1605 1612 - __btree_split_node(as, trans, b, n); 1606 + __btree_split_node(as, trans, b, n, keys); 1613 1607 1614 1608 if (keys) { 1615 1609 btree_split_insert_keys(as, trans, path, n1, keys);
+11 -8
fs/bcachefs/buckets.h
··· 103 103 return gens->b + b; 104 104 } 105 105 106 - static inline u8 bucket_gen_get(struct bch_dev *ca, size_t b) 106 + static inline int bucket_gen_get_rcu(struct bch_dev *ca, size_t b) 107 + { 108 + u8 *gen = bucket_gen(ca, b); 109 + return gen ? *gen : -1; 110 + } 111 + 112 + static inline int bucket_gen_get(struct bch_dev *ca, size_t b) 107 113 { 108 114 rcu_read_lock(); 109 - u8 gen = *bucket_gen(ca, b); 115 + int ret = bucket_gen_get_rcu(ca, b); 110 116 rcu_read_unlock(); 111 - return gen; 117 + return ret; 112 118 } 113 119 114 120 static inline size_t PTR_BUCKET_NR(const struct bch_dev *ca, ··· 175 169 176 170 static inline int dev_ptr_stale_rcu(struct bch_dev *ca, const struct bch_extent_ptr *ptr) 177 171 { 178 - u8 *gen = bucket_gen(ca, PTR_BUCKET_NR(ca, ptr)); 179 - if (!gen) 180 - return -1; 181 - return gen_after(*gen, ptr->gen); 172 + int gen = bucket_gen_get_rcu(ca, PTR_BUCKET_NR(ca, ptr)); 173 + return gen < 0 ? gen : gen_after(gen, ptr->gen); 182 174 } 183 175 184 176 /** ··· 188 184 rcu_read_lock(); 189 185 int ret = dev_ptr_stale_rcu(ca, ptr); 190 186 rcu_read_unlock(); 191 - 192 187 return ret; 193 188 } 194 189
+1
fs/bcachefs/errcode.h
··· 84 84 x(ENOMEM, ENOMEM_dev_alloc) \ 85 85 x(ENOMEM, ENOMEM_disk_accounting) \ 86 86 x(ENOMEM, ENOMEM_stripe_head_alloc) \ 87 + x(ENOMEM, ENOMEM_journal_read_bucket) \ 87 88 x(ENOSPC, ENOSPC_disk_reservation) \ 88 89 x(ENOSPC, ENOSPC_bucket_alloc) \ 89 90 x(ENOSPC, ENOSPC_disk_label_add) \
+5 -5
fs/bcachefs/io_read.c
··· 262 262 bio_free_pages(&(*rbio)->bio); 263 263 kfree(*rbio); 264 264 *rbio = NULL; 265 - kfree(op); 265 + /* We may have added to the rhashtable and thus need rcu freeing: */ 266 + kfree_rcu(op, rcu); 266 267 bch2_write_ref_put(c, BCH_WRITE_REF_promote); 267 268 return ERR_PTR(ret); 268 269 } ··· 803 802 PTR_BUCKET_POS(ca, &ptr), 804 803 BTREE_ITER_cached); 805 804 806 - u8 *gen = bucket_gen(ca, iter.pos.offset); 807 - if (gen) { 808 - 805 + int gen = bucket_gen_get(ca, iter.pos.offset); 806 + if (gen >= 0) { 809 807 prt_printf(&buf, "Attempting to read from stale dirty pointer:\n"); 810 808 printbuf_indent_add(&buf, 2); 811 809 812 810 bch2_bkey_val_to_text(&buf, c, k); 813 811 prt_newline(&buf); 814 812 815 - prt_printf(&buf, "memory gen: %u", *gen); 813 + prt_printf(&buf, "memory gen: %u", gen); 816 814 817 815 ret = lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_slot(&iter))); 818 816 if (!ret) {
+2 -5
fs/bcachefs/io_write.c
··· 1300 1300 bucket_to_u64(i->b), 1301 1301 BUCKET_NOCOW_LOCK_UPDATE); 1302 1302 1303 - rcu_read_lock(); 1304 - u8 *gen = bucket_gen(ca, i->b.offset); 1305 - stale = !gen ? -1 : gen_after(*gen, i->gen); 1306 - rcu_read_unlock(); 1307 - 1303 + int gen = bucket_gen_get(ca, i->b.offset); 1304 + stale = gen < 0 ? gen : gen_after(gen, i->gen); 1308 1305 if (unlikely(stale)) { 1309 1306 stale_at = i; 1310 1307 goto err_bucket_stale;
+2
fs/bcachefs/journal_io.c
··· 1012 1012 nr_bvecs = buf_pages(buf->data, sectors_read << 9); 1013 1013 1014 1014 bio = bio_kmalloc(nr_bvecs, GFP_KERNEL); 1015 + if (!bio) 1016 + return -BCH_ERR_ENOMEM_journal_read_bucket; 1015 1017 bio_init(bio, ca->disk_sb.bdev, bio->bi_inline_vecs, nr_bvecs, REQ_OP_READ); 1016 1018 1017 1019 bio->bi_iter.bi_sector = offset;
+2 -2
fs/bcachefs/opts.c
··· 226 226 #define OPT_UINT(_min, _max) .type = BCH_OPT_UINT, \ 227 227 .min = _min, .max = _max 228 228 #define OPT_STR(_choices) .type = BCH_OPT_STR, \ 229 - .min = 0, .max = ARRAY_SIZE(_choices), \ 229 + .min = 0, .max = ARRAY_SIZE(_choices) - 1, \ 230 230 .choices = _choices 231 231 #define OPT_STR_NOLIMIT(_choices) .type = BCH_OPT_STR, \ 232 232 .min = 0, .max = U64_MAX, \ ··· 428 428 prt_printf(out, "%lli", v); 429 429 break; 430 430 case BCH_OPT_STR: 431 - if (v < opt->min || v >= opt->max - 1) 431 + if (v < opt->min || v >= opt->max) 432 432 prt_printf(out, "(invalid option %lli)", v); 433 433 else if (flags & OPT_SHOW_FULL_LIST) 434 434 prt_string_option(out, opt->choices, v);
+7
fs/bcachefs/recovery.c
··· 862 862 if (ret) 863 863 goto err; 864 864 865 + /* 866 + * Normally set by the appropriate recovery pass: when cleared, this 867 + * indicates we're in early recovery and btree updates should be done by 868 + * being applied to the journal replay keys. _Must_ be cleared before 869 + * multithreaded use: 870 + */ 871 + set_bit(BCH_FS_may_go_rw, &c->flags); 865 872 clear_bit(BCH_FS_fsck_running, &c->flags); 866 873 867 874 /* in case we don't run journal replay, i.e. norecovery mode */
+6
fs/bcachefs/recovery_passes.c
··· 221 221 { 222 222 int ret = 0; 223 223 224 + /* 225 + * We can't allow set_may_go_rw to be excluded; that would cause us to 226 + * use the journal replay keys for updates where it's not expected. 227 + */ 228 + c->opts.recovery_passes_exclude &= ~BCH_RECOVERY_PASS_set_may_go_rw; 229 + 224 230 while (c->curr_recovery_pass < ARRAY_SIZE(recovery_pass_fns)) { 225 231 if (c->opts.recovery_pass_last && 226 232 c->curr_recovery_pass > c->opts.recovery_pass_last)
+5
fs/bcachefs/tests.c
··· 809 809 unsigned i; 810 810 u64 time; 811 811 812 + if (nr == 0 || nr_threads == 0) { 813 + pr_err("nr of iterations or threads is not allowed to be 0"); 814 + return -EINVAL; 815 + } 816 + 812 817 atomic_set(&j.ready, nr_threads); 813 818 init_waitqueue_head(&j.ready_wait); 814 819