Commit 80666096 authored by Todd Poynor's avatar Todd Poynor Committed by Greg Kroah-Hartman

staging: gasket: core: remove static function forward declarations

Remove forward declarations of static functions, move code to avoid
forward references, for kernel style.
Signed-off-by: default avatarTodd Poynor <toddpoynor@google.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5298ff58
......@@ -67,61 +67,6 @@ enum do_map_region_status {
DO_MAP_REGION_INVALID,
};
/* Function declarations; comments are with definitions. */
static int __init gasket_init(void);
static void __exit gasket_exit(void);
static int gasket_pci_probe(
struct pci_dev *pci_dev, const struct pci_device_id *id);
static void gasket_pci_remove(struct pci_dev *pci_dev);
static int gasket_setup_pci(struct pci_dev *pci_dev, struct gasket_dev *dev);
static void gasket_cleanup_pci(struct gasket_dev *dev);
static int gasket_map_pci_bar(struct gasket_dev *dev, int bar_num);
static void gasket_unmap_pci_bar(struct gasket_dev *dev, int bar_num);
static int gasket_alloc_dev(
struct gasket_internal_desc *internal_desc, struct device *dev,
struct gasket_dev **pdev, const char *kobj_name);
static void gasket_free_dev(struct gasket_dev *dev);
static int gasket_find_dev_slot(
struct gasket_internal_desc *internal_desc, const char *kobj_name);
static int gasket_add_cdev(
struct gasket_cdev_info *dev_info,
const struct file_operations *file_ops, struct module *owner);
static int gasket_enable_dev(
struct gasket_internal_desc *internal_desc,
struct gasket_dev *gasket_dev);
static void gasket_disable_dev(struct gasket_dev *gasket_dev);
static struct gasket_internal_desc *lookup_internal_desc(
struct pci_dev *pci_dev);
static ssize_t gasket_sysfs_data_show(
struct device *device, struct device_attribute *attr, char *buf);
static int gasket_mmap(struct file *filp, struct vm_area_struct *vma);
static int gasket_open(struct inode *inode, struct file *file);
static int gasket_release(struct inode *inode, struct file *file);
static long gasket_ioctl(struct file *filp, uint cmd, ulong arg);
static int gasket_mm_vma_bar_offset(
const struct gasket_dev *gasket_dev, const struct vm_area_struct *vma,
ulong *bar_offset);
static bool gasket_mm_get_mapping_addrs(
const struct gasket_mappable_region *region, ulong bar_offset,
ulong requested_length, struct gasket_mappable_region *mappable_region,
ulong *virt_offset);
static enum do_map_region_status do_map_region(
const struct gasket_dev *gasket_dev, struct vm_area_struct *vma,
struct gasket_mappable_region *map_region);
static int gasket_get_hw_status(struct gasket_dev *gasket_dev);
/* Global data definitions. */
/* Mutex - only for framework-wide data. Other data should be protected by
* finer-grained locks.
......@@ -157,48 +102,6 @@ enum gasket_sysfs_attribute_type {
ATTR_USER_MEM_RANGES
};
/* File operations for all Gasket devices. */
static const struct file_operations gasket_file_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.mmap = gasket_mmap,
.open = gasket_open,
.release = gasket_release,
.unlocked_ioctl = gasket_ioctl,
};
/* These attributes apply to all Gasket driver instances. */
static const struct gasket_sysfs_attribute gasket_sysfs_generic_attrs[] = {
GASKET_SYSFS_RO(bar_offsets, gasket_sysfs_data_show, ATTR_BAR_OFFSETS),
GASKET_SYSFS_RO(bar_sizes, gasket_sysfs_data_show, ATTR_BAR_SIZES),
GASKET_SYSFS_RO(driver_version, gasket_sysfs_data_show,
ATTR_DRIVER_VERSION),
GASKET_SYSFS_RO(framework_version, gasket_sysfs_data_show,
ATTR_FRAMEWORK_VERSION),
GASKET_SYSFS_RO(device_type, gasket_sysfs_data_show, ATTR_DEVICE_TYPE),
GASKET_SYSFS_RO(revision, gasket_sysfs_data_show,
ATTR_HARDWARE_REVISION),
GASKET_SYSFS_RO(pci_address, gasket_sysfs_data_show, ATTR_PCI_ADDRESS),
GASKET_SYSFS_RO(status, gasket_sysfs_data_show, ATTR_STATUS),
GASKET_SYSFS_RO(is_device_owned, gasket_sysfs_data_show,
ATTR_IS_DEVICE_OWNED),
GASKET_SYSFS_RO(device_owner, gasket_sysfs_data_show,
ATTR_DEVICE_OWNER),
GASKET_SYSFS_RO(write_open_count, gasket_sysfs_data_show,
ATTR_WRITE_OPEN_COUNT),
GASKET_SYSFS_RO(reset_count, gasket_sysfs_data_show, ATTR_RESET_COUNT),
GASKET_SYSFS_RO(user_mem_ranges, gasket_sysfs_data_show,
ATTR_USER_MEM_RANGES),
GASKET_END_OF_ATTR_ARRAY
};
MODULE_DESCRIPTION("Google Gasket driver framework");
MODULE_VERSION(GASKET_FRAMEWORK_VERSION);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rob Springer <rspringer@google.com>");
module_init(gasket_init);
module_exit(gasket_exit);
/* Perform a standard Gasket callback. */
static inline int check_and_invoke_callback(
struct gasket_dev *gasket_dev, int (*cb_function)(struct gasket_dev *))
......@@ -239,165 +142,43 @@ static int gasket_owned_by_current_tgid(struct gasket_cdev_info *info)
(info->ownership.owner == current->tgid));
}
static int __init gasket_init(void)
/*
* Find the next free gasket_internal_dev slot.
*
* Returns the located slot number on success or a negative number on failure.
*/
static int gasket_find_dev_slot(
struct gasket_internal_desc *internal_desc, const char *kobj_name)
{
int i;
pr_info("Performing one-time init of the Gasket framework.\n");
/* Check for duplicates and find a free slot. */
mutex_lock(&g_mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
g_descs[i].driver_desc = NULL;
mutex_init(&g_descs[i].mutex);
}
gasket_sysfs_init();
mutex_unlock(&g_mutex);
return 0;
}
static void __exit gasket_exit(void)
{
/* No deinit/dealloc needed at present. */
pr_info("Removing Gasket framework module.\n");
}
/* See gasket_core.h for description. */
int gasket_register_device(const struct gasket_driver_desc *driver_desc)
{
int i, ret;
int desc_idx = -1;
struct gasket_internal_desc *internal;
pr_info("Initializing Gasket framework device\n");
/* Check for duplicates and find a free slot. */
mutex_lock(&g_mutex);
mutex_lock(&internal_desc->mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
if (g_descs[i].driver_desc == driver_desc) {
pr_err("%s driver already loaded/registered\n",
driver_desc->name);
mutex_unlock(&g_mutex);
/* Search for a previous instance of this device. */
for (i = 0; i < GASKET_DEV_MAX; i++) {
if (internal_desc->devs[i] &&
strcmp(internal_desc->devs[i]->kobj_name, kobj_name) == 0) {
pr_err("Duplicate device %s\n", kobj_name);
mutex_unlock(&internal_desc->mutex);
return -EBUSY;
}
}
/* This and the above loop could be combined, but this reads easier. */
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
if (!g_descs[i].driver_desc) {
g_descs[i].driver_desc = driver_desc;
desc_idx = i;
/* Find a free device slot. */
for (i = 0; i < GASKET_DEV_MAX; i++) {
if (!internal_desc->devs[i])
break;
}
}
mutex_unlock(&g_mutex);
pr_info("Loaded %s driver, framework version %s\n",
driver_desc->name, GASKET_FRAMEWORK_VERSION);
if (desc_idx == -1) {
pr_err("Too many Gasket drivers loaded: %d\n",
GASKET_FRAMEWORK_DESC_MAX);
if (i == GASKET_DEV_MAX) {
pr_err("Too many registered devices; max %d\n", GASKET_DEV_MAX);
mutex_unlock(&internal_desc->mutex);
return -EBUSY;
}
/* Internal structure setup. */
pr_debug("Performing initial internal structure setup.\n");
internal = &g_descs[desc_idx];
mutex_init(&internal->mutex);
memset(internal->devs, 0, sizeof(struct gasket_dev *) * GASKET_DEV_MAX);
memset(&internal->pci, 0, sizeof(internal->pci));
internal->pci.name = driver_desc->name;
internal->pci.id_table = driver_desc->pci_id_table;
internal->pci.probe = gasket_pci_probe;
internal->pci.remove = gasket_pci_remove;
internal->class =
class_create(driver_desc->module, driver_desc->name);
if (IS_ERR(internal->class)) {
pr_err("Cannot register %s class [ret=%ld]\n",
driver_desc->name, PTR_ERR(internal->class));
ret = PTR_ERR(internal->class);
goto unregister_gasket_driver;
}
/*
* Not using pci_register_driver() (without underscores), as it
* depends on KBUILD_MODNAME, and this is a shared file.
*/
pr_debug("Registering PCI driver.\n");
ret = __pci_register_driver(
&internal->pci, driver_desc->module, driver_desc->name);
if (ret) {
pr_err("cannot register pci driver [ret=%d]\n", ret);
goto fail1;
}
pr_debug("Registering char driver.\n");
ret = register_chrdev_region(
MKDEV(driver_desc->major, driver_desc->minor), GASKET_DEV_MAX,
driver_desc->name);
if (ret) {
pr_err("cannot register char driver [ret=%d]\n", ret);
goto fail2;
}
pr_info("Driver registered successfully.\n");
return 0;
fail2:
pci_unregister_driver(&internal->pci);
fail1:
class_destroy(internal->class);
unregister_gasket_driver:
mutex_lock(&g_mutex);
g_descs[desc_idx].driver_desc = NULL;
mutex_unlock(&g_mutex);
return ret;
}
EXPORT_SYMBOL(gasket_register_device);
/* See gasket_core.h for description. */
void gasket_unregister_device(const struct gasket_driver_desc *driver_desc)
{
int i, desc_idx;
struct gasket_internal_desc *internal_desc = NULL;
mutex_lock(&g_mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
if (g_descs[i].driver_desc == driver_desc) {
internal_desc = &g_descs[i];
desc_idx = i;
break;
}
}
mutex_unlock(&g_mutex);
if (!internal_desc) {
pr_err("request to unregister unknown desc: %s, %d:%d\n",
driver_desc->name, driver_desc->major,
driver_desc->minor);
return;
}
unregister_chrdev_region(
MKDEV(driver_desc->major, driver_desc->minor), GASKET_DEV_MAX);
pci_unregister_driver(&internal_desc->pci);
class_destroy(internal_desc->class);
/* Finally, effectively "remove" the driver. */
mutex_lock(&g_mutex);
g_descs[desc_idx].driver_desc = NULL;
mutex_unlock(&g_mutex);
pr_info("removed %s driver\n", driver_desc->name);
mutex_unlock(&internal_desc->mutex);
return i;
}
EXPORT_SYMBOL(gasket_unregister_device);
/*
* Allocate and initialize a Gasket device structure, add the device to the
......@@ -474,265 +255,21 @@ static void gasket_free_dev(struct gasket_dev *gasket_dev)
}
/*
* Find the next free gasket_internal_dev slot.
* Maps the specified bar into kernel space.
*
* Returns the located slot number on success or a negative number on failure.
* Returns 0 on success, a negative error code otherwise.
* A zero-sized BAR will not be mapped, but is not an error.
*/
static int gasket_find_dev_slot(
struct gasket_internal_desc *internal_desc, const char *kobj_name)
static int gasket_map_pci_bar(struct gasket_dev *gasket_dev, int bar_num)
{
int i;
struct gasket_internal_desc *internal_desc = gasket_dev->internal_desc;
const struct gasket_driver_desc *driver_desc =
internal_desc->driver_desc;
ulong desc_bytes = driver_desc->bar_descriptions[bar_num].size;
int ret;
mutex_lock(&internal_desc->mutex);
/* Search for a previous instance of this device. */
for (i = 0; i < GASKET_DEV_MAX; i++) {
if (internal_desc->devs[i] &&
strcmp(internal_desc->devs[i]->kobj_name, kobj_name) == 0) {
pr_err("Duplicate device %s\n", kobj_name);
mutex_unlock(&internal_desc->mutex);
return -EBUSY;
}
}
/* Find a free device slot. */
for (i = 0; i < GASKET_DEV_MAX; i++) {
if (!internal_desc->devs[i])
break;
}
if (i == GASKET_DEV_MAX) {
pr_err("Too many registered devices; max %d\n", GASKET_DEV_MAX);
mutex_unlock(&internal_desc->mutex);
return -EBUSY;
}
mutex_unlock(&internal_desc->mutex);
return i;
}
/*
* PCI subsystem probe function.
*
* Called when a Gasket device is found. Allocates device metadata, maps device
* memory, and calls gasket_enable_dev to prepare the device for active use.
*
* Returns 0 if successful and a negative value otherwise.
*/
static int gasket_pci_probe(
struct pci_dev *pci_dev, const struct pci_device_id *id)
{
int ret;
const char *kobj_name = dev_name(&pci_dev->dev);
struct gasket_internal_desc *internal_desc;
struct gasket_dev *gasket_dev;
const struct gasket_driver_desc *driver_desc;
struct device *parent;
pr_info("Add Gasket device %s\n", kobj_name);
mutex_lock(&g_mutex);
internal_desc = lookup_internal_desc(pci_dev);
mutex_unlock(&g_mutex);
if (!internal_desc) {
pr_err("PCI probe called for unknown driver type\n");
return -ENODEV;
}
driver_desc = internal_desc->driver_desc;
parent = &pci_dev->dev;
ret = gasket_alloc_dev(internal_desc, parent, &gasket_dev, kobj_name);
if (ret)
return ret;
gasket_dev->pci_dev = pci_dev_get(pci_dev);
if (IS_ERR_OR_NULL(gasket_dev->dev_info.device)) {
pr_err("Cannot create %s device %s [ret = %ld]\n",
driver_desc->name, gasket_dev->dev_info.name,
PTR_ERR(gasket_dev->dev_info.device));
ret = -ENODEV;
goto fail1;
}
ret = gasket_setup_pci(pci_dev, gasket_dev);
if (ret)
goto fail2;
ret = check_and_invoke_callback(gasket_dev, driver_desc->add_dev_cb);
if (ret) {
dev_err(gasket_dev->dev, "Error in add device cb: %d\n", ret);
goto fail2;
}
ret = gasket_sysfs_create_mapping(
gasket_dev->dev_info.device, gasket_dev);
if (ret)
goto fail3;
/*
* Once we've created the mapping structures successfully, attempt to
* create a symlink to the pci directory of this object.
*/
ret = sysfs_create_link(&gasket_dev->dev_info.device->kobj,
&pci_dev->dev.kobj, dev_name(&pci_dev->dev));
if (ret) {
dev_err(gasket_dev->dev,
"Cannot create sysfs pci link: %d\n", ret);
goto fail3;
}
ret = gasket_sysfs_create_entries(
gasket_dev->dev_info.device, gasket_sysfs_generic_attrs);
if (ret)
goto fail4;
ret = check_and_invoke_callback(
gasket_dev, driver_desc->sysfs_setup_cb);
if (ret) {
dev_err(gasket_dev->dev, "Error in sysfs setup cb: %d\n", ret);
goto fail5;
}
ret = gasket_enable_dev(internal_desc, gasket_dev);
if (ret) {
pr_err("cannot setup %s device\n", driver_desc->name);
gasket_disable_dev(gasket_dev);
goto fail5;
}
return 0;
fail5:
check_and_invoke_callback(gasket_dev, driver_desc->sysfs_cleanup_cb);
fail4:
fail3:
gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
fail2:
gasket_cleanup_pci(gasket_dev);
check_and_invoke_callback(gasket_dev, driver_desc->remove_dev_cb);
device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
fail1:
gasket_free_dev(gasket_dev);
return ret;
}
/*
* PCI subsystem remove function.
*
* Called to remove a Gasket device. Finds the device in the device list and
* cleans up metadata.
*/
static void gasket_pci_remove(struct pci_dev *pci_dev)
{
int i;
struct gasket_internal_desc *internal_desc;
struct gasket_dev *gasket_dev = NULL;
const struct gasket_driver_desc *driver_desc;
/* Find the device desc. */
mutex_lock(&g_mutex);
internal_desc = lookup_internal_desc(pci_dev);
if (!internal_desc) {
mutex_unlock(&g_mutex);
return;
}
mutex_unlock(&g_mutex);
driver_desc = internal_desc->driver_desc;
/* Now find the specific device */
mutex_lock(&internal_desc->mutex);
for (i = 0; i < GASKET_DEV_MAX; i++) {
if (internal_desc->devs[i] &&
internal_desc->devs[i]->pci_dev == pci_dev) {
gasket_dev = internal_desc->devs[i];
break;
}
}
mutex_unlock(&internal_desc->mutex);
if (!gasket_dev)
return;
pr_info("remove %s device %s\n", internal_desc->driver_desc->name,
gasket_dev->kobj_name);
gasket_disable_dev(gasket_dev);
gasket_cleanup_pci(gasket_dev);
check_and_invoke_callback(gasket_dev, driver_desc->sysfs_cleanup_cb);
gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
check_and_invoke_callback(gasket_dev, driver_desc->remove_dev_cb);
device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
gasket_free_dev(gasket_dev);
}
/*
* Setup PCI & set up memory mapping for the specified device.
*
* Enables the PCI device, reads the BAR registers and sets up pointers to the
* device's memory mapped IO space.
*
* Returns 0 on success and a negative value otherwise.
*/
static int gasket_setup_pci(
struct pci_dev *pci_dev, struct gasket_dev *gasket_dev)
{
int i, mapped_bars, ret;
ret = pci_enable_device(pci_dev);
if (ret) {
dev_err(gasket_dev->dev, "cannot enable PCI device\n");
return ret;
}
pci_set_master(pci_dev);
for (i = 0; i < GASKET_NUM_BARS; i++) {
ret = gasket_map_pci_bar(gasket_dev, i);
if (ret) {
mapped_bars = i;
goto fail;
}
}
return 0;
fail:
for (i = 0; i < mapped_bars; i++)
gasket_unmap_pci_bar(gasket_dev, i);
pci_disable_device(pci_dev);
return -ENOMEM;
}
/* Unmaps memory and cleans up PCI for the specified device. */
static void gasket_cleanup_pci(struct gasket_dev *gasket_dev)
{
int i;
for (i = 0; i < GASKET_NUM_BARS; i++)
gasket_unmap_pci_bar(gasket_dev, i);
pci_disable_device(gasket_dev->pci_dev);
}
/*
* Maps the specified bar into kernel space.
*
* Returns 0 on success, a negative error code otherwise.
* A zero-sized BAR will not be mapped, but is not an error.
*/
static int gasket_map_pci_bar(struct gasket_dev *gasket_dev, int bar_num)
{
struct gasket_internal_desc *internal_desc = gasket_dev->internal_desc;
const struct gasket_driver_desc *driver_desc =
internal_desc->driver_desc;
ulong desc_bytes = driver_desc->bar_descriptions[bar_num].size;
int ret;
if (desc_bytes == 0)
return 0;
if (desc_bytes == 0)
return 0;
if (driver_desc->bar_descriptions[bar_num].type != PCI_BAR) {
/* not PCI: skip this entry */
......@@ -826,320 +363,329 @@ static void gasket_unmap_pci_bar(struct gasket_dev *dev, int bar_num)
release_mem_region(base, bytes);
}
/* Add a char device and related info. */
static int gasket_add_cdev(
struct gasket_cdev_info *dev_info,
const struct file_operations *file_ops, struct module *owner)
{
int ret;
cdev_init(&dev_info->cdev, file_ops);
dev_info->cdev.owner = owner;
ret = cdev_add(&dev_info->cdev, dev_info->devt, 1);
if (ret) {
dev_err(dev_info->gasket_dev_ptr->dev,
"cannot add char device [ret=%d]\n", ret);
return ret;
}
dev_info->cdev_added = 1;
return 0;
}
/* Perform final init and marks the device as active. */
static int gasket_enable_dev(
struct gasket_internal_desc *internal_desc,
struct gasket_dev *gasket_dev)
/*
* Setup PCI & set up memory mapping for the specified device.
*
* Enables the PCI device, reads the BAR registers and sets up pointers to the
* device's memory mapped IO space.
*
* Returns 0 on success and a negative value otherwise.
*/
static int gasket_setup_pci(
struct pci_dev *pci_dev, struct gasket_dev *gasket_dev)
{
int tbl_idx;
int ret;
const struct gasket_driver_desc *driver_desc =
internal_desc->driver_desc;
int i, mapped_bars, ret;
ret = gasket_interrupt_init(
gasket_dev, driver_desc->name,
driver_desc->interrupt_type, driver_desc->interrupts,
driver_desc->num_interrupts, driver_desc->interrupt_pack_width,
driver_desc->interrupt_bar_index,
driver_desc->wire_interrupt_offsets);
ret = pci_enable_device(pci_dev);
if (ret) {
dev_err(gasket_dev->dev,
"Critical failure to allocate interrupts: %d\n", ret);
gasket_interrupt_cleanup(gasket_dev);
dev_err(gasket_dev->dev, "cannot enable PCI device\n");
return ret;
}
for (tbl_idx = 0; tbl_idx < driver_desc->num_page_tables; tbl_idx++) {
dev_dbg(gasket_dev->dev, "Initializing page table %d.\n",
tbl_idx);
ret = gasket_page_table_init(
&gasket_dev->page_table[tbl_idx],
&gasket_dev->bar_data[
driver_desc->page_table_bar_index],
&driver_desc->page_table_configs[tbl_idx],
gasket_dev->dev, gasket_dev->pci_dev);
pci_set_master(pci_dev);
for (i = 0; i < GASKET_NUM_BARS; i++) {
ret = gasket_map_pci_bar(gasket_dev, i);
if (ret) {
dev_err(gasket_dev->dev,
"Couldn't init page table %d: %d\n",
tbl_idx, ret);
return ret;
mapped_bars = i;
goto fail;
}
/*
* Make sure that the page table is clear and set to simple
* addresses.
*/
gasket_page_table_reset(gasket_dev->page_table[tbl_idx]);
}
/*
* hardware_revision_cb returns a positive integer (the rev) if
* successful.)
*/
ret = check_and_invoke_callback(
gasket_dev, driver_desc->hardware_revision_cb);
if (ret < 0) {
dev_err(gasket_dev->dev,
"Error getting hardware revision: %d\n", ret);
return ret;
}
gasket_dev->hardware_revision = ret;
return 0;
ret = check_and_invoke_callback(gasket_dev, driver_desc->enable_dev_cb);
if (ret) {
dev_err(gasket_dev->dev, "Error in enable device cb: %d\n",
ret);
return ret;
}
fail:
for (i = 0; i < mapped_bars; i++)
gasket_unmap_pci_bar(gasket_dev, i);
/* device_status_cb returns a device status, not an error code. */
gasket_dev->status = gasket_get_hw_status(gasket_dev);
if (gasket_dev->status == GASKET_STATUS_DEAD)
dev_err(gasket_dev->dev, "Device reported as unhealthy.\n");
pci_disable_device(pci_dev);
return -ENOMEM;
}
ret = gasket_add_cdev(
&gasket_dev->dev_info, &gasket_file_ops, driver_desc->module);
if (ret)
return ret;
/* Unmaps memory and cleans up PCI for the specified device. */
static void gasket_cleanup_pci(struct gasket_dev *gasket_dev)
{
int i;
return 0;
for (i = 0; i < GASKET_NUM_BARS; i++)
gasket_unmap_pci_bar(gasket_dev, i);
pci_disable_device(gasket_dev->pci_dev);
}
/* Disable device operations. */
static void gasket_disable_dev(struct gasket_dev *gasket_dev)
/* Determine the health of the Gasket device. */
static int gasket_get_hw_status(struct gasket_dev *gasket_dev)
{
int status;
int i;
const struct gasket_driver_desc *driver_desc =
gasket_dev->internal_desc->driver_desc;
int i;
/* Only delete the device if it has been successfully added. */
if (gasket_dev->dev_info.cdev_added)
cdev_del(&gasket_dev->dev_info.cdev);
gasket_dev->status = GASKET_STATUS_DEAD;
status = gasket_check_and_invoke_callback_nolock(
gasket_dev, driver_desc->device_status_cb);
if (status != GASKET_STATUS_ALIVE) {
dev_dbg(gasket_dev->dev, "Hardware reported status %d.\n",
status);
return status;
}
gasket_interrupt_cleanup(gasket_dev);
status = gasket_interrupt_system_status(gasket_dev);
if (status != GASKET_STATUS_ALIVE) {
dev_dbg(gasket_dev->dev,
"Interrupt system reported status %d.\n", status);
return status;
}
for (i = 0; i < driver_desc->num_page_tables; ++i) {
if (gasket_dev->page_table[i]) {
gasket_page_table_reset(gasket_dev->page_table[i]);
gasket_page_table_cleanup(gasket_dev->page_table[i]);
status = gasket_page_table_system_status(
gasket_dev->page_table[i]);
if (status != GASKET_STATUS_ALIVE) {
dev_dbg(gasket_dev->dev,
"Page table %d reported status %d.\n",
i, status);
return status;
}
}
check_and_invoke_callback(gasket_dev, driver_desc->disable_dev_cb);
return GASKET_STATUS_ALIVE;
}
/*
* Registered descriptor lookup.
*
* Precondition: Called with g_mutex held (to avoid a race on return).
* Returns NULL if no matching device was found.
*/
static struct gasket_internal_desc *lookup_internal_desc(
struct pci_dev *pci_dev)
static ssize_t gasket_write_mappable_regions(
char *buf, const struct gasket_driver_desc *driver_desc, int bar_index)
{
int i;
ssize_t written;
ssize_t total_written = 0;
ulong min_addr, max_addr;
struct gasket_bar_desc bar_desc =
driver_desc->bar_descriptions[bar_index];
__must_hold(&g_mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
if (g_descs[i].driver_desc &&
g_descs[i].driver_desc->pci_id_table &&
pci_match_id(g_descs[i].driver_desc->pci_id_table, pci_dev))
return &g_descs[i];
if (bar_desc.permissions == GASKET_NOMAP)
return 0;
for (i = 0;
i < bar_desc.num_mappable_regions && total_written < PAGE_SIZE;
i++) {
min_addr = bar_desc.mappable_regions[i].start -
driver_desc->legacy_mmap_address_offset;
max_addr = bar_desc.mappable_regions[i].start -
driver_desc->legacy_mmap_address_offset +
bar_desc.mappable_regions[i].length_bytes;
written = scnprintf(buf, PAGE_SIZE - total_written,
"0x%08lx-0x%08lx\n", min_addr, max_addr);
total_written += written;
buf += written;
}
return NULL;
return total_written;
}
/**
* Lookup a name by number in a num_name table.
* @num: Number to lookup.
* @table: Array of num_name structures, the table for the lookup.
*
* Description: Searches for num in the table. If found, the
* corresponding name is returned; otherwise NULL
* is returned.
*
* The table must have a NULL name pointer at the end.
*/
const char *gasket_num_name_lookup(
uint num, const struct gasket_num_name *table)
static ssize_t gasket_sysfs_data_show(
struct device *device, struct device_attribute *attr, char *buf)
{
uint i = 0;
int i, ret = 0;
ssize_t current_written = 0;
const struct gasket_driver_desc *driver_desc;
struct gasket_dev *gasket_dev;
struct gasket_sysfs_attribute *gasket_attr;
const struct gasket_bar_desc *bar_desc;
enum gasket_sysfs_attribute_type sysfs_type;
while (table[i].snn_name) {
if (num == table[i].snn_num)
break;
++i;
gasket_dev = gasket_sysfs_get_device_data(device);
if (!gasket_dev) {
dev_err(device, "No sysfs mapping found for device\n");
return 0;
}
return table[i].snn_name;
}
EXPORT_SYMBOL(gasket_num_name_lookup);
/*
* Open the char device file.
*
* If the open is for writing, and the device is not owned, this process becomes
* the owner. If the open is for writing and the device is already owned by
* some other process, it is an error. If this process is the owner, increment
* the open count.
*
* Returns 0 if successful, a negative error number otherwise.
*/
static int gasket_open(struct inode *inode, struct file *filp)
{
int ret;
struct gasket_dev *gasket_dev;
const struct gasket_driver_desc *driver_desc;
struct gasket_ownership *ownership;
char task_name[TASK_COMM_LEN];
struct gasket_cdev_info *dev_info =
container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
struct pid_namespace *pid_ns = task_active_pid_ns(current);
int is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);
gasket_attr = gasket_sysfs_get_attr(device, attr);
if (!gasket_attr) {
dev_err(device, "No sysfs attr found for device\n");
gasket_sysfs_put_device_data(device, gasket_dev);
return 0;
}
gasket_dev = dev_info->gasket_dev_ptr;
driver_desc = gasket_dev->internal_desc->driver_desc;
ownership = &dev_info->ownership;
get_task_comm(task_name, current);
filp->private_data = gasket_dev;
inode->i_size = 0;
dev_dbg(gasket_dev->dev,
"Attempting to open with tgid %u (%s) (f_mode: 0%03o, "
"fmode_write: %d is_root: %u)\n",
current->tgid, task_name, filp->f_mode,
(filp->f_mode & FMODE_WRITE), is_root);
/* Always allow non-writing accesses. */
if (!(filp->f_mode & FMODE_WRITE)) {
dev_dbg(gasket_dev->dev, "Allowing read-only opening.\n");
return 0;
sysfs_type =
(enum gasket_sysfs_attribute_type)gasket_attr->data.attr_type;
switch (sysfs_type) {
case ATTR_BAR_OFFSETS:
for (i = 0; i < GASKET_NUM_BARS; i++) {
bar_desc = &driver_desc->bar_descriptions[i];
if (bar_desc->size == 0)
continue;
current_written =
snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
(ulong)bar_desc->base);
buf += current_written;
ret += current_written;
}
break;
case ATTR_BAR_SIZES:
for (i = 0; i < GASKET_NUM_BARS; i++) {
bar_desc = &driver_desc->bar_descriptions[i];
if (bar_desc->size == 0)
continue;
current_written =
snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
(ulong)bar_desc->size);
buf += current_written;
ret += current_written;
}
break;
case ATTR_DRIVER_VERSION:
ret = snprintf(
buf, PAGE_SIZE, "%s\n",
gasket_dev->internal_desc->driver_desc->driver_version);
break;
case ATTR_FRAMEWORK_VERSION:
ret = snprintf(
buf, PAGE_SIZE, "%s\n", GASKET_FRAMEWORK_VERSION);
break;
case ATTR_DEVICE_TYPE:
ret = snprintf(
buf, PAGE_SIZE, "%s\n",
gasket_dev->internal_desc->driver_desc->name);
break;
case ATTR_HARDWARE_REVISION:
ret = snprintf(
buf, PAGE_SIZE, "%d\n", gasket_dev->hardware_revision);
break;
case ATTR_PCI_ADDRESS:
ret = snprintf(buf, PAGE_SIZE, "%s\n", gasket_dev->kobj_name);
break;
case ATTR_STATUS:
ret = snprintf(
buf, PAGE_SIZE, "%s\n",
gasket_num_name_lookup(
gasket_dev->status, gasket_status_name_table));
break;
case ATTR_IS_DEVICE_OWNED:
ret = snprintf(
buf, PAGE_SIZE, "%d\n",
gasket_dev->dev_info.ownership.is_owned);
break;
case ATTR_DEVICE_OWNER:
ret = snprintf(
buf, PAGE_SIZE, "%d\n",
gasket_dev->dev_info.ownership.owner);
break;
case ATTR_WRITE_OPEN_COUNT:
ret = snprintf(
buf, PAGE_SIZE, "%d\n",
gasket_dev->dev_info.ownership.write_open_count);
break;
case ATTR_RESET_COUNT:
ret = snprintf(buf, PAGE_SIZE, "%d\n", gasket_dev->reset_count);
break;
case ATTR_USER_MEM_RANGES:
for (i = 0; i < GASKET_NUM_BARS; ++i) {
current_written = gasket_write_mappable_regions(
buf, driver_desc, i);
buf += current_written;
ret += current_written;
}
break;
default:
dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
attr->attr.name);
ret = 0;
break;
}
mutex_lock(&gasket_dev->mutex);
gasket_sysfs_put_attr(device, gasket_attr);
gasket_sysfs_put_device_data(device, gasket_dev);
return ret;
}
dev_dbg(gasket_dev->dev,
"Current owner open count (owning tgid %u): %d.\n",
ownership->owner, ownership->write_open_count);
/* These attributes apply to all Gasket driver instances. */
static const struct gasket_sysfs_attribute gasket_sysfs_generic_attrs[] = {
GASKET_SYSFS_RO(bar_offsets, gasket_sysfs_data_show, ATTR_BAR_OFFSETS),
GASKET_SYSFS_RO(bar_sizes, gasket_sysfs_data_show, ATTR_BAR_SIZES),
GASKET_SYSFS_RO(driver_version, gasket_sysfs_data_show,
ATTR_DRIVER_VERSION),
GASKET_SYSFS_RO(framework_version, gasket_sysfs_data_show,
ATTR_FRAMEWORK_VERSION),
GASKET_SYSFS_RO(device_type, gasket_sysfs_data_show, ATTR_DEVICE_TYPE),
GASKET_SYSFS_RO(revision, gasket_sysfs_data_show,
ATTR_HARDWARE_REVISION),
GASKET_SYSFS_RO(pci_address, gasket_sysfs_data_show, ATTR_PCI_ADDRESS),
GASKET_SYSFS_RO(status, gasket_sysfs_data_show, ATTR_STATUS),
GASKET_SYSFS_RO(is_device_owned, gasket_sysfs_data_show,
ATTR_IS_DEVICE_OWNED),
GASKET_SYSFS_RO(device_owner, gasket_sysfs_data_show,
ATTR_DEVICE_OWNER),
GASKET_SYSFS_RO(write_open_count, gasket_sysfs_data_show,
ATTR_WRITE_OPEN_COUNT),
GASKET_SYSFS_RO(reset_count, gasket_sysfs_data_show, ATTR_RESET_COUNT),
GASKET_SYSFS_RO(user_mem_ranges, gasket_sysfs_data_show,
ATTR_USER_MEM_RANGES),
GASKET_END_OF_ATTR_ARRAY
};
/* Opening a node owned by another TGID is an error (unless root) */
if (ownership->is_owned && ownership->owner != current->tgid &&
!is_root) {
dev_err(gasket_dev->dev,
"Process %u is opening a node held by %u.\n",
current->tgid, ownership->owner);
mutex_unlock(&gasket_dev->mutex);
return -EPERM;
}
/* Add a char device and related info. */
static int gasket_add_cdev(
struct gasket_cdev_info *dev_info,
const struct file_operations *file_ops, struct module *owner)
{
int ret;
/* If the node is not owned, assign it to the current TGID. */
if (!ownership->is_owned) {
ret = gasket_check_and_invoke_callback_nolock(
gasket_dev, driver_desc->device_open_cb);
cdev_init(&dev_info->cdev, file_ops);
dev_info->cdev.owner = owner;
ret = cdev_add(&dev_info->cdev, dev_info->devt, 1);
if (ret) {
dev_err(gasket_dev->dev,
"Error in device open cb: %d\n", ret);
mutex_unlock(&gasket_dev->mutex);
dev_err(dev_info->gasket_dev_ptr->dev,
"cannot add char device [ret=%d]\n", ret);
return ret;
}
ownership->is_owned = 1;
ownership->owner = current->tgid;
dev_dbg(gasket_dev->dev, "Device owner is now tgid %u\n",
ownership->owner);
}
ownership->write_open_count++;
dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
ownership->owner, ownership->write_open_count);
dev_info->cdev_added = 1;
mutex_unlock(&gasket_dev->mutex);
return 0;
}
/*
* Called on a close of the device file. If this process is the owner,
* decrement the open count. On last close by the owner, free up buffers and
* eventfd contexts, and release ownership.
*
* Returns 0 if successful, a negative error number otherwise.
*/
static int gasket_release(struct inode *inode, struct file *file)
/* Disable device operations. */
static void gasket_disable_dev(struct gasket_dev *gasket_dev)
{
const struct gasket_driver_desc *driver_desc =
gasket_dev->internal_desc->driver_desc;
int i;
struct gasket_dev *gasket_dev;
struct gasket_ownership *ownership;
const struct gasket_driver_desc *driver_desc;
char task_name[TASK_COMM_LEN];
struct gasket_cdev_info *dev_info =
container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
struct pid_namespace *pid_ns = task_active_pid_ns(current);
int is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);
gasket_dev = dev_info->gasket_dev_ptr;
driver_desc = gasket_dev->internal_desc->driver_desc;
ownership = &dev_info->ownership;
get_task_comm(task_name, current);
mutex_lock(&gasket_dev->mutex);
dev_dbg(gasket_dev->dev,
"Releasing device node. Call origin: tgid %u (%s) "
"(f_mode: 0%03o, fmode_write: %d, is_root: %u)\n",
current->tgid, task_name, file->f_mode,
(file->f_mode & FMODE_WRITE), is_root);
dev_dbg(gasket_dev->dev, "Current open count (owning tgid %u): %d\n",
ownership->owner, ownership->write_open_count);
/* Only delete the device if it has been successfully added. */
if (gasket_dev->dev_info.cdev_added)
cdev_del(&gasket_dev->dev_info.cdev);
if (file->f_mode & FMODE_WRITE) {
ownership->write_open_count--;
if (ownership->write_open_count == 0) {
dev_dbg(gasket_dev->dev, "Device is now free\n");
ownership->is_owned = 0;
ownership->owner = 0;
gasket_dev->status = GASKET_STATUS_DEAD;
/* Forces chip reset before we unmap the page tables. */
driver_desc->device_reset_cb(gasket_dev, 0);
gasket_interrupt_cleanup(gasket_dev);
for (i = 0; i < driver_desc->num_page_tables; ++i) {
gasket_page_table_unmap_all(
gasket_dev->page_table[i]);
gasket_page_table_garbage_collect(
gasket_dev->page_table[i]);
gasket_free_coherent_memory_all(gasket_dev, i);
if (gasket_dev->page_table[i]) {
gasket_page_table_reset(gasket_dev->page_table[i]);
gasket_page_table_cleanup(gasket_dev->page_table[i]);
}
/* Closes device, enters power save. */
gasket_check_and_invoke_callback_nolock(
gasket_dev, driver_desc->device_close_cb);
}
check_and_invoke_callback(gasket_dev, driver_desc->disable_dev_cb);
}
/*
* Registered descriptor lookup.
*
* Precondition: Called with g_mutex held (to avoid a race on return).
* Returns NULL if no matching device was found.
*/
static struct gasket_internal_desc *lookup_internal_desc(
struct pci_dev *pci_dev)
{
int i;
__must_hold(&g_mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
if (g_descs[i].driver_desc &&
g_descs[i].driver_desc->pci_id_table &&
pci_match_id(g_descs[i].driver_desc->pci_id_table, pci_dev))
return &g_descs[i];
}
dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
ownership->owner, ownership->write_open_count);
mutex_unlock(&gasket_dev->mutex);
return 0;
return NULL;
}
/*
......@@ -1301,11 +847,41 @@ static bool gasket_mm_get_mapping_addrs(
return false;
}
int gasket_mm_unmap_region(
const struct gasket_dev *gasket_dev, struct vm_area_struct *vma,
const struct gasket_mappable_region *map_region)
/*
* Calculates the offset where the VMA range begins in its containing BAR.
* The offset is written into bar_offset on success.
* Returns zero on success, anything else on error.
*/
static int gasket_mm_vma_bar_offset(
const struct gasket_dev *gasket_dev, const struct vm_area_struct *vma,
ulong *bar_offset)
{
ulong bar_offset;
ulong raw_offset;
int bar_index;
const struct gasket_driver_desc *driver_desc =
gasket_dev->internal_desc->driver_desc;
raw_offset = (vma->vm_pgoff << PAGE_SHIFT) +
driver_desc->legacy_mmap_address_offset;
bar_index = gasket_get_bar_index(gasket_dev, raw_offset);
if (bar_index < 0) {
dev_err(gasket_dev->dev,
"Unable to find matching bar for address 0x%lx\n",
raw_offset);
trace_gasket_mmap_exit(bar_index);
return bar_index;
}
*bar_offset =
raw_offset - driver_desc->bar_descriptions[bar_index].base;
return 0;
}
int gasket_mm_unmap_region(
const struct gasket_dev *gasket_dev, struct vm_area_struct *vma,
const struct gasket_mappable_region *map_region)
{
ulong bar_offset;
ulong virt_offset;
struct gasket_mappable_region mappable_region;
int ret;
......@@ -1407,36 +983,6 @@ static enum do_map_region_status do_map_region(
return DO_MAP_REGION_FAILURE;
}
/*
* Calculates the offset where the VMA range begins in its containing BAR.
* The offset is written into bar_offset on success.
* Returns zero on success, anything else on error.
*/
static int gasket_mm_vma_bar_offset(
const struct gasket_dev *gasket_dev, const struct vm_area_struct *vma,
ulong *bar_offset)
{
ulong raw_offset;
int bar_index;
const struct gasket_driver_desc *driver_desc =
gasket_dev->internal_desc->driver_desc;
raw_offset = (vma->vm_pgoff << PAGE_SHIFT) +
driver_desc->legacy_mmap_address_offset;
bar_index = gasket_get_bar_index(gasket_dev, raw_offset);
if (bar_index < 0) {
dev_err(gasket_dev->dev,
"Unable to find matching bar for address 0x%lx\n",
raw_offset);
trace_gasket_mmap_exit(bar_index);
return bar_index;
}
*bar_offset =
raw_offset - driver_desc->bar_descriptions[bar_index].base;
return 0;
}
/* Map a region of coherent memory. */
static int gasket_mmap_coherent(
struct gasket_dev *gasket_dev, struct vm_area_struct *vma)
......@@ -1626,41 +1172,149 @@ static int gasket_mmap(struct file *filp, struct vm_area_struct *vma)
return ret;
}
/* Determine the health of the Gasket device. */
static int gasket_get_hw_status(struct gasket_dev *gasket_dev)
/*
* Open the char device file.
*
* If the open is for writing, and the device is not owned, this process becomes
* the owner. If the open is for writing and the device is already owned by
* some other process, it is an error. If this process is the owner, increment
* the open count.
*
* Returns 0 if successful, a negative error number otherwise.
*/
static int gasket_open(struct inode *inode, struct file *filp)
{
int status;
int i;
const struct gasket_driver_desc *driver_desc =
gasket_dev->internal_desc->driver_desc;
int ret;
struct gasket_dev *gasket_dev;
const struct gasket_driver_desc *driver_desc;
struct gasket_ownership *ownership;
char task_name[TASK_COMM_LEN];
struct gasket_cdev_info *dev_info =
container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
struct pid_namespace *pid_ns = task_active_pid_ns(current);
int is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);
status = gasket_check_and_invoke_callback_nolock(
gasket_dev, driver_desc->device_status_cb);
if (status != GASKET_STATUS_ALIVE) {
dev_dbg(gasket_dev->dev, "Hardware reported status %d.\n",
status);
return status;
gasket_dev = dev_info->gasket_dev_ptr;
driver_desc = gasket_dev->internal_desc->driver_desc;
ownership = &dev_info->ownership;
get_task_comm(task_name, current);
filp->private_data = gasket_dev;
inode->i_size = 0;
dev_dbg(gasket_dev->dev,
"Attempting to open with tgid %u (%s) (f_mode: 0%03o, "
"fmode_write: %d is_root: %u)\n",
current->tgid, task_name, filp->f_mode,
(filp->f_mode & FMODE_WRITE), is_root);
/* Always allow non-writing accesses. */
if (!(filp->f_mode & FMODE_WRITE)) {
dev_dbg(gasket_dev->dev, "Allowing read-only opening.\n");
return 0;
}
status = gasket_interrupt_system_status(gasket_dev);
if (status != GASKET_STATUS_ALIVE) {
mutex_lock(&gasket_dev->mutex);
dev_dbg(gasket_dev->dev,
"Interrupt system reported status %d.\n", status);
return status;
"Current owner open count (owning tgid %u): %d.\n",
ownership->owner, ownership->write_open_count);
/* Opening a node owned by another TGID is an error (unless root) */
if (ownership->is_owned && ownership->owner != current->tgid &&
!is_root) {
dev_err(gasket_dev->dev,
"Process %u is opening a node held by %u.\n",
current->tgid, ownership->owner);
mutex_unlock(&gasket_dev->mutex);
return -EPERM;
}
/* If the node is not owned, assign it to the current TGID. */
if (!ownership->is_owned) {
ret = gasket_check_and_invoke_callback_nolock(
gasket_dev, driver_desc->device_open_cb);
if (ret) {
dev_err(gasket_dev->dev,
"Error in device open cb: %d\n", ret);
mutex_unlock(&gasket_dev->mutex);
return ret;
}
ownership->is_owned = 1;
ownership->owner = current->tgid;
dev_dbg(gasket_dev->dev, "Device owner is now tgid %u\n",
ownership->owner);
}
ownership->write_open_count++;
dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
ownership->owner, ownership->write_open_count);
mutex_unlock(&gasket_dev->mutex);
return 0;
}
/*
* Called on a close of the device file. If this process is the owner,
* decrement the open count. On last close by the owner, free up buffers and
* eventfd contexts, and release ownership.
*
* Returns 0 if successful, a negative error number otherwise.
*/
static int gasket_release(struct inode *inode, struct file *file)
{
int i;
struct gasket_dev *gasket_dev;
struct gasket_ownership *ownership;
const struct gasket_driver_desc *driver_desc;
char task_name[TASK_COMM_LEN];
struct gasket_cdev_info *dev_info =
container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
struct pid_namespace *pid_ns = task_active_pid_ns(current);
int is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);
gasket_dev = dev_info->gasket_dev_ptr;
driver_desc = gasket_dev->internal_desc->driver_desc;
ownership = &dev_info->ownership;
get_task_comm(task_name, current);
mutex_lock(&gasket_dev->mutex);
dev_dbg(gasket_dev->dev,
"Releasing device node. Call origin: tgid %u (%s) "
"(f_mode: 0%03o, fmode_write: %d, is_root: %u)\n",
current->tgid, task_name, file->f_mode,
(file->f_mode & FMODE_WRITE), is_root);
dev_dbg(gasket_dev->dev, "Current open count (owning tgid %u): %d\n",
ownership->owner, ownership->write_open_count);
if (file->f_mode & FMODE_WRITE) {
ownership->write_open_count--;
if (ownership->write_open_count == 0) {
dev_dbg(gasket_dev->dev, "Device is now free\n");
ownership->is_owned = 0;
ownership->owner = 0;
/* Forces chip reset before we unmap the page tables. */
driver_desc->device_reset_cb(gasket_dev, 0);
for (i = 0; i < driver_desc->num_page_tables; ++i) {
status = gasket_page_table_system_status(
gasket_page_table_unmap_all(
gasket_dev->page_table[i]);
if (status != GASKET_STATUS_ALIVE) {
dev_dbg(gasket_dev->dev,
"Page table %d reported status %d.\n",
i, status);
return status;
gasket_page_table_garbage_collect(
gasket_dev->page_table[i]);
gasket_free_coherent_memory_all(gasket_dev, i);
}
/* Closes device, enters power save. */
gasket_check_and_invoke_callback_nolock(
gasket_dev, driver_desc->device_close_cb);
}
}
return GASKET_STATUS_ALIVE;
dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
ownership->owner, ownership->write_open_count);
mutex_unlock(&gasket_dev->mutex);
return 0;
}
/*
......@@ -1702,209 +1356,333 @@ static long gasket_ioctl(struct file *filp, uint cmd, ulong arg)
return gasket_handle_ioctl(filp, cmd, argp);
}
int gasket_reset(struct gasket_dev *gasket_dev, uint reset_type)
{
int ret;
mutex_lock(&gasket_dev->mutex);
ret = gasket_reset_nolock(gasket_dev, reset_type);
mutex_unlock(&gasket_dev->mutex);
return ret;
}
EXPORT_SYMBOL(gasket_reset);
/* File operations for all Gasket devices. */
static const struct file_operations gasket_file_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.mmap = gasket_mmap,
.open = gasket_open,
.release = gasket_release,
.unlocked_ioctl = gasket_ioctl,
};
int gasket_reset_nolock(struct gasket_dev *gasket_dev, uint reset_type)
/* Perform final init and marks the device as active. */
static int gasket_enable_dev(
struct gasket_internal_desc *internal_desc,
struct gasket_dev *gasket_dev)
{
int tbl_idx;
int ret;
int i;
const struct gasket_driver_desc *driver_desc;
driver_desc = gasket_dev->internal_desc->driver_desc;
if (!driver_desc->device_reset_cb)
return 0;
const struct gasket_driver_desc *driver_desc =
internal_desc->driver_desc;
/* Perform a device reset of the requested type. */
ret = driver_desc->device_reset_cb(gasket_dev, reset_type);
ret = gasket_interrupt_init(
gasket_dev, driver_desc->name,
driver_desc->interrupt_type, driver_desc->interrupts,
driver_desc->num_interrupts, driver_desc->interrupt_pack_width,
driver_desc->interrupt_bar_index,
driver_desc->wire_interrupt_offsets);
if (ret) {
dev_dbg(gasket_dev->dev, "Device reset cb returned %d.\n",
ret);
dev_err(gasket_dev->dev,
"Critical failure to allocate interrupts: %d\n", ret);
gasket_interrupt_cleanup(gasket_dev);
return ret;
}
/* Reinitialize the page tables and interrupt framework. */
for (i = 0; i < driver_desc->num_page_tables; ++i)
gasket_page_table_reset(gasket_dev->page_table[i]);
for (tbl_idx = 0; tbl_idx < driver_desc->num_page_tables; tbl_idx++) {
dev_dbg(gasket_dev->dev, "Initializing page table %d.\n",
tbl_idx);
ret = gasket_page_table_init(
&gasket_dev->page_table[tbl_idx],
&gasket_dev->bar_data[
driver_desc->page_table_bar_index],
&driver_desc->page_table_configs[tbl_idx],
gasket_dev->dev, gasket_dev->pci_dev);
if (ret) {
dev_err(gasket_dev->dev,
"Couldn't init page table %d: %d\n",
tbl_idx, ret);
return ret;
}
/*
* Make sure that the page table is clear and set to simple
* addresses.
*/
gasket_page_table_reset(gasket_dev->page_table[tbl_idx]);
}
ret = gasket_interrupt_reinit(gasket_dev);
/*
* hardware_revision_cb returns a positive integer (the rev) if
* successful.)
*/
ret = check_and_invoke_callback(
gasket_dev, driver_desc->hardware_revision_cb);
if (ret < 0) {
dev_err(gasket_dev->dev,
"Error getting hardware revision: %d\n", ret);
return ret;
}
gasket_dev->hardware_revision = ret;
ret = check_and_invoke_callback(gasket_dev, driver_desc->enable_dev_cb);
if (ret) {
dev_dbg(gasket_dev->dev, "Unable to reinit interrupts: %d.\n",
dev_err(gasket_dev->dev, "Error in enable device cb: %d\n",
ret);
return ret;
}
/* Get current device health. */
/* device_status_cb returns a device status, not an error code. */
gasket_dev->status = gasket_get_hw_status(gasket_dev);
if (gasket_dev->status == GASKET_STATUS_DEAD) {
dev_dbg(gasket_dev->dev, "Device reported as dead.\n");
return -EINVAL;
}
if (gasket_dev->status == GASKET_STATUS_DEAD)
dev_err(gasket_dev->dev, "Device reported as unhealthy.\n");
ret = gasket_add_cdev(
&gasket_dev->dev_info, &gasket_file_ops, driver_desc->module);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL(gasket_reset_nolock);
gasket_ioctl_permissions_cb_t gasket_get_ioctl_permissions_cb(
struct gasket_dev *gasket_dev)
/*
* PCI subsystem probe function.
*
* Called when a Gasket device is found. Allocates device metadata, maps device
* memory, and calls gasket_enable_dev to prepare the device for active use.
*
* Returns 0 if successful and a negative value otherwise.
*/
static int gasket_pci_probe(
struct pci_dev *pci_dev, const struct pci_device_id *id)
{
return gasket_dev->internal_desc->driver_desc->ioctl_permissions_cb;
}
EXPORT_SYMBOL(gasket_get_ioctl_permissions_cb);
int ret;
const char *kobj_name = dev_name(&pci_dev->dev);
struct gasket_internal_desc *internal_desc;
struct gasket_dev *gasket_dev;
const struct gasket_driver_desc *driver_desc;
struct device *parent;
static ssize_t gasket_write_mappable_regions(
char *buf, const struct gasket_driver_desc *driver_desc, int bar_index)
{
int i;
ssize_t written;
ssize_t total_written = 0;
ulong min_addr, max_addr;
struct gasket_bar_desc bar_desc =
driver_desc->bar_descriptions[bar_index];
pr_info("Add Gasket device %s\n", kobj_name);
if (bar_desc.permissions == GASKET_NOMAP)
return 0;
for (i = 0;
i < bar_desc.num_mappable_regions && total_written < PAGE_SIZE;
i++) {
min_addr = bar_desc.mappable_regions[i].start -
driver_desc->legacy_mmap_address_offset;
max_addr = bar_desc.mappable_regions[i].start -
driver_desc->legacy_mmap_address_offset +
bar_desc.mappable_regions[i].length_bytes;
written = scnprintf(buf, PAGE_SIZE - total_written,
"0x%08lx-0x%08lx\n", min_addr, max_addr);
total_written += written;
buf += written;
mutex_lock(&g_mutex);
internal_desc = lookup_internal_desc(pci_dev);
mutex_unlock(&g_mutex);
if (!internal_desc) {
pr_err("PCI probe called for unknown driver type\n");
return -ENODEV;
}
return total_written;
}
static ssize_t gasket_sysfs_data_show(
struct device *device, struct device_attribute *attr, char *buf)
{
int i, ret = 0;
ssize_t current_written = 0;
const struct gasket_driver_desc *driver_desc;
struct gasket_dev *gasket_dev;
struct gasket_sysfs_attribute *gasket_attr;
const struct gasket_bar_desc *bar_desc;
enum gasket_sysfs_attribute_type sysfs_type;
driver_desc = internal_desc->driver_desc;
gasket_dev = gasket_sysfs_get_device_data(device);
if (!gasket_dev) {
dev_err(device, "No sysfs mapping found for device\n");
return 0;
parent = &pci_dev->dev;
ret = gasket_alloc_dev(internal_desc, parent, &gasket_dev, kobj_name);
if (ret)
return ret;
gasket_dev->pci_dev = pci_dev_get(pci_dev);
if (IS_ERR_OR_NULL(gasket_dev->dev_info.device)) {
pr_err("Cannot create %s device %s [ret = %ld]\n",
driver_desc->name, gasket_dev->dev_info.name,
PTR_ERR(gasket_dev->dev_info.device));
ret = -ENODEV;
goto fail1;
}
gasket_attr = gasket_sysfs_get_attr(device, attr);
if (!gasket_attr) {
dev_err(device, "No sysfs attr found for device\n");
gasket_sysfs_put_device_data(device, gasket_dev);
return 0;
ret = gasket_setup_pci(pci_dev, gasket_dev);
if (ret)
goto fail2;
ret = check_and_invoke_callback(gasket_dev, driver_desc->add_dev_cb);
if (ret) {
dev_err(gasket_dev->dev, "Error in add device cb: %d\n", ret);
goto fail2;
}
driver_desc = gasket_dev->internal_desc->driver_desc;
ret = gasket_sysfs_create_mapping(
gasket_dev->dev_info.device, gasket_dev);
if (ret)
goto fail3;
sysfs_type =
(enum gasket_sysfs_attribute_type)gasket_attr->data.attr_type;
switch (sysfs_type) {
case ATTR_BAR_OFFSETS:
for (i = 0; i < GASKET_NUM_BARS; i++) {
bar_desc = &driver_desc->bar_descriptions[i];
if (bar_desc->size == 0)
continue;
current_written =
snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
(ulong)bar_desc->base);
buf += current_written;
ret += current_written;
/*
* Once we've created the mapping structures successfully, attempt to
* create a symlink to the pci directory of this object.
*/
ret = sysfs_create_link(&gasket_dev->dev_info.device->kobj,
&pci_dev->dev.kobj, dev_name(&pci_dev->dev));
if (ret) {
dev_err(gasket_dev->dev,
"Cannot create sysfs pci link: %d\n", ret);
goto fail3;
}
break;
case ATTR_BAR_SIZES:
for (i = 0; i < GASKET_NUM_BARS; i++) {
bar_desc = &driver_desc->bar_descriptions[i];
if (bar_desc->size == 0)
continue;
current_written =
snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
(ulong)bar_desc->size);
buf += current_written;
ret += current_written;
ret = gasket_sysfs_create_entries(
gasket_dev->dev_info.device, gasket_sysfs_generic_attrs);
if (ret)
goto fail4;
ret = check_and_invoke_callback(
gasket_dev, driver_desc->sysfs_setup_cb);
if (ret) {
dev_err(gasket_dev->dev, "Error in sysfs setup cb: %d\n", ret);
goto fail5;
}
ret = gasket_enable_dev(internal_desc, gasket_dev);
if (ret) {
pr_err("cannot setup %s device\n", driver_desc->name);
gasket_disable_dev(gasket_dev);
goto fail5;
}
return 0;
fail5:
check_and_invoke_callback(gasket_dev, driver_desc->sysfs_cleanup_cb);
fail4:
fail3:
gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
fail2:
gasket_cleanup_pci(gasket_dev);
check_and_invoke_callback(gasket_dev, driver_desc->remove_dev_cb);
device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
fail1:
gasket_free_dev(gasket_dev);
return ret;
}
/*
* PCI subsystem remove function.
*
* Called to remove a Gasket device. Finds the device in the device list and
* cleans up metadata.
*/
static void gasket_pci_remove(struct pci_dev *pci_dev)
{
int i;
struct gasket_internal_desc *internal_desc;
struct gasket_dev *gasket_dev = NULL;
const struct gasket_driver_desc *driver_desc;
/* Find the device desc. */
mutex_lock(&g_mutex);
internal_desc = lookup_internal_desc(pci_dev);
if (!internal_desc) {
mutex_unlock(&g_mutex);
return;
}
mutex_unlock(&g_mutex);
driver_desc = internal_desc->driver_desc;
/* Now find the specific device */
mutex_lock(&internal_desc->mutex);
for (i = 0; i < GASKET_DEV_MAX; i++) {
if (internal_desc->devs[i] &&
internal_desc->devs[i]->pci_dev == pci_dev) {
gasket_dev = internal_desc->devs[i];
break;
case ATTR_DRIVER_VERSION:
ret = snprintf(
buf, PAGE_SIZE, "%s\n",
gasket_dev->internal_desc->driver_desc->driver_version);
break;
case ATTR_FRAMEWORK_VERSION:
ret = snprintf(
buf, PAGE_SIZE, "%s\n", GASKET_FRAMEWORK_VERSION);
break;
case ATTR_DEVICE_TYPE:
ret = snprintf(
buf, PAGE_SIZE, "%s\n",
gasket_dev->internal_desc->driver_desc->name);
break;
case ATTR_HARDWARE_REVISION:
ret = snprintf(
buf, PAGE_SIZE, "%d\n", gasket_dev->hardware_revision);
break;
case ATTR_PCI_ADDRESS:
ret = snprintf(buf, PAGE_SIZE, "%s\n", gasket_dev->kobj_name);
break;
case ATTR_STATUS:
ret = snprintf(
buf, PAGE_SIZE, "%s\n",
gasket_num_name_lookup(
gasket_dev->status, gasket_status_name_table));
break;
case ATTR_IS_DEVICE_OWNED:
ret = snprintf(
buf, PAGE_SIZE, "%d\n",
gasket_dev->dev_info.ownership.is_owned);
break;
case ATTR_DEVICE_OWNER:
ret = snprintf(
buf, PAGE_SIZE, "%d\n",
gasket_dev->dev_info.ownership.owner);
break;
case ATTR_WRITE_OPEN_COUNT:
ret = snprintf(
buf, PAGE_SIZE, "%d\n",
gasket_dev->dev_info.ownership.write_open_count);
break;
case ATTR_RESET_COUNT:
ret = snprintf(buf, PAGE_SIZE, "%d\n", gasket_dev->reset_count);
}
}
mutex_unlock(&internal_desc->mutex);
if (!gasket_dev)
return;
pr_info("remove %s device %s\n", internal_desc->driver_desc->name,
gasket_dev->kobj_name);
gasket_disable_dev(gasket_dev);
gasket_cleanup_pci(gasket_dev);
check_and_invoke_callback(gasket_dev, driver_desc->sysfs_cleanup_cb);
gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
check_and_invoke_callback(gasket_dev, driver_desc->remove_dev_cb);
device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
gasket_free_dev(gasket_dev);
}
/**
* Lookup a name by number in a num_name table.
* @num: Number to lookup.
* @table: Array of num_name structures, the table for the lookup.
*
* Description: Searches for num in the table. If found, the
* corresponding name is returned; otherwise NULL
* is returned.
*
* The table must have a NULL name pointer at the end.
*/
const char *gasket_num_name_lookup(
uint num, const struct gasket_num_name *table)
{
uint i = 0;
while (table[i].snn_name) {
if (num == table[i].snn_num)
break;
case ATTR_USER_MEM_RANGES:
for (i = 0; i < GASKET_NUM_BARS; ++i) {
current_written = gasket_write_mappable_regions(
buf, driver_desc, i);
buf += current_written;
ret += current_written;
++i;
}
break;
default:
dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
attr->attr.name);
ret = 0;
break;
return table[i].snn_name;
}
EXPORT_SYMBOL(gasket_num_name_lookup);
int gasket_reset(struct gasket_dev *gasket_dev, uint reset_type)
{
int ret;
mutex_lock(&gasket_dev->mutex);
ret = gasket_reset_nolock(gasket_dev, reset_type);
mutex_unlock(&gasket_dev->mutex);
return ret;
}
EXPORT_SYMBOL(gasket_reset);
int gasket_reset_nolock(struct gasket_dev *gasket_dev, uint reset_type)
{
int ret;
int i;
const struct gasket_driver_desc *driver_desc;
driver_desc = gasket_dev->internal_desc->driver_desc;
if (!driver_desc->device_reset_cb)
return 0;
/* Perform a device reset of the requested type. */
ret = driver_desc->device_reset_cb(gasket_dev, reset_type);
if (ret) {
dev_dbg(gasket_dev->dev, "Device reset cb returned %d.\n",
ret);
return ret;
}
gasket_sysfs_put_attr(device, gasket_attr);
gasket_sysfs_put_device_data(device, gasket_dev);
/* Reinitialize the page tables and interrupt framework. */
for (i = 0; i < driver_desc->num_page_tables; ++i)
gasket_page_table_reset(gasket_dev->page_table[i]);
ret = gasket_interrupt_reinit(gasket_dev);
if (ret) {
dev_dbg(gasket_dev->dev, "Unable to reinit interrupts: %d.\n",
ret);
return ret;
}
/* Get current device health. */
gasket_dev->status = gasket_get_hw_status(gasket_dev);
if (gasket_dev->status == GASKET_STATUS_DEAD) {
dev_dbg(gasket_dev->dev, "Device reported as dead.\n");
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(gasket_reset_nolock);
gasket_ioctl_permissions_cb_t gasket_get_ioctl_permissions_cb(
struct gasket_dev *gasket_dev)
{
return gasket_dev->internal_desc->driver_desc->ioctl_permissions_cb;
}
EXPORT_SYMBOL(gasket_get_ioctl_permissions_cb);
/* Get the driver structure for a given gasket_dev.
* @dev: pointer to gasket_dev, implementing the requested driver.
......@@ -1954,3 +1732,169 @@ int gasket_wait_with_reschedule(
return -ETIMEDOUT;
}
EXPORT_SYMBOL(gasket_wait_with_reschedule);
/* See gasket_core.h for description. */
int gasket_register_device(const struct gasket_driver_desc *driver_desc)
{
int i, ret;
int desc_idx = -1;
struct gasket_internal_desc *internal;
pr_info("Initializing Gasket framework device\n");
/* Check for duplicates and find a free slot. */
mutex_lock(&g_mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
if (g_descs[i].driver_desc == driver_desc) {
pr_err("%s driver already loaded/registered\n",
driver_desc->name);
mutex_unlock(&g_mutex);
return -EBUSY;
}
}
/* This and the above loop could be combined, but this reads easier. */
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
if (!g_descs[i].driver_desc) {
g_descs[i].driver_desc = driver_desc;
desc_idx = i;
break;
}
}
mutex_unlock(&g_mutex);
pr_info("Loaded %s driver, framework version %s\n",
driver_desc->name, GASKET_FRAMEWORK_VERSION);
if (desc_idx == -1) {
pr_err("Too many Gasket drivers loaded: %d\n",
GASKET_FRAMEWORK_DESC_MAX);
return -EBUSY;
}
/* Internal structure setup. */
pr_debug("Performing initial internal structure setup.\n");
internal = &g_descs[desc_idx];
mutex_init(&internal->mutex);
memset(internal->devs, 0, sizeof(struct gasket_dev *) * GASKET_DEV_MAX);
memset(&internal->pci, 0, sizeof(internal->pci));
internal->pci.name = driver_desc->name;
internal->pci.id_table = driver_desc->pci_id_table;
internal->pci.probe = gasket_pci_probe;
internal->pci.remove = gasket_pci_remove;
internal->class =
class_create(driver_desc->module, driver_desc->name);
if (IS_ERR(internal->class)) {
pr_err("Cannot register %s class [ret=%ld]\n",
driver_desc->name, PTR_ERR(internal->class));
ret = PTR_ERR(internal->class);
goto unregister_gasket_driver;
}
/*
* Not using pci_register_driver() (without underscores), as it
* depends on KBUILD_MODNAME, and this is a shared file.
*/
pr_debug("Registering PCI driver.\n");
ret = __pci_register_driver(
&internal->pci, driver_desc->module, driver_desc->name);
if (ret) {
pr_err("cannot register pci driver [ret=%d]\n", ret);
goto fail1;
}
pr_debug("Registering char driver.\n");
ret = register_chrdev_region(
MKDEV(driver_desc->major, driver_desc->minor), GASKET_DEV_MAX,
driver_desc->name);
if (ret) {
pr_err("cannot register char driver [ret=%d]\n", ret);
goto fail2;
}
pr_info("Driver registered successfully.\n");
return 0;
fail2:
pci_unregister_driver(&internal->pci);
fail1:
class_destroy(internal->class);
unregister_gasket_driver:
mutex_lock(&g_mutex);
g_descs[desc_idx].driver_desc = NULL;
mutex_unlock(&g_mutex);
return ret;
}
EXPORT_SYMBOL(gasket_register_device);
/* See gasket_core.h for description. */
void gasket_unregister_device(const struct gasket_driver_desc *driver_desc)
{
int i, desc_idx;
struct gasket_internal_desc *internal_desc = NULL;
mutex_lock(&g_mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
if (g_descs[i].driver_desc == driver_desc) {
internal_desc = &g_descs[i];
desc_idx = i;
break;
}
}
mutex_unlock(&g_mutex);
if (!internal_desc) {
pr_err("request to unregister unknown desc: %s, %d:%d\n",
driver_desc->name, driver_desc->major,
driver_desc->minor);
return;
}
unregister_chrdev_region(
MKDEV(driver_desc->major, driver_desc->minor), GASKET_DEV_MAX);
pci_unregister_driver(&internal_desc->pci);
class_destroy(internal_desc->class);
/* Finally, effectively "remove" the driver. */
mutex_lock(&g_mutex);
g_descs[desc_idx].driver_desc = NULL;
mutex_unlock(&g_mutex);
pr_info("removed %s driver\n", driver_desc->name);
}
EXPORT_SYMBOL(gasket_unregister_device);
static int __init gasket_init(void)
{
int i;
pr_info("Performing one-time init of the Gasket framework.\n");
/* Check for duplicates and find a free slot. */
mutex_lock(&g_mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
g_descs[i].driver_desc = NULL;
mutex_init(&g_descs[i].mutex);
}
gasket_sysfs_init();
mutex_unlock(&g_mutex);
return 0;
}
static void __exit gasket_exit(void)
{
/* No deinit/dealloc needed at present. */
pr_info("Removing Gasket framework module.\n");
}
MODULE_DESCRIPTION("Google Gasket driver framework");
MODULE_VERSION(GASKET_FRAMEWORK_VERSION);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rob Springer <rspringer@google.com>");
module_init(gasket_init);
module_exit(gasket_exit);
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