diff options
author | Dave Chinner <david@fromorbit.com> | 2008-11-17 01:37:10 -0500 |
---|---|---|
committer | Lachlan McIlroy <lachlan@redback.melbourne.sgi.com> | 2008-11-17 01:37:10 -0500 |
commit | cc09c0dc57de7f7d2ed89d480b5653e5f6a32f2c (patch) | |
tree | 3c6145ccb00f603c47e020cfa45f159ab76cf9bf /fs/xfs/xfs_log.c | |
parent | 6307091fe69ae74747298bdcaf43119ad67bda3a (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.c | 39 |
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 */ |
103 | STATIC xlog_ticket_t *xlog_ticket_get(xlog_t *log, | 103 | STATIC 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); |
108 | STATIC void xlog_ticket_put(xlog_t *log, xlog_ticket_t *ticket); | ||
109 | 108 | ||
110 | #if defined(DEBUG) | 109 | #if defined(DEBUG) |
111 | STATIC void xlog_verify_dest_ptr(xlog_t *log, __psint_t ptr); | 110 | STATIC 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 | */ |
3227 | STATIC void | 3226 | void |
3228 | xlog_ticket_put(xlog_t *log, | 3227 | xfs_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 | ||
3237 | xlog_ticket_t * | ||
3238 | xfs_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 | */ |
3239 | STATIC xlog_ticket_t * | 3249 | STATIC xlog_ticket_t * |
3240 | xlog_ticket_get(xlog_t *log, | 3250 | xlog_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 | /****************************************************************************** |