DF-2832 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | --- a/sys/kern/vfs_sync.c +++ b/sys/kern/vfs_sync.c @@ -198,6 +198,8 @@ int slot; ctx = vp->v_mount->mnt_syncer_ctx; + if (ctx == NULL) + return; lwkt_gettoken(&ctx->sc_token); if (vp->v_flag & VONWORKLST) { @@ -234,6 +236,8 @@ struct syncer_ctx *ctx; ctx = vp->v_mount->mnt_syncer_ctx; + if (ctx == NULL) + return; lwkt_gettoken(&ctx->sc_token); if ((vp->v_flag & (VISDIRTY | VONWORKLST | VOBJDIRTY)) == VONWORKLST && @@ -281,6 +285,8 @@ if ((vp->v_flag & VISDIRTY) == 0) { ctx = vp->v_mount->mnt_syncer_ctx; vsetflags(vp, VISDIRTY); + if (ctx == NULL) + return; lwkt_gettoken(&ctx->sc_token); if ((vp->v_flag & VONWORKLST) == 0) vn_syncer_add(vp, syncdelay); @@ -296,6 +302,8 @@ if ((vp->v_flag & VOBJDIRTY) == 0) { ctx = vp->v_mount->mnt_syncer_ctx; vsetflags(vp, VOBJDIRTY); + if (ctx == NULL) + return; lwkt_gettoken(&ctx->sc_token); if ((vp->v_flag & VONWORKLST) == 0) vn_syncer_add(vp, syncdelay); @@ -350,11 +358,20 @@ lwkt_gettoken(&ctx->sc_token); } - mp->mnt_syncer_ctx = NULL; lwkt_reltoken(&ctx->sc_token); + /* + * Interlock against the lock-free trigger_syncer*() / speedup_syncer() + * users, which load mp->mnt_syncer_ctx and then operate on the ctx + * without any other synchronization. Publish the NULL and free the + * ctx while holding mnt_token exclusively so any user that already + * loaded the pointer completes before the memory is destroyed. + */ + lwkt_gettoken(&mp->mnt_token); + mp->mnt_syncer_ctx = NULL; hashdestroy(ctx->syncer_workitem_pending, M_DEVBUF, ctx->syncer_mask); kfree(ctx, M_TEMP); + lwkt_reltoken(&mp->mnt_token); } struct thread *updatethread; @@ -567,8 +584,12 @@ */ atomic_add_int(&rushjob, 1); ++stat_rush_requests; - if (mp && mp->mnt_syncer_ctx) - wakeup(mp->mnt_syncer_ctx); + if (mp) { + lwkt_gettoken_shared(&mp->mnt_token); + if (mp->mnt_syncer_ctx) + wakeup(mp->mnt_syncer_ctx); + lwkt_reltoken(&mp->mnt_token); + } } /* @@ -581,10 +602,14 @@ { struct syncer_ctx *ctx; - if (mp && (ctx = mp->mnt_syncer_ctx) != NULL) { + if (mp == NULL) + return; + lwkt_gettoken_shared(&mp->mnt_token); + if ((ctx = mp->mnt_syncer_ctx) != NULL) { if (atomic_fetchadd_int(&ctx->syncer_trigger, 2) <= 1) wakeup(ctx); } + lwkt_reltoken(&mp->mnt_token); } void @@ -592,9 +617,13 @@ { struct syncer_ctx *ctx; - if (mp && (ctx = mp->mnt_syncer_ctx) != NULL) { + if (mp == NULL) + return; + lwkt_gettoken_shared(&mp->mnt_token); + if ((ctx = mp->mnt_syncer_ctx) != NULL) { atomic_add_int(&ctx->syncer_trigger, -2); } + lwkt_reltoken(&mp->mnt_token); } /* @@ -605,12 +634,16 @@ { struct syncer_ctx *ctx; - if (mp && (ctx = mp->mnt_syncer_ctx) != NULL) { + if (mp == NULL) + return; + lwkt_gettoken_shared(&mp->mnt_token); + if ((ctx = mp->mnt_syncer_ctx) != NULL) { if ((ctx->syncer_trigger & 1) == 0) { atomic_set_int(&ctx->syncer_trigger, 1); wakeup(ctx); } } + lwkt_reltoken(&mp->mnt_token); } /* |