From: Mika Westerberg <mika.westerberg@linux.intel.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
"David S . Miller" <davem@davemloft.net>
Cc: Andreas Noever <andreas.noever@gmail.com>,
Michael Jamet <michael.jamet@intel.com>,
Yehezkel Bernat <yehezkel.bernat@intel.com>,
Amir Levy <amir.jer.levy@intel.com>,
Mario.Limonciello@dell.com, Lukas Wunner <lukas@wunner.de>,
Andy Shevchenko <andriy.shevchenko@linux.intel.com>,
Mika Westerberg <mika.westerberg@linux.intel.com>,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org
Subject: [PATCH 13/16] thunderbolt: Add polling mode for rings
Date: Mon, 18 Sep 2017 18:30:46 +0300 [thread overview]
Message-ID: <20170918153049.44185-14-mika.westerberg@linux.intel.com> (raw)
In-Reply-To: <20170918153049.44185-1-mika.westerberg@linux.intel.com>
In order to support things like networking over Thunderbolt cable, there
needs to be a way to switch the ring to a mode where it can be polled
with the interrupt masked. We implement such mode so that the caller can
allocate a ring by passing pointer to a function that is then called
when an interrupt is triggered. Completed frames can be fetched using
tb_ring_poll() and the interrupt can be re-enabled when the caller is
finished with polling by using tb_ring_poll_complete().
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Michael Jamet <michael.jamet@intel.com>
Reviewed-by: Yehezkel Bernat <yehezkel.bernat@intel.com>
---
drivers/thunderbolt/ctl.c | 2 +-
drivers/thunderbolt/nhi.c | 126 ++++++++++++++++++++++++++++++++++++++++----
include/linux/thunderbolt.h | 23 +++++---
3 files changed, 134 insertions(+), 17 deletions(-)
diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c
index dd10789e1dbb..d079dbba2c03 100644
--- a/drivers/thunderbolt/ctl.c
+++ b/drivers/thunderbolt/ctl.c
@@ -619,7 +619,7 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
goto err;
ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff,
- 0xffff);
+ 0xffff, NULL, NULL);
if (!ctl->rx)
goto err;
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index cf1397afa72f..5bc3f77cc1f3 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -252,7 +252,8 @@ static void ring_work(struct work_struct *work)
* Do not hold on to it.
*/
list_del_init(&frame->list);
- frame->callback(ring, frame, canceled);
+ if (frame->callback)
+ frame->callback(ring, frame, canceled);
}
}
@@ -273,11 +274,106 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame)
}
EXPORT_SYMBOL_GPL(__tb_ring_enqueue);
+/**
+ * tb_ring_poll() - Poll one completed frame from the ring
+ * @ring: Ring to poll
+ *
+ * This function can be called when @start_poll callback of the @ring
+ * has been called. It will read one completed frame from the ring and
+ * return it to the caller. Returns %NULL if there is no more completed
+ * frames.
+ */
+struct ring_frame *tb_ring_poll(struct tb_ring *ring)
+{
+ struct ring_frame *frame = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->lock, flags);
+ if (!ring->running)
+ goto unlock;
+ if (ring_empty(ring))
+ goto unlock;
+
+ if (ring->descriptors[ring->tail].flags & RING_DESC_COMPLETED) {
+ frame = list_first_entry(&ring->in_flight, typeof(*frame),
+ list);
+ list_del_init(&frame->list);
+
+ if (!ring->is_tx) {
+ frame->size = ring->descriptors[ring->tail].length;
+ frame->eof = ring->descriptors[ring->tail].eof;
+ frame->sof = ring->descriptors[ring->tail].sof;
+ frame->flags = ring->descriptors[ring->tail].flags;
+ }
+
+ ring->tail = (ring->tail + 1) % ring->size;
+ }
+
+unlock:
+ spin_unlock_irqrestore(&ring->lock, flags);
+ return frame;
+}
+EXPORT_SYMBOL_GPL(tb_ring_poll);
+
+static void __ring_interrupt_mask(struct tb_ring *ring, bool mask)
+{
+ int idx = ring_interrupt_index(ring);
+ int reg = REG_RING_INTERRUPT_BASE + idx / 32 * 4;
+ int bit = idx % 32;
+ u32 val;
+
+ val = ioread32(ring->nhi->iobase + reg);
+ if (mask)
+ val &= ~BIT(bit);
+ else
+ val |= BIT(bit);
+ iowrite32(val, ring->nhi->iobase + reg);
+}
+
+/* Both @nhi->lock and @ring->lock should be held */
+static void __ring_interrupt(struct tb_ring *ring)
+{
+ if (!ring->running)
+ return;
+
+ if (ring->start_poll) {
+ __ring_interrupt_mask(ring, false);
+ ring->start_poll(ring->poll_data);
+ } else {
+ schedule_work(&ring->work);
+ }
+}
+
+/**
+ * tb_ring_poll_complete() - Re-start interrupt for the ring
+ * @ring: Ring to re-start the interrupt
+ *
+ * This will re-start (unmask) the ring interrupt once the user is done
+ * with polling.
+ */
+void tb_ring_poll_complete(struct tb_ring *ring)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->nhi->lock, flags);
+ spin_lock(&ring->lock);
+ if (ring->start_poll)
+ __ring_interrupt_mask(ring, false);
+ spin_unlock(&ring->lock);
+ spin_unlock_irqrestore(&ring->nhi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(tb_ring_poll_complete);
+
static irqreturn_t ring_msix(int irq, void *data)
{
struct tb_ring *ring = data;
- schedule_work(&ring->work);
+ spin_lock(&ring->nhi->lock);
+ spin_lock(&ring->lock);
+ __ring_interrupt(ring);
+ spin_unlock(&ring->lock);
+ spin_unlock(&ring->nhi->lock);
+
return IRQ_HANDLED;
}
@@ -317,7 +413,9 @@ static void ring_release_msix(struct tb_ring *ring)
static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
bool transmit, unsigned int flags,
- u16 sof_mask, u16 eof_mask)
+ u16 sof_mask, u16 eof_mask,
+ void (*start_poll)(void *),
+ void *poll_data)
{
struct tb_ring *ring = NULL;
dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n",
@@ -346,6 +444,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
ring->head = 0;
ring->tail = 0;
ring->running = false;
+ ring->start_poll = start_poll;
+ ring->poll_data = poll_data;
ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev,
size * sizeof(*ring->descriptors),
@@ -399,7 +499,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags)
{
- return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0);
+ return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, NULL, NULL);
}
EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
@@ -411,11 +511,17 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
* @flags: Flags for the ring
* @sof_mask: Mask of PDF values that start a frame
* @eof_mask: Mask of PDF values that end a frame
+ * @start_poll: If not %NULL the ring will call this function when an
+ * interrupt is triggered and masked, instead of callback
+ * in each Rx frame.
+ * @poll_data: Optional data passed to @start_poll
*/
struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
- unsigned int flags, u16 sof_mask, u16 eof_mask)
+ unsigned int flags, u16 sof_mask, u16 eof_mask,
+ void (*start_poll)(void *), void *poll_data)
{
- return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask);
+ return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask,
+ start_poll, poll_data);
}
EXPORT_SYMBOL_GPL(tb_ring_alloc_rx);
@@ -556,6 +662,7 @@ void tb_ring_free(struct tb_ring *ring)
dev_WARN(&ring->nhi->pdev->dev, "%s %d still running\n",
RING_TYPE(ring), ring->hop);
}
+ spin_unlock_irq(&ring->nhi->lock);
ring_release_msix(ring);
@@ -572,7 +679,6 @@ void tb_ring_free(struct tb_ring *ring)
RING_TYPE(ring),
ring->hop);
- spin_unlock_irq(&ring->nhi->lock);
/**
* ring->work can no longer be scheduled (it is scheduled only
* by nhi_interrupt_work, ring_stop and ring_msix). Wait for it
@@ -682,8 +788,10 @@ static void nhi_interrupt_work(struct work_struct *work)
hop);
continue;
}
- /* we do not check ring->running, this is done in ring->work */
- schedule_work(&ring->work);
+
+ spin_lock(&ring->lock);
+ __ring_interrupt(ring);
+ spin_unlock(&ring->lock);
}
spin_unlock_irq(&nhi->lock);
}
diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h
index 62adb97d77f1..9a63210d5084 100644
--- a/include/linux/thunderbolt.h
+++ b/include/linux/thunderbolt.h
@@ -446,6 +446,9 @@ struct tb_nhi {
* @flags: Ring specific flags
* @sof_mask: Bit mask used to detect start of frame PDF
* @eof_mask: Bit mask used to detect end of frame PDF
+ * @start_poll: Called when ring interrupt is triggered to start
+ * polling. Passing %NULL keeps the ring in interrupt mode.
+ * @poll_data: Data passed to @start_poll
*/
struct tb_ring {
spinlock_t lock;
@@ -466,6 +469,8 @@ struct tb_ring {
unsigned int flags;
u16 sof_mask;
u16 eof_mask;
+ void (*start_poll)(void *data);
+ void *poll_data;
};
/* Leave ring interrupt enabled on suspend */
@@ -499,7 +504,7 @@ enum ring_desc_flags {
/**
* struct ring_frame - For use with ring_rx/ring_tx
* @buffer_phy: DMA mapped address of the frame
- * @callback: Callback called when the frame is finished
+ * @callback: Callback called when the frame is finished (optional)
* @list: Frame is linked to a queue using this
* @size: Size of the frame in bytes (%0 means %4096)
* @flags: Flags for the frame (see &enum ring_desc_flags)
@@ -522,8 +527,8 @@ struct ring_frame {
struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags);
struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
- unsigned int flags, u16 sof_mask,
- u16 eof_mask);
+ unsigned int flags, u16 sof_mask, u16 eof_mask,
+ void (*start_poll)(void *), void *poll_data);
void tb_ring_start(struct tb_ring *ring);
void tb_ring_stop(struct tb_ring *ring);
void tb_ring_free(struct tb_ring *ring);
@@ -535,8 +540,8 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame);
* @ring: Ring to enqueue the frame
* @frame: Frame to enqueue
*
- * @frame->buffer, @frame->buffer_phy and @frame->callback have to be set. The
- * buffer must contain at least %TB_FRAME_SIZE bytes.
+ * @frame->buffer, @frame->buffer_phy have to be set. The buffer must
+ * contain at least %TB_FRAME_SIZE bytes.
*
* @frame->callback will be invoked with @frame->size, @frame->flags,
* @frame->eof, @frame->sof set once the frame has been received.
@@ -557,8 +562,8 @@ static inline int tb_ring_rx(struct tb_ring *ring, struct ring_frame *frame)
* @ring: Ring the enqueue the frame
* @frame: Frame to enqueue
*
- * @frame->buffer, @frame->buffer_phy, @frame->callback, @frame->size,
- * @frame->eof and @frame->sof have to be set.
+ * @frame->buffer, @frame->buffer_phy, @frame->size, @frame->eof and
+ * @frame->sof have to be set.
*
* @frame->callback will be invoked with once the frame has been transmitted.
*
@@ -573,4 +578,8 @@ static inline int tb_ring_tx(struct tb_ring *ring, struct ring_frame *frame)
return __tb_ring_enqueue(ring, frame);
}
+/* Used only when the ring is in polling mode */
+struct ring_frame *tb_ring_poll(struct tb_ring *ring);
+void tb_ring_poll_complete(struct tb_ring *ring);
+
#endif /* THUNDERBOLT_H_ */
--
2.13.5
next prev parent reply other threads:[~2017-09-18 15:33 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-09-18 15:30 [PATCH 00/16] Thunderbolt networking Mika Westerberg
2017-09-18 15:30 ` [PATCH 01/16] byteorder: Move {cpu_to_be32,be32_to_cpu}_array() from Thunderbolt to core Mika Westerberg
2017-09-18 15:30 ` [PATCH 02/16] thunderbolt: Add support for XDomain properties Mika Westerberg
2017-09-18 15:30 ` [PATCH 03/16] thunderbolt: Move enum tb_cfg_pkg_type to thunderbolt.h Mika Westerberg
2017-09-18 15:30 ` [PATCH 04/16] thunderbolt: Move thunderbolt domain structure " Mika Westerberg
2017-09-18 15:30 ` [PATCH 05/16] thunderbolt: Move tb_switch_phy_port_from_link() " Mika Westerberg
2017-09-18 15:30 ` [PATCH 06/16] thunderbolt: Add support for XDomain discovery protocol Mika Westerberg
2017-09-18 16:12 ` Dan Williams
2017-09-18 16:15 ` Dan Williams
2017-09-19 7:40 ` Mika Westerberg
2017-09-18 15:30 ` [PATCH 07/16] thunderbolt: Configure interrupt throttling for all interrupts Mika Westerberg
2017-09-18 15:30 ` [PATCH 08/16] thunderbolt: Add support for frame mode Mika Westerberg
2017-09-18 15:30 ` [PATCH 09/16] thunderbolt: Export ring handling functions to modules Mika Westerberg
2017-09-18 15:30 ` [PATCH 10/16] thunderbolt: Move ring descriptor flags to thunderbolt.h Mika Westerberg
2017-09-18 15:30 ` [PATCH 11/16] thunderbolt: Use spinlock in ring serialization Mika Westerberg
2017-09-18 15:30 ` [PATCH 12/16] thunderbolt: Use spinlock in NHI serialization Mika Westerberg
2017-09-18 15:30 ` Mika Westerberg [this message]
2017-09-18 15:30 ` [PATCH 14/16] thunderbolt: Add function to retrieve DMA device for the ring Mika Westerberg
2017-09-18 15:30 ` [PATCH 15/16] thunderbolt: Allocate ring HopID automatically if requested Mika Westerberg
2017-09-18 15:30 ` [PATCH 16/16] thunderbolt: Add support for networking over Thunderbolt cable Mika Westerberg
2017-09-18 23:21 ` Andrew Lunn
2017-09-19 7:12 ` Mika Westerberg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170918153049.44185-14-mika.westerberg@linux.intel.com \
--to=mika.westerberg@linux.intel.com \
--cc=Mario.Limonciello@dell.com \
--cc=amir.jer.levy@intel.com \
--cc=andreas.noever@gmail.com \
--cc=andriy.shevchenko@linux.intel.com \
--cc=davem@davemloft.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lukas@wunner.de \
--cc=michael.jamet@intel.com \
--cc=netdev@vger.kernel.org \
--cc=yehezkel.bernat@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.