aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_log.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2010-03-22 20:21:11 -0400
committerAlex Elder <aelder@sgi.com>2010-05-19 10:58:09 -0400
commit9b9fc2b7602ed671d1a8524d4c31302b89554947 (patch)
tree866a68bb120cf08a09e573fe013ba0e121a2224b /fs/xfs/xfs_log.c
parentb1c1b5b6108ad8e5991a614514f41da436c659d6 (diff)
xfs: log ticket reservation underestimates the number of iclogs
When allocation a ticket for a transaction, the ticket is initialised with the worst case log space usage based on the number of bytes the transaction may consume. Part of this calculation is the number of log headers required for the iclog space used up by the transaction. This calculation makes an undocumented assumption that if the transaction uses the log header space reservation on an iclog, then it consumes either the entire iclog or it completes. That is - the transaction that is first in an iclog is the transaction that the log header reservation is accounted to. If the transaction is larger than the iclog, then it will use the entire iclog itself. Document this assumption. Further, the current calculation uses the rule that we can fit iclog_size bytes of transaction data into an iclog. This is in correct - the amount of space available in an iclog for transaction data is the size of the iclog minus the space used for log record headers. This means that the calculation is out by 512 bytes per 32k of log space the transaction can consume. This is rarely an issue because maximally sized transactions are extremely uncommon, and for 4k block size filesystems maximal transaction reservations are about 400kb. Hence the error in this case is less than the size of an iclog, so that makes it even harder to hit. However, anyone using larger directory blocks (16k directory blocks push the maximum transaction size to approx. 900k on a 4k block size filesystem) or larger block size (e.g. 64k blocks push transactions to the 3-4MB size) could see the error grow to more than an iclog and at this point the transaction is guaranteed to get a reservation underrun and shutdown the filesystem. Fix this by adjusting the calculation to calculate the correct number of iclogs required and account for them all up front. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/xfs/xfs_log.c')
-rw-r--r--fs/xfs/xfs_log.c55
1 files changed, 42 insertions, 13 deletions
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 8556c59628ba..81323d73a4ee 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -664,7 +664,10 @@ xfs_log_item_init(
664/* 664/*
665 * Write region vectors to log. The write happens using the space reservation 665 * Write region vectors to log. The write happens using the space reservation
666 * of the ticket (tic). It is not a requirement that all writes for a given 666 * of the ticket (tic). It is not a requirement that all writes for a given
667 * transaction occur with one call to xfs_log_write(). 667 * transaction occur with one call to xfs_log_write(). However, it is important
668 * to note that the transaction reservation code makes an assumption about the
669 * number of log headers a transaction requires that may be violated if you
670 * don't pass all the transaction vectors in one call....
668 */ 671 */
669int 672int
670xfs_log_write( 673xfs_log_write(
@@ -3170,14 +3173,16 @@ xfs_log_ticket_get(
3170 * Allocate and initialise a new log ticket. 3173 * Allocate and initialise a new log ticket.
3171 */ 3174 */
3172STATIC xlog_ticket_t * 3175STATIC xlog_ticket_t *
3173xlog_ticket_alloc(xlog_t *log, 3176xlog_ticket_alloc(
3174 int unit_bytes, 3177 struct log *log,
3175 int cnt, 3178 int unit_bytes,
3176 char client, 3179 int cnt,
3177 uint xflags) 3180 char client,
3181 uint xflags)
3178{ 3182{
3179 xlog_ticket_t *tic; 3183 struct xlog_ticket *tic;
3180 uint num_headers; 3184 uint num_headers;
3185 int iclog_space;
3181 3186
3182 tic = kmem_zone_zalloc(xfs_log_ticket_zone, KM_SLEEP|KM_MAYFAIL); 3187 tic = kmem_zone_zalloc(xfs_log_ticket_zone, KM_SLEEP|KM_MAYFAIL);
3183 if (!tic) 3188 if (!tic)
@@ -3221,16 +3226,40 @@ xlog_ticket_alloc(xlog_t *log,
3221 /* for start-rec */ 3226 /* for start-rec */
3222 unit_bytes += sizeof(xlog_op_header_t); 3227 unit_bytes += sizeof(xlog_op_header_t);
3223 3228
3224 /* for LR headers */ 3229 /*
3225 num_headers = ((unit_bytes + log->l_iclog_size-1) >> log->l_iclog_size_log); 3230 * for LR headers - the space for data in an iclog is the size minus
3231 * the space used for the headers. If we use the iclog size, then we
3232 * undercalculate the number of headers required.
3233 *
3234 * Furthermore - the addition of op headers for split-recs might
3235 * increase the space required enough to require more log and op
3236 * headers, so take that into account too.
3237 *
3238 * IMPORTANT: This reservation makes the assumption that if this
3239 * transaction is the first in an iclog and hence has the LR headers
3240 * accounted to it, then the remaining space in the iclog is
3241 * exclusively for this transaction. i.e. if the transaction is larger
3242 * than the iclog, it will be the only thing in that iclog.
3243 * Fundamentally, this means we must pass the entire log vector to
3244 * xlog_write to guarantee this.
3245 */
3246 iclog_space = log->l_iclog_size - log->l_iclog_hsize;
3247 num_headers = howmany(unit_bytes, iclog_space);
3248
3249 /* for split-recs - ophdrs added when data split over LRs */
3250 unit_bytes += sizeof(xlog_op_header_t) * num_headers;
3251
3252 /* add extra header reservations if we overrun */
3253 while (!num_headers ||
3254 howmany(unit_bytes, iclog_space) > num_headers) {
3255 unit_bytes += sizeof(xlog_op_header_t);
3256 num_headers++;
3257 }
3226 unit_bytes += log->l_iclog_hsize * num_headers; 3258 unit_bytes += log->l_iclog_hsize * num_headers;
3227 3259
3228 /* for commit-rec LR header - note: padding will subsume the ophdr */ 3260 /* for commit-rec LR header - note: padding will subsume the ophdr */
3229 unit_bytes += log->l_iclog_hsize; 3261 unit_bytes += log->l_iclog_hsize;
3230 3262
3231 /* for split-recs - ophdrs added when data split over LRs */
3232 unit_bytes += sizeof(xlog_op_header_t) * num_headers;
3233
3234 /* for roundoff padding for transaction data and one for commit record */ 3263 /* for roundoff padding for transaction data and one for commit record */
3235 if (xfs_sb_version_haslogv2(&log->l_mp->m_sb) && 3264 if (xfs_sb_version_haslogv2(&log->l_mp->m_sb) &&
3236 log->l_mp->m_sb.sb_logsunit > 1) { 3265 log->l_mp->m_sb.sb_logsunit > 1) {
@@ -3252,7 +3281,7 @@ xlog_ticket_alloc(xlog_t *log,
3252 tic->t_trans_type = 0; 3281 tic->t_trans_type = 0;
3253 if (xflags & XFS_LOG_PERM_RESERV) 3282 if (xflags & XFS_LOG_PERM_RESERV)
3254 tic->t_flags |= XLOG_TIC_PERM_RESERV; 3283 tic->t_flags |= XLOG_TIC_PERM_RESERV;
3255 sv_init(&(tic->t_wait), SV_DEFAULT, "logtick"); 3284 sv_init(&tic->t_wait, SV_DEFAULT, "logtick");
3256 3285
3257 xlog_tic_reset_res(tic); 3286 xlog_tic_reset_res(tic);
3258 3287