In order to make the sparc64 iommu code "mpsafe", I need to make sure
the extent manager can be used in an mpsafe manner. The current code
isn't really safe since the extent manager needs to allocate region
descriptors whenever we do a bus_dmamap_load(). The diff below adds a
function to provide the extent manager with a region descriptor such
that the next extent_alloc_xxx() call can use that one instead of
allocating a new one.
I deliberately chose to add a seperate API, since
extent_alloc_subregion() already has too many arguments.
The 2nd diff shows how I use this in the sparc64 iommu code.
opinions?
Index: sys/extent.h
===================================================================
RCS file: /cvs/src/sys/sys/extent.h,v
retrieving revision 1.12
diff -u -p -r1.12 extent.h
--- sys/extent.h 19 Apr 2009 15:26:52 -0000 1.12
+++ sys/extent.h 20 Jan 2014 04:01:45 -0000
@@ -44,6 +44,7 @@ struct extent_region {
/* er_flags */
#define ER_ALLOC 0x01 /* region descriptor dynamically allocated */
+#define ER_DISCARD 0x02 /* discard region descriptor after use */
struct extent {
char *ex_name; /* name of extent */
@@ -101,13 +102,15 @@ struct extent_fixed {
void extent_print_all(void);
struct extent *extent_create(char *, u_long, u_long, int,
- caddr_t, size_t, int);
+ void *, size_t, int);
void extent_destroy(struct extent *);
int extent_alloc_subregion(struct extent *, u_long, u_long,
u_long, u_long, u_long, u_long, int, u_long *);
int extent_alloc_region(struct extent *, u_long, u_long, int);
int extent_free(struct extent *, u_long, u_long, int);
void extent_print(struct extent *);
+void extent_supply_region_descriptor(struct extent *,
+ struct extent_region *);
/* Simple case of extent_alloc_subregion() */
#define extent_alloc(_ex, _size, _alignment, _skew, _boundary, \
Index: kern/subr_extent.c
===================================================================
RCS file: /cvs/src/sys/kern/subr_extent.c,v
retrieving revision 1.48
diff -u -p -r1.48 subr_extent.c
--- kern/subr_extent.c 8 Aug 2013 23:25:06 -0000 1.48
+++ kern/subr_extent.c 20 Jan 2014 04:01:45 -0000
@@ -157,7 +157,7 @@ extent_print_all(void)
* Allocate and initialize an extent map.
*/
struct extent *
-extent_create(char *name, u_long start, u_long end, int mtype, caddr_t storage,
+extent_create(char *name, u_long start, u_long end, int mtype, void *storage,
size_t storagesize, int flags)
{
struct extent *ex;
@@ -1111,6 +1111,9 @@ extent_alloc_region_descriptor(struct ex
static void
extent_free_region_descriptor(struct extent *ex, struct extent_region *rp)
{
+ if (rp->er_flags & ER_DISCARD)
+ return;
+
if (ex->ex_flags & EXF_FIXED) {
struct extent_fixed *fex = (struct extent_fixed *)ex;
@@ -1149,7 +1152,17 @@ extent_free_region_descriptor(struct ext
pool_put(&ex_region_pl, rp);
}
-
+void
+extent_supply_region_descriptor(struct extent *ex, struct extent_region *rp)
+{
+ struct extent_fixed *fex = (struct extent_fixed *)ex;
+
+ KASSERT(ex->ex_flags & EXF_FIXED);
+
+ rp->er_flags = ER_DISCARD;
+ LIST_INSERT_HEAD(&fex->fex_freelist, rp, er_link);
+}
+
#if defined(DIAGNOSTIC) || defined(DDB) || !defined(_KERNEL)
void
Index: arch/sparc64/dev/iommu.c
===================================================================
RCS file: /cvs/src/sys/arch/sparc64/dev/iommu.c,v
retrieving revision 1.66
diff -u -p -r1.66 iommu.c
--- arch/sparc64/dev/iommu.c 15 Jan 2013 03:14:01 -0000 1.66
+++ arch/sparc64/dev/iommu.c 20 Jan 2014 04:03:41 -0000
@@ -226,7 +226,7 @@ iommu_init(char *name, struct iommu_stat
#endif
is->is_dvmamap = extent_create(name,
is->is_dvmabase, (u_long)is->is_dvmaend + 1,
- M_DEVBUF, 0, 0, EX_NOWAIT);
+ M_DEVBUF, &is->is_fex, sizeof(is->is_fex), EX_NOCOALESCE);
mtx_init(&is->is_mtx, IPL_HIGH);
/*
@@ -749,6 +749,7 @@ iommu_dvmamap_load(bus_dma_tag_t t, bus_
* If our segment size is larger than the boundary we need to
* split the transfer up into little pieces ourselves.
*/
+ extent_supply_region_descriptor(is->is_dvmamap, &ims->ims_er);
err = extent_alloc_subregion(is->is_dvmamap, sgstart, sgend,
sgsize, align, 0, (sgsize > boundary) ? 0 : boundary,
EX_NOWAIT | EX_BOUNDZERO, (u_long *)&dvmaddr);
@@ -956,6 +957,7 @@ iommu_dvmamap_load_raw(bus_dma_tag_t t,
* If our segment size is larger than the boundary we need to
* split the transfer up into little pieces ourselves.
*/
+ extent_supply_region_descriptor(is->is_dvmamap, &ims->ims_er);
err = extent_alloc_subregion(is->is_dvmamap, sgstart, sgend,
sgsize, align, 0, (sgsize > boundary) ? 0 : boundary,
EX_NOWAIT | EX_BOUNDZERO, (u_long *)&dvmaddr);
Index: arch/sparc64/dev/iommuvar.h
===================================================================
RCS file: /cvs/src/sys/arch/sparc64/dev/iommuvar.h,v
retrieving revision 1.15
diff -u -p -r1.15 iommuvar.h
--- arch/sparc64/dev/iommuvar.h 4 May 2009 16:48:37 -0000 1.15
+++ arch/sparc64/dev/iommuvar.h 20 Jan 2014 04:01:45 -0000
@@ -37,6 +37,7 @@
#include <sys/tree.h>
#endif
+#include <sys/extent.h>
#include <sys/mutex.h>
/*
@@ -94,6 +95,7 @@ struct iommu_map_state {
struct strbuf_ctl *ims_sb; /* Link to parent */
struct iommu_state *ims_iommu;
int ims_flags;
+ struct extent_region ims_er;
struct iommu_page_map ims_map; /* map must be last (array at end) */
};
#define IOMMU_MAP_STREAM 1
@@ -110,6 +112,7 @@ struct iommu_state {
int64_t is_cr; /* Control register value */
struct mutex is_mtx;
struct extent *is_dvmamap; /* DVMA map for this instance */
+ struct extent_fixed is_fex;
int is_flags;
#define IOMMU_FLUSH_CACHE 0x00000001
Index: arch/sparc64/dev/viommu.c
===================================================================
RCS file: /cvs/src/sys/arch/sparc64/dev/viommu.c,v
retrieving revision 1.13
diff -u -p -r1.13 viommu.c
--- arch/sparc64/dev/viommu.c 15 Jan 2013 03:14:01 -0000 1.13
+++ arch/sparc64/dev/viommu.c 20 Jan 2014 04:04:15 -0000
@@ -127,7 +127,7 @@ viommu_init(char *name, struct iommu_sta
printf("dvma map %x-%x", is->is_dvmabase, is->is_dvmaend);
is->is_dvmamap = extent_create(name,
is->is_dvmabase, (u_long)is->is_dvmaend + 1,
- M_DEVBUF, 0, 0, EX_NOWAIT);
+ M_DEVBUF, &is->is_fex, sizeof(is->is_fex), EX_NOCOALESCE);
mtx_init(&is->is_mtx, IPL_HIGH);
printf("\n");
@@ -356,6 +356,7 @@ viommu_dvmamap_load(bus_dma_tag_t t, bus
* If our segment size is larger than the boundary we need to
* split the transfer up into little pieces ourselves.
*/
+ extent_supply_region_descriptor(is->is_dvmamap, &ims->ims_er);
err = extent_alloc_subregion(is->is_dvmamap, sgstart, sgend,
sgsize, align, 0, (sgsize > boundary) ? 0 : boundary,
EX_NOWAIT | EX_BOUNDZERO, (u_long *)&dvmaddr);
@@ -539,6 +540,7 @@ viommu_dvmamap_load_raw(bus_dma_tag_t t,
* If our segment size is larger than the boundary we need to
* split the transfer up into little pieces ourselves.
*/
+ extent_supply_region_descriptor(is->is_dvmamap, &ims->ims_er);
err = extent_alloc_subregion(is->is_dvmamap, sgstart, sgend,
sgsize, align, 0, (sgsize > boundary) ? 0 : boundary,
EX_NOWAIT | EX_BOUNDZERO, (u_long *)&dvmaddr);