1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) International Business Machines Corp., 2000-2004 |
4 | */ |
5 | |
6 | #include <linux/fs.h> |
7 | #include <linux/buffer_head.h> |
8 | #include <linux/quotaops.h> |
9 | #include <linux/blkdev.h> |
10 | #include "jfs_incore.h" |
11 | #include "jfs_filsys.h" |
12 | #include "jfs_metapage.h" |
13 | #include "jfs_dinode.h" |
14 | #include "jfs_imap.h" |
15 | #include "jfs_dmap.h" |
16 | #include "jfs_superblock.h" |
17 | #include "jfs_txnmgr.h" |
18 | #include "jfs_debug.h" |
19 | |
20 | #define BITSPERPAGE (PSIZE << 3) |
21 | #define L2MEGABYTE 20 |
22 | #define MEGABYTE (1 << L2MEGABYTE) |
23 | #define MEGABYTE32 (MEGABYTE << 5) |
24 | |
25 | /* convert block number to bmap file page number */ |
26 | #define BLKTODMAPN(b)\ |
27 | (((b) >> 13) + ((b) >> 23) + ((b) >> 33) + 3 + 1) |
28 | |
29 | /* |
30 | * jfs_extendfs() |
31 | * |
32 | * function: extend file system; |
33 | * |
34 | * |-------------------------------|----------|----------| |
35 | * file system space fsck inline log |
36 | * workspace space |
37 | * |
38 | * input: |
39 | * new LVSize: in LV blocks (required) |
40 | * new LogSize: in LV blocks (optional) |
41 | * new FSSize: in LV blocks (optional) |
42 | * |
43 | * new configuration: |
44 | * 1. set new LogSize as specified or default from new LVSize; |
45 | * 2. compute new FSCKSize from new LVSize; |
46 | * 3. set new FSSize as MIN(FSSize, LVSize-(LogSize+FSCKSize)) where |
47 | * assert(new FSSize >= old FSSize), |
48 | * i.e., file system must not be shrunk; |
49 | */ |
50 | int jfs_extendfs(struct super_block *sb, s64 newLVSize, int newLogSize) |
51 | { |
52 | int rc = 0; |
53 | struct jfs_sb_info *sbi = JFS_SBI(sb); |
54 | struct inode *ipbmap = sbi->ipbmap; |
55 | struct inode *ipbmap2; |
56 | struct inode *ipimap = sbi->ipimap; |
57 | struct jfs_log *log = sbi->log; |
58 | struct bmap *bmp = sbi->bmap; |
59 | s64 newLogAddress, newFSCKAddress; |
60 | int newFSCKSize; |
61 | s64 newMapSize = 0, mapSize; |
62 | s64 XAddress, XSize, nblocks, xoff, xaddr, t64; |
63 | s64 oldLVSize; |
64 | s64 newFSSize; |
65 | s64 VolumeSize; |
66 | int newNpages = 0, nPages, newPage, xlen, t32; |
67 | int tid; |
68 | int log_formatted = 0; |
69 | struct inode *iplist[1]; |
70 | struct jfs_superblock *j_sb, *j_sb2; |
71 | s64 old_agsize; |
72 | int agsizechanged = 0; |
73 | struct buffer_head *bh, *bh2; |
74 | |
75 | /* If the volume hasn't grown, get out now */ |
76 | |
77 | if (sbi->mntflag & JFS_INLINELOG) |
78 | oldLVSize = addressPXD(pxd: &sbi->logpxd) + lengthPXD(pxd: &sbi->logpxd); |
79 | else |
80 | oldLVSize = addressPXD(pxd: &sbi->fsckpxd) + |
81 | lengthPXD(pxd: &sbi->fsckpxd); |
82 | |
83 | if (oldLVSize >= newLVSize) { |
84 | printk(KERN_WARNING |
85 | "jfs_extendfs: volume hasn't grown, returning\n" ); |
86 | goto out; |
87 | } |
88 | |
89 | VolumeSize = sb_bdev_nr_blocks(sb); |
90 | if (VolumeSize) { |
91 | if (newLVSize > VolumeSize) { |
92 | printk(KERN_WARNING "jfs_extendfs: invalid size\n" ); |
93 | rc = -EINVAL; |
94 | goto out; |
95 | } |
96 | } else { |
97 | /* check the device */ |
98 | bh = sb_bread(sb, block: newLVSize - 1); |
99 | if (!bh) { |
100 | printk(KERN_WARNING "jfs_extendfs: invalid size\n" ); |
101 | rc = -EINVAL; |
102 | goto out; |
103 | } |
104 | bforget(bh); |
105 | } |
106 | |
107 | /* Can't extend write-protected drive */ |
108 | |
109 | if (isReadOnly(inode: ipbmap)) { |
110 | printk(KERN_WARNING "jfs_extendfs: read-only file system\n" ); |
111 | rc = -EROFS; |
112 | goto out; |
113 | } |
114 | |
115 | /* |
116 | * reconfigure LV spaces |
117 | * --------------------- |
118 | * |
119 | * validate new size, or, if not specified, determine new size |
120 | */ |
121 | |
122 | /* |
123 | * reconfigure inline log space: |
124 | */ |
125 | if ((sbi->mntflag & JFS_INLINELOG)) { |
126 | if (newLogSize == 0) { |
127 | /* |
128 | * no size specified: default to 1/256 of aggregate |
129 | * size; rounded up to a megabyte boundary; |
130 | */ |
131 | newLogSize = newLVSize >> 8; |
132 | t32 = (1 << (20 - sbi->l2bsize)) - 1; |
133 | newLogSize = (newLogSize + t32) & ~t32; |
134 | newLogSize = |
135 | min(newLogSize, MEGABYTE32 >> sbi->l2bsize); |
136 | } else { |
137 | /* |
138 | * convert the newLogSize to fs blocks. |
139 | * |
140 | * Since this is given in megabytes, it will always be |
141 | * an even number of pages. |
142 | */ |
143 | newLogSize = (newLogSize * MEGABYTE) >> sbi->l2bsize; |
144 | } |
145 | |
146 | } else |
147 | newLogSize = 0; |
148 | |
149 | newLogAddress = newLVSize - newLogSize; |
150 | |
151 | /* |
152 | * reconfigure fsck work space: |
153 | * |
154 | * configure it to the end of the logical volume regardless of |
155 | * whether file system extends to the end of the aggregate; |
156 | * Need enough 4k pages to cover: |
157 | * - 1 bit per block in aggregate rounded up to BPERDMAP boundary |
158 | * - 1 extra page to handle control page and intermediate level pages |
159 | * - 50 extra pages for the chkdsk service log |
160 | */ |
161 | t64 = ((newLVSize - newLogSize + BPERDMAP - 1) >> L2BPERDMAP) |
162 | << L2BPERDMAP; |
163 | t32 = DIV_ROUND_UP(t64, BITSPERPAGE) + 1 + 50; |
164 | newFSCKSize = t32 << sbi->l2nbperpage; |
165 | newFSCKAddress = newLogAddress - newFSCKSize; |
166 | |
167 | /* |
168 | * compute new file system space; |
169 | */ |
170 | newFSSize = newLVSize - newLogSize - newFSCKSize; |
171 | |
172 | /* file system cannot be shrunk */ |
173 | if (newFSSize < bmp->db_mapsize) { |
174 | rc = -EINVAL; |
175 | goto out; |
176 | } |
177 | |
178 | /* |
179 | * If we're expanding enough that the inline log does not overlap |
180 | * the old one, we can format the new log before we quiesce the |
181 | * filesystem. |
182 | */ |
183 | if ((sbi->mntflag & JFS_INLINELOG) && (newLogAddress > oldLVSize)) { |
184 | if ((rc = lmLogFormat(log, logAddress: newLogAddress, logSize: newLogSize))) |
185 | goto out; |
186 | log_formatted = 1; |
187 | } |
188 | /* |
189 | * quiesce file system |
190 | * |
191 | * (prepare to move the inline log and to prevent map update) |
192 | * |
193 | * block any new transactions and wait for completion of |
194 | * all wip transactions and flush modified pages s.t. |
195 | * on-disk file system is in consistent state and |
196 | * log is not required for recovery. |
197 | */ |
198 | txQuiesce(sb); |
199 | |
200 | /* Reset size of direct inode */ |
201 | sbi->direct_inode->i_size = bdev_nr_bytes(bdev: sb->s_bdev); |
202 | |
203 | if (sbi->mntflag & JFS_INLINELOG) { |
204 | /* |
205 | * deactivate old inline log |
206 | */ |
207 | lmLogShutdown(log); |
208 | |
209 | /* |
210 | * mark on-disk super block for fs in transition; |
211 | * |
212 | * update on-disk superblock for the new space configuration |
213 | * of inline log space and fsck work space descriptors: |
214 | * N.B. FS descriptor is NOT updated; |
215 | * |
216 | * crash recovery: |
217 | * logredo(): if FM_EXTENDFS, return to fsck() for cleanup; |
218 | * fsck(): if FM_EXTENDFS, reformat inline log and fsck |
219 | * workspace from superblock inline log descriptor and fsck |
220 | * workspace descriptor; |
221 | */ |
222 | |
223 | /* read in superblock */ |
224 | if ((rc = readSuper(sb, &bh))) |
225 | goto error_out; |
226 | j_sb = (struct jfs_superblock *)bh->b_data; |
227 | |
228 | /* mark extendfs() in progress */ |
229 | j_sb->s_state |= cpu_to_le32(FM_EXTENDFS); |
230 | j_sb->s_xsize = cpu_to_le64(newFSSize); |
231 | PXDaddress(pxd: &j_sb->s_xfsckpxd, addr: newFSCKAddress); |
232 | PXDlength(pxd: &j_sb->s_xfsckpxd, len: newFSCKSize); |
233 | PXDaddress(pxd: &j_sb->s_xlogpxd, addr: newLogAddress); |
234 | PXDlength(pxd: &j_sb->s_xlogpxd, len: newLogSize); |
235 | |
236 | /* synchronously update superblock */ |
237 | mark_buffer_dirty(bh); |
238 | sync_dirty_buffer(bh); |
239 | brelse(bh); |
240 | |
241 | /* |
242 | * format new inline log synchronously; |
243 | * |
244 | * crash recovery: if log move in progress, |
245 | * reformat log and exit success; |
246 | */ |
247 | if (!log_formatted) |
248 | if ((rc = lmLogFormat(log, logAddress: newLogAddress, logSize: newLogSize))) |
249 | goto error_out; |
250 | |
251 | /* |
252 | * activate new log |
253 | */ |
254 | log->base = newLogAddress; |
255 | log->size = newLogSize >> (L2LOGPSIZE - sb->s_blocksize_bits); |
256 | if ((rc = lmLogInit(log))) |
257 | goto error_out; |
258 | } |
259 | |
260 | /* |
261 | * extend block allocation map |
262 | * --------------------------- |
263 | * |
264 | * extendfs() for new extension, retry after crash recovery; |
265 | * |
266 | * note: both logredo() and fsck() rebuild map from |
267 | * the bitmap and configuration parameter from superblock |
268 | * (disregarding all other control information in the map); |
269 | * |
270 | * superblock: |
271 | * s_size: aggregate size in physical blocks; |
272 | */ |
273 | /* |
274 | * compute the new block allocation map configuration |
275 | * |
276 | * map dinode: |
277 | * di_size: map file size in byte; |
278 | * di_nblocks: number of blocks allocated for map file; |
279 | * di_mapsize: number of blocks in aggregate (covered by map); |
280 | * map control page: |
281 | * db_mapsize: number of blocks in aggregate (covered by map); |
282 | */ |
283 | newMapSize = newFSSize; |
284 | /* number of data pages of new bmap file: |
285 | * roundup new size to full dmap page boundary and |
286 | * add 1 extra dmap page for next extendfs() |
287 | */ |
288 | t64 = (newMapSize - 1) + BPERDMAP; |
289 | newNpages = BLKTODMAPN(t64) + 1; |
290 | |
291 | /* |
292 | * extend map from current map (WITHOUT growing mapfile) |
293 | * |
294 | * map new extension with unmapped part of the last partial |
295 | * dmap page, if applicable, and extra page(s) allocated |
296 | * at end of bmap by mkfs() or previous extendfs(); |
297 | */ |
298 | extendBmap: |
299 | /* compute number of blocks requested to extend */ |
300 | mapSize = bmp->db_mapsize; |
301 | XAddress = mapSize; /* eXtension Address */ |
302 | XSize = newMapSize - mapSize; /* eXtension Size */ |
303 | old_agsize = bmp->db_agsize; /* We need to know if this changes */ |
304 | |
305 | /* compute number of blocks that can be extended by current mapfile */ |
306 | t64 = dbMapFileSizeToMapSize(ipbmap); |
307 | if (mapSize > t64) { |
308 | printk(KERN_ERR "jfs_extendfs: mapSize (0x%Lx) > t64 (0x%Lx)\n" , |
309 | (long long) mapSize, (long long) t64); |
310 | rc = -EIO; |
311 | goto error_out; |
312 | } |
313 | nblocks = min(t64 - mapSize, XSize); |
314 | |
315 | /* |
316 | * update map pages for new extension: |
317 | * |
318 | * update/init dmap and bubble up the control hierarchy |
319 | * incrementally fold up dmaps into upper levels; |
320 | * update bmap control page; |
321 | */ |
322 | if ((rc = dbExtendFS(ipbmap, blkno: XAddress, nblocks))) |
323 | goto error_out; |
324 | |
325 | agsizechanged |= (bmp->db_agsize != old_agsize); |
326 | |
327 | /* |
328 | * the map now has extended to cover additional nblocks: |
329 | * dn_mapsize = oldMapsize + nblocks; |
330 | */ |
331 | /* ipbmap->i_mapsize += nblocks; */ |
332 | XSize -= nblocks; |
333 | |
334 | /* |
335 | * grow map file to cover remaining extension |
336 | * and/or one extra dmap page for next extendfs(); |
337 | * |
338 | * allocate new map pages and its backing blocks, and |
339 | * update map file xtree |
340 | */ |
341 | /* compute number of data pages of current bmap file */ |
342 | nPages = ipbmap->i_size >> L2PSIZE; |
343 | |
344 | /* need to grow map file ? */ |
345 | if (nPages == newNpages) |
346 | goto finalizeBmap; |
347 | |
348 | /* |
349 | * grow bmap file for the new map pages required: |
350 | * |
351 | * allocate growth at the start of newly extended region; |
352 | * bmap file only grows sequentially, i.e., both data pages |
353 | * and possibly xtree index pages may grow in append mode, |
354 | * s.t. logredo() can reconstruct pre-extension state |
355 | * by washing away bmap file of pages outside s_size boundary; |
356 | */ |
357 | /* |
358 | * journal map file growth as if a regular file growth: |
359 | * (note: bmap is created with di_mode = IFJOURNAL|IFREG); |
360 | * |
361 | * journaling of bmap file growth is not required since |
362 | * logredo() do/can not use log records of bmap file growth |
363 | * but it provides careful write semantics, pmap update, etc.; |
364 | */ |
365 | /* synchronous write of data pages: bmap data pages are |
366 | * cached in meta-data cache, and not written out |
367 | * by txCommit(); |
368 | */ |
369 | rc = filemap_fdatawait(mapping: ipbmap->i_mapping); |
370 | if (rc) |
371 | goto error_out; |
372 | |
373 | rc = filemap_write_and_wait(mapping: ipbmap->i_mapping); |
374 | if (rc) |
375 | goto error_out; |
376 | |
377 | diWriteSpecial(ipbmap, 0); |
378 | |
379 | newPage = nPages; /* first new page number */ |
380 | xoff = newPage << sbi->l2nbperpage; |
381 | xlen = (newNpages - nPages) << sbi->l2nbperpage; |
382 | xlen = min(xlen, (int) nblocks) & ~(sbi->nbperpage - 1); |
383 | xaddr = XAddress; |
384 | |
385 | tid = txBegin(sb, COMMIT_FORCE); |
386 | |
387 | if ((rc = xtAppend(tid, ip: ipbmap, xflag: 0, xoff, maxblocks: nblocks, xlenp: &xlen, xaddrp: &xaddr, flag: 0))) { |
388 | txEnd(tid); |
389 | goto error_out; |
390 | } |
391 | /* update bmap file size */ |
392 | ipbmap->i_size += xlen << sbi->l2bsize; |
393 | inode_add_bytes(inode: ipbmap, bytes: xlen << sbi->l2bsize); |
394 | |
395 | iplist[0] = ipbmap; |
396 | rc = txCommit(tid, 1, &iplist[0], COMMIT_FORCE); |
397 | |
398 | txEnd(tid); |
399 | |
400 | if (rc) |
401 | goto error_out; |
402 | |
403 | /* |
404 | * map file has been grown now to cover extension to further out; |
405 | * di_size = new map file size; |
406 | * |
407 | * if huge extension, the previous extension based on previous |
408 | * map file size may not have been sufficient to cover whole extension |
409 | * (it could have been used up for new map pages), |
410 | * but the newly grown map file now covers lot bigger new free space |
411 | * available for further extension of map; |
412 | */ |
413 | /* any more blocks to extend ? */ |
414 | if (XSize) |
415 | goto extendBmap; |
416 | |
417 | finalizeBmap: |
418 | /* finalize bmap */ |
419 | dbFinalizeBmap(ipbmap); |
420 | |
421 | /* |
422 | * update inode allocation map |
423 | * --------------------------- |
424 | * |
425 | * move iag lists from old to new iag; |
426 | * agstart field is not updated for logredo() to reconstruct |
427 | * iag lists if system crash occurs. |
428 | * (computation of ag number from agstart based on agsize |
429 | * will correctly identify the new ag); |
430 | */ |
431 | /* if new AG size the same as old AG size, done! */ |
432 | if (agsizechanged) { |
433 | if ((rc = diExtendFS(ipimap, ipbmap))) |
434 | goto error_out; |
435 | |
436 | /* finalize imap */ |
437 | if ((rc = diSync(ipimap))) |
438 | goto error_out; |
439 | } |
440 | |
441 | /* |
442 | * finalize |
443 | * -------- |
444 | * |
445 | * extension is committed when on-disk super block is |
446 | * updated with new descriptors: logredo will recover |
447 | * crash before it to pre-extension state; |
448 | */ |
449 | |
450 | /* sync log to skip log replay of bmap file growth transaction; */ |
451 | /* lmLogSync(log, 1); */ |
452 | |
453 | /* |
454 | * synchronous write bmap global control page; |
455 | * for crash before completion of write |
456 | * logredo() will recover to pre-extendfs state; |
457 | * for crash after completion of write, |
458 | * logredo() will recover post-extendfs state; |
459 | */ |
460 | if ((rc = dbSync(ipbmap))) |
461 | goto error_out; |
462 | |
463 | /* |
464 | * copy primary bmap inode to secondary bmap inode |
465 | */ |
466 | |
467 | ipbmap2 = diReadSpecial(sb, BMAP_I, 1); |
468 | if (ipbmap2 == NULL) { |
469 | printk(KERN_ERR "jfs_extendfs: diReadSpecial(bmap) failed\n" ); |
470 | goto error_out; |
471 | } |
472 | memcpy(&JFS_IP(ipbmap2)->i_xtroot, &JFS_IP(ipbmap)->i_xtroot, 288); |
473 | ipbmap2->i_size = ipbmap->i_size; |
474 | ipbmap2->i_blocks = ipbmap->i_blocks; |
475 | |
476 | diWriteSpecial(ipbmap2, 1); |
477 | diFreeSpecial(ipbmap2); |
478 | |
479 | /* |
480 | * update superblock |
481 | */ |
482 | if ((rc = readSuper(sb, &bh))) |
483 | goto error_out; |
484 | j_sb = (struct jfs_superblock *)bh->b_data; |
485 | |
486 | /* mark extendfs() completion */ |
487 | j_sb->s_state &= cpu_to_le32(~FM_EXTENDFS); |
488 | j_sb->s_size = cpu_to_le64(bmp->db_mapsize << |
489 | le16_to_cpu(j_sb->s_l2bfactor)); |
490 | j_sb->s_agsize = cpu_to_le32(bmp->db_agsize); |
491 | |
492 | /* update inline log space descriptor */ |
493 | if (sbi->mntflag & JFS_INLINELOG) { |
494 | PXDaddress(pxd: &(j_sb->s_logpxd), addr: newLogAddress); |
495 | PXDlength(pxd: &(j_sb->s_logpxd), len: newLogSize); |
496 | } |
497 | |
498 | /* record log's mount serial number */ |
499 | j_sb->s_logserial = cpu_to_le32(log->serial); |
500 | |
501 | /* update fsck work space descriptor */ |
502 | PXDaddress(pxd: &(j_sb->s_fsckpxd), addr: newFSCKAddress); |
503 | PXDlength(pxd: &(j_sb->s_fsckpxd), len: newFSCKSize); |
504 | j_sb->s_fscklog = 1; |
505 | /* sb->s_fsckloglen remains the same */ |
506 | |
507 | /* Update secondary superblock */ |
508 | bh2 = sb_bread(sb, SUPER2_OFF >> sb->s_blocksize_bits); |
509 | if (bh2) { |
510 | j_sb2 = (struct jfs_superblock *)bh2->b_data; |
511 | memcpy(j_sb2, j_sb, sizeof (struct jfs_superblock)); |
512 | |
513 | mark_buffer_dirty(bh); |
514 | sync_dirty_buffer(bh: bh2); |
515 | brelse(bh: bh2); |
516 | } |
517 | |
518 | /* write primary superblock */ |
519 | mark_buffer_dirty(bh); |
520 | sync_dirty_buffer(bh); |
521 | brelse(bh); |
522 | |
523 | goto resume; |
524 | |
525 | error_out: |
526 | jfs_error(sb, "\n" ); |
527 | |
528 | resume: |
529 | /* |
530 | * resume file system transactions |
531 | */ |
532 | txResume(sb); |
533 | |
534 | out: |
535 | return rc; |
536 | } |
537 | |