@@ -56,8 +56,7 @@ xfs_calc_buf_res(
56
56
* Per-extent log reservation for the btree changes involved in freeing or
57
57
* allocating an extent. In classic XFS there were two trees that will be
58
58
* modified (bnobt + cntbt). With rmap enabled, there are three trees
59
- * (rmapbt). With reflink, there are four trees (refcountbt). The number of
60
- * blocks reserved is based on the formula:
59
+ * (rmapbt). The number of blocks reserved is based on the formula:
61
60
*
62
61
* num trees * ((2 blocks/level * max depth) - 1)
63
62
*
@@ -73,12 +72,23 @@ xfs_allocfree_log_count(
73
72
blocks = num_ops * 2 * (2 * mp -> m_alloc_maxlevels - 1 );
74
73
if (xfs_has_rmapbt (mp ))
75
74
blocks += num_ops * (2 * mp -> m_rmap_maxlevels - 1 );
76
- if (xfs_has_reflink (mp ))
77
- blocks += num_ops * (2 * mp -> m_refc_maxlevels - 1 );
78
75
79
76
return blocks ;
80
77
}
81
78
79
+ /*
80
+ * Per-extent log reservation for refcount btree changes. These are never done
81
+ * in the same transaction as an allocation or a free, so we compute them
82
+ * separately.
83
+ */
84
+ static unsigned int
85
+ xfs_refcountbt_block_count (
86
+ struct xfs_mount * mp ,
87
+ unsigned int num_ops )
88
+ {
89
+ return num_ops * (2 * mp -> m_refc_maxlevels - 1 );
90
+ }
91
+
82
92
/*
83
93
* Logging inodes is really tricksy. They are logged in memory format,
84
94
* which means that what we write into the log doesn't directly translate into
@@ -233,6 +243,28 @@ xfs_rtalloc_log_count(
233
243
* register overflow from temporaries in the calculations.
234
244
*/
235
245
246
+ /*
247
+ * Compute the log reservation required to handle the refcount update
248
+ * transaction. Refcount updates are always done via deferred log items.
249
+ *
250
+ * This is calculated as:
251
+ * Data device refcount updates (t1):
252
+ * the agfs of the ags containing the blocks: nr_ops * sector size
253
+ * the refcount btrees: nr_ops * 1 trees * (2 * max depth - 1) * block size
254
+ */
255
+ static unsigned int
256
+ xfs_calc_refcountbt_reservation (
257
+ struct xfs_mount * mp ,
258
+ unsigned int nr_ops )
259
+ {
260
+ unsigned int blksz = XFS_FSB_TO_B (mp , 1 );
261
+
262
+ if (!xfs_has_reflink (mp ))
263
+ return 0 ;
264
+
265
+ return xfs_calc_buf_res (nr_ops , mp -> m_sb .sb_sectsize ) +
266
+ xfs_calc_buf_res (xfs_refcountbt_block_count (mp , nr_ops ), blksz );
267
+ }
236
268
237
269
/*
238
270
* In a write transaction we can allocate a maximum of 2
@@ -255,12 +287,14 @@ xfs_rtalloc_log_count(
255
287
* the agfls of the ags containing the blocks: 2 * sector size
256
288
* the super block free block counter: sector size
257
289
* the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
290
+ * And any refcount updates that happen in a separate transaction (t4).
258
291
*/
259
292
STATIC uint
260
293
xfs_calc_write_reservation (
261
- struct xfs_mount * mp )
294
+ struct xfs_mount * mp ,
295
+ bool for_minlogsize )
262
296
{
263
- unsigned int t1 , t2 , t3 ;
297
+ unsigned int t1 , t2 , t3 , t4 ;
264
298
unsigned int blksz = XFS_FSB_TO_B (mp , 1 );
265
299
266
300
t1 = xfs_calc_inode_res (mp , 1 ) +
@@ -282,7 +316,36 @@ xfs_calc_write_reservation(
282
316
t3 = xfs_calc_buf_res (5 , mp -> m_sb .sb_sectsize ) +
283
317
xfs_calc_buf_res (xfs_allocfree_log_count (mp , 2 ), blksz );
284
318
285
- return XFS_DQUOT_LOGRES (mp ) + max3 (t1 , t2 , t3 );
319
+ /*
320
+ * In the early days of reflink, we included enough reservation to log
321
+ * two refcountbt splits for each transaction. The codebase runs
322
+ * refcountbt updates in separate transactions now, so to compute the
323
+ * minimum log size, add the refcountbtree splits back to t1 and t3 and
324
+ * do not account them separately as t4. Reflink did not support
325
+ * realtime when the reservations were established, so no adjustment to
326
+ * t2 is needed.
327
+ */
328
+ if (for_minlogsize ) {
329
+ unsigned int adj = 0 ;
330
+
331
+ if (xfs_has_reflink (mp ))
332
+ adj = xfs_calc_buf_res (
333
+ xfs_refcountbt_block_count (mp , 2 ),
334
+ blksz );
335
+ t1 += adj ;
336
+ t3 += adj ;
337
+ return XFS_DQUOT_LOGRES (mp ) + max3 (t1 , t2 , t3 );
338
+ }
339
+
340
+ t4 = xfs_calc_refcountbt_reservation (mp , 1 );
341
+ return XFS_DQUOT_LOGRES (mp ) + max (t4 , max3 (t1 , t2 , t3 ));
342
+ }
343
+
344
+ unsigned int
345
+ xfs_calc_write_reservation_minlogsize (
346
+ struct xfs_mount * mp )
347
+ {
348
+ return xfs_calc_write_reservation (mp , true);
286
349
}
287
350
288
351
/*
@@ -304,12 +367,14 @@ xfs_calc_write_reservation(
304
367
* the realtime summary: 2 exts * 1 block
305
368
* worst case split in allocation btrees per extent assuming 2 extents:
306
369
* 2 exts * 2 trees * (2 * max depth - 1) * block size
370
+ * And any refcount updates that happen in a separate transaction (t4).
307
371
*/
308
372
STATIC uint
309
373
xfs_calc_itruncate_reservation (
310
- struct xfs_mount * mp )
374
+ struct xfs_mount * mp ,
375
+ bool for_minlogsize )
311
376
{
312
- unsigned int t1 , t2 , t3 ;
377
+ unsigned int t1 , t2 , t3 , t4 ;
313
378
unsigned int blksz = XFS_FSB_TO_B (mp , 1 );
314
379
315
380
t1 = xfs_calc_inode_res (mp , 1 ) +
@@ -326,7 +391,33 @@ xfs_calc_itruncate_reservation(
326
391
t3 = 0 ;
327
392
}
328
393
329
- return XFS_DQUOT_LOGRES (mp ) + max3 (t1 , t2 , t3 );
394
+ /*
395
+ * In the early days of reflink, we included enough reservation to log
396
+ * four refcountbt splits in the same transaction as bnobt/cntbt
397
+ * updates. The codebase runs refcountbt updates in separate
398
+ * transactions now, so to compute the minimum log size, add the
399
+ * refcount btree splits back here and do not compute them separately
400
+ * as t4. Reflink did not support realtime when the reservations were
401
+ * established, so do not adjust t3.
402
+ */
403
+ if (for_minlogsize ) {
404
+ if (xfs_has_reflink (mp ))
405
+ t2 += xfs_calc_buf_res (
406
+ xfs_refcountbt_block_count (mp , 4 ),
407
+ blksz );
408
+
409
+ return XFS_DQUOT_LOGRES (mp ) + max3 (t1 , t2 , t3 );
410
+ }
411
+
412
+ t4 = xfs_calc_refcountbt_reservation (mp , 2 );
413
+ return XFS_DQUOT_LOGRES (mp ) + max (t4 , max3 (t1 , t2 , t3 ));
414
+ }
415
+
416
+ unsigned int
417
+ xfs_calc_itruncate_reservation_minlogsize (
418
+ struct xfs_mount * mp )
419
+ {
420
+ return xfs_calc_itruncate_reservation (mp , true);
330
421
}
331
422
332
423
/*
@@ -792,13 +883,21 @@ xfs_calc_qm_setqlim_reservation(void)
792
883
*/
793
884
STATIC uint
794
885
xfs_calc_qm_dqalloc_reservation (
795
- struct xfs_mount * mp )
886
+ struct xfs_mount * mp ,
887
+ bool for_minlogsize )
796
888
{
797
- return xfs_calc_write_reservation (mp ) +
889
+ return xfs_calc_write_reservation (mp , for_minlogsize ) +
798
890
xfs_calc_buf_res (1 ,
799
891
XFS_FSB_TO_B (mp , XFS_DQUOT_CLUSTER_SIZE_FSB ) - 1 );
800
892
}
801
893
894
+ unsigned int
895
+ xfs_calc_qm_dqalloc_reservation_minlogsize (
896
+ struct xfs_mount * mp )
897
+ {
898
+ return xfs_calc_qm_dqalloc_reservation (mp , true);
899
+ }
900
+
802
901
/*
803
902
* Syncing the incore super block changes to disk.
804
903
* the super block to reflect the changes: sector size
@@ -821,11 +920,11 @@ xfs_trans_resv_calc(
821
920
* The following transactions are logged in physical format and
822
921
* require a permanent reservation on space.
823
922
*/
824
- resp -> tr_write .tr_logres = xfs_calc_write_reservation (mp );
923
+ resp -> tr_write .tr_logres = xfs_calc_write_reservation (mp , false );
825
924
resp -> tr_write .tr_logcount = XFS_WRITE_LOG_COUNT ;
826
925
resp -> tr_write .tr_logflags |= XFS_TRANS_PERM_LOG_RES ;
827
926
828
- resp -> tr_itruncate .tr_logres = xfs_calc_itruncate_reservation (mp );
927
+ resp -> tr_itruncate .tr_logres = xfs_calc_itruncate_reservation (mp , false );
829
928
resp -> tr_itruncate .tr_logcount = XFS_ITRUNCATE_LOG_COUNT ;
830
929
resp -> tr_itruncate .tr_logflags |= XFS_TRANS_PERM_LOG_RES ;
831
930
@@ -882,7 +981,8 @@ xfs_trans_resv_calc(
882
981
resp -> tr_growrtalloc .tr_logcount = XFS_DEFAULT_PERM_LOG_COUNT ;
883
982
resp -> tr_growrtalloc .tr_logflags |= XFS_TRANS_PERM_LOG_RES ;
884
983
885
- resp -> tr_qm_dqalloc .tr_logres = xfs_calc_qm_dqalloc_reservation (mp );
984
+ resp -> tr_qm_dqalloc .tr_logres = xfs_calc_qm_dqalloc_reservation (mp ,
985
+ false);
886
986
resp -> tr_qm_dqalloc .tr_logcount = XFS_WRITE_LOG_COUNT ;
887
987
resp -> tr_qm_dqalloc .tr_logflags |= XFS_TRANS_PERM_LOG_RES ;
888
988
0 commit comments