提交 9a69f508 编写于 作者: S Simon Que 提交者: Greg Kroah-Hartman

drivers/staging: Gasket driver framework + Apex driver

The Gasket (Google ASIC Software, Kernel Extensions, and Tools) kernel
framework is a generic, flexible system that supports thin kernel
drivers. Gasket kernel drivers are expected to handle opening and
closing devices, mmap'ing BAR space as requested, a small selection of
ioctls, and handling page table translation (covered below). Any other
functions should be handled by userspace code.

The Gasket common module is not enough to run a device. In order to
customize the Gasket code for a given piece of hardware, a device
specific module must be created. At a minimum, this module must define a
struct gasket_driver_desc containing the device-specific data for use by
the framework; in addition, the module must declare an __init function
that calls gasket_register_device with the module's gasket_driver_desc
struct. Finally, the driver must define an exit function that calls
gasket_unregister_device with the module's gasket_driver_desc struct.

One of the core assumptions of the Gasket framework is that precisely
one process is allowed to have an open write handle to the device node
at any given time. (That process may, once it has one write handle, open
any number of additional write handles.) This is accomplished by
tracking open and close data for each driver instance.
Signed-off-by: NRob Springer <rspringer@google.com>
Signed-off-by: NJohn Joseph <jnjoseph@google.com>
Signed-off-by: NSimon Que <sque@chromium.org>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
上级 ee55fe55
......@@ -5928,6 +5928,13 @@ F: scripts/gcc-plugin.sh
F: scripts/Makefile.gcc-plugins
F: Documentation/gcc-plugins.txt
GASKET DRIVER FRAMEWORK
M: Rob Springer <rspringer@google.com>
M: John Joseph <jnjoseph@google.com>
M: Ben Chan <benchan@chromium.org>
S: Maintained
F: drivers/staging/gasket/
GCOV BASED KERNEL PROFILING
M: Peter Oberparleiter <oberpar@linux.ibm.com>
S: Maintained
......
......@@ -124,4 +124,6 @@ source "drivers/staging/mt7621-eth/Kconfig"
source "drivers/staging/mt7621-dts/Kconfig"
source "drivers/staging/gasket/Kconfig"
endif # STAGING
......@@ -53,3 +53,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/
obj-$(CONFIG_SOC_MT7621) += mt7621-eth/
obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
menu "Gasket devices"
config STAGING_GASKET_FRAMEWORK
tristate "Gasket framework"
depends on PCI && X86_64
help
This framework supports Gasket-compatible devices, such as Apex.
It is required for any of the following module(s).
To compile this driver as a module, choose M here. The module
will be called "gasket".
config STAGING_APEX_DRIVER
tristate "Apex Driver"
depends on STAGING_GASKET_FRAMEWORK
help
This driver supports the Apex device. Say Y if you want to
include this driver in the kernel.
To compile this driver as a module, choose M here. The module
will be called "apex".
endmenu
#
# Makefile for Gasket framework and dependent drivers.
#
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket.o
obj-$(CONFIG_STAGING_APEX_DRIVER) += apex.o
gasket-objs := gasket_core.o gasket_ioctl.o gasket_interrupt.o gasket_page_table.o gasket_sysfs.o
apex-objs := apex_driver.o
This is a list of things that need to be done to get this driver out of the
staging directory.
- Use SPDX tags to show the license of the file, and no more "boiler-plate"
license text is needed.
- Remove static function declarations.
- Document sysfs files with Documentation/ABI/ entries.
- Use misc interface instead of major number for driver version description.
- Add descriptions of module_param's
- Remove gasket-specific logging functions.
- apex_get_status() should actually check status.
- Static functions don't need kernel doc formatting, can be simplified.
- Fix multi-line alignment formatting to look like:
int ret = long_function_name(device, VARIABLE1, VARIABLE2,
VARIABLE3, VARIABLE4);
- "drivers" should never be dealing with "raw" sysfs calls or mess around with
kobjects at all. The driver core should handle all of this for you
automaically. There should not be a need for raw attribute macros.
/*
* Apex kernel-userspace interface definition(s).
*
* Copyright (C) 2018 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __APEX_H__
#define __APEX_H__
#include <linux/ioctl.h>
#include <linux/bitops.h>
#include "gasket.h"
/* Structural definitions/macros. */
/* The number of PCI BARs. */
#define APEX_NUM_BARS 3
/* Size of a memory page in bytes, and the related number of bits to shift. */
#define APEX_PAGE_SHIFT 12
#define APEX_PAGE_SIZE BIT(APEX_PAGE_SHIFT)
#define APEX_EXTENDED_SHIFT 63 /* Extended address bit position. */
/* Addresses are 2^3=8 bytes each. */
/* page in second level page table */
/* holds APEX_PAGE_SIZE/8 addresses */
#define APEX_ADDR_SHIFT 3
#define APEX_LEVEL_SHIFT (APEX_PAGE_SHIFT - APEX_ADDR_SHIFT)
#define APEX_LEVEL_SIZE BIT(APEX_LEVEL_SHIFT)
#define APEX_PAGE_TABLE_MAX 65536
#define APEX_SIMPLE_PAGE_MAX APEX_PAGE_TABLE_MAX
#define APEX_EXTENDED_PAGE_MAX (APEX_PAGE_TABLE_MAX << APEX_LEVEL_SHIFT)
/* Check reset 120 times */
#define APEX_RESET_RETRY 120
/* Wait 100 ms between checks. Total 12 sec wait maximum. */
#define APEX_RESET_DELAY 100
#define APEX_CHIP_INIT_DONE 2
#define APEX_RESET_ACCEPTED 0
enum apex_reset_types {
APEX_CHIP_REINIT_RESET = 3,
};
/* Interrupt defines */
/* Gasket device interrupts enums must be dense (i.e., no empty slots). */
enum apex_interrupt {
APEX_INTERRUPT_INSTR_QUEUE = 0,
APEX_INTERRUPT_INPUT_ACTV_QUEUE = 1,
APEX_INTERRUPT_PARAM_QUEUE = 2,
APEX_INTERRUPT_OUTPUT_ACTV_QUEUE = 3,
APEX_INTERRUPT_SC_HOST_0 = 4,
APEX_INTERRUPT_SC_HOST_1 = 5,
APEX_INTERRUPT_SC_HOST_2 = 6,
APEX_INTERRUPT_SC_HOST_3 = 7,
APEX_INTERRUPT_TOP_LEVEL_0 = 8,
APEX_INTERRUPT_TOP_LEVEL_1 = 9,
APEX_INTERRUPT_TOP_LEVEL_2 = 10,
APEX_INTERRUPT_TOP_LEVEL_3 = 11,
APEX_INTERRUPT_FATAL_ERR = 12,
APEX_INTERRUPT_COUNT = 13,
};
/*
* Clock Gating ioctl.
*/
struct apex_gate_clock_ioctl {
/* Enter or leave clock gated state. */
u64 enable;
/* If set, enter clock gating state, regardless of custom block's
* internal idle state
*/
u64 force_idle;
};
/* Base number for all Apex-common IOCTLs */
#define APEX_IOCTL_BASE 0x7F
/* Enable/Disable clock gating. */
#define APEX_IOCTL_GATE_CLOCK \
_IOW(APEX_IOCTL_BASE, 0, struct apex_gate_clock_ioctl)
#endif /* __APEX_H__ */
此差异已折叠。
/* Common Gasket device kernel and user space declarations.
*
* Copyright (C) 2018 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __GASKET_H__
#define __GASKET_H__
#include <linux/ioctl.h>
#include <linux/types.h>
/* ioctl structure declarations */
/* Ioctl structures are padded to a multiple of 64 bits */
/* and padded to put 64 bit values on 64 bit boundaries. */
/* Unsigned 64 bit integers are used to hold pointers. */
/* This helps compatibility between 32 and 64 bits. */
/*
* Common structure for ioctls associating an eventfd with a device interrupt,
* when using the Gasket interrupt module.
*/
struct gasket_interrupt_eventfd {
u64 interrupt;
u64 event_fd;
};
/*
* Common structure for ioctls mapping and unmapping buffers when using the
* Gasket page_table module.
*/
struct gasket_page_table_ioctl {
u64 page_table_index;
u64 size;
u64 host_address;
u64 device_address;
};
/*
* Common structure for ioctls mapping and unmapping buffers when using the
* Gasket page_table module.
* dma_address: phys addr start of coherent memory, allocated by kernel
*/
struct gasket_coherent_alloc_config_ioctl {
u64 page_table_index;
u64 enable;
u64 size;
u64 dma_address;
};
/* Base number for all Gasket-common IOCTLs */
#define GASKET_IOCTL_BASE 0xDC
/* Reset the device using the specified reset type. */
#define GASKET_IOCTL_RESET _IOW(GASKET_IOCTL_BASE, 0, unsigned long)
/* Associate the specified [event]fd with the specified interrupt. */
#define GASKET_IOCTL_SET_EVENTFD \
_IOW(GASKET_IOCTL_BASE, 1, struct gasket_interrupt_eventfd)
/*
* Clears any eventfd associated with the specified interrupt. The (ulong)
* argument is the interrupt number to clear.
*/
#define GASKET_IOCTL_CLEAR_EVENTFD _IOW(GASKET_IOCTL_BASE, 2, unsigned long)
/*
* [Loopbacks only] Requests that the loopback device send the specified
* interrupt to the host. The (ulong) argument is the number of the interrupt to
* send.
*/
#define GASKET_IOCTL_LOOPBACK_INTERRUPT \
_IOW(GASKET_IOCTL_BASE, 3, unsigned long)
/* Queries the kernel for the number of page tables supported by the device. */
#define GASKET_IOCTL_NUMBER_PAGE_TABLES _IOR(GASKET_IOCTL_BASE, 4, u64)
/*
* Queries the kernel for the maximum size of the page table. Only the size and
* page_table_index fields are used from the struct gasket_page_table_ioctl.
*/
#define GASKET_IOCTL_PAGE_TABLE_SIZE \
_IOWR(GASKET_IOCTL_BASE, 5, struct gasket_page_table_ioctl)
/*
* Queries the kernel for the current simple page table size. Only the size and
* page_table_index fields are used from the struct gasket_page_table_ioctl.
*/
#define GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE \
_IOWR(GASKET_IOCTL_BASE, 6, struct gasket_page_table_ioctl)
/*
* Tells the kernel to change the split between the number of simple and
* extended entries in the given page table. Only the size and page_table_index
* fields are used from the struct gasket_page_table_ioctl.
*/
#define GASKET_IOCTL_PARTITION_PAGE_TABLE \
_IOW(GASKET_IOCTL_BASE, 7, struct gasket_page_table_ioctl)
/*
* Tells the kernel to map size bytes at host_address to device_address in
* page_table_index page table.
*/
#define GASKET_IOCTL_MAP_BUFFER \
_IOW(GASKET_IOCTL_BASE, 8, struct gasket_page_table_ioctl)
/*
* Tells the kernel to unmap size bytes at host_address from device_address in
* page_table_index page table.
*/
#define GASKET_IOCTL_UNMAP_BUFFER \
_IOW(GASKET_IOCTL_BASE, 9, struct gasket_page_table_ioctl)
/* Clear the interrupt counts stored for this device. */
#define GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS _IO(GASKET_IOCTL_BASE, 10)
/* Enable/Disable and configure the coherent allocator. */
#define GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR \
_IOWR(GASKET_IOCTL_BASE, 11, struct gasket_coherent_alloc_config_ioctl)
#endif /* __GASKET_H__ */
/* Copyright (C) 2018 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __GASKET_CONSTANTS_H__
#define __GASKET_CONSTANTS_H__
#define GASKET_FRAMEWORK_VERSION "1.1.1"
/*
* The maximum number of simultaneous device types supported by the framework.
*/
#define GASKET_FRAMEWORK_DESC_MAX 2
/* The maximum devices per each type. */
#define GASKET_DEV_MAX 256
/* The number of supported (and possible) PCI BARs. */
#define GASKET_NUM_BARS 6
/* The number of supported Gasket page tables per device. */
#define GASKET_MAX_NUM_PAGE_TABLES 1
/* Maximum length of device names (driver name + minor number suffix + NULL). */
#define GASKET_NAME_MAX 32
/* Device status enumeration. */
enum gasket_status {
/*
* A device is DEAD if it has not been initialized or has had an error.
*/
GASKET_STATUS_DEAD = 0,
/*
* A device is LAMED if the hardware is healthy but the kernel was
* unable to enable some functionality (e.g. interrupts).
*/
GASKET_STATUS_LAMED,
/* A device is ALIVE if it is ready for operation. */
GASKET_STATUS_ALIVE,
/*
* This status is set when the driver is exiting and waiting for all
* handles to be closed.
*/
GASKET_STATUS_DRIVER_EXIT,
};
#endif
此差异已折叠。
此差异已折叠。
/* Copyright (C) 2018 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "gasket_interrupt.h"
#include "gasket_constants.h"
#include "gasket_core.h"
#include "gasket_logging.h"
#include "gasket_sysfs.h"
#include <linux/interrupt.h>
#include <linux/version.h>
#ifdef GASKET_KERNEL_TRACE_SUPPORT
#define CREATE_TRACE_POINTS
#include <trace/events/gasket_interrupt.h>
#else
#define trace_gasket_interrupt_event(x, ...)
#endif
/* Retry attempts if the requested number of interrupts aren't available. */
#define MSIX_RETRY_COUNT 3
/* Instance interrupt management data. */
struct gasket_interrupt_data {
/* The name associated with this interrupt data. */
const char *name;
/* Interrupt type. See gasket_interrupt_type in gasket_core.h */
int type;
/* The PCI device [if any] associated with the owning device. */
struct pci_dev *pci_dev;
/* Set to 1 if MSI-X has successfully been configred, 0 otherwise. */
int msix_configured;
/* The number of interrupts requested by the owning device. */
int num_interrupts;
/* A pointer to the interrupt descriptor struct for this device. */
const struct gasket_interrupt_desc *interrupts;
/* The index of the bar into which interrupts should be mapped. */
int interrupt_bar_index;
/* The width of a single interrupt in a packed interrupt register. */
int pack_width;
/* offset of wire interrupt registers */
const struct gasket_wire_interrupt_offsets *wire_interrupt_offsets;
/*
* Design-wise, these elements should be bundled together, but
* pci_enable_msix's interface requires that they be managed
* individually (requires array of struct msix_entry).
*/
/* The number of successfully configured interrupts. */
int num_configured;
/* The MSI-X data for each requested/configured interrupt. */
struct msix_entry *msix_entries;
/* The eventfd "callback" data for each interrupt. */
struct eventfd_ctx **eventfd_ctxs;
/* The number of times each interrupt has been called. */
ulong *interrupt_counts;
/* Linux IRQ number. */
int irq;
};
/* Function definitions. */
static ssize_t interrupt_sysfs_show(
struct device *device, struct device_attribute *attr, char *buf);
static irqreturn_t gasket_msix_interrupt_handler(int irq, void *dev_id);
/* Structures to display interrupt counts in sysfs. */
enum interrupt_sysfs_attribute_type {
ATTR_INTERRUPT_COUNTS,
};
static struct gasket_sysfs_attribute interrupt_sysfs_attrs[] = {
GASKET_SYSFS_RO(
interrupt_counts, interrupt_sysfs_show, ATTR_INTERRUPT_COUNTS),
GASKET_END_OF_ATTR_ARRAY,
};
/*
* Set up device registers for interrupt handling.
* @gasket_dev: The Gasket information structure for this device.
*
* Sets up the device registers with the correct indices for the relevant
* interrupts.
*/
static void gasket_interrupt_setup(struct gasket_dev *gasket_dev);
/* MSIX init and cleanup. */
static int gasket_interrupt_msix_init(
struct gasket_interrupt_data *interrupt_data);
static void gasket_interrupt_msix_cleanup(
struct gasket_interrupt_data *interrupt_data);
static void force_msix_interrupt_unmasking(struct gasket_dev *gasket_dev);
int gasket_interrupt_init(
struct gasket_dev *gasket_dev, const char *name, int type,
const struct gasket_interrupt_desc *interrupts,
int num_interrupts, int pack_width, int bar_index,
const struct gasket_wire_interrupt_offsets *wire_int_offsets)
{
int ret;
struct gasket_interrupt_data *interrupt_data;
interrupt_data = kzalloc(
sizeof(struct gasket_interrupt_data), GFP_KERNEL);
if (!interrupt_data)
return -ENOMEM;
gasket_dev->interrupt_data = interrupt_data;
interrupt_data->name = name;
interrupt_data->type = type;
interrupt_data->pci_dev = gasket_dev->pci_dev;
interrupt_data->num_interrupts = num_interrupts;
interrupt_data->interrupts = interrupts;
interrupt_data->interrupt_bar_index = bar_index;
interrupt_data->pack_width = pack_width;
interrupt_data->num_configured = 0;
interrupt_data->wire_interrupt_offsets = wire_int_offsets;
/* Allocate all dynamic structures. */
interrupt_data->msix_entries = kzalloc(
sizeof(struct msix_entry) * num_interrupts, GFP_KERNEL);
if (!interrupt_data->msix_entries) {
kfree(interrupt_data);
return -ENOMEM;
}
interrupt_data->eventfd_ctxs = kzalloc(
sizeof(struct eventfd_ctx *) * num_interrupts, GFP_KERNEL);
if (!interrupt_data->eventfd_ctxs) {
kfree(interrupt_data->msix_entries);
kfree(interrupt_data);
return -ENOMEM;
}
interrupt_data->interrupt_counts = kzalloc(
sizeof(ulong) * num_interrupts, GFP_KERNEL);
if (!interrupt_data->interrupt_counts) {
kfree(interrupt_data->eventfd_ctxs);
kfree(interrupt_data->msix_entries);
kfree(interrupt_data);
return -ENOMEM;
}
switch (interrupt_data->type) {
case PCI_MSIX:
ret = gasket_interrupt_msix_init(interrupt_data);
if (ret)
break;
force_msix_interrupt_unmasking(gasket_dev);
break;
case PCI_MSI:
case PLATFORM_WIRE:
default:
gasket_nodev_error(
"Cannot handle unsupported interrupt type %d.",
interrupt_data->type);
ret = -EINVAL;
};
if (ret) {
/* Failing to setup interrupts will cause the device to report
* GASKET_STATUS_LAMED. But it is not fatal.
*/
gasket_log_warn(
gasket_dev, "Couldn't initialize interrupts: %d", ret);
return 0;
}
gasket_interrupt_setup(gasket_dev);
gasket_sysfs_create_entries(
gasket_dev->dev_info.device, interrupt_sysfs_attrs);
return 0;
}
static int gasket_interrupt_msix_init(
struct gasket_interrupt_data *interrupt_data)
{
int ret = 1;
int i;
for (i = 0; i < interrupt_data->num_interrupts; i++) {
interrupt_data->msix_entries[i].entry = i;
interrupt_data->msix_entries[i].vector = 0;
interrupt_data->eventfd_ctxs[i] = NULL;
}
/* Retry MSIX_RETRY_COUNT times if not enough IRQs are available. */
for (i = 0; i < MSIX_RETRY_COUNT && ret > 0; i++)
ret = pci_enable_msix_exact(interrupt_data->pci_dev,
interrupt_data->msix_entries,
interrupt_data->num_interrupts);
if (ret)
return ret > 0 ? -EBUSY : ret;
interrupt_data->msix_configured = 1;
for (i = 0; i < interrupt_data->num_interrupts; i++) {
ret = request_irq(
interrupt_data->msix_entries[i].vector,
gasket_msix_interrupt_handler, 0, interrupt_data->name,
interrupt_data);
if (ret) {
gasket_nodev_error(
"Cannot get IRQ for interrupt %d, vector %d; "
"%d\n",
i, interrupt_data->msix_entries[i].vector, ret);
return ret;
}
interrupt_data->num_configured++;
}
return 0;
}
static void gasket_interrupt_msix_cleanup(
struct gasket_interrupt_data *interrupt_data)
{
int i;
for (i = 0; i < interrupt_data->num_configured; i++)
free_irq(interrupt_data->msix_entries[i].vector,
interrupt_data);
interrupt_data->num_configured = 0;
if (interrupt_data->msix_configured)
pci_disable_msix(interrupt_data->pci_dev);
interrupt_data->msix_configured = 0;
}
/*
* On QCM DragonBoard, we exit gasket_interrupt_msix_init() and kernel interrupt
* setup code with MSIX vectors masked. This is wrong because nothing else in
* the driver will normally touch the MSIX vectors.
*
* As a temporary hack, force unmasking there.
*
* TODO: Figure out why QCM kernel doesn't unmask the MSIX vectors, after
* gasket_interrupt_msix_init(), and remove this code.
*/
static void force_msix_interrupt_unmasking(struct gasket_dev *gasket_dev)
{
int i;
#define MSIX_VECTOR_SIZE 16
#define MSIX_MASK_BIT_OFFSET 12
#define APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE 0x46800
for (i = 0; i < gasket_dev->interrupt_data->num_configured; i++) {
/* Check if the MSIX vector is unmasked */
ulong location = APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE +
MSIX_MASK_BIT_OFFSET + i * MSIX_VECTOR_SIZE;
u32 mask =
gasket_dev_read_32(
gasket_dev,
gasket_dev->interrupt_data->interrupt_bar_index,
location);
if (!(mask & 1))
continue;
/* Unmask the msix vector (clear 32 bits) */
gasket_dev_write_32(
gasket_dev, 0,
gasket_dev->interrupt_data->interrupt_bar_index,
location);
}
#undef MSIX_VECTOR_SIZE
#undef MSIX_MASK_BIT_OFFSET
#undef APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE
}
int gasket_interrupt_reinit(struct gasket_dev *gasket_dev)
{
int ret;
if (!gasket_dev->interrupt_data) {
gasket_log_error(
gasket_dev,
"Attempted to reinit uninitialized interrupt data.");
return -EINVAL;
}
switch (gasket_dev->interrupt_data->type) {
case PCI_MSIX:
gasket_interrupt_msix_cleanup(gasket_dev->interrupt_data);
ret = gasket_interrupt_msix_init(gasket_dev->interrupt_data);
if (ret)
break;
force_msix_interrupt_unmasking(gasket_dev);
break;
case PCI_MSI:
case PLATFORM_WIRE:
default:
gasket_nodev_error(
"Cannot handle unsupported interrupt type %d.",
gasket_dev->interrupt_data->type);
ret = -EINVAL;
};
if (ret) {
/* Failing to setup MSIx will cause the device
* to report GASKET_STATUS_LAMED, but is not fatal.
*/
gasket_log_warn(gasket_dev, "Couldn't init msix: %d", ret);
return 0;
}
gasket_interrupt_setup(gasket_dev);
return 0;
}
/* See gasket_interrupt.h for description. */
int gasket_interrupt_reset_counts(struct gasket_dev *gasket_dev)
{
gasket_log_debug(gasket_dev, "Clearing interrupt counts.");
memset(gasket_dev->interrupt_data->interrupt_counts, 0,
gasket_dev->interrupt_data->num_interrupts *
sizeof(*gasket_dev->interrupt_data->interrupt_counts));
return 0;
}
/*
* Set up device registers for interrupt handling.
* @gasket_dev: The Gasket information structure for this device.
*
* Sets up the device registers with the correct indices for the relevant
* interrupts.
*/
static void gasket_interrupt_setup(struct gasket_dev *gasket_dev)
{
int i;
int pack_shift;
ulong mask;
ulong value;
struct gasket_interrupt_data *interrupt_data =
gasket_dev->interrupt_data;
if (!interrupt_data) {
gasket_log_error(
gasket_dev, "Interrupt data is not initialized.");
return;
}
gasket_log_debug(gasket_dev, "Running interrupt setup.");
if (interrupt_data->type == PLATFORM_WIRE ||
interrupt_data->type == PCI_MSI) {
/* Nothing needs to be done for platform or PCI devices. */
return;
}
if (interrupt_data->type != PCI_MSIX) {
gasket_nodev_error(
"Cannot handle unsupported interrupt type %d.",
interrupt_data->type);
return;
}
/* Setup the MSIX table. */
for (i = 0; i < interrupt_data->num_interrupts; i++) {
/*
* If the interrupt is not packed, we can write the index into
* the register directly. If not, we need to deal with a read-
* modify-write and shift based on the packing index.
*/
gasket_log_debug(
gasket_dev,
"Setting up interrupt index %d with index 0x%llx and "
"packing %d",
interrupt_data->interrupts[i].index,
interrupt_data->interrupts[i].reg,
interrupt_data->interrupts[i].packing);
if (interrupt_data->interrupts[i].packing == UNPACKED) {
value = interrupt_data->interrupts[i].index;
} else {
switch (interrupt_data->interrupts[i].packing) {
case PACK_0:
pack_shift = 0;
break;
case PACK_1:
pack_shift = interrupt_data->pack_width;
break;
case PACK_2:
pack_shift = 2 * interrupt_data->pack_width;
break;
case PACK_3:
pack_shift = 3 * interrupt_data->pack_width;
break;
default:
gasket_nodev_error(
"Found interrupt description with "
"unknown enum %d.",
interrupt_data->interrupts[i].packing);
return;
}
mask = ~(0xFFFF << pack_shift);
value = gasket_dev_read_64(
gasket_dev,
interrupt_data->interrupt_bar_index,
interrupt_data->interrupts[i].reg) &
mask;
value |= interrupt_data->interrupts[i].index
<< pack_shift;
}
gasket_dev_write_64(gasket_dev, value,
interrupt_data->interrupt_bar_index,
interrupt_data->interrupts[i].reg);
}
}
/* See gasket_interrupt.h for description. */
void gasket_interrupt_pause(struct gasket_dev *gasket_dev, int enable_pause)
{
WARN_ON(!gasket_dev);
if (!gasket_dev->interrupt_data)
return; /* nothing to do */
if (gasket_dev->interrupt_data->type == PCI_MSI ||
gasket_dev->interrupt_data->type == PCI_MSIX) {
/* Nothing to be done for MSI/MSIX just yet. */
}
if (gasket_dev->interrupt_data->type == PLATFORM_WIRE) {
/* Nothing to be done for PLATFORM_WIRE */
}
}
EXPORT_SYMBOL(gasket_interrupt_pause);
void gasket_interrupt_cleanup(struct gasket_dev *gasket_dev)
{
struct gasket_interrupt_data *interrupt_data =
gasket_dev->interrupt_data;
/*
* It is possible to get an error code from gasket_interrupt_init
* before interrupt_data has been allocated, so check it.
*/
if (!interrupt_data)
return;
switch (interrupt_data->type) {
case PCI_MSIX:
gasket_interrupt_msix_cleanup(interrupt_data);
break;
case PCI_MSI:
case PLATFORM_WIRE:
default:
gasket_nodev_error(
"Cannot handle unsupported interrupt type %d.",
interrupt_data->type);
};
kfree(interrupt_data->interrupt_counts);
kfree(interrupt_data->eventfd_ctxs);
kfree(interrupt_data->msix_entries);
kfree(interrupt_data);
gasket_dev->interrupt_data = NULL;
}
int gasket_interrupt_system_status(struct gasket_dev *gasket_dev)
{
if (!gasket_dev->interrupt_data) {
gasket_nodev_info("Interrupt data is null.");
return GASKET_STATUS_DEAD;
}
if (!gasket_dev->interrupt_data->msix_configured) {
gasket_nodev_info("Interrupt not initialized.");
return GASKET_STATUS_LAMED;
}
if (gasket_dev->interrupt_data->num_configured !=
gasket_dev->interrupt_data->num_interrupts) {
gasket_nodev_info("Not all interrupts were configured.");
return GASKET_STATUS_LAMED;
}
return GASKET_STATUS_ALIVE;
}
int gasket_interrupt_set_eventfd(
struct gasket_interrupt_data *interrupt_data, int interrupt,
int event_fd)
{
struct eventfd_ctx *ctx = eventfd_ctx_fdget(event_fd);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
if (interrupt < 0 || interrupt > interrupt_data->num_interrupts)
return -EINVAL;
interrupt_data->eventfd_ctxs[interrupt] = ctx;
return 0;
}
int gasket_interrupt_clear_eventfd(
struct gasket_interrupt_data *interrupt_data, int interrupt)
{
if (interrupt < 0 || interrupt > interrupt_data->num_interrupts)
return -EINVAL;
interrupt_data->eventfd_ctxs[interrupt] = NULL;
return 0;
}
int gasket_interrupt_trigger_eventfd(
struct gasket_interrupt_data *interrupt_data, int interrupt)
{
struct eventfd_ctx *ctx = interrupt_data->eventfd_ctxs[interrupt];
if (!ctx)
return -EINVAL;
eventfd_signal(ctx, 1);
return 0;
}
struct msix_entry *gasket_interrupt_get_msix_entries(
struct gasket_interrupt_data *interrupt_data)
{
return interrupt_data->msix_entries;
}
struct eventfd_ctx **gasket_interrupt_get_eventfd_ctxs(
struct gasket_interrupt_data *interrupt_data)
{
return interrupt_data->eventfd_ctxs;
}
EXPORT_SYMBOL(gasket_interrupt_get_eventfd_ctxs);
static ssize_t interrupt_sysfs_show(
struct device *device, struct device_attribute *attr, char *buf)
{
int i, ret;
ssize_t written = 0, total_written = 0;
struct gasket_interrupt_data *interrupt_data;
struct gasket_dev *gasket_dev;
struct gasket_sysfs_attribute *gasket_attr;
enum interrupt_sysfs_attribute_type sysfs_type;
gasket_dev = gasket_sysfs_get_device_data(device);
if (!gasket_dev) {
gasket_nodev_error(
"No sysfs mapping found for device 0x%p", device);
return 0;
}
gasket_attr = gasket_sysfs_get_attr(device, attr);
if (!gasket_attr) {
gasket_nodev_error(
"No sysfs attr data found for device 0x%p", device);
gasket_sysfs_put_device_data(device, gasket_dev);
return 0;
}
sysfs_type = (enum interrupt_sysfs_attribute_type)
gasket_attr->data.attr_type;
interrupt_data = gasket_dev->interrupt_data;
switch (sysfs_type) {
case ATTR_INTERRUPT_COUNTS:
for (i = 0; i < interrupt_data->num_interrupts; ++i) {
written =
scnprintf(buf, PAGE_SIZE - total_written,
"0x%02x: %ld\n", i,
interrupt_data->interrupt_counts[i]);
total_written += written;
buf += written;
}
ret = total_written;
break;
default:
gasket_log_error(
gasket_dev, "Unknown attribute: %s", attr->attr.name);
ret = 0;
break;
}
gasket_sysfs_put_attr(device, gasket_attr);
gasket_sysfs_put_device_data(device, gasket_dev);
return ret;
}
/*
* MSIX interrupt handler, used with PCI driver.
*/
static irqreturn_t gasket_msix_interrupt_handler(int irq, void *dev_id)
{
struct eventfd_ctx *ctx;
struct gasket_interrupt_data *interrupt_data = dev_id;
int interrupt = -1;
int i;
/* If this linear lookup is a problem, we can maintain a map/hash. */
for (i = 0; i < interrupt_data->num_interrupts; i++) {
if (interrupt_data->msix_entries[i].vector == irq) {
interrupt = interrupt_data->msix_entries[i].entry;
break;
}
}
if (interrupt == -1) {
gasket_nodev_error("Received unknown irq %d", irq);
return IRQ_HANDLED;
}
trace_gasket_interrupt_event(interrupt_data->name, interrupt);
ctx = interrupt_data->eventfd_ctxs[interrupt];
if (ctx)
eventfd_signal(ctx, 1);
++(interrupt_data->interrupt_counts[interrupt]);
return IRQ_HANDLED;
}
/*
* Gasket common interrupt module. Defines functions for enabling
* eventfd-triggered interrupts between a Gasket device and a host process.
*
* Copyright (C) 2018 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __GASKET_INTERRUPT_H__
#define __GASKET_INTERRUPT_H__
#include <linux/eventfd.h>
#include <linux/pci.h>
#include "gasket_core.h"
/* Note that this currently assumes that device interrupts are a dense set,
* numbered from 0 - (num_interrupts - 1). Should this have to change, these
* APIs will have to be updated.
*/
/* Opaque type used to hold interrupt subsystem data. */
struct gasket_interrupt_data;
/*
* Initialize the interrupt module.
* @gasket_dev: The Gasket device structure for the device to be initted.
* @type: Type of the interrupt. (See gasket_interrupt_type).
* @name: The name to associate with these interrupts.
* @interrupts: An array of all interrupt descriptions for this device.
* @num_interrupts: The length of the @interrupts array.
* @pack_width: The width, in bits, of a single field in a packed interrupt reg.
* @bar_index: The bar containing all interrupt registers.
*
* Allocates and initializes data to track interrupt state for a device.
* After this call, no interrupts will be configured/delivered; call
* gasket_interrupt_set_vector[_packed] to associate each interrupt with an
* __iomem location, then gasket_interrupt_set_eventfd to associate an eventfd
* with an interrupt.
*
* If num_interrupts interrupts are not available, this call will return a
* negative error code. In that case, gasket_interrupt_cleanup should still be
* called. Returns 0 on success (which can include a device where interrupts
* are not possible to set up, but is otherwise OK; that device will report
* status LAMED.)
*/
int gasket_interrupt_init(
struct gasket_dev *gasket_dev, const char *name, int type,
const struct gasket_interrupt_desc *interrupts,
int num_interrupts, int pack_width, int bar_index,
const struct gasket_wire_interrupt_offsets *wire_int_offsets);
/*
* Clean up a device's interrupt structure.
* @gasket_dev: The Gasket information structure for this device.
*
* Cleans up the device's interrupts and deallocates data.
*/
void gasket_interrupt_cleanup(struct gasket_dev *gasket_dev);
/*
* Clean up and re-initialize the MSI-x subsystem.
* @gasket_dev: The Gasket information structure for this device.
*
* Performs a teardown of the MSI-x subsystem and re-initializes it. Does not
* free the underlying data structures. Returns 0 on success and an error code
* on error.
*/
int gasket_interrupt_reinit(struct gasket_dev *gasket_dev);
/*
* Reset the counts stored in the interrupt subsystem.
* @gasket_dev: The Gasket information structure for this device.
*
* Sets the counts of all interrupts in the subsystem to 0.
*/
int gasket_interrupt_reset_counts(struct gasket_dev *gasket_dev);
/*
* Associates an eventfd with a device interrupt.
* @data: Pointer to device interrupt data.
* @interrupt: The device interrupt to configure.
* @event_fd: The eventfd to associate with the interrupt.
*
* Prepares the host to receive notification of device interrupts by associating
* event_fd with interrupt. Upon receipt of a device interrupt, event_fd will be
* signaled, after successful configuration.
*
* Returns 0 on success, a negative error code otherwise.
*/
int gasket_interrupt_set_eventfd(
struct gasket_interrupt_data *interrupt_data, int interrupt,
int event_fd);
/*
* Removes an interrupt-eventfd association.
* @data: Pointer to device interrupt data.
* @interrupt: The device interrupt to de-associate.
*
* Removes any eventfd associated with the specified interrupt, if any.
*/
int gasket_interrupt_clear_eventfd(
struct gasket_interrupt_data *interrupt_data, int interrupt);
/*
* Signals the eventfd associated with interrupt.
* @data: Pointer to device interrupt data.
* @interrupt: The device interrupt to signal for.
*
* Simulates a device interrupt by signaling the eventfd associated with
* interrupt, if any.
* Returns 0 if the eventfd was successfully triggered, a negative error code
* otherwise (if, for example, no eventfd was associated with interrupt).
*/
int gasket_interrupt_trigger_eventfd(
struct gasket_interrupt_data *interrupt_data, int interrupt);
/*
* The below functions exist for backwards compatibility.
* No new uses should be written.
*/
/*
* Retrieve a pointer to data's MSI-X
* entries.
* @data: The interrupt data from which to extract.
*
* Returns the internal pointer to data's MSI-X entries.
*/
struct msix_entry *gasket_interrupt_get_msix_entries(
struct gasket_interrupt_data *interrupt_data);
/*
* Get a pointer to data's eventfd contexts.
* @data: The interrupt data from which to extract.
*
* Returns the internal pointer to data's eventfd contexts.
*
* This function exists for backwards compatibility with older drivers.
* No new uses should be written.
*/
struct eventfd_ctx **gasket_interrupt_get_eventfd_ctxs(
struct gasket_interrupt_data *interrupt_data);
/*
* Get the health of the interrupt subsystem.
* @gasket_dev: The Gasket device struct.
*
* Returns DEAD if not set up, LAMED if initialization failed, and ALIVE
* otherwise.
*/
int gasket_interrupt_system_status(struct gasket_dev *gasket_dev);
/*
* Masks interrupts and de-register the handler.
* After an interrupt pause it is not guaranteed that the chip registers will
* be accessible anymore, since the chip may be in a power save mode,
* which means that the interrupt handler (if it were to happen) may not
* have a way to clear the interrupt condition.
* @gasket_dev: The Gasket device struct
* @enable_pause: Whether to pause or unpause the interrupts.
*/
void gasket_interrupt_pause(struct gasket_dev *gasket_dev, int enable_pause);
#endif
此差异已折叠。
/* Copyright (C) 2018 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __GASKET_IOCTL_H__
#define __GASKET_IOCTL_H__
#include "gasket_core.h"
/*
* Handle Gasket common ioctls.
* @filp: Pointer to the ioctl's file.
* @cmd: Ioctl command.
* @arg: Ioctl argument pointer.
*
* Returns 0 on success and nonzero on failure.
*/
long gasket_handle_ioctl(struct file *filp, uint cmd, ulong arg);
/*
* Determines if an ioctl is part of the standard Gasket framework.
* @cmd: The ioctl number to handle.
*
* Returns 1 if the ioctl is supported and 0 otherwise.
*/
long gasket_is_supported_ioctl(uint cmd);
#endif
/* Common logging utilities for the Gasket driver framework.
*
* Copyright (C) 2018 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/pci.h>
#include <linux/printk.h>
#ifndef _GASKET_LOGGING_H_
#define _GASKET_LOGGING_H_
/* Base macro; other logging can/should be built on top of this. */
#define gasket_dev_log(level, device, pci_dev, format, arg...) \
if (false) { \
if (pci_dev) { \
dev_##level(&(pci_dev)->dev, "%s: " format "\n", \
__func__, ##arg); \
} else { \
gasket_nodev_log(level, format, ##arg); \
} \
}
/* "No-device" logging macros. */
#define gasket_nodev_log(level, format, arg...) \
if (false) pr_##level("gasket: %s: " format "\n", __func__, ##arg)
#define gasket_nodev_debug(format, arg...) \
if (false) gasket_nodev_log(debug, format, ##arg)
#define gasket_nodev_info(format, arg...) \
if (false) gasket_nodev_log(info, format, ##arg)
#define gasket_nodev_warn(format, arg...) \
if (false) gasket_nodev_log(warn, format, ##arg)
#define gasket_nodev_error(format, arg...) \
if (false) gasket_nodev_log(err, format, ##arg)
/* gasket_dev logging macros */
#define gasket_log_info(gasket_dev, format, arg...) \
if (false) gasket_dev_log(info, (gasket_dev)->dev_info.device, \
(gasket_dev)->pci_dev, format, ##arg)
#define gasket_log_warn(gasket_dev, format, arg...) \
if (false) gasket_dev_log(warn, (gasket_dev)->dev_info.device, \
(gasket_dev)->pci_dev, format, ##arg)
#define gasket_log_error(gasket_dev, format, arg...) \
if (false) gasket_dev_log(err, (gasket_dev)->dev_info.device, \
(gasket_dev)->pci_dev, format, ##arg)
#define gasket_log_debug(gasket_dev, format, arg...) \
if (false){ \
if ((gasket_dev)->pci_dev) { \
dev_dbg(&((gasket_dev)->pci_dev)->dev, "%s: " format \
"\n", __func__, ##arg); \
} else { \
gasket_nodev_log(debug, format, ##arg); \
} \
}
#endif
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册