diff options
Diffstat (limited to 'drivers/dax/dax.c')
-rw-r--r-- | drivers/dax/dax.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c index 26ec39ddf21f..ed758b74ddf0 100644 --- a/drivers/dax/dax.c +++ b/drivers/dax/dax.c | |||
@@ -75,6 +75,73 @@ struct dax_dev { | |||
75 | struct resource res[0]; | 75 | struct resource res[0]; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | static ssize_t id_show(struct device *dev, | ||
79 | struct device_attribute *attr, char *buf) | ||
80 | { | ||
81 | struct dax_region *dax_region; | ||
82 | ssize_t rc = -ENXIO; | ||
83 | |||
84 | device_lock(dev); | ||
85 | dax_region = dev_get_drvdata(dev); | ||
86 | if (dax_region) | ||
87 | rc = sprintf(buf, "%d\n", dax_region->id); | ||
88 | device_unlock(dev); | ||
89 | |||
90 | return rc; | ||
91 | } | ||
92 | static DEVICE_ATTR_RO(id); | ||
93 | |||
94 | static ssize_t region_size_show(struct device *dev, | ||
95 | struct device_attribute *attr, char *buf) | ||
96 | { | ||
97 | struct dax_region *dax_region; | ||
98 | ssize_t rc = -ENXIO; | ||
99 | |||
100 | device_lock(dev); | ||
101 | dax_region = dev_get_drvdata(dev); | ||
102 | if (dax_region) | ||
103 | rc = sprintf(buf, "%llu\n", (unsigned long long) | ||
104 | resource_size(&dax_region->res)); | ||
105 | device_unlock(dev); | ||
106 | |||
107 | return rc; | ||
108 | } | ||
109 | static struct device_attribute dev_attr_region_size = __ATTR(size, 0444, | ||
110 | region_size_show, NULL); | ||
111 | |||
112 | static ssize_t align_show(struct device *dev, | ||
113 | struct device_attribute *attr, char *buf) | ||
114 | { | ||
115 | struct dax_region *dax_region; | ||
116 | ssize_t rc = -ENXIO; | ||
117 | |||
118 | device_lock(dev); | ||
119 | dax_region = dev_get_drvdata(dev); | ||
120 | if (dax_region) | ||
121 | rc = sprintf(buf, "%u\n", dax_region->align); | ||
122 | device_unlock(dev); | ||
123 | |||
124 | return rc; | ||
125 | } | ||
126 | static DEVICE_ATTR_RO(align); | ||
127 | |||
128 | static struct attribute *dax_region_attributes[] = { | ||
129 | &dev_attr_region_size.attr, | ||
130 | &dev_attr_align.attr, | ||
131 | &dev_attr_id.attr, | ||
132 | NULL, | ||
133 | }; | ||
134 | |||
135 | static const struct attribute_group dax_region_attribute_group = { | ||
136 | .name = "dax_region", | ||
137 | .attrs = dax_region_attributes, | ||
138 | }; | ||
139 | |||
140 | static const struct attribute_group *dax_region_attribute_groups[] = { | ||
141 | &dax_region_attribute_group, | ||
142 | NULL, | ||
143 | }; | ||
144 | |||
78 | static struct inode *dax_alloc_inode(struct super_block *sb) | 145 | static struct inode *dax_alloc_inode(struct super_block *sb) |
79 | { | 146 | { |
80 | return kmem_cache_alloc(dax_cache, GFP_KERNEL); | 147 | return kmem_cache_alloc(dax_cache, GFP_KERNEL); |
@@ -200,12 +267,31 @@ void dax_region_put(struct dax_region *dax_region) | |||
200 | } | 267 | } |
201 | EXPORT_SYMBOL_GPL(dax_region_put); | 268 | EXPORT_SYMBOL_GPL(dax_region_put); |
202 | 269 | ||
270 | static void dax_region_unregister(void *region) | ||
271 | { | ||
272 | struct dax_region *dax_region = region; | ||
273 | |||
274 | sysfs_remove_groups(&dax_region->dev->kobj, | ||
275 | dax_region_attribute_groups); | ||
276 | dax_region_put(dax_region); | ||
277 | } | ||
278 | |||
203 | struct dax_region *alloc_dax_region(struct device *parent, int region_id, | 279 | struct dax_region *alloc_dax_region(struct device *parent, int region_id, |
204 | struct resource *res, unsigned int align, void *addr, | 280 | struct resource *res, unsigned int align, void *addr, |
205 | unsigned long pfn_flags) | 281 | unsigned long pfn_flags) |
206 | { | 282 | { |
207 | struct dax_region *dax_region; | 283 | struct dax_region *dax_region; |
208 | 284 | ||
285 | /* | ||
286 | * The DAX core assumes that it can store its private data in | ||
287 | * parent->driver_data. This WARN is a reminder / safeguard for | ||
288 | * developers of device-dax drivers. | ||
289 | */ | ||
290 | if (dev_get_drvdata(parent)) { | ||
291 | dev_WARN(parent, "dax core failed to setup private data\n"); | ||
292 | return NULL; | ||
293 | } | ||
294 | |||
209 | if (!IS_ALIGNED(res->start, align) | 295 | if (!IS_ALIGNED(res->start, align) |
210 | || !IS_ALIGNED(resource_size(res), align)) | 296 | || !IS_ALIGNED(resource_size(res), align)) |
211 | return NULL; | 297 | return NULL; |
@@ -214,6 +300,7 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, | |||
214 | if (!dax_region) | 300 | if (!dax_region) |
215 | return NULL; | 301 | return NULL; |
216 | 302 | ||
303 | dev_set_drvdata(parent, dax_region); | ||
217 | memcpy(&dax_region->res, res, sizeof(*res)); | 304 | memcpy(&dax_region->res, res, sizeof(*res)); |
218 | dax_region->pfn_flags = pfn_flags; | 305 | dax_region->pfn_flags = pfn_flags; |
219 | kref_init(&dax_region->kref); | 306 | kref_init(&dax_region->kref); |
@@ -222,7 +309,14 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, | |||
222 | dax_region->align = align; | 309 | dax_region->align = align; |
223 | dax_region->dev = parent; | 310 | dax_region->dev = parent; |
224 | dax_region->base = addr; | 311 | dax_region->base = addr; |
312 | if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { | ||
313 | kfree(dax_region); | ||
314 | return NULL;; | ||
315 | } | ||
225 | 316 | ||
317 | kref_get(&dax_region->kref); | ||
318 | if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region)) | ||
319 | return NULL; | ||
226 | return dax_region; | 320 | return dax_region; |
227 | } | 321 | } |
228 | EXPORT_SYMBOL_GPL(alloc_dax_region); | 322 | EXPORT_SYMBOL_GPL(alloc_dax_region); |