net: ethernet: ti: cpts: move tx timestamp processing to ptp worker only
[linux-2.6-microblaze.git] / drivers / net / ethernet / ti / cpts.c
index 6efb809..55ba6b4 100644 (file)
@@ -21,6 +21,8 @@
 #include "cpts.h"
 
 #define CPTS_SKB_TX_WORK_TIMEOUT 1 /* jiffies */
+#define CPTS_SKB_RX_TX_TMO 100 /*ms */
+#define CPTS_EVENT_RX_TX_TIMEOUT (100) /* ms */
 
 struct cpts_skb_cb_data {
        u32 skb_mtype_seqid;
@@ -92,46 +94,6 @@ static void cpts_purge_txq(struct cpts *cpts)
                dev_dbg(cpts->dev, "txq cleaned up %d\n", removed);
 }
 
-static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
-{
-       struct sk_buff *skb, *tmp;
-       bool found = false;
-       u32 mtype_seqid;
-
-       mtype_seqid = event->high &
-                     ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) |
-                      (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) |
-                      (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT));
-
-       /* no need to grab txq.lock as access is always done under cpts->lock */
-       skb_queue_walk_safe(&cpts->txq, skb, tmp) {
-               struct skb_shared_hwtstamps ssh;
-               struct cpts_skb_cb_data *skb_cb =
-                                       (struct cpts_skb_cb_data *)skb->cb;
-
-               if (mtype_seqid == skb_cb->skb_mtype_seqid) {
-                       memset(&ssh, 0, sizeof(ssh));
-                       ssh.hwtstamp = ns_to_ktime(event->timestamp);
-                       skb_tstamp_tx(skb, &ssh);
-                       found = true;
-                       __skb_unlink(skb, &cpts->txq);
-                       dev_consume_skb_any(skb);
-                       dev_dbg(cpts->dev, "match tx timestamp mtype_seqid %08x\n",
-                               mtype_seqid);
-                       break;
-               }
-
-               if (time_after(jiffies, skb_cb->tmo)) {
-                       /* timeout any expired skbs over 1s */
-                       dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
-                       __skb_unlink(skb, &cpts->txq);
-                       dev_consume_skb_any(skb);
-               }
-       }
-
-       return found;
-}
-
 /*
  * Returns zero if matching event type was found.
  */
@@ -151,7 +113,6 @@ static int cpts_fifo_read(struct cpts *cpts, int match)
                }
 
                event = list_first_entry(&cpts->pool, struct cpts_event, list);
-               event->tmo = jiffies + 2;
                event->high = hi;
                event->low = lo;
                event->timestamp = timecounter_cyc2time(&cpts->tc, event->low);
@@ -169,14 +130,10 @@ static int cpts_fifo_read(struct cpts *cpts, int match)
                        }
                        break;
                case CPTS_EV_TX:
-                       if (cpts_match_tx_ts(cpts, event)) {
-                               /* if the new event matches an existing skb,
-                                * then don't queue it
-                                */
-                               break;
-                       }
-                       /* fall through */
                case CPTS_EV_RX:
+                       event->tmo = jiffies +
+                               msecs_to_jiffies(CPTS_EVENT_RX_TX_TIMEOUT);
+
                        list_del_init(&event->list);
                        list_add_tail(&event->list, &cpts->events);
                        break;
@@ -297,6 +254,84 @@ static int cpts_ptp_enable(struct ptp_clock_info *ptp,
        return -EOPNOTSUPP;
 }
 
+static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
+{
+       struct sk_buff_head txq_list;
+       struct sk_buff *skb, *tmp;
+       unsigned long flags;
+       bool found = false;
+       u32 mtype_seqid;
+
+       mtype_seqid = event->high &
+                     ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) |
+                      (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) |
+                      (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT));
+
+       __skb_queue_head_init(&txq_list);
+
+       spin_lock_irqsave(&cpts->txq.lock, flags);
+       skb_queue_splice_init(&cpts->txq, &txq_list);
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
+
+       skb_queue_walk_safe(&txq_list, skb, tmp) {
+               struct skb_shared_hwtstamps ssh;
+               struct cpts_skb_cb_data *skb_cb =
+                                       (struct cpts_skb_cb_data *)skb->cb;
+
+               if (mtype_seqid == skb_cb->skb_mtype_seqid) {
+                       memset(&ssh, 0, sizeof(ssh));
+                       ssh.hwtstamp = ns_to_ktime(event->timestamp);
+                       skb_tstamp_tx(skb, &ssh);
+                       found = true;
+                       __skb_unlink(skb, &txq_list);
+                       dev_consume_skb_any(skb);
+                       dev_dbg(cpts->dev, "match tx timestamp mtype_seqid %08x\n",
+                               mtype_seqid);
+                       break;
+               }
+
+               if (time_after(jiffies, skb_cb->tmo)) {
+                       /* timeout any expired skbs over 1s */
+                       dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
+                       __skb_unlink(skb, &txq_list);
+                       dev_consume_skb_any(skb);
+               }
+       }
+
+       spin_lock_irqsave(&cpts->txq.lock, flags);
+       skb_queue_splice(&txq_list, &cpts->txq);
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
+
+       return found;
+}
+
+static void cpts_process_events(struct cpts *cpts)
+{
+       struct list_head *this, *next;
+       struct cpts_event *event;
+       LIST_HEAD(events_free);
+       unsigned long flags;
+       LIST_HEAD(events);
+
+       spin_lock_irqsave(&cpts->lock, flags);
+       list_splice_init(&cpts->events, &events);
+       spin_unlock_irqrestore(&cpts->lock, flags);
+
+       list_for_each_safe(this, next, &events) {
+               event = list_entry(this, struct cpts_event, list);
+               if (cpts_match_tx_ts(cpts, event) ||
+                   time_after(jiffies, event->tmo)) {
+                       list_del_init(&event->list);
+                       list_add(&event->list, &events_free);
+               }
+       }
+
+       spin_lock_irqsave(&cpts->lock, flags);
+       list_splice_tail(&events, &cpts->events);
+       list_splice_tail(&events_free, &cpts->pool);
+       spin_unlock_irqrestore(&cpts->lock, flags);
+}
+
 static long cpts_overflow_check(struct ptp_clock_info *ptp)
 {
        struct cpts *cpts = container_of(ptp, struct cpts, info);
@@ -305,17 +340,20 @@ static long cpts_overflow_check(struct ptp_clock_info *ptp)
        u64 ns;
 
        spin_lock_irqsave(&cpts->lock, flags);
-
        cpts_update_cur_time(cpts, -1, NULL);
+       spin_unlock_irqrestore(&cpts->lock, flags);
 
        ns = timecounter_read(&cpts->tc);
 
+       cpts_process_events(cpts);
+
+       spin_lock_irqsave(&cpts->txq.lock, flags);
        if (!skb_queue_empty(&cpts->txq)) {
                cpts_purge_txq(cpts);
                if (!skb_queue_empty(&cpts->txq))
                        delay = CPTS_SKB_TX_WORK_TIMEOUT;
        }
-       spin_unlock_irqrestore(&cpts->lock, flags);
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
 
        dev_dbg(cpts->dev, "cpts overflow check at %lld\n", ns);
        return (long)delay;
@@ -409,19 +447,6 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb,
                        break;
                }
        }
-
-       if (ev_type == CPTS_EV_TX && !ns) {
-               struct cpts_skb_cb_data *skb_cb =
-                               (struct cpts_skb_cb_data *)skb->cb;
-               /* Not found, add frame to queue for processing later.
-                * The periodic FIFO check will handle this.
-                */
-               skb_get(skb);
-               /* get the timestamp for timeouts */
-               skb_cb->tmo = jiffies + msecs_to_jiffies(100);
-               __skb_queue_tail(&cpts->txq, skb);
-               ptp_schedule_worker(cpts->clock, 0);
-       }
        spin_unlock_irqrestore(&cpts->lock, flags);
 
        return ns;
@@ -455,9 +480,7 @@ EXPORT_SYMBOL_GPL(cpts_rx_timestamp);
 void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
        struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb;
-       struct skb_shared_hwtstamps ssh;
        int ret;
-       u64 ns;
 
        if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
                return;
@@ -471,12 +494,12 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
        dev_dbg(cpts->dev, "%s mtype seqid %08x\n",
                __func__, skb_cb->skb_mtype_seqid);
 
-       ns = cpts_find_ts(cpts, skb, CPTS_EV_TX, skb_cb->skb_mtype_seqid);
-       if (!ns)
-               return;
-       memset(&ssh, 0, sizeof(ssh));
-       ssh.hwtstamp = ns_to_ktime(ns);
-       skb_tstamp_tx(skb, &ssh);
+       /* Always defer TX TS processing to PTP worker */
+       skb_get(skb);
+       /* get the timestamp for timeouts */
+       skb_cb->tmo = jiffies + msecs_to_jiffies(CPTS_SKB_RX_TX_TMO);
+       skb_queue_tail(&cpts->txq, skb);
+       ptp_schedule_worker(cpts->clock, 0);
 }
 EXPORT_SYMBOL_GPL(cpts_tx_timestamp);