aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_log.c
diff options
context:
space:
mode:
authorDave Chinner <david@fromorbit.com>2008-11-17 01:37:10 -0500
committerLachlan McIlroy <lachlan@redback.melbourne.sgi.com>2008-11-17 01:37:10 -0500
commitcc09c0dc57de7f7d2ed89d480b5653e5f6a32f2c (patch)
tree3c6145ccb00f603c47e020cfa45f159ab76cf9bf /fs/xfs/xfs_log.c
parent6307091fe69ae74747298bdcaf43119ad67bda3a (diff)
[XFS] Fix double free of log tickets
When an I/O error occurs during an intermediate commit on a rolling transaction, xfs_trans_commit() will free the transaction structure and the related ticket. However, the duplicate transaction that gets used as the transaction continues still contains a pointer to the ticket. Hence when the duplicate transaction is cancelled and freed, we free the ticket a second time. Add reference counting to the ticket so that we hold an extra reference to the ticket over the transaction commit. We drop the extra reference once we have checked that the transaction commit did not return an error, thus avoiding a double free on commit error. Credit to Nick Piggin for tripping over the problem. SGI-PV: 989741 Signed-off-by: Dave Chinner <david@fromorbit.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_log.c')
-rw-r--r--fs/xfs/xfs_log.c39
1 files changed, 25 insertions, 14 deletions
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 92c20a8d9e69..4bf44aef644c 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -100,12 +100,11 @@ STATIC void xlog_ungrant_log_space(xlog_t *log,
100 100
101 101
102/* local ticket functions */ 102/* local ticket functions */
103STATIC xlog_ticket_t *xlog_ticket_get(xlog_t *log, 103STATIC xlog_ticket_t *xlog_ticket_alloc(xlog_t *log,
104 int unit_bytes, 104 int unit_bytes,
105 int count, 105 int count,
106 char clientid, 106 char clientid,
107 uint flags); 107 uint flags);
108STATIC void xlog_ticket_put(xlog_t *log, xlog_ticket_t *ticket);
109 108
110#if defined(DEBUG) 109#if defined(DEBUG)
111STATIC void xlog_verify_dest_ptr(xlog_t *log, __psint_t ptr); 110STATIC void xlog_verify_dest_ptr(xlog_t *log, __psint_t ptr);
@@ -360,7 +359,7 @@ xfs_log_done(xfs_mount_t *mp,
360 */ 359 */
361 xlog_trace_loggrant(log, ticket, "xfs_log_done: (non-permanent)"); 360 xlog_trace_loggrant(log, ticket, "xfs_log_done: (non-permanent)");
362 xlog_ungrant_log_space(log, ticket); 361 xlog_ungrant_log_space(log, ticket);
363 xlog_ticket_put(log, ticket); 362 xfs_log_ticket_put(ticket);
364 } else { 363 } else {
365 xlog_trace_loggrant(log, ticket, "xfs_log_done: (permanent)"); 364 xlog_trace_loggrant(log, ticket, "xfs_log_done: (permanent)");
366 xlog_regrant_reserve_log_space(log, ticket); 365 xlog_regrant_reserve_log_space(log, ticket);
@@ -514,7 +513,7 @@ xfs_log_reserve(xfs_mount_t *mp,
514 retval = xlog_regrant_write_log_space(log, internal_ticket); 513 retval = xlog_regrant_write_log_space(log, internal_ticket);
515 } else { 514 } else {
516 /* may sleep if need to allocate more tickets */ 515 /* may sleep if need to allocate more tickets */
517 internal_ticket = xlog_ticket_get(log, unit_bytes, cnt, 516 internal_ticket = xlog_ticket_alloc(log, unit_bytes, cnt,
518 client, flags); 517 client, flags);
519 if (!internal_ticket) 518 if (!internal_ticket)
520 return XFS_ERROR(ENOMEM); 519 return XFS_ERROR(ENOMEM);
@@ -749,7 +748,7 @@ xfs_log_unmount_write(xfs_mount_t *mp)
749 if (tic) { 748 if (tic) {
750 xlog_trace_loggrant(log, tic, "unmount rec"); 749 xlog_trace_loggrant(log, tic, "unmount rec");
751 xlog_ungrant_log_space(log, tic); 750 xlog_ungrant_log_space(log, tic);
752 xlog_ticket_put(log, tic); 751 xfs_log_ticket_put(tic);
753 } 752 }
754 } else { 753 } else {
755 /* 754 /*
@@ -3222,22 +3221,33 @@ xlog_state_want_sync(xlog_t *log, xlog_in_core_t *iclog)
3222 */ 3221 */
3223 3222
3224/* 3223/*
3225 * Free a used ticket. 3224 * Free a used ticket when it's refcount falls to zero.
3226 */ 3225 */
3227STATIC void 3226void
3228xlog_ticket_put(xlog_t *log, 3227xfs_log_ticket_put(
3229 xlog_ticket_t *ticket) 3228 xlog_ticket_t *ticket)
3230{ 3229{
3231 sv_destroy(&ticket->t_wait); 3230 ASSERT(atomic_read(&ticket->t_ref) > 0);
3232 kmem_zone_free(xfs_log_ticket_zone, ticket); 3231 if (atomic_dec_and_test(&ticket->t_ref)) {
3233} /* xlog_ticket_put */ 3232 sv_destroy(&ticket->t_wait);
3233 kmem_zone_free(xfs_log_ticket_zone, ticket);
3234 }
3235}
3234 3236
3237xlog_ticket_t *
3238xfs_log_ticket_get(
3239 xlog_ticket_t *ticket)
3240{
3241 ASSERT(atomic_read(&ticket->t_ref) > 0);
3242 atomic_inc(&ticket->t_ref);
3243 return ticket;
3244}
3235 3245
3236/* 3246/*
3237 * Allocate and initialise a new log ticket. 3247 * Allocate and initialise a new log ticket.
3238 */ 3248 */
3239STATIC xlog_ticket_t * 3249STATIC xlog_ticket_t *
3240xlog_ticket_get(xlog_t *log, 3250xlog_ticket_alloc(xlog_t *log,
3241 int unit_bytes, 3251 int unit_bytes,
3242 int cnt, 3252 int cnt,
3243 char client, 3253 char client,
@@ -3308,6 +3318,7 @@ xlog_ticket_get(xlog_t *log,
3308 unit_bytes += 2*BBSIZE; 3318 unit_bytes += 2*BBSIZE;
3309 } 3319 }
3310 3320
3321 atomic_set(&tic->t_ref, 1);
3311 tic->t_unit_res = unit_bytes; 3322 tic->t_unit_res = unit_bytes;
3312 tic->t_curr_res = unit_bytes; 3323 tic->t_curr_res = unit_bytes;
3313 tic->t_cnt = cnt; 3324 tic->t_cnt = cnt;
@@ -3323,7 +3334,7 @@ xlog_ticket_get(xlog_t *log,
3323 xlog_tic_reset_res(tic); 3334 xlog_tic_reset_res(tic);
3324 3335
3325 return tic; 3336 return tic;
3326} /* xlog_ticket_get */ 3337}
3327 3338
3328 3339
3329/****************************************************************************** 3340/******************************************************************************