diff options
author | Keith Busch <keith.busch@intel.com> | 2013-02-19 12:17:58 -0500 |
---|---|---|
committer | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2013-04-16 15:43:55 -0400 |
commit | 5e82e952f04681c10f35e02ee0a4a43ec027137a (patch) | |
tree | ac827583d123873bf5fbfdeb2b6b68389a1322af | |
parent | 1c9b52651dad0ff1fa71fc6205c86d972f25bcc0 (diff) |
NVMe: Add a character device for each nvme device
Registers a miscellaneous device for each nvme controller probed. This
creates character device files as /dev/nvmeN, where N is the device
instance, and supports nvme admin ioctl commands so devices without
namespaces can be managed.
Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
-rw-r--r-- | drivers/block/nvme-core.c | 74 | ||||
-rw-r--r-- | include/linux/nvme.h | 5 |
2 files changed, 69 insertions, 10 deletions
diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index dbd2103533c1..40908a06bd5e 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c | |||
@@ -1647,6 +1647,56 @@ static void nvme_release_instance(struct nvme_dev *dev) | |||
1647 | spin_unlock(&dev_list_lock); | 1647 | spin_unlock(&dev_list_lock); |
1648 | } | 1648 | } |
1649 | 1649 | ||
1650 | static void nvme_free_dev(struct kref *kref) | ||
1651 | { | ||
1652 | struct nvme_dev *dev = container_of(kref, struct nvme_dev, kref); | ||
1653 | nvme_dev_remove(dev); | ||
1654 | pci_disable_msix(dev->pci_dev); | ||
1655 | iounmap(dev->bar); | ||
1656 | nvme_release_instance(dev); | ||
1657 | nvme_release_prp_pools(dev); | ||
1658 | pci_disable_device(dev->pci_dev); | ||
1659 | pci_release_regions(dev->pci_dev); | ||
1660 | kfree(dev->queues); | ||
1661 | kfree(dev->entry); | ||
1662 | kfree(dev); | ||
1663 | } | ||
1664 | |||
1665 | static int nvme_dev_open(struct inode *inode, struct file *f) | ||
1666 | { | ||
1667 | struct nvme_dev *dev = container_of(f->private_data, struct nvme_dev, | ||
1668 | miscdev); | ||
1669 | kref_get(&dev->kref); | ||
1670 | f->private_data = dev; | ||
1671 | return 0; | ||
1672 | } | ||
1673 | |||
1674 | static int nvme_dev_release(struct inode *inode, struct file *f) | ||
1675 | { | ||
1676 | struct nvme_dev *dev = f->private_data; | ||
1677 | kref_put(&dev->kref, nvme_free_dev); | ||
1678 | return 0; | ||
1679 | } | ||
1680 | |||
1681 | static long nvme_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg) | ||
1682 | { | ||
1683 | struct nvme_dev *dev = f->private_data; | ||
1684 | switch (cmd) { | ||
1685 | case NVME_IOCTL_ADMIN_CMD: | ||
1686 | return nvme_user_admin_cmd(dev, (void __user *)arg); | ||
1687 | default: | ||
1688 | return -ENOTTY; | ||
1689 | } | ||
1690 | } | ||
1691 | |||
1692 | static const struct file_operations nvme_dev_fops = { | ||
1693 | .owner = THIS_MODULE, | ||
1694 | .open = nvme_dev_open, | ||
1695 | .release = nvme_dev_release, | ||
1696 | .unlocked_ioctl = nvme_dev_ioctl, | ||
1697 | .compat_ioctl = nvme_dev_ioctl, | ||
1698 | }; | ||
1699 | |||
1650 | static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) | 1700 | static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
1651 | { | 1701 | { |
1652 | int bars, result = -ENOMEM; | 1702 | int bars, result = -ENOMEM; |
@@ -1705,8 +1755,20 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1705 | if (result) | 1755 | if (result) |
1706 | goto delete; | 1756 | goto delete; |
1707 | 1757 | ||
1758 | scnprintf(dev->name, sizeof(dev->name), "nvme%d", dev->instance); | ||
1759 | dev->miscdev.minor = MISC_DYNAMIC_MINOR; | ||
1760 | dev->miscdev.parent = &pdev->dev; | ||
1761 | dev->miscdev.name = dev->name; | ||
1762 | dev->miscdev.fops = &nvme_dev_fops; | ||
1763 | result = misc_register(&dev->miscdev); | ||
1764 | if (result) | ||
1765 | goto remove; | ||
1766 | |||
1767 | kref_init(&dev->kref); | ||
1708 | return 0; | 1768 | return 0; |
1709 | 1769 | ||
1770 | remove: | ||
1771 | nvme_dev_remove(dev); | ||
1710 | delete: | 1772 | delete: |
1711 | spin_lock(&dev_list_lock); | 1773 | spin_lock(&dev_list_lock); |
1712 | list_del(&dev->node); | 1774 | list_del(&dev->node); |
@@ -1732,16 +1794,8 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1732 | static void nvme_remove(struct pci_dev *pdev) | 1794 | static void nvme_remove(struct pci_dev *pdev) |
1733 | { | 1795 | { |
1734 | struct nvme_dev *dev = pci_get_drvdata(pdev); | 1796 | struct nvme_dev *dev = pci_get_drvdata(pdev); |
1735 | nvme_dev_remove(dev); | 1797 | misc_deregister(&dev->miscdev); |
1736 | pci_disable_msix(pdev); | 1798 | kref_put(&dev->kref, nvme_free_dev); |
1737 | iounmap(dev->bar); | ||
1738 | nvme_release_instance(dev); | ||
1739 | nvme_release_prp_pools(dev); | ||
1740 | pci_disable_device(pdev); | ||
1741 | pci_release_regions(pdev); | ||
1742 | kfree(dev->queues); | ||
1743 | kfree(dev->entry); | ||
1744 | kfree(dev); | ||
1745 | } | 1799 | } |
1746 | 1800 | ||
1747 | /* These functions are yet to be implemented */ | 1801 | /* These functions are yet to be implemented */ |
diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 7ae7ecfc0947..9b6fba872f47 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h | |||
@@ -507,6 +507,8 @@ struct nvme_admin_cmd { | |||
507 | 507 | ||
508 | #ifdef __KERNEL__ | 508 | #ifdef __KERNEL__ |
509 | #include <linux/pci.h> | 509 | #include <linux/pci.h> |
510 | #include <linux/miscdevice.h> | ||
511 | #include <linux/kref.h> | ||
510 | 512 | ||
511 | #define NVME_IO_TIMEOUT (5 * HZ) | 513 | #define NVME_IO_TIMEOUT (5 * HZ) |
512 | 514 | ||
@@ -527,6 +529,9 @@ struct nvme_dev { | |||
527 | struct msix_entry *entry; | 529 | struct msix_entry *entry; |
528 | struct nvme_bar __iomem *bar; | 530 | struct nvme_bar __iomem *bar; |
529 | struct list_head namespaces; | 531 | struct list_head namespaces; |
532 | struct kref kref; | ||
533 | struct miscdevice miscdev; | ||
534 | char name[12]; | ||
530 | char serial[20]; | 535 | char serial[20]; |
531 | char model[40]; | 536 | char model[40]; |
532 | char firmware_rev[8]; | 537 | char firmware_rev[8]; |