DF-2990 / fix.diff
--- a/sys/vfs/ufs/ffs_softdep.c +++ b/sys/vfs/ufs/ffs_softdep.c @@ -1007,22 +1007,37 @@ struct newblk *newblk; struct newblk_hashhead *newblkhd; + /* + * The newblk hash chains are shared with the removal sites in + * softdep_setup_allocdirect() and setup_allocindir_phase2(), which + * run under the softdep lock. Take the lock here as well so that + * lock-free inserts/lookups cannot interleave with locked removes + * on the same chain (the pagedep and inodedep lookups already + * follow this discipline). + */ + ACQUIRE_LOCK(&lk); newblkhd = NEWBLK_HASH(fs, newblkno); top: *newblkpp = newblk_find(newblkhd, fs, newblkno); - if (*newblkpp) + if (*newblkpp) { + FREE_LOCK(&lk); return(1); - if ((flags & DEPALLOC) == 0) + } + if ((flags & DEPALLOC) == 0) { + FREE_LOCK(&lk); return (0); - if (sema_get(&newblk_in_progress, NULL) == 0) + } + if (sema_get(&newblk_in_progress, &lk) == 0) goto top; + FREE_LOCK(&lk); newblk = kmalloc(sizeof(struct newblk), M_NEWBLK, M_SOFTDEP_FLAGS | M_ZERO); + ACQUIRE_LOCK(&lk); if (newblk_find(newblkhd, fs, newblkno)) { kprintf("newblk_lookup: blocking race avoided\n"); - sema_release(&pagedep_in_progress, NULL); + sema_release(&newblk_in_progress, &lk); kfree(newblk, M_NEWBLK); goto top; } @@ -1030,8 +1045,9 @@ newblk->nb_fs = fs; newblk->nb_newblkno = newblkno; LIST_INSERT_HEAD(newblkhd, newblk, nb_hash); - sema_release(&newblk_in_progress, NULL); + sema_release(&newblk_in_progress, &lk); *newblkpp = newblk; + FREE_LOCK(&lk); return (0); } |