forked from Minki/linux
misc: mic: add threaded irq support in host driver
Convert mic_request_irq to mic_request_threaded_irq to support threaded irq for virtual devices on mic bus. Reviewed-by: Ashutosh Dixit <ashutosh.dixit@intel.com> Reviewed-by: Nikhil Rao <nikhil.rao@intel.com> Reviewed-by: Sudeep Dutt <sudeep.dutt@intel.com> Signed-off-by: Siva Yerramreddy <yshivakrishna@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
726526c355
commit
b8e439f48a
@ -24,28 +24,29 @@
|
||||
#include "../common/mic_dev.h"
|
||||
#include "mic_device.h"
|
||||
|
||||
/*
|
||||
* mic_invoke_callback - Invoke callback functions registered for
|
||||
* the corresponding source id.
|
||||
*
|
||||
* @mdev: pointer to the mic_device instance
|
||||
* @idx: The interrupt source id.
|
||||
*
|
||||
* Returns none.
|
||||
*/
|
||||
static inline void mic_invoke_callback(struct mic_device *mdev, int idx)
|
||||
static irqreturn_t mic_thread_fn(int irq, void *dev)
|
||||
{
|
||||
struct mic_device *mdev = dev;
|
||||
struct mic_intr_info *intr_info = mdev->intr_info;
|
||||
struct mic_irq_info *irq_info = &mdev->irq_info;
|
||||
struct mic_intr_cb *intr_cb;
|
||||
struct pci_dev *pdev = container_of(mdev->sdev->parent,
|
||||
struct pci_dev, dev);
|
||||
struct pci_dev, dev);
|
||||
int i;
|
||||
|
||||
spin_lock(&mdev->irq_info.mic_intr_lock);
|
||||
list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list)
|
||||
if (intr_cb->func)
|
||||
intr_cb->func(pdev->irq, intr_cb->data);
|
||||
spin_unlock(&mdev->irq_info.mic_intr_lock);
|
||||
spin_lock(&irq_info->mic_thread_lock);
|
||||
for (i = intr_info->intr_start_idx[MIC_INTR_DB];
|
||||
i < intr_info->intr_len[MIC_INTR_DB]; i++)
|
||||
if (test_and_clear_bit(i, &irq_info->mask)) {
|
||||
list_for_each_entry(intr_cb, &irq_info->cb_list[i],
|
||||
list)
|
||||
if (intr_cb->thread_fn)
|
||||
intr_cb->thread_fn(pdev->irq,
|
||||
intr_cb->data);
|
||||
}
|
||||
spin_unlock(&irq_info->mic_thread_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* mic_interrupt - Generic interrupt handler for
|
||||
* MSI and INTx based interrupts.
|
||||
@ -53,7 +54,11 @@ static inline void mic_invoke_callback(struct mic_device *mdev, int idx)
|
||||
static irqreturn_t mic_interrupt(int irq, void *dev)
|
||||
{
|
||||
struct mic_device *mdev = dev;
|
||||
struct mic_intr_info *info = mdev->intr_info;
|
||||
struct mic_intr_info *intr_info = mdev->intr_info;
|
||||
struct mic_irq_info *irq_info = &mdev->irq_info;
|
||||
struct mic_intr_cb *intr_cb;
|
||||
struct pci_dev *pdev = container_of(mdev->sdev->parent,
|
||||
struct pci_dev, dev);
|
||||
u32 mask;
|
||||
int i;
|
||||
|
||||
@ -61,12 +66,19 @@ static irqreturn_t mic_interrupt(int irq, void *dev)
|
||||
if (!mask)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = info->intr_start_idx[MIC_INTR_DB];
|
||||
i < info->intr_len[MIC_INTR_DB]; i++)
|
||||
if (mask & BIT(i))
|
||||
mic_invoke_callback(mdev, i);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
spin_lock(&irq_info->mic_intr_lock);
|
||||
for (i = intr_info->intr_start_idx[MIC_INTR_DB];
|
||||
i < intr_info->intr_len[MIC_INTR_DB]; i++)
|
||||
if (mask & BIT(i)) {
|
||||
list_for_each_entry(intr_cb, &irq_info->cb_list[i],
|
||||
list)
|
||||
if (intr_cb->handler)
|
||||
intr_cb->handler(pdev->irq,
|
||||
intr_cb->data);
|
||||
set_bit(i, &irq_info->mask);
|
||||
}
|
||||
spin_unlock(&irq_info->mic_intr_lock);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
/* Return the interrupt offset from the index. Index is 0 based. */
|
||||
@ -99,14 +111,15 @@ static struct msix_entry *mic_get_available_vector(struct mic_device *mdev)
|
||||
*
|
||||
* @mdev: pointer to the mic_device instance
|
||||
* @idx: The source id to be registered.
|
||||
* @func: The function to be called when the source id receives
|
||||
* @handler: The function to be called when the source id receives
|
||||
* the interrupt.
|
||||
* @thread_fn: thread fn. corresponding to the handler
|
||||
* @data: Private data of the requester.
|
||||
* Return the callback structure that was registered or an
|
||||
* appropriate error on failure.
|
||||
*/
|
||||
static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev,
|
||||
u8 idx, irqreturn_t (*func) (int irq, void *dev),
|
||||
u8 idx, irq_handler_t handler, irq_handler_t thread_fn,
|
||||
void *data)
|
||||
{
|
||||
struct mic_intr_cb *intr_cb;
|
||||
@ -117,7 +130,8 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev,
|
||||
if (!intr_cb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
intr_cb->func = func;
|
||||
intr_cb->handler = handler;
|
||||
intr_cb->thread_fn = thread_fn;
|
||||
intr_cb->data = data;
|
||||
intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida,
|
||||
0, 0, GFP_KERNEL);
|
||||
@ -126,9 +140,11 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev,
|
||||
goto ida_fail;
|
||||
}
|
||||
|
||||
spin_lock(&mdev->irq_info.mic_thread_lock);
|
||||
spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
|
||||
list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]);
|
||||
spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
|
||||
spin_unlock(&mdev->irq_info.mic_thread_lock);
|
||||
|
||||
return intr_cb;
|
||||
ida_fail:
|
||||
@ -152,8 +168,9 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx)
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock(&mdev->irq_info.mic_thread_lock);
|
||||
spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
|
||||
for (i = 0; i < MIC_NUM_OFFSETS; i++) {
|
||||
spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
|
||||
list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) {
|
||||
intr_cb = list_entry(pos, struct mic_intr_cb, list);
|
||||
if (intr_cb->cb_id == idx) {
|
||||
@ -163,11 +180,13 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx)
|
||||
kfree(intr_cb);
|
||||
spin_unlock_irqrestore(
|
||||
&mdev->irq_info.mic_intr_lock, flags);
|
||||
spin_unlock(&mdev->irq_info.mic_thread_lock);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
|
||||
spin_unlock(&mdev->irq_info.mic_thread_lock);
|
||||
return MIC_NUM_OFFSETS;
|
||||
}
|
||||
|
||||
@ -242,6 +261,7 @@ static int mic_setup_callbacks(struct mic_device *mdev)
|
||||
INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]);
|
||||
ida_init(&mdev->irq_info.cb_ida);
|
||||
spin_lock_init(&mdev->irq_info.mic_intr_lock);
|
||||
spin_lock_init(&mdev->irq_info.mic_thread_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -258,14 +278,12 @@ static void mic_release_callbacks(struct mic_device *mdev)
|
||||
struct mic_intr_cb *intr_cb;
|
||||
int i;
|
||||
|
||||
spin_lock(&mdev->irq_info.mic_thread_lock);
|
||||
spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
|
||||
for (i = 0; i < MIC_NUM_OFFSETS; i++) {
|
||||
spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
|
||||
|
||||
if (list_empty(&mdev->irq_info.cb_list[i])) {
|
||||
spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock,
|
||||
flags);
|
||||
if (list_empty(&mdev->irq_info.cb_list[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) {
|
||||
intr_cb = list_entry(pos, struct mic_intr_cb, list);
|
||||
@ -274,8 +292,9 @@ static void mic_release_callbacks(struct mic_device *mdev)
|
||||
intr_cb->cb_id);
|
||||
kfree(intr_cb);
|
||||
}
|
||||
spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
|
||||
spin_unlock(&mdev->irq_info.mic_thread_lock);
|
||||
ida_destroy(&mdev->irq_info.cb_ida);
|
||||
kfree(mdev->irq_info.cb_list);
|
||||
}
|
||||
@ -313,7 +332,8 @@ static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev)
|
||||
goto err_nomem2;
|
||||
}
|
||||
|
||||
rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev);
|
||||
rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn,
|
||||
0, "mic-msi", mdev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "Error allocating MSI interrupt\n");
|
||||
goto err_irq_req_fail;
|
||||
@ -353,8 +373,8 @@ static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev)
|
||||
goto err_nomem;
|
||||
}
|
||||
|
||||
rc = request_irq(pdev->irq, mic_interrupt,
|
||||
IRQF_SHARED, "mic-intx", mdev);
|
||||
rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn,
|
||||
IRQF_SHARED, "mic-intx", mdev);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
@ -391,13 +411,14 @@ int mic_next_db(struct mic_device *mdev)
|
||||
#define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT)
|
||||
|
||||
/**
|
||||
* mic_request_irq - request an irq. mic_mutex needs
|
||||
* mic_request_threaded_irq - request an irq. mic_mutex needs
|
||||
* to be held before calling this function.
|
||||
*
|
||||
* @mdev: pointer to mic_device instance
|
||||
* @func: The callback function that handles the interrupt.
|
||||
* @handler: The callback function that handles the interrupt.
|
||||
* The function needs to call ack_interrupts
|
||||
* (mdev->ops->ack_interrupt(mdev)) when handling the interrupts.
|
||||
* @thread_fn: thread fn required by request_threaded_irq.
|
||||
* @name: The ASCII name of the callee requesting the irq.
|
||||
* @data: private data that is returned back when calling the
|
||||
* function handler.
|
||||
@ -412,10 +433,11 @@ int mic_next_db(struct mic_device *mdev)
|
||||
* error code.
|
||||
*
|
||||
*/
|
||||
struct mic_irq *mic_request_irq(struct mic_device *mdev,
|
||||
irqreturn_t (*func)(int irq, void *dev),
|
||||
const char *name, void *data, int intr_src,
|
||||
enum mic_intr_type type)
|
||||
struct mic_irq *
|
||||
mic_request_threaded_irq(struct mic_device *mdev,
|
||||
irq_handler_t handler, irq_handler_t thread_fn,
|
||||
const char *name, void *data, int intr_src,
|
||||
enum mic_intr_type type)
|
||||
{
|
||||
u16 offset;
|
||||
int rc = 0;
|
||||
@ -444,7 +466,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev,
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = request_irq(msix->vector, func, 0, name, data);
|
||||
rc = request_threaded_irq(msix->vector, handler, thread_fn,
|
||||
0, name, data);
|
||||
if (rc) {
|
||||
dev_dbg(mdev->sdev->parent,
|
||||
"request irq failed rc = %d\n", rc);
|
||||
@ -458,8 +481,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev,
|
||||
dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n",
|
||||
msix->vector, intr_src);
|
||||
} else {
|
||||
intr_cb = mic_register_intr_callback(mdev,
|
||||
offset, func, data);
|
||||
intr_cb = mic_register_intr_callback(mdev, offset, handler,
|
||||
thread_fn, data);
|
||||
if (IS_ERR(intr_cb)) {
|
||||
dev_err(mdev->sdev->parent,
|
||||
"No available callback entries for use\n");
|
||||
@ -487,9 +510,9 @@ err:
|
||||
* needs to be held before calling this function.
|
||||
*
|
||||
* @mdev: pointer to mic_device instance
|
||||
* @cookie: cookie obtained during a successful call to mic_request_irq
|
||||
* @cookie: cookie obtained during a successful call to mic_request_threaded_irq
|
||||
* @data: private data specified by the calling function during the
|
||||
* mic_request_irq
|
||||
* mic_request_threaded_irq
|
||||
*
|
||||
* returns: none.
|
||||
*/
|
||||
|
@ -21,6 +21,8 @@
|
||||
#ifndef _MIC_INTR_H_
|
||||
#define _MIC_INTR_H_
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/interrupt.h>
|
||||
/*
|
||||
* The minimum number of msix vectors required for normal operation.
|
||||
* 3 for virtio network, console and block devices.
|
||||
@ -68,7 +70,11 @@ struct mic_intr_info {
|
||||
* @num_vectors: The number of MSI/MSI-x vectors that have been allocated.
|
||||
* @cb_ida: callback ID allocator to track the callbacks registered.
|
||||
* @mic_intr_lock: spinlock to protect the interrupt callback list.
|
||||
* @mic_thread_lock: spinlock to protect the thread callback list.
|
||||
* This lock is used to protect against thread_fn while
|
||||
* mic_intr_lock is used to protect against interrupt handler.
|
||||
* @cb_list: Array of callback lists one for each source.
|
||||
* @mask: Mask used by the main thread fn to call the underlying thread fns.
|
||||
*/
|
||||
struct mic_irq_info {
|
||||
int next_avail_src;
|
||||
@ -77,19 +83,23 @@ struct mic_irq_info {
|
||||
u16 num_vectors;
|
||||
struct ida cb_ida;
|
||||
spinlock_t mic_intr_lock;
|
||||
spinlock_t mic_thread_lock;
|
||||
struct list_head *cb_list;
|
||||
unsigned long mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mic_intr_cb - Interrupt callback structure.
|
||||
*
|
||||
* @func: The callback function
|
||||
* @handler: The callback function
|
||||
* @thread_fn: The thread_fn.
|
||||
* @data: Private data of the requester.
|
||||
* @cb_id: The callback id. Identifies this callback.
|
||||
* @list: list head pointing to the next callback structure.
|
||||
*/
|
||||
struct mic_intr_cb {
|
||||
irqreturn_t (*func) (int irq, void *data);
|
||||
irq_handler_t handler;
|
||||
irq_handler_t thread_fn;
|
||||
void *data;
|
||||
int cb_id;
|
||||
struct list_head list;
|
||||
@ -124,11 +134,11 @@ struct mic_hw_intr_ops {
|
||||
};
|
||||
|
||||
int mic_next_db(struct mic_device *mdev);
|
||||
struct mic_irq *mic_request_irq(struct mic_device *mdev,
|
||||
irqreturn_t (*func)(int irq, void *data),
|
||||
const char *name, void *data, int intr_src,
|
||||
enum mic_intr_type type);
|
||||
|
||||
struct mic_irq *
|
||||
mic_request_threaded_irq(struct mic_device *mdev,
|
||||
irq_handler_t handler, irq_handler_t thread_fn,
|
||||
const char *name, void *data, int intr_src,
|
||||
enum mic_intr_type type);
|
||||
void mic_free_irq(struct mic_device *mdev,
|
||||
struct mic_irq *cookie, void *data);
|
||||
int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev);
|
||||
|
@ -389,8 +389,9 @@ static int mic_probe(struct pci_dev *pdev,
|
||||
mutex_lock(&mdev->mic_mutex);
|
||||
|
||||
mdev->shutdown_db = mic_next_db(mdev);
|
||||
mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db,
|
||||
"shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB);
|
||||
mdev->shutdown_cookie = mic_request_threaded_irq(mdev, mic_shutdown_db,
|
||||
NULL, "shutdown-interrupt", mdev,
|
||||
mdev->shutdown_db, MIC_INTR_DB);
|
||||
if (IS_ERR(mdev->shutdown_cookie)) {
|
||||
rc = PTR_ERR(mdev->shutdown_cookie);
|
||||
mutex_unlock(&mdev->mic_mutex);
|
||||
|
@ -594,8 +594,10 @@ int mic_virtio_add_device(struct mic_vdev *mvdev,
|
||||
snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id,
|
||||
mvdev->virtio_id);
|
||||
mvdev->virtio_db = mic_next_db(mdev);
|
||||
mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler,
|
||||
irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB);
|
||||
mvdev->virtio_cookie = mic_request_threaded_irq(mdev,
|
||||
mic_virtio_intr_handler,
|
||||
NULL, irqname, mvdev,
|
||||
mvdev->virtio_db, MIC_INTR_DB);
|
||||
if (IS_ERR(mvdev->virtio_cookie)) {
|
||||
ret = PTR_ERR(mvdev->virtio_cookie);
|
||||
dev_dbg(mdev->sdev->parent, "request irq failed\n");
|
||||
|
Loading…
Reference in New Issue
Block a user