#include <linux/module.h>
#include <linux/device.h>
#include <linux/pfn_t.h>
+#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/dax.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include "dax.h"
-static int dax_major;
+static dev_t dax_devt;
static struct class *dax_class;
static DEFINE_IDA(dax_minor_ida);
+static int nr_dax = CONFIG_NR_DEV_DAX;
+module_param(nr_dax, int, S_IRUGO);
+MODULE_PARM_DESC(nr_dax, "max number of device-dax instances");
/**
* struct dax_region - mapping infrastructure for dax devices
* struct dax_dev - subdivision of a dax region
* @region - parent region
* @dev - device backing the character device
+ * @cdev - core chardev data
* @alive - !alive + rcu grace period == no new mappings can be established
* @id - child id in the region
* @num_resources - number of physical address extents in this device
struct dax_dev {
struct dax_region *region;
struct device dev;
+ struct cdev cdev;
bool alive;
int id;
int num_resources;
return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags);
}
-static int __match_devt(struct device *dev, const void *data)
-{
- const dev_t *devt = data;
-
- return dev->devt == *devt;
-}
-
-static struct device *dax_dev_find(dev_t dev_t)
-{
- return class_find_device(dax_class, NULL, &dev_t, __match_devt);
-}
-
static int dax_open(struct inode *inode, struct file *filp)
{
- struct dax_dev *dax_dev = NULL;
- struct device *dev;
-
- dev = dax_dev_find(inode->i_rdev);
- if (!dev)
- return -ENXIO;
+ struct dax_dev *dax_dev;
- dax_dev = to_dax_dev(dev);
- dev_dbg(dev, "%s\n", __func__);
+ dax_dev = container_of(inode->i_cdev, struct dax_dev, cdev);
+ dev_dbg(&dax_dev->dev, "%s\n", __func__);
filp->private_data = dax_dev;
inode->i_flags = S_DAX;
static int dax_release(struct inode *inode, struct file *filp)
{
struct dax_dev *dax_dev = filp->private_data;
- struct device *dev = &dax_dev->dev;
-
- dev_dbg(dev, "%s\n", __func__);
- put_device(dev);
+ dev_dbg(&dax_dev->dev, "%s\n", __func__);
return 0;
}
static void unregister_dax_dev(void *dev)
{
struct dax_dev *dax_dev = to_dax_dev(dev);
+ struct cdev *cdev = &dax_dev->cdev;
dev_dbg(dev, "%s\n", __func__);
*/
dax_dev->alive = false;
synchronize_rcu();
+ cdev_del(cdev);
device_unregister(dev);
}
struct device *parent = dax_region->dev;
struct dax_dev *dax_dev;
struct device *dev;
+ struct cdev *cdev;
int rc, minor;
dev_t dev_t;
dax_dev = kzalloc(sizeof(*dax_dev) + sizeof(*res) * count, GFP_KERNEL);
if (!dax_dev)
return -ENOMEM;
- memcpy(dax_dev->res, res, sizeof(*res) * count);
- dax_dev->num_resources = count;
- dax_dev->alive = true;
- dax_dev->region = dax_region;
- kref_get(&dax_region->kref);
dax_dev->id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL);
if (dax_dev->id < 0) {
goto err_minor;
}
- dev_t = MKDEV(dax_major, minor);
-
+ /* device_initialize() so cdev can reference kobj parent */
+ dev_t = MKDEV(MAJOR(dax_devt), minor);
dev = &dax_dev->dev;
device_initialize(dev);
+
+ cdev = &dax_dev->cdev;
+ cdev_init(cdev, &dax_fops);
+ cdev->owner = parent->driver->owner;
+ cdev->kobj.parent = &dev->kobj;
+ rc = cdev_add(&dax_dev->cdev, dev_t, 1);
+ if (rc)
+ goto err_cdev;
+
+ /* from here on we're committed to teardown via dax_dev_release() */
+ memcpy(dax_dev->res, res, sizeof(*res) * count);
+ dax_dev->num_resources = count;
+ dax_dev->alive = true;
+ dax_dev->region = dax_region;
+ kref_get(&dax_region->kref);
+
dev->devt = dev_t;
dev->class = dax_class;
dev->parent = parent;
return devm_add_action_or_reset(dax_region->dev, unregister_dax_dev, dev);
+ err_cdev:
+ ida_simple_remove(&dax_minor_ida, minor);
err_minor:
ida_simple_remove(&dax_region->ida, dax_dev->id);
err_id:
{
int rc;
- rc = register_chrdev(0, "dax", &dax_fops);
- if (rc < 0)
+ nr_dax = max(nr_dax, 256);
+ rc = alloc_chrdev_region(&dax_devt, 0, nr_dax, "dax");
+ if (rc)
return rc;
- dax_major = rc;
dax_class = class_create(THIS_MODULE, "dax");
- if (IS_ERR(dax_class)) {
- unregister_chrdev(dax_major, "dax");
- return PTR_ERR(dax_class);
- }
+ if (IS_ERR(dax_class))
+ unregister_chrdev_region(dax_devt, nr_dax);
- return 0;
+ return PTR_ERR_OR_ZERO(dax_class);
}
static void __exit dax_exit(void)
{
class_destroy(dax_class);
- unregister_chrdev(dax_major, "dax");
+ unregister_chrdev_region(dax_devt, nr_dax);
ida_destroy(&dax_minor_ida);
}