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 2019-07-04 02:51:34 +00:00
parent a95e42fe08
commit fb8867395a
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.
*
* Redistribution and use in source and binary forms, with or without
@ -36,7 +36,8 @@
#define PROTO_RES_BUSDMA 11
struct proto_res {
int r_type;
u_int r_type:8;
u_int r_opened:1;
int r_rid;
union {
struct resource *res;
@ -47,13 +48,14 @@ struct proto_res {
void *cookie;
struct cdev *cdev;
} r_u;
uintptr_t r_opened;
};
struct proto_softc {
device_t sc_dev;
struct proto_res sc_res[PROTO_RES_MAX];
int sc_rescnt;
int sc_opencnt;
struct mtx sc_mtx;
};
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.
*
* Redistribution and use in source and binary forms, with or without
@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/queue.h>
#include <sys/rman.h>
#include <sys/sbuf.h>
#include <sys/sx.h>
#include <sys/uio.h>
#include <vm/vm.h>
#include <vm/pmap.h>
@ -355,6 +356,7 @@ proto_busdma_attach(struct proto_softc *sc)
struct proto_busdma *busdma;
busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO);
sx_init(&busdma->sxlck, "proto-busdma");
return (busdma);
}
@ -363,6 +365,7 @@ proto_busdma_detach(struct proto_softc *sc, struct proto_busdma *busdma)
{
proto_busdma_cleanup(sc, busdma);
sx_destroy(&busdma->sxlck);
free(busdma, M_PROTO_BUSDMA);
return (0);
}
@ -373,10 +376,12 @@ proto_busdma_cleanup(struct proto_softc *sc, struct proto_busdma *busdma)
struct proto_md *md, *md1;
struct proto_tag *tag, *tag1;
sx_xlock(&busdma->sxlck);
LIST_FOREACH_SAFE(md, &busdma->mds, mds, md1)
proto_busdma_md_destroy_internal(busdma, md);
LIST_FOREACH_SAFE(tag, &busdma->tags, tags, tag1)
proto_busdma_tag_destroy(busdma, tag);
sx_xunlock(&busdma->sxlck);
return (0);
}
@ -388,6 +393,8 @@ proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma,
struct proto_md *md;
int error;
sx_xlock(&busdma->sxlck);
error = 0;
switch (ioc->request) {
case PROTO_IOC_BUSDMA_TAG_CREATE:
@ -470,6 +477,9 @@ proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma,
error = EINVAL;
break;
}
sx_xunlock(&busdma->sxlck);
return (error);
}
@ -477,11 +487,20 @@ int
proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr)
{
struct proto_md *md;
int result;
sx_xlock(&busdma->sxlck);
result = 0;
LIST_FOREACH(md, &busdma->mds, mds) {
if (physaddr >= trunc_page(md->physaddr) &&
physaddr <= trunc_page(md->physaddr + md->tag->maxsz))
return (1);
physaddr <= trunc_page(md->physaddr + md->tag->maxsz)) {
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.
*
* 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_md) mds;
bus_dma_tag_t bd_roottag;
struct sx sxlck;
};
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.
*
* 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->sc_dev = dev;
mtx_init(&sc->sc_mtx, "proto-softc", NULL, MTX_DEF);
for (res = 0; res < sc->sc_rescnt; res++) {
r = sc->sc_res + res;
@ -231,15 +232,16 @@ proto_detach(device_t dev)
sc = device_get_softc(dev);
/* Don't detach if we have open device files. */
for (res = 0; res < sc->sc_rescnt; res++) {
r = sc->sc_res + res;
if (r->r_opened)
return (EBUSY);
}
mtx_lock(&sc->sc_mtx);
if (sc->sc_opencnt == 0)
sc->sc_opencnt = -1;
mtx_unlock(&sc->sc_mtx);
if (sc->sc_opencnt > 0)
return (EBUSY);
for (res = 0; res < sc->sc_rescnt; res++) {
r = sc->sc_res + res;
switch (r->r_type) {
case SYS_RES_IRQ:
/* XXX TODO */
@ -252,21 +254,25 @@ proto_detach(device_t dev)
break;
case SYS_RES_MEMORY:
case SYS_RES_IOPORT:
destroy_dev(r->r_u.cdev);
bus_release_resource(dev, r->r_type, r->r_rid,
r->r_d.res);
destroy_dev(r->r_u.cdev);
break;
case PROTO_RES_PCICFG:
destroy_dev(r->r_u.cdev);
break;
case PROTO_RES_BUSDMA:
proto_busdma_detach(sc, r->r_d.busdma);
destroy_dev(r->r_u.cdev);
proto_busdma_detach(sc, r->r_d.busdma);
break;
}
r->r_type = PROTO_RES_UNUSED;
}
mtx_lock(&sc->sc_mtx);
sc->sc_rescnt = 0;
sc->sc_opencnt = 0;
mtx_unlock(&sc->sc_mtx);
mtx_destroy(&sc->sc_mtx);
return (0);
}
@ -278,11 +284,23 @@ static int
proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
{
struct proto_res *r;
struct proto_softc *sc;
int error;
r = cdev->si_drv2;
if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc))
return (EBUSY);
return (0);
sc = cdev->si_drv1;
mtx_lock(&sc->sc_mtx);
if (sc->sc_opencnt >= 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
@ -290,14 +308,24 @@ proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
{
struct proto_res *r;
struct proto_softc *sc;
int error;
sc = cdev->si_drv1;
r = cdev->si_drv2;
if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL))
return (ENXIO);
if (r->r_type == PROTO_RES_BUSDMA)
proto_busdma_cleanup(sc, r->r_d.busdma);
return (0);
mtx_lock(&sc->sc_mtx);
if (sc->sc_opencnt > 0) {
r = cdev->si_drv2;
if (r->r_opened) {
if (r->r_type == PROTO_RES_BUSDMA)
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