Pull jfs updates from Dave Kleikamp: "More robust data integrity checking and some fixes" * tag 'jfs-7.1' of github.com:kleikamp/linux-shaggy: jfs: avoid -Wtautological-constant-out-of-range-compare warning again JFS: always load filesystem UUID during mount jfs: hold LOG_LOCK on umount to avoid null-ptr-deref jfs: Set the lbmDone flag at the end of lbmIODone jfs: fix corrupted list in dbUpdatePMap jfs: add dmapctl integrity check to prevent invalid operations jfs: add dtpage integrity check to prevent index/pointer overflows jfs: add dtroot integrity check to prevent index out-of-bounds
Unified · Split
| @@ -133,6 +133,93 @@ static const s8 budtab[256] = { · 132 unmodified lines | ||
| 133 | 133 | 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1 |
| 134 | 134 | }; |
| 135 | 135 | |
| 136 | +/* | |
| 137 | + * check_dmapctl - Validate integrity of a dmapctl structure | |
| 138 | + * @dcp: Pointer to the dmapctl structure to check | |
| 139 | + * | |
| 140 | + * Return: true if valid, false if corrupted | |
| 141 | + */ | |
| 142 | +static bool check_dmapctl(struct dmapctl *dcp) | |
| 143 | +{ | |
| 144 | + s8 budmin = dcp->budmin; | |
| 145 | + u32 nleafs, l2nleafs, leafidx, height; | |
| 146 | + int i; | |
| 147 | + | |
| 148 | + nleafs = le32_to_cpu(dcp->nleafs); | |
| 149 | + /* Check basic field ranges */ | |
| 150 | + if (unlikely(nleafs > LPERCTL)) { | |
| 151 | + jfs_err("dmapctl: invalid nleafs %u (max %u)", | |
| 152 | + nleafs, LPERCTL); | |
| 153 | + return false; | |
| 154 | + } | |
| 155 | + | |
| 156 | + l2nleafs = le32_to_cpu(dcp->l2nleafs); | |
| 157 | + if (unlikely(l2nleafs > L2LPERCTL)) { | |
| 158 | + jfs_err("dmapctl: invalid l2nleafs %u (max %u)", | |
| 159 | + l2nleafs, L2LPERCTL); | |
| 160 | + return false; | |
| 161 | + } | |
| 162 | + | |
| 163 | + /* Verify nleafs matches l2nleafs (must be power of two) */ | |
| 164 | + if (unlikely((1U << l2nleafs) != nleafs)) { | |
| 165 | + jfs_err("dmapctl: nleafs %u != 2^%u", | |
| 166 | + nleafs, l2nleafs); | |
| 167 | + return false; | |
| 168 | + } | |
| 169 | + | |
| 170 | + leafidx = le32_to_cpu(dcp->leafidx); | |
| 171 | + /* Check leaf index matches expected position */ | |
| 172 | + if (unlikely(leafidx != CTLLEAFIND)) { | |
| 173 | + jfs_err("dmapctl: invalid leafidx %u (expected %u)", | |
| 174 | + leafidx, CTLLEAFIND); | |
| 175 | + return false; | |
| 176 | + } | |
| 177 | + | |
| 178 | + height = le32_to_cpu(dcp->height); | |
| 179 | + /* Check tree height is within valid range */ | |
| 180 | + if (unlikely(height > (L2LPERCTL >> 1))) { | |
| 181 | + jfs_err("dmapctl: invalid height %u (max %u)", | |
| 182 | + height, L2LPERCTL >> 1); | |
| 183 | + return false; | |
| 184 | + } | |
| 185 | + | |
| 186 | + /* Check budmin is valid (cannot be NOFREE for non-empty tree) */ | |
| 187 | + if (budmin == NOFREE) { | |
| 188 | + if (unlikely(nleafs > 0)) { | |
| 189 | + jfs_err("dmapctl: budmin is NOFREE but nleafs %u", | |
| 190 | + nleafs); | |
| 191 | + return false; | |
| 192 | + } | |
| 193 | + } else if (unlikely(budmin < BUDMIN)) { | |
| 194 | + jfs_err("dmapctl: invalid budmin %d (min %d)", | |
| 195 | + budmin, BUDMIN); | |
| 196 | + return false; | |
| 197 | + } | |
| 198 | + | |
| 199 | + /* Check leaf nodes fit within stree array */ | |
| 200 | + if (unlikely(leafidx + nleafs > CTLTREESIZE)) { | |
| 201 | + jfs_err("dmapctl: leaf range exceeds stree size (end %u > %u)", | |
| 202 | + leafidx + nleafs, CTLTREESIZE); | |
| 203 | + return false; | |
| 204 | + } | |
| 205 | + | |
| 206 | + /* Check leaf nodes have valid values */ | |
| 207 | + for (i = leafidx; i < leafidx + nleafs; i++) { | |
| 208 | + s8 val = dcp->stree[i]; | |
| 209 | + | |
| 210 | + if (unlikely(val < NOFREE)) { | |
| 211 | + jfs_err("dmapctl: invalid leaf value %d at index %d", | |
| 212 | + val, i); | |
| 213 | + return false; | |
| 214 | + } else if (unlikely(val > 31)) { | |
| 215 | + jfs_err("dmapctl: leaf value %d too large at index %d", val, i); | |
| 216 | + return false; | |
| 217 | + } | |
| 218 | + } | |
| 219 | + | |
| 220 | + return true; | |
| 221 | +} | |
| 222 | + | |
| 136 | 223 | /* |
| 137 | 224 | * NAME: dbMount() |
| 138 | 225 | * |
| @@ -1372,7 +1459,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) · 1233 unmodified lines | ||
| 1372 | 1459 | dcp = (struct dmapctl *) mp->data; |
| 1373 | 1460 | budmin = dcp->budmin; |
| 1374 | 1461 | |
| 1375 | − if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { | |
| 1462 | + if (unlikely(!check_dmapctl(dcp))) { | |
| 1376 | 1463 | jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n"); |
| 1377 | 1464 | release_metapage(mp); |
| 1378 | 1465 | return -EIO; |
| @@ -1702,7 +1789,7 @@ static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno) · 323 unmodified lines | ||
| 1702 | 1789 | dcp = (struct dmapctl *) mp->data; |
| 1703 | 1790 | budmin = dcp->budmin; |
| 1704 | 1791 | |
| 1705 | − if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { | |
| 1792 | + if (unlikely(!check_dmapctl(dcp))) { | |
| 1706 | 1793 | jfs_error(bmp->db_ipbmap->i_sb, |
| 1707 | 1794 | "Corrupt dmapctl page\n"); |
| 1708 | 1795 | release_metapage(mp); |
| @@ -2485,7 +2572,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level) · 776 unmodified lines | ||
| 2485 | 2572 | return -EIO; |
| 2486 | 2573 | dcp = (struct dmapctl *) mp->data; |
| 2487 | 2574 | |
| 2488 | − if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { | |
| 2575 | + if (unlikely(!check_dmapctl(dcp))) { | |
| 2489 | 2576 | jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n"); |
| 2490 | 2577 | release_metapage(mp); |
| 2491 | 2578 | return -EIO; |
| @@ -3454,6 +3541,11 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) · 962 unmodified lines | ||
| 3454 | 3541 | return -EIO; |
| 3455 | 3542 | } |
| 3456 | 3543 | l2dcp = (struct dmapctl *) l2mp->data; |
| 3544 | + if (unlikely(!check_dmapctl(l2dcp))) { | |
| 3545 | + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); | |
| 3546 | + release_metapage(l2mp); | |
| 3547 | + return -EIO; | |
| 3548 | + } | |
| 3457 | 3549 | |
| 3458 | 3550 | /* compute start L1 */ |
| 3459 | 3551 | k = blkno >> L2MAXL1SIZE; |
| @@ -3471,6 +3563,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) · 11 unmodified lines | ||
| 3471 | 3563 | if (l1mp == NULL) |
| 3472 | 3564 | goto errout; |
| 3473 | 3565 | l1dcp = (struct dmapctl *) l1mp->data; |
| 3566 | + if (unlikely(!check_dmapctl(l1dcp))) { | |
| 3567 | + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); | |
| 3568 | + goto errout; | |
| 3569 | + } | |
| 3474 | 3570 | |
| 3475 | 3571 | /* compute start L0 */ |
| 3476 | 3572 | j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE; |
| @@ -3484,6 +3580,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) · 7 unmodified lines | ||
| 3484 | 3580 | goto errout; |
| 3485 | 3581 | |
| 3486 | 3582 | l1dcp = (struct dmapctl *) l1mp->data; |
| 3583 | + if (unlikely(!check_dmapctl(l1dcp))) { | |
| 3584 | + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); | |
| 3585 | + goto errout; | |
| 3586 | + } | |
| 3487 | 3587 | |
| 3488 | 3588 | /* compute start L0 */ |
| 3489 | 3589 | j = 0; |
| @@ -3503,6 +3603,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) · 13 unmodified lines | ||
| 3503 | 3603 | if (l0mp == NULL) |
| 3504 | 3604 | goto errout; |
| 3505 | 3605 | l0dcp = (struct dmapctl *) l0mp->data; |
| 3606 | + if (unlikely(!check_dmapctl(l0dcp))) { | |
| 3607 | + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); | |
| 3608 | + goto errout; | |
| 3609 | + } | |
| 3506 | 3610 | |
| 3507 | 3611 | /* compute start dmap */ |
| 3508 | 3612 | i = (blkno & (MAXL0SIZE - 1)) >> |
| @@ -3518,6 +3622,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) · 9 unmodified lines | ||
| 3518 | 3622 | goto errout; |
| 3519 | 3623 | |
| 3520 | 3624 | l0dcp = (struct dmapctl *) l0mp->data; |
| 3625 | + if (unlikely(!check_dmapctl(l0dcp))) { | |
| 3626 | + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); | |
| 3627 | + goto errout; | |
| 3628 | + } | |
| 3521 | 3629 | |
| 3522 | 3630 | /* compute start dmap */ |
| 3523 | 3631 | i = 0; |
| @@ -115,10 +115,7 @@ struct dtsplit { · 114 unmodified lines | ||
| 115 | 115 | do { \ |
| 116 | 116 | BT_GETPAGE(IP, BN, MP, dtpage_t, SIZE, P, RC, i_dtroot); \ |
| 117 | 117 | if (!(RC)) { \ |
| 118 | − if (((P)->header.nextindex > \ | |
| 119 | − (((BN) == 0) ? DTROOTMAXSLOT : (P)->header.maxslot)) || \ | |
| 120 | − ((BN) && (((P)->header.maxslot > DTPAGEMAXSLOT) || \ | |
| 121 | − ((P)->header.stblindex >= DTPAGEMAXSLOT)))) { \ | |
| 118 | + if ((BN) && !check_dtpage(P)) { \ | |
| 122 | 119 | BT_PUTPAGE(MP); \ |
| 123 | 120 | jfs_error((IP)->i_sb, \ |
| 124 | 121 | "DT_GETPAGE: dtree page corrupt\n"); \ |
| @@ -4297,3 +4294,190 @@ int dtModify(tid_t tid, struct inode *ip, · 4172 unmodified lines | ||
| 4297 | 4294 | |
| 4298 | 4295 | return 0; |
| 4299 | 4296 | } |
| 4297 | + | |
| 4298 | +bool check_dtroot(dtroot_t *p) | |
| 4299 | +{ | |
| 4300 | + DECLARE_BITMAP(bitmap, DTROOTMAXSLOT) = {0}; | |
| 4301 | + int i; | |
| 4302 | + | |
| 4303 | + /* freecnt cannot be negative or exceed DTROOTMAXSLOT-1 | |
| 4304 | + * (since slot[0] is occupied by the header). | |
| 4305 | + */ | |
| 4306 | + if (unlikely(p->header.freecnt < 0 || | |
| 4307 | + p->header.freecnt > DTROOTMAXSLOT - 1)) { | |
| 4308 | + jfs_err("Bad freecnt:%d in dtroot\n", p->header.freecnt); | |
| 4309 | + return false; | |
| 4310 | + } else if (p->header.freecnt == 0) { | |
| 4311 | + /* No free slots: freelist must be -1 */ | |
| 4312 | + if (unlikely(p->header.freelist != -1)) { | |
| 4313 | + jfs_err("freecnt=0, but freelist=%d in dtroot\n", | |
| 4314 | + p->header.freelist); | |
| 4315 | + return false; | |
| 4316 | + } | |
| 4317 | + } else { | |
| 4318 | + int fsi, i; | |
| 4319 | + /* When there are free slots, freelist must be a valid slot index in | |
| 4320 | + * 1~DTROOTMAXSLOT-1(since slot[0] is occupied by the header). | |
| 4321 | + */ | |
| 4322 | + if (unlikely(p->header.freelist < 1 || | |
| 4323 | + p->header.freelist >= DTROOTMAXSLOT)) { | |
| 4324 | + jfs_err("Bad freelist:%d in dtroot\n", p->header.freelist); | |
| 4325 | + return false; | |
| 4326 | + } | |
| 4327 | + | |
| 4328 | + /* Traverse the free list to check validity of all node indices */ | |
| 4329 | + fsi = p->header.freelist; | |
| 4330 | + for (i = 0; i < p->header.freecnt - 1; i++) { | |
| 4331 | + /* Check for duplicate indices in the free list */ | |
| 4332 | + if (unlikely(__test_and_set_bit(fsi, bitmap))) { | |
| 4333 | + jfs_err("duplicate index%d in slot in dtroot\n", fsi); | |
| 4334 | + return false; | |
| 4335 | + } | |
| 4336 | + fsi = p->slot[fsi].next; | |
| 4337 | + | |
| 4338 | + /* Ensure the next slot index in the free list is valid */ | |
| 4339 | + if (unlikely(fsi < 1 || fsi >= DTROOTMAXSLOT)) { | |
| 4340 | + jfs_err("Bad index:%d in slot in dtroot\n", fsi); | |
| 4341 | + return false; | |
| 4342 | + } | |
| 4343 | + } | |
| 4344 | + | |
| 4345 | + /* The last node in the free list must terminate with next = -1 */ | |
| 4346 | + if (unlikely(p->slot[fsi].next != -1)) { | |
| 4347 | + jfs_err("Bad next:%d of the last slot in dtroot\n", | |
| 4348 | + p->slot[fsi].next); | |
| 4349 | + return false; | |
| 4350 | + } | |
| 4351 | + } | |
| 4352 | + | |
| 4353 | + /* Validate nextindex (next free entry index in stbl) | |
| 4354 | + * stbl array has size 8 (indices 0~7). | |
| 4355 | + * It may get set to 8 when the last free slot has been filled. | |
| 4356 | + */ | |
| 4357 | + if (unlikely(p->header.nextindex > ARRAY_SIZE(p->header.stbl))) { | |
| 4358 | + jfs_err("Bad nextindex:%d in dtroot\n", p->header.nextindex); | |
| 4359 | + return false; | |
| 4360 | + } | |
| 4361 | + | |
| 4362 | + /* Validate index validity of stbl array (8 elements) | |
| 4363 | + * Each entry in stbl is a slot index, with valid range: -1 (invalid) | |
| 4364 | + * or 0~8 (slot[0]~slot[8]) | |
| 4365 | + */ | |
| 4366 | + for (i = 0; i < p->header.nextindex; i++) { | |
| 4367 | + int idx = p->header.stbl[i]; | |
| 4368 | + | |
| 4369 | + if (unlikely(idx < 0 || idx >= 9)) { | |
| 4370 | + jfs_err("Bad index:%d of stbl[%d] in dtroot\n", idx, i); | |
| 4371 | + return false; /* stbl entry points out of slot array range */ | |
| 4372 | + } | |
| 4373 | + | |
| 4374 | + /* Check for duplicate valid indices (skip check for idx=0) */ | |
| 4375 | + if (unlikely(idx && __test_and_set_bit(idx, bitmap))) { | |
| 4376 | + jfs_err("Duplicate index:%d in stbl in dtroot\n", idx); | |
| 4377 | + return false; | |
| 4378 | + } | |
| 4379 | + } | |
| 4380 | + | |
| 4381 | + return true; | |
| 4382 | +} | |
| 4383 | + | |
| 4384 | +bool check_dtpage(dtpage_t *p) | |
| 4385 | +{ | |
| 4386 | + DECLARE_BITMAP(bitmap, DTPAGEMAXSLOT) = {0}; | |
| 4387 | + const int stblsize = ((PSIZE >> L2DTSLOTSIZE) + 31) >> L2DTSLOTSIZE; | |
| 4388 | + int i; | |
| 4389 | + | |
| 4390 | + /* Validate maxslot (maximum number of slots in the page) | |
| 4391 | + * dtpage_t slot array is defined to hold up to DTPAGEMAXSLOT (128) slots | |
| 4392 | + */ | |
| 4393 | + if (unlikely(p->header.maxslot != DTPAGEMAXSLOT)) { | |
| 4394 | + jfs_err("Bad maxslot:%d in dtpage (expected %d)\n", | |
| 4395 | + p->header.maxslot, DTPAGEMAXSLOT); | |
| 4396 | + return false; | |
| 4397 | + } | |
| 4398 | + | |
| 4399 | + /* freecnt cannot be negative or exceed DTPAGEMAXSLOT-1 | |
| 4400 | + * (since slot[0] is occupied by the header). | |
| 4401 | + */ | |
| 4402 | + if (unlikely(p->header.freecnt < 0 || | |
| 4403 | + p->header.freecnt > DTPAGEMAXSLOT - 1)) { | |
| 4404 | + jfs_err("Bad freecnt:%d in dtpage\n", p->header.freecnt); | |
| 4405 | + return false; | |
| 4406 | + } else if (p->header.freecnt == 0) { | |
| 4407 | + /* No free slots: freelist must be -1 */ | |
| 4408 | + if (unlikely(p->header.freelist != -1)) { | |
| 4409 | + jfs_err("freecnt=0 but freelist=%d in dtpage\n", | |
| 4410 | + p->header.freelist); | |
| 4411 | + return false; | |
| 4412 | + } | |
| 4413 | + } else { | |
| 4414 | + int fsi; | |
| 4415 | + | |
| 4416 | + if (unlikely(p->header.freelist < 1)) { | |
| 4417 | + jfs_err("Bad freelist:%d in dtpage\n", p->header.freelist); | |
| 4418 | + return false; | |
| 4419 | + } | |
| 4420 | + | |
| 4421 | + /* Traverse the free list to check validity of all node indices */ | |
| 4422 | + fsi = p->header.freelist; | |
| 4423 | + for (i = 0; i < p->header.freecnt - 1; i++) { | |
| 4424 | + /* Check for duplicate indices in the free list */ | |
| 4425 | + if (unlikely(__test_and_set_bit(fsi, bitmap))) { | |
| 4426 | + jfs_err("duplicate index%d in slot in dtpage\n", fsi); | |
| 4427 | + return false; | |
| 4428 | + } | |
| 4429 | + fsi = p->slot[fsi].next; | |
| 4430 | + | |
| 4431 | + /* Ensure the next slot index in the free list is valid */ | |
| 4432 | + if (unlikely(fsi < 1 || fsi >= DTPAGEMAXSLOT)) { | |
| 4433 | + jfs_err("Bad index:%d in slot in dtpage\n", fsi); | |
| 4434 | + return false; | |
| 4435 | + } | |
| 4436 | + } | |
| 4437 | + | |
| 4438 | + /* The last node in the free list must terminate with next = -1 */ | |
| 4439 | + if (unlikely(p->slot[fsi].next != -1)) { | |
| 4440 | + jfs_err("Bad next:%d of the last slot in dtpage\n", | |
| 4441 | + p->slot[fsi].next); | |
| 4442 | + return false; | |
| 4443 | + } | |
| 4444 | + } | |
| 4445 | + | |
| 4446 | + /* stbl must be little then DTPAGEMAXSLOT */ | |
| 4447 | + if (unlikely(p->header.stblindex >= DTPAGEMAXSLOT - stblsize)) { | |
| 4448 | + jfs_err("Bad stblindex:%d in dtpage (stbl size %d)\n", | |
| 4449 | + p->header.stblindex, stblsize); | |
| 4450 | + return false; | |
| 4451 | + } | |
| 4452 | + | |
| 4453 | + /* nextindex must be little then stblsize*32 */ | |
| 4454 | + if (unlikely(p->header.nextindex > (stblsize << L2DTSLOTSIZE))) { | |
| 4455 | + jfs_err("Bad nextindex:%d in dtpage (stbl size %d)\n", | |
| 4456 | + p->header.nextindex, stblsize); | |
| 4457 | + return false; | |
| 4458 | + } | |
| 4459 | + | |
| 4460 | + /* Validate stbl entries | |
| 4461 | + * Each entry is a slot index, valid range: -1 (invalid) or | |
| 4462 | + * [0, nextindex-1] (valid data slots) | |
| 4463 | + * (stblindex and higher slots are reserved for stbl itself) | |
| 4464 | + */ | |
| 4465 | + for (i = 0; i < p->header.nextindex; i++) { | |
| 4466 | + int idx = DT_GETSTBL(p)[i]; | |
| 4467 | + | |
| 4468 | + /* Check if index is out of valid data slot range */ | |
| 4469 | + if (unlikely(idx < 1 || idx >= DTPAGEMAXSLOT)) { | |
| 4470 | + jfs_err("Bad stbl[%d] index:%d (stblindex %d) in dtpage\n", | |
| 4471 | + i, idx, p->header.stblindex); | |
| 4472 | + return false; | |
| 4473 | + } | |
| 4474 | + | |
| 4475 | + /* Check for duplicate valid indices (skip -1) */ | |
| 4476 | + if (unlikely(__test_and_set_bit(idx, bitmap))) { | |
| 4477 | + jfs_err("Duplicate index:%d in stbl of dtpage\n", idx); | |
| 4478 | + return false; | |
| 4479 | + } | |
| 4480 | + } | |
| 4481 | + | |
| 4482 | + return true; | |
| 4483 | +} | |
| @@ -253,4 +253,8 @@ extern int dtModify(tid_t tid, struct inode *ip, struct component_name * key, · 252 unmodified lines | ||
| 253 | 253 | ino_t * orig_ino, ino_t new_ino, int flag); |
| 254 | 254 | |
| 255 | 255 | extern int jfs_readdir(struct file *file, struct dir_context *ctx); |
| 256 | + | |
| 257 | +extern bool check_dtroot(dtroot_t *p); | |
| 258 | + | |
| 259 | +extern bool check_dtpage(dtpage_t *p); | |
| 256 | 260 | #endif /* !_H_JFS_DTREE */ |
| @@ -3102,6 +3102,10 @@ static int copy_from_dinode(struct dinode * dip, struct inode *ip) · 3101 unmodified lines | ||
| 3102 | 3102 | |
| 3103 | 3103 | if (S_ISDIR(ip->i_mode)) { |
| 3104 | 3104 | memcpy(&jfs_ip->u.dir, &dip->u._dir, 384); |
| 3105 | + if (!check_dtroot(&jfs_ip->i_dtroot)) { | |
| 3106 | + jfs_error(ip->i_sb, "Corrupt dtroot\n"); | |
| 3107 | + return -EIO; | |
| 3108 | + } | |
| 3105 | 3109 | } else if (S_ISREG(ip->i_mode) || S_ISLNK(ip->i_mode)) { |
| 3106 | 3110 | memcpy(&jfs_ip->i_xtroot, &dip->di_xtroot, 288); |
| 3107 | 3111 | } else |
| @@ -74,12 +74,6 @@ static struct lbuf *log_redrive_list; · 73 unmodified lines | ||
| 74 | 74 | static DEFINE_SPINLOCK(log_redrive_lock); |
| 75 | 75 | |
| 76 | 76 | |
| 77 | −/* | |
| 78 | − * log read/write serialization (per log) | |
| 79 | − */ | |
| 80 | −#define LOG_LOCK_INIT(log) mutex_init(&(log)->loglock) | |
| 81 | −#define LOG_LOCK(log) mutex_lock(&((log)->loglock)) | |
| 82 | −#define LOG_UNLOCK(log) mutex_unlock(&((log)->loglock)) | |
| 83 | 77 | |
| 84 | 78 | |
| 85 | 79 | /* |
| @@ -204,9 +198,13 @@ static void write_special_inodes(struct jfs_log *log, · 118 unmodified lines | ||
| 204 | 198 | struct jfs_sb_info *sbi; |
| 205 | 199 | |
| 206 | 200 | list_for_each_entry(sbi, &log->sb_list, log_list) { |
| 207 | − writer(sbi->ipbmap->i_mapping); | |
| 208 | − writer(sbi->ipimap->i_mapping); | |
| 209 | − writer(sbi->direct_inode->i_mapping); | |
| 201 | + /* These pointers can be NULL before list_del during umount */ | |
| 202 | + if (sbi->ipbmap) | |
| 203 | + writer(sbi->ipbmap->i_mapping); | |
| 204 | + if (sbi->ipimap) | |
| 205 | + writer(sbi->ipimap->i_mapping); | |
| 206 | + if (sbi->direct_inode) | |
| 207 | + writer(sbi->direct_inode->i_mapping); | |
| 210 | 208 | } |
| 211 | 209 | } |
| 212 | 210 | |
| @@ -2180,8 +2178,6 @@ static void lbmIODone(struct bio *bio) · 1967 unmodified lines | ||
| 2180 | 2178 | |
| 2181 | 2179 | LCACHE_LOCK(flags); /* disable+lock */ |
| 2182 | 2180 | |
| 2183 | − bp->l_flag |= lbmDONE; | |
| 2184 | − | |
| 2185 | 2181 | if (bio->bi_status) { |
| 2186 | 2182 | bp->l_flag |= lbmERROR; |
| 2187 | 2183 | |
| @@ -2196,12 +2192,10 @@ static void lbmIODone(struct bio *bio) · 8 unmodified lines | ||
| 2196 | 2192 | if (bp->l_flag & lbmREAD) { |
| 2197 | 2193 | bp->l_flag &= ~lbmREAD; |
| 2198 | 2194 | |
| 2199 | − LCACHE_UNLOCK(flags); /* unlock+enable */ | |
| 2200 | − | |
| 2201 | 2195 | /* wakeup I/O initiator */ |
| 2202 | 2196 | LCACHE_WAKEUP(&bp->l_ioevent); |
| 2203 | 2197 | |
| 2204 | − return; | |
| 2198 | + goto out; | |
| 2205 | 2199 | } |
| 2206 | 2200 | |
| 2207 | 2201 | /* |
| @@ -2225,8 +2219,7 @@ static void lbmIODone(struct bio *bio) · 17 unmodified lines | ||
| 2225 | 2219 | |
| 2226 | 2220 | if (bp->l_flag & lbmDIRECT) { |
| 2227 | 2221 | LCACHE_WAKEUP(&bp->l_ioevent); |
| 2228 | − LCACHE_UNLOCK(flags); | |
| 2229 | − return; | |
| 2222 | + goto out; | |
| 2230 | 2223 | } |
| 2231 | 2224 | |
| 2232 | 2225 | tail = log->wqueue; |
| @@ -2278,8 +2271,6 @@ static void lbmIODone(struct bio *bio) · 45 unmodified lines | ||
| 2278 | 2271 | * leave buffer for i/o initiator to dispose |
| 2279 | 2272 | */ |
| 2280 | 2273 | if (bp->l_flag & lbmSYNC) { |
| 2281 | − LCACHE_UNLOCK(flags); /* unlock+enable */ | |
| 2282 | − | |
| 2283 | 2274 | /* wakeup I/O initiator */ |
| 2284 | 2275 | LCACHE_WAKEUP(&bp->l_ioevent); |
| 2285 | 2276 | } |
| @@ -2290,6 +2281,7 @@ static void lbmIODone(struct bio *bio) · 4 unmodified lines | ||
| 2290 | 2281 | else if (bp->l_flag & lbmGC) { |
| 2291 | 2282 | LCACHE_UNLOCK(flags); |
| 2292 | 2283 | lmPostGC(bp); |
| 2284 | + LCACHE_LOCK(flags); /* disable+lock */ | |
| 2293 | 2285 | } |
| 2294 | 2286 | |
| 2295 | 2287 | /* |
| @@ -2302,9 +2294,11 @@ static void lbmIODone(struct bio *bio) · 6 unmodified lines | ||
| 2302 | 2294 | assert(bp->l_flag & lbmRELEASE); |
| 2303 | 2295 | assert(bp->l_flag & lbmFREE); |
| 2304 | 2296 | lbmfree(bp); |
| 2305 | − | |
| 2306 | − LCACHE_UNLOCK(flags); /* unlock+enable */ | |
| 2307 | 2297 | } |
| 2298 | + | |
| 2299 | +out: | |
| 2300 | + bp->l_flag |= lbmDONE; | |
| 2301 | + LCACHE_UNLOCK(flags); | |
| 2308 | 2302 | } |
| 2309 | 2303 | |
| 2310 | 2304 | int jfsIOWait(void *arg) |
| @@ -402,6 +402,13 @@ struct jfs_log { · 401 unmodified lines | ||
| 402 | 402 | int no_integrity; /* 3: flag to disable journaling to disk */ |
| 403 | 403 | }; |
| 404 | 404 | |
| 405 | +/* | |
| 406 | + * log read/write serialization (per log) | |
| 407 | + */ | |
| 408 | +#define LOG_LOCK_INIT(log) mutex_init(&(log)->loglock) | |
| 409 | +#define LOG_LOCK(log) mutex_lock(&((log)->loglock)) | |
| 410 | +#define LOG_UNLOCK(log) mutex_unlock(&((log)->loglock)) | |
| 411 | + | |
| 405 | 412 | /* |
| 406 | 413 | * Log flag |
| 407 | 414 | */ |
| @@ -270,6 +270,7 @@ static inline struct metapage *alloc_metapage(gfp_t gfp_mask) · 269 unmodified lines | ||
| 270 | 270 | mp->clsn = 0; |
| 271 | 271 | mp->log = NULL; |
| 272 | 272 | init_waitqueue_head(&mp->wait); |
| 273 | + INIT_LIST_HEAD(&mp->synclist); | |
| 273 | 274 | } |
| 274 | 275 | return mp; |
| 275 | 276 | } |
| @@ -379,7 +380,7 @@ static void remove_from_logsync(struct metapage *mp) · 103 unmodified lines | ||
| 379 | 380 | mp->lsn = 0; |
| 380 | 381 | mp->clsn = 0; |
| 381 | 382 | log->count--; |
| 382 | − list_del(&mp->synclist); | |
| 383 | + list_del_init(&mp->synclist); | |
| 383 | 384 | } |
| 384 | 385 | LOGSYNC_UNLOCK(log, flags); |
| 385 | 386 | } |
| @@ -378,11 +378,12 @@ static int chkSuper(struct super_block *sb) · 377 unmodified lines | ||
| 378 | 378 | sbi->nbperpage = PSIZE >> sbi->l2bsize; |
| 379 | 379 | sbi->l2nbperpage = L2PSIZE - sbi->l2bsize; |
| 380 | 380 | sbi->l2niperblk = sbi->l2bsize - L2DISIZE; |
| 381 | + uuid_copy(&sbi->uuid, &j_sb->s_uuid); | |
| 382 | + | |
| 381 | 383 | if (sbi->mntflag & JFS_INLINELOG) |
| 382 | 384 | sbi->logpxd = j_sb->s_logpxd; |
| 383 | 385 | else { |
| 384 | 386 | sbi->logdev = new_decode_dev(le32_to_cpu(j_sb->s_logdev)); |
| 385 | − uuid_copy(&sbi->uuid, &j_sb->s_uuid); | |
| 386 | 387 | uuid_copy(&sbi->loguuid, &j_sb->s_loguuid); |
| 387 | 388 | } |
| 388 | 389 | sbi->fsckpxd = j_sb->s_fsckpxd; |
| @@ -275,6 +275,7 @@ int txInit(void) · 274 unmodified lines | ||
| 275 | 275 | for (k = 0; k < nTxBlock; k++) { |
| 276 | 276 | init_waitqueue_head(&TxBlock[k].gcwait); |
| 277 | 277 | init_waitqueue_head(&TxBlock[k].waitor); |
| 278 | + INIT_LIST_HEAD(&TxBlock[k].synclist); | |
| 278 | 279 | } |
| 279 | 280 | |
| 280 | 281 | for (k = 1; k < nTxBlock - 1; k++) { |
| @@ -974,7 +975,7 @@ static void txUnlock(struct tblock * tblk) · 693 unmodified lines | ||
| 974 | 975 | if (tblk->lsn) { |
| 975 | 976 | LOGSYNC_LOCK(log, flags); |
| 976 | 977 | log->count--; |
| 977 | − list_del(&tblk->synclist); | |
| 978 | + list_del_init(&tblk->synclist); | |
| 978 | 979 | LOGSYNC_UNLOCK(log, flags); |
| 979 | 980 | } |
| 980 | 981 | } |
| @@ -20,6 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | #include "jfs_superblock.h" |
| 21 | 21 | #include "jfs_dmap.h" |
| 22 | 22 | #include "jfs_imap.h" |
| 23 | +#include "jfs_logmgr.h" | |
| 23 | 24 | #include "jfs_metapage.h" |
| 24 | 25 | #include "jfs_debug.h" |
| 25 | 26 | |
| @@ -57,6 +58,12 @@ int jfs_umount(struct super_block *sb) · 31 unmodified lines | ||
| 57 | 58 | */ |
| 58 | 59 | jfs_flush_journal(log, 2); |
| 59 | 60 | |
| 61 | + /* | |
| 62 | + * Hold log lock so write_special_inodes (lmLogSync) cannot see | |
| 63 | + * this sbi with a NULL inode pointer while iterating log->sb_list. | |
| 64 | + */ | |
| 65 | + if (log) | |
| 66 | + LOG_LOCK(log); | |
| 60 | 67 | /* |
| 61 | 68 | * close fileset inode allocation map (aka fileset inode) |
| 62 | 69 | */ |
| @@ -95,6 +102,9 @@ int jfs_umount(struct super_block *sb) · 32 unmodified lines | ||
| 95 | 102 | */ |
| 96 | 103 | filemap_write_and_wait(sbi->direct_inode->i_mapping); |
| 97 | 104 | |
| 105 | + if (log) | |
| 106 | + LOG_UNLOCK(log); | |
| 107 | + | |
| 98 | 108 | /* |
| 99 | 109 | * ensure all file system file pages are propagated to their |
| 100 | 110 | * home blocks on disk (and their in-memory buffer pages are |