Module Name: src Committed By: riastradh Date: Tue Dec 28 13:27:32 UTC 2021
Modified Files: src/sys/dev/ata: wd.c wdvar.h Log Message: wd(4): Fix bugs in softbadsect handling. - Don't copyout kernel virtual addresses (of SLIST entries) that userland won't use anyway. => The structure still has space for this pointer; it's just always null when userland gets it now. - Don't copyout under a lock. - Stop and return error if copyout fails (unless we've already copied some out). - Don't kmem_free under a lock. XXX Unclear whether anyone actually uses WD_SOFTBADSECT or why -- it's always been disabled by default. Maybe we should just remove it? To generate a diff of this commit: cvs rdiff -u -r1.465 -r1.466 src/sys/dev/ata/wd.c cvs rdiff -u -r1.50 -r1.51 src/sys/dev/ata/wdvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/ata/wd.c diff -u src/sys/dev/ata/wd.c:1.465 src/sys/dev/ata/wd.c:1.466 --- src/sys/dev/ata/wd.c:1.465 Mon Sep 28 12:47:49 2020 +++ src/sys/dev/ata/wd.c Tue Dec 28 13:27:32 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: wd.c,v 1.465 2020/09/28 12:47:49 jakllsch Exp $ */ +/* $NetBSD: wd.c,v 1.466 2021/12/28 13:27:32 riastradh Exp $ */ /* * Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved. @@ -54,7 +54,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.465 2020/09/28 12:47:49 jakllsch Exp $"); +__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.466 2021/12/28 13:27:32 riastradh Exp $"); #include "opt_ata.h" #include "opt_wd.h" @@ -316,6 +316,7 @@ wdattach(device_t parent, device_t self, mutex_init(&wd->sc_lock, MUTEX_DEFAULT, IPL_BIO); #ifdef WD_SOFTBADSECT SLIST_INIT(&wd->sc_bslist); + cv_init(&wd->sc_bslist_cv, "wdbadsect"); #endif wd->atabus = adev->adev_bustype; wd->inflight = 0; @@ -587,6 +588,11 @@ wddetach(device_t self, int flags) wd_sysctl_detach(wd); +#ifdef WD_SOFTBADSECT + KASSERT(SLIST_EMPTY(&wd->sc_bslist)); + cv_destroy(&wd->sc_bslist_cv); +#endif + mutex_destroy(&wd->sc_lock); wd->drvp->drive_type = ATA_DRIVET_NONE; /* no drive any more here */ @@ -1279,13 +1285,13 @@ wdioctl(dev_t dev, u_long cmd, void *add return 0; #endif #ifdef WD_SOFTBADSECT - case DIOCBSLIST : - { + case DIOCBSLIST: { uint32_t count, missing, skip; struct disk_badsecinfo dbsi; - struct disk_badsectors *dbs; + struct disk_badsectors *dbs, dbsbuf; size_t available; uint8_t *laddr; + int error; dbsi = *(struct disk_badsecinfo *)addr; missing = wd->sc_bscount; @@ -1303,7 +1309,9 @@ wdioctl(dev_t dev, u_long cmd, void *add * back to user space whilst the summary is returned via * the struct passed in via the ioctl. */ + error = 0; mutex_enter(&wd->sc_lock); + wd->sc_bslist_inuse++; SLIST_FOREACH(dbs, &wd->sc_bslist, dbs_next) { if (skip > 0) { missing--; @@ -1313,30 +1321,57 @@ wdioctl(dev_t dev, u_long cmd, void *add if (available < sizeof(*dbs)) break; available -= sizeof(*dbs); - copyout(dbs, laddr, sizeof(*dbs)); + memset(&dbsbuf, 0, sizeof(dbsbuf)); + dbsbuf.dbs_min = dbs->dbs_min; + dbsbuf.dbs_max = dbs->dbs_max; + dbsbuf.dbs_failedat = dbs->dbs_failedat; + mutex_exit(&wd->sc_lock); + error = copyout(&dbsbuf, laddr, sizeof(dbsbuf)); + mutex_enter(&wd->sc_lock); + if (error) + break; laddr += sizeof(*dbs); missing--; count++; } + if (--wd->sc_bslist_inuse == 0) + cv_broadcast(&wd->sc_bslist_cv); mutex_exit(&wd->sc_lock); dbsi.dbsi_left = missing; dbsi.dbsi_copied = count; *(struct disk_badsecinfo *)addr = dbsi; - return 0; + + /* + * If we copied anything out, ignore error and return + * success -- can't back it out. + */ + return count ? 0 : error; } - case DIOCBSFLUSH : + case DIOCBSFLUSH: { + int error; + /* Clean out the bad sector list */ mutex_enter(&wd->sc_lock); + while (wd->sc_bslist_inuse) { + error = cv_wait_sig(&wd->sc_bslist_cv, &wd->sc_lock); + if (error) { + mutex_exit(&wd->sc_lock); + return error; + } + } while (!SLIST_EMPTY(&wd->sc_bslist)) { struct disk_badsectors *dbs = SLIST_FIRST(&wd->sc_bslist); SLIST_REMOVE_HEAD(&wd->sc_bslist, dbs_next); + mutex_exit(&wd->sc_lock); kmem_free(dbs, sizeof(*dbs)); + mutex_enter(&wd->sc_lock); } mutex_exit(&wd->sc_lock); wd->sc_bscount = 0; return 0; + } #endif #ifdef notyet Index: src/sys/dev/ata/wdvar.h diff -u src/sys/dev/ata/wdvar.h:1.50 src/sys/dev/ata/wdvar.h:1.51 --- src/sys/dev/ata/wdvar.h:1.50 Mon Mar 2 16:01:56 2020 +++ src/sys/dev/ata/wdvar.h Tue Dec 28 13:27:32 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: wdvar.h,v 1.50 2020/03/02 16:01:56 riastradh Exp $ */ +/* $NetBSD: wdvar.h,v 1.51 2021/12/28 13:27:32 riastradh Exp $ */ /* * Copyright (c) 1998, 2001 Manuel Bouyer. @@ -31,8 +31,20 @@ #include "opt_wd.h" #endif -#include <dev/dkvar.h> +#include <sys/types.h> + +#include <sys/callout.h> +#include <sys/condvar.h> +#include <sys/disk.h> +#include <sys/mutex.h> #include <sys/sysctl.h> +#include <sys/queue.h> + +#include <dev/ata/atareg.h> +#include <dev/ata/atavar.h> +#include <dev/dkvar.h> + +struct sysctllog; struct wd_softc { /* General disk infos */ @@ -64,6 +76,8 @@ struct wd_softc { #ifdef WD_SOFTBADSECT SLIST_HEAD(, disk_badsectors) sc_bslist; u_int sc_bscount; + kcondvar_t sc_bslist_cv; + u_int sc_bslist_inuse; #endif /* Retry/requeue failed transfers */