Index: sys/dev/proto/proto.h =================================================================== --- sys/dev/proto/proto.h +++ sys/dev/proto/proto.h @@ -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; + int r_type:8; + int r_opened:1; int r_rid; union { struct resource *res; @@ -47,13 +48,14 @@ 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; Index: sys/dev/proto/proto_busdma.h =================================================================== --- sys/dev/proto/proto_busdma.h +++ sys/dev/proto/proto_busdma.h @@ -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 @@ LIST_HEAD(,proto_tag) tags; LIST_HEAD(,proto_md) mds; bus_dma_tag_t bd_roottag; + struct mtx mtx; }; struct proto_busdma *proto_busdma_attach(struct proto_softc *); Index: sys/dev/proto/proto_busdma.c =================================================================== --- sys/dev/proto/proto_busdma.c +++ sys/dev/proto/proto_busdma.c @@ -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 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -62,14 +63,15 @@ static int proto_busdma_tag_create(struct proto_busdma *busdma, struct proto_tag *parent, - struct proto_ioc_busdma *ioc) + struct proto_ioc_busdma *ioc, struct proto_tag *tag) { - struct proto_tag *tag; /* Make sure that when a boundary is specified, it's a power of 2 */ if (ioc->u.tag.bndry != 0 && - (ioc->u.tag.bndry & (ioc->u.tag.bndry - 1)) != 0) + (ioc->u.tag.bndry & (ioc->u.tag.bndry - 1)) != 0) { + free(tag, M_PROTO_BUSDMA); return (EINVAL); + } /* * If nsegs is 1, ignore maxsegsz. What this means is that if we have @@ -79,7 +81,6 @@ if (ioc->u.tag.maxsegsz > ioc->u.tag.maxsz || ioc->u.tag.nsegs == 1) ioc->u.tag.maxsegsz = ioc->u.tag.maxsz; - tag = malloc(sizeof(*tag), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); if (parent != NULL) { tag->parent = parent; LIST_INSERT_HEAD(&parent->children, tag, peers); @@ -172,13 +173,11 @@ static int proto_busdma_mem_alloc(struct proto_busdma *busdma, struct proto_tag *tag, - struct proto_ioc_busdma *ioc) + struct proto_ioc_busdma *ioc, struct proto_md *md) { struct proto_callback_bundle pcb; - struct proto_md *md; int error; - md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); md->tag = tag; error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry, @@ -227,12 +226,10 @@ static int proto_busdma_md_create(struct proto_busdma *busdma, struct proto_tag *tag, - struct proto_ioc_busdma *ioc) + struct proto_ioc_busdma *ioc, struct proto_md *md) { - struct proto_md *md; int error; - md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); md->tag = tag; error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry, @@ -355,6 +352,7 @@ struct proto_busdma *busdma; busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); + mtx_init(&busdma->mtx, "proto-busdma", NULL, MTX_DEF); return (busdma); } @@ -363,6 +361,7 @@ { proto_busdma_cleanup(sc, busdma); + mtx_destroy(&busdma->mtx); free(busdma, M_PROTO_BUSDMA); return (0); } @@ -373,10 +372,12 @@ struct proto_md *md, *md1; struct proto_tag *tag, *tag1; + mtx_lock(&busdma->mtx); 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); + mtx_unlock(&busdma->mtx); return (0); } @@ -384,23 +385,37 @@ proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma, struct proto_ioc_busdma *ioc, struct thread *td) { - struct proto_tag *tag; + struct proto_tag *ptag, *tag; struct proto_md *md; int error; + /* Allocate structures prior to acquiring the lock */ + switch (ioc->request) { + case PROTO_IOC_BUSDMA_TAG_CREATE: + case PROTO_IOC_BUSDMA_TAG_DERIVE: + tag = malloc(sizeof(*tag), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); + break; + case PROTO_IOC_BUSDMA_MEM_ALLOC: + case PROTO_IOC_BUSDMA_MD_CREATE: + md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); + break; + } + + mtx_lock(&busdma->mtx); error = 0; switch (ioc->request) { case PROTO_IOC_BUSDMA_TAG_CREATE: busdma->bd_roottag = bus_get_dma_tag(sc->sc_dev); - error = proto_busdma_tag_create(busdma, NULL, ioc); + error = proto_busdma_tag_create(busdma, NULL, ioc, tag); break; case PROTO_IOC_BUSDMA_TAG_DERIVE: - tag = proto_busdma_tag_lookup(busdma, ioc->key); - if (tag == NULL) { + ptag = proto_busdma_tag_lookup(busdma, ioc->key); + if (ptag == NULL) { + free(tag, M_PROTO_BUSDMA); error = EINVAL; break; } - error = proto_busdma_tag_create(busdma, tag, ioc); + error = proto_busdma_tag_create(busdma, ptag, ioc, tag); break; case PROTO_IOC_BUSDMA_TAG_DESTROY: tag = proto_busdma_tag_lookup(busdma, ioc->key); @@ -413,10 +428,11 @@ case PROTO_IOC_BUSDMA_MEM_ALLOC: tag = proto_busdma_tag_lookup(busdma, ioc->u.md.tag); if (tag == NULL) { + free(md, M_PROTO_BUSDMA); error = EINVAL; break; } - error = proto_busdma_mem_alloc(busdma, tag, ioc); + error = proto_busdma_mem_alloc(busdma, tag, ioc, md); break; case PROTO_IOC_BUSDMA_MEM_FREE: md = proto_busdma_md_lookup(busdma, ioc->key); @@ -429,10 +445,11 @@ case PROTO_IOC_BUSDMA_MD_CREATE: tag = proto_busdma_tag_lookup(busdma, ioc->u.md.tag); if (tag == NULL) { + free(md, M_PROTO_BUSDMA); error = EINVAL; break; } - error = proto_busdma_md_create(busdma, tag, ioc); + error = proto_busdma_md_create(busdma, tag, ioc, md); break; case PROTO_IOC_BUSDMA_MD_DESTROY: md = proto_busdma_md_lookup(busdma, ioc->key); @@ -470,6 +487,7 @@ error = EINVAL; break; } + mtx_unlock(&busdma->mtx); return (error); } @@ -477,11 +495,17 @@ proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr) { struct proto_md *md; + int result; + mtx_lock(&busdma->mtx); + 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); + mtx_unlock(&busdma->mtx); + return (result); } Index: sys/dev/proto/proto_core.c =================================================================== --- sys/dev/proto/proto_core.c +++ sys/dev/proto/proto_core.c @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 @@ { 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