Lock busdma operations and serialize detach against open/close

Use sx to allow M_WAITOK allocations (suggested by markj).

admbugs: 782
Reviewed by:	markj
This commit is contained in:
Marcel Moolenaar 2019-07-04 02:51:34 +00:00
parent 38e220e8df
commit 9f011bca82
4 changed files with 77 additions and 27 deletions

View File

@ -1,5 +1,5 @@
/*- /*-
* Copyright (c) 2014, 2015 Marcel Moolenaar * Copyright (c) 2014, 2015, 2019 Marcel Moolenaar
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -36,7 +36,8 @@
#define PROTO_RES_BUSDMA 11 #define PROTO_RES_BUSDMA 11
struct proto_res { struct proto_res {
int r_type; u_int r_type:8;
u_int r_opened:1;
int r_rid; int r_rid;
union { union {
struct resource *res; struct resource *res;
@ -47,13 +48,14 @@ struct proto_res {
void *cookie; void *cookie;
struct cdev *cdev; struct cdev *cdev;
} r_u; } r_u;
uintptr_t r_opened;
}; };
struct proto_softc { struct proto_softc {
device_t sc_dev; device_t sc_dev;
struct proto_res sc_res[PROTO_RES_MAX]; struct proto_res sc_res[PROTO_RES_MAX];
int sc_rescnt; int sc_rescnt;
int sc_opencnt;
struct mtx sc_mtx;
}; };
extern devclass_t proto_devclass; extern devclass_t proto_devclass;

View File

@ -1,5 +1,5 @@
/*- /*-
* Copyright (c) 2015 Marcel Moolenaar * Copyright (c) 2015, 2019 Marcel Moolenaar
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/queue.h> #include <sys/queue.h>
#include <sys/rman.h> #include <sys/rman.h>
#include <sys/sbuf.h> #include <sys/sbuf.h>
#include <sys/sx.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <vm/vm.h> #include <vm/vm.h>
#include <vm/pmap.h> #include <vm/pmap.h>
@ -355,6 +356,7 @@ proto_busdma_attach(struct proto_softc *sc)
struct proto_busdma *busdma; struct proto_busdma *busdma;
busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO);
sx_init(&busdma->sxlck, "proto-busdma");
return (busdma); return (busdma);
} }
@ -363,6 +365,7 @@ proto_busdma_detach(struct proto_softc *sc, struct proto_busdma *busdma)
{ {
proto_busdma_cleanup(sc, busdma); proto_busdma_cleanup(sc, busdma);
sx_destroy(&busdma->sxlck);
free(busdma, M_PROTO_BUSDMA); free(busdma, M_PROTO_BUSDMA);
return (0); return (0);
} }
@ -373,10 +376,12 @@ proto_busdma_cleanup(struct proto_softc *sc, struct proto_busdma *busdma)
struct proto_md *md, *md1; struct proto_md *md, *md1;
struct proto_tag *tag, *tag1; struct proto_tag *tag, *tag1;
sx_xlock(&busdma->sxlck);
LIST_FOREACH_SAFE(md, &busdma->mds, mds, md1) LIST_FOREACH_SAFE(md, &busdma->mds, mds, md1)
proto_busdma_md_destroy_internal(busdma, md); proto_busdma_md_destroy_internal(busdma, md);
LIST_FOREACH_SAFE(tag, &busdma->tags, tags, tag1) LIST_FOREACH_SAFE(tag, &busdma->tags, tags, tag1)
proto_busdma_tag_destroy(busdma, tag); proto_busdma_tag_destroy(busdma, tag);
sx_xunlock(&busdma->sxlck);
return (0); return (0);
} }
@ -388,6 +393,8 @@ proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma,
struct proto_md *md; struct proto_md *md;
int error; int error;
sx_xlock(&busdma->sxlck);
error = 0; error = 0;
switch (ioc->request) { switch (ioc->request) {
case PROTO_IOC_BUSDMA_TAG_CREATE: case PROTO_IOC_BUSDMA_TAG_CREATE:
@ -470,6 +477,9 @@ proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma,
error = EINVAL; error = EINVAL;
break; break;
} }
sx_xunlock(&busdma->sxlck);
return (error); return (error);
} }
@ -477,11 +487,20 @@ int
proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr) proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr)
{ {
struct proto_md *md; struct proto_md *md;
int result;
sx_xlock(&busdma->sxlck);
result = 0;
LIST_FOREACH(md, &busdma->mds, mds) { LIST_FOREACH(md, &busdma->mds, mds) {
if (physaddr >= trunc_page(md->physaddr) && if (physaddr >= trunc_page(md->physaddr) &&
physaddr <= trunc_page(md->physaddr + md->tag->maxsz)) physaddr <= trunc_page(md->physaddr + md->tag->maxsz)) {
return (1); result = 1;
break;
}
} }
return (0);
sx_xunlock(&busdma->sxlck);
return (result);
} }

View File

@ -1,5 +1,5 @@
/*- /*-
* Copyright (c) 2015 Marcel Moolenaar * Copyright (c) 2015, 2019 Marcel Moolenaar
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -60,6 +60,7 @@ struct proto_busdma {
LIST_HEAD(,proto_tag) tags; LIST_HEAD(,proto_tag) tags;
LIST_HEAD(,proto_md) mds; LIST_HEAD(,proto_md) mds;
bus_dma_tag_t bd_roottag; bus_dma_tag_t bd_roottag;
struct sx sxlck;
}; };
struct proto_busdma *proto_busdma_attach(struct proto_softc *); struct proto_busdma *proto_busdma_attach(struct proto_softc *);

View File

@ -1,5 +1,5 @@
/*- /*-
* Copyright (c) 2014, 2015 Marcel Moolenaar * Copyright (c) 2014, 2015, 2019 Marcel Moolenaar
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -184,6 +184,7 @@ proto_attach(device_t dev)
sc = device_get_softc(dev); sc = device_get_softc(dev);
sc->sc_dev = dev; sc->sc_dev = dev;
mtx_init(&sc->sc_mtx, "proto-softc", NULL, MTX_DEF);
for (res = 0; res < sc->sc_rescnt; res++) { for (res = 0; res < sc->sc_rescnt; res++) {
r = sc->sc_res + res; r = sc->sc_res + res;
@ -231,15 +232,16 @@ proto_detach(device_t dev)
sc = device_get_softc(dev); sc = device_get_softc(dev);
/* Don't detach if we have open device files. */ mtx_lock(&sc->sc_mtx);
for (res = 0; res < sc->sc_rescnt; res++) { if (sc->sc_opencnt == 0)
r = sc->sc_res + res; sc->sc_opencnt = -1;
if (r->r_opened) mtx_unlock(&sc->sc_mtx);
return (EBUSY); if (sc->sc_opencnt > 0)
} return (EBUSY);
for (res = 0; res < sc->sc_rescnt; res++) { for (res = 0; res < sc->sc_rescnt; res++) {
r = sc->sc_res + res; r = sc->sc_res + res;
switch (r->r_type) { switch (r->r_type) {
case SYS_RES_IRQ: case SYS_RES_IRQ:
/* XXX TODO */ /* XXX TODO */
@ -252,21 +254,25 @@ proto_detach(device_t dev)
break; break;
case SYS_RES_MEMORY: case SYS_RES_MEMORY:
case SYS_RES_IOPORT: case SYS_RES_IOPORT:
destroy_dev(r->r_u.cdev);
bus_release_resource(dev, r->r_type, r->r_rid, bus_release_resource(dev, r->r_type, r->r_rid,
r->r_d.res); r->r_d.res);
destroy_dev(r->r_u.cdev);
break; break;
case PROTO_RES_PCICFG: case PROTO_RES_PCICFG:
destroy_dev(r->r_u.cdev); destroy_dev(r->r_u.cdev);
break; break;
case PROTO_RES_BUSDMA: case PROTO_RES_BUSDMA:
proto_busdma_detach(sc, r->r_d.busdma);
destroy_dev(r->r_u.cdev); destroy_dev(r->r_u.cdev);
proto_busdma_detach(sc, r->r_d.busdma);
break; break;
} }
r->r_type = PROTO_RES_UNUSED; r->r_type = PROTO_RES_UNUSED;
} }
mtx_lock(&sc->sc_mtx);
sc->sc_rescnt = 0; sc->sc_rescnt = 0;
sc->sc_opencnt = 0;
mtx_unlock(&sc->sc_mtx);
mtx_destroy(&sc->sc_mtx);
return (0); return (0);
} }
@ -278,11 +284,23 @@ static int
proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
{ {
struct proto_res *r; struct proto_res *r;
struct proto_softc *sc;
int error;
r = cdev->si_drv2; sc = cdev->si_drv1;
if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc)) mtx_lock(&sc->sc_mtx);
return (EBUSY); if (sc->sc_opencnt >= 0) {
return (0); r = cdev->si_drv2;
if (!r->r_opened) {
r->r_opened = 1;
sc->sc_opencnt++;
error = 0;
} else
error = EBUSY;
} else
error = ENXIO;
mtx_unlock(&sc->sc_mtx);
return (error);
} }
static int static int
@ -290,14 +308,24 @@ proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
{ {
struct proto_res *r; struct proto_res *r;
struct proto_softc *sc; struct proto_softc *sc;
int error;
sc = cdev->si_drv1; sc = cdev->si_drv1;
r = cdev->si_drv2; mtx_lock(&sc->sc_mtx);
if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL)) if (sc->sc_opencnt > 0) {
return (ENXIO); r = cdev->si_drv2;
if (r->r_type == PROTO_RES_BUSDMA) if (r->r_opened) {
proto_busdma_cleanup(sc, r->r_d.busdma); if (r->r_type == PROTO_RES_BUSDMA)
return (0); proto_busdma_cleanup(sc, r->r_d.busdma);
r->r_opened = 0;
sc->sc_opencnt--;
error = 0;
} else
error = ENXIO;
} else
error = ENXIO;
mtx_unlock(&sc->sc_mtx);
return (error);
} }
static int static int