From: Todd Poynor <toddpoynor@xxxxxxxxxx> Remove forward declarations of static functions, move code to avoid forward references, for kernel style. Signed-off-by: Todd Poynor <toddpoynor@xxxxxxxxxx> --- drivers/staging/gasket/gasket_core.c | 1900 +++++++++++++------------- 1 file changed, 922 insertions(+), 978 deletions(-) diff --git a/drivers/staging/gasket/gasket_core.c b/drivers/staging/gasket/gasket_core.c index c00774059f9e..b5a7254fbfb3 100644 --- a/drivers/staging/gasket/gasket_core.c +++ b/drivers/staging/gasket/gasket_core.c @@ -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@xxxxxxxxxx>"); -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); - 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); + 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; - 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) +/* 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); + /* Only delete the device if it has been successfully added. */ + if (gasket_dev->dev_info.cdev_added) + cdev_del(&gasket_dev->dev_info.cdev); - 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); + gasket_dev->status = GASKET_STATUS_DEAD; - 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_interrupt_cleanup(gasket_dev); - /* 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) { + if (gasket_dev->page_table[i]) { + gasket_page_table_reset(gasket_dev->page_table[i]); + gasket_page_table_cleanup(gasket_dev->page_table[i]); + } + } - 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); - } + check_and_invoke_callback(gasket_dev, driver_desc->disable_dev_cb); +} - /* Closes device, enters power save. */ - gasket_check_and_invoke_callback_nolock( - gasket_dev, driver_desc->device_close_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,12 +847,42 @@ 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) -{ - ulong bar_offset; - ulong virt_offset; +/* + * 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; +} + +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) { - dev_dbg(gasket_dev->dev, - "Interrupt system reported status %d.\n", status); - return status; + mutex_lock(&gasket_dev->mutex); + + dev_dbg(gasket_dev->dev, + "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; } - for (i = 0; i < driver_desc->num_page_tables; ++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; + /* 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); } - return GASKET_STATUS_ALIVE; + 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) { + 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); + } + + /* Closes device, enters power save. */ + gasket_check_and_invoke_callback_nolock( + gasket_dev, driver_desc->device_close_cb); + } + } + + 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; + const struct gasket_driver_desc *driver_desc = + internal_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); + 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; + + 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; } -static ssize_t gasket_sysfs_data_show( - struct device *device, struct device_attribute *attr, char *buf) +/* + * 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, ret = 0; - ssize_t current_written = 0; + int i; + struct gasket_internal_desc *internal_desc; + struct gasket_dev *gasket_dev = NULL; 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; - - gasket_dev = gasket_sysfs_get_device_data(device); - if (!gasket_dev) { - dev_err(device, "No sysfs mapping found for device\n"); - return 0; + /* 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); - 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; + 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); - driver_desc = gasket_dev->internal_desc->driver_desc; + if (!gasket_dev) + return; - 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; + 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; + ++i; } - gasket_sysfs_put_attr(device, gasket_attr); - gasket_sysfs_put_device_data(device, gasket_dev); + 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; + } + + /* 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@xxxxxxxxxx>"); +module_init(gasket_init); +module_exit(gasket_exit); -- 2.18.0.345.g5c9ce644c3-goog _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel