Commit a834a36b authored by Uma Krishnan's avatar Uma Krishnan Committed by Martin K. Petersen

scsi: cxlflash: Create character device to provide host management interface

The cxlflash driver currently lacks host management interface. Future
devices supported by cxlflash will provide a variety of host-wide
management functions. Examples include LUN provisioning, hardware debug
support, and firmware download.

In order to provide a way to manage the device, a character device will
be created during probe of each adapter. This device will support a set of
ioctls defined in the SISLite specification from which administrators can
manage the adapter.
Signed-off-by: default avatarUma Krishnan <ukrishn@linux.vnet.ibm.com>
Acked-by: default avatarMatthew R. Ochs <mrochs@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 7c4c41f1
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#define _CXLFLASH_COMMON_H #define _CXLFLASH_COMMON_H
#include <linux/async.h> #include <linux/async.h>
#include <linux/cdev.h>
#include <linux/irq_poll.h> #include <linux/irq_poll.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
...@@ -86,7 +87,8 @@ enum cxlflash_init_state { ...@@ -86,7 +87,8 @@ enum cxlflash_init_state {
INIT_STATE_NONE, INIT_STATE_NONE,
INIT_STATE_PCI, INIT_STATE_PCI,
INIT_STATE_AFU, INIT_STATE_AFU,
INIT_STATE_SCSI INIT_STATE_SCSI,
INIT_STATE_CDEV
}; };
enum cxlflash_state { enum cxlflash_state {
...@@ -116,6 +118,8 @@ struct cxlflash_cfg { ...@@ -116,6 +118,8 @@ struct cxlflash_cfg {
struct pci_device_id *dev_id; struct pci_device_id *dev_id;
struct Scsi_Host *host; struct Scsi_Host *host;
int num_fc_ports; int num_fc_ports;
struct cdev cdev;
struct device *chardev;
ulong cxlflash_regs_pci; ulong cxlflash_regs_pci;
......
...@@ -34,6 +34,10 @@ MODULE_AUTHOR("Manoj N. Kumar <manoj@linux.vnet.ibm.com>"); ...@@ -34,6 +34,10 @@ MODULE_AUTHOR("Manoj N. Kumar <manoj@linux.vnet.ibm.com>");
MODULE_AUTHOR("Matthew R. Ochs <mrochs@linux.vnet.ibm.com>"); MODULE_AUTHOR("Matthew R. Ochs <mrochs@linux.vnet.ibm.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static struct class *cxlflash_class;
static u32 cxlflash_major;
static DECLARE_BITMAP(cxlflash_minor, CXLFLASH_MAX_ADAPTERS);
/** /**
* process_cmd_err() - command error handler * process_cmd_err() - command error handler
* @cmd: AFU command that experienced the error. * @cmd: AFU command that experienced the error.
...@@ -862,6 +866,47 @@ static void notify_shutdown(struct cxlflash_cfg *cfg, bool wait) ...@@ -862,6 +866,47 @@ static void notify_shutdown(struct cxlflash_cfg *cfg, bool wait)
} }
} }
/**
* cxlflash_get_minor() - gets the first available minor number
*
* Return: Unique minor number that can be used to create the character device.
*/
static int cxlflash_get_minor(void)
{
int minor;
long bit;
bit = find_first_zero_bit(cxlflash_minor, CXLFLASH_MAX_ADAPTERS);
if (bit >= CXLFLASH_MAX_ADAPTERS)
return -1;
minor = bit & MINORMASK;
set_bit(minor, cxlflash_minor);
return minor;
}
/**
* cxlflash_put_minor() - releases the minor number
* @minor: Minor number that is no longer needed.
*/
static void cxlflash_put_minor(int minor)
{
clear_bit(minor, cxlflash_minor);
}
/**
* cxlflash_release_chrdev() - release the character device for the host
* @cfg: Internal structure associated with the host.
*/
static void cxlflash_release_chrdev(struct cxlflash_cfg *cfg)
{
put_device(cfg->chardev);
device_unregister(cfg->chardev);
cfg->chardev = NULL;
cdev_del(&cfg->cdev);
cxlflash_put_minor(MINOR(cfg->cdev.dev));
}
/** /**
* cxlflash_remove() - PCI entry point to tear down host * cxlflash_remove() - PCI entry point to tear down host
* @pdev: PCI device associated with the host. * @pdev: PCI device associated with the host.
...@@ -897,6 +942,8 @@ static void cxlflash_remove(struct pci_dev *pdev) ...@@ -897,6 +942,8 @@ static void cxlflash_remove(struct pci_dev *pdev)
cxlflash_stop_term_user_contexts(cfg); cxlflash_stop_term_user_contexts(cfg);
switch (cfg->init_state) { switch (cfg->init_state) {
case INIT_STATE_CDEV:
cxlflash_release_chrdev(cfg);
case INIT_STATE_SCSI: case INIT_STATE_SCSI:
cxlflash_term_local_luns(cfg); cxlflash_term_local_luns(cfg);
scsi_remove_host(cfg->host); scsi_remove_host(cfg->host);
...@@ -3119,6 +3166,86 @@ static void cxlflash_worker_thread(struct work_struct *work) ...@@ -3119,6 +3166,86 @@ static void cxlflash_worker_thread(struct work_struct *work)
scsi_scan_host(cfg->host); scsi_scan_host(cfg->host);
} }
/**
* cxlflash_chr_open() - character device open handler
* @inode: Device inode associated with this character device.
* @file: File pointer for this device.
*
* Only users with admin privileges are allowed to open the character device.
*
* Return: 0 on success, -errno on failure
*/
static int cxlflash_chr_open(struct inode *inode, struct file *file)
{
struct cxlflash_cfg *cfg;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
cfg = container_of(inode->i_cdev, struct cxlflash_cfg, cdev);
file->private_data = cfg;
return 0;
}
/*
* Character device file operations
*/
static const struct file_operations cxlflash_chr_fops = {
.owner = THIS_MODULE,
.open = cxlflash_chr_open,
};
/**
* init_chrdev() - initialize the character device for the host
* @cfg: Internal structure associated with the host.
*
* Return: 0 on success, -errno on failure
*/
static int init_chrdev(struct cxlflash_cfg *cfg)
{
struct device *dev = &cfg->dev->dev;
struct device *char_dev;
dev_t devno;
int minor;
int rc = 0;
minor = cxlflash_get_minor();
if (unlikely(minor < 0)) {
dev_err(dev, "%s: Exhausted allowed adapters\n", __func__);
rc = -ENOSPC;
goto out;
}
devno = MKDEV(cxlflash_major, minor);
cdev_init(&cfg->cdev, &cxlflash_chr_fops);
rc = cdev_add(&cfg->cdev, devno, 1);
if (rc) {
dev_err(dev, "%s: cdev_add failed rc=%d\n", __func__, rc);
goto err1;
}
char_dev = device_create(cxlflash_class, NULL, devno,
NULL, "cxlflash%d", minor);
if (IS_ERR(char_dev)) {
rc = PTR_ERR(char_dev);
dev_err(dev, "%s: device_create failed rc=%d\n",
__func__, rc);
goto err2;
}
cfg->chardev = char_dev;
out:
dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
return rc;
err2:
cdev_del(&cfg->cdev);
err1:
cxlflash_put_minor(minor);
goto out;
}
/** /**
* cxlflash_probe() - PCI entry point to add host * cxlflash_probe() - PCI entry point to add host
* @pdev: PCI device associated with the host. * @pdev: PCI device associated with the host.
...@@ -3229,6 +3356,13 @@ static int cxlflash_probe(struct pci_dev *pdev, ...@@ -3229,6 +3356,13 @@ static int cxlflash_probe(struct pci_dev *pdev,
} }
cfg->init_state = INIT_STATE_SCSI; cfg->init_state = INIT_STATE_SCSI;
rc = init_chrdev(cfg);
if (rc) {
dev_err(dev, "%s: init_chrdev failed rc=%d\n", __func__, rc);
goto out_remove;
}
cfg->init_state = INIT_STATE_CDEV;
if (wq_has_sleeper(&cfg->reset_waitq)) { if (wq_has_sleeper(&cfg->reset_waitq)) {
cfg->state = STATE_PROBED; cfg->state = STATE_PROBED;
wake_up_all(&cfg->reset_waitq); wake_up_all(&cfg->reset_waitq);
...@@ -3331,6 +3465,63 @@ static void cxlflash_pci_resume(struct pci_dev *pdev) ...@@ -3331,6 +3465,63 @@ static void cxlflash_pci_resume(struct pci_dev *pdev)
scsi_unblock_requests(cfg->host); scsi_unblock_requests(cfg->host);
} }
/**
* cxlflash_devnode() - provides devtmpfs for devices in the cxlflash class
* @dev: Character device.
* @mode: Mode that can be used to verify access.
*
* Return: Allocated string describing the devtmpfs structure.
*/
static char *cxlflash_devnode(struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "cxlflash/%s", dev_name(dev));
}
/**
* cxlflash_class_init() - create character device class
*
* Return: 0 on success, -errno on failure
*/
static int cxlflash_class_init(void)
{
dev_t devno;
int rc = 0;
rc = alloc_chrdev_region(&devno, 0, CXLFLASH_MAX_ADAPTERS, "cxlflash");
if (unlikely(rc)) {
pr_err("%s: alloc_chrdev_region failed rc=%d\n", __func__, rc);
goto out;
}
cxlflash_major = MAJOR(devno);
cxlflash_class = class_create(THIS_MODULE, "cxlflash");
if (IS_ERR(cxlflash_class)) {
rc = PTR_ERR(cxlflash_class);
pr_err("%s: class_create failed rc=%d\n", __func__, rc);
goto err;
}
cxlflash_class->devnode = cxlflash_devnode;
out:
pr_debug("%s: returning rc=%d\n", __func__, rc);
return rc;
err:
unregister_chrdev_region(devno, CXLFLASH_MAX_ADAPTERS);
goto out;
}
/**
* cxlflash_class_exit() - destroy character device class
*/
static void cxlflash_class_exit(void)
{
dev_t devno = MKDEV(cxlflash_major, 0);
class_destroy(cxlflash_class);
unregister_chrdev_region(devno, CXLFLASH_MAX_ADAPTERS);
}
static const struct pci_error_handlers cxlflash_err_handler = { static const struct pci_error_handlers cxlflash_err_handler = {
.error_detected = cxlflash_pci_error_detected, .error_detected = cxlflash_pci_error_detected,
.slot_reset = cxlflash_pci_slot_reset, .slot_reset = cxlflash_pci_slot_reset,
...@@ -3356,10 +3547,23 @@ static struct pci_driver cxlflash_driver = { ...@@ -3356,10 +3547,23 @@ static struct pci_driver cxlflash_driver = {
*/ */
static int __init init_cxlflash(void) static int __init init_cxlflash(void)
{ {
int rc;
check_sizes(); check_sizes();
cxlflash_list_init(); cxlflash_list_init();
rc = cxlflash_class_init();
if (unlikely(rc))
goto out;
return pci_register_driver(&cxlflash_driver); rc = pci_register_driver(&cxlflash_driver);
if (unlikely(rc))
goto err;
out:
pr_debug("%s: returning rc=%d\n", __func__, rc);
return rc;
err:
cxlflash_class_exit();
goto out;
} }
/** /**
...@@ -3371,6 +3575,7 @@ static void __exit exit_cxlflash(void) ...@@ -3371,6 +3575,7 @@ static void __exit exit_cxlflash(void)
cxlflash_free_errpage(); cxlflash_free_errpage();
pci_unregister_driver(&cxlflash_driver); pci_unregister_driver(&cxlflash_driver);
cxlflash_class_exit();
} }
module_init(init_cxlflash); module_init(init_cxlflash);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#define CXLFLASH_NAME "cxlflash" #define CXLFLASH_NAME "cxlflash"
#define CXLFLASH_ADAPTER_NAME "IBM POWER CXL Flash Adapter" #define CXLFLASH_ADAPTER_NAME "IBM POWER CXL Flash Adapter"
#define CXLFLASH_MAX_ADAPTERS 32
#define PCI_DEVICE_ID_IBM_CORSA 0x04F0 #define PCI_DEVICE_ID_IBM_CORSA 0x04F0
#define PCI_DEVICE_ID_IBM_FLASH_GT 0x0600 #define PCI_DEVICE_ID_IBM_FLASH_GT 0x0600
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment