+/**
+ * vport_get_stats - retrieve device stats (for kernel callers)
+ *
+ * @vport: vport from which to retrieve the stats
+ * @stats: location to store stats
+ *
+ * Retrieves transmit, receive, and error stats for the given device.
+ */
+int vport_get_stats(struct vport *vport, struct odp_vport_stats *stats)
+{
+ struct odp_vport_stats dev_stats;
+ struct odp_vport_stats *dev_statsp = NULL;
+ int err;
+
+ if (vport->ops->get_stats) {
+ if (vport->ops->flags & VPORT_F_GEN_STATS)
+ dev_statsp = &dev_stats;
+ else
+ dev_statsp = stats;
+
+ rcu_read_lock();
+ err = vport->ops->get_stats(vport, dev_statsp);
+ rcu_read_unlock();
+
+ if (err)
+ goto out;
+ }
+
+ if (vport->ops->flags & VPORT_F_GEN_STATS) {
+ int i;
+
+ /* We potentially have 3 sources of stats that need to be
+ * combined: those we have collected (split into err_stats and
+ * percpu_stats), offset_stats from set_stats(), and device
+ * error stats from get_stats() (for errors that happen
+ * downstream and therefore aren't reported through our
+ * vport_record_error() function). */
+
+ spin_lock_bh(&vport->stats_lock);
+
+ memcpy(stats, &vport->offset_stats, sizeof(struct odp_vport_stats));
+
+ stats->rx_errors += vport->err_stats.rx_errors
+ + vport->err_stats.rx_frame_err
+ + vport->err_stats.rx_over_err
+ + vport->err_stats.rx_crc_err;
+ stats->tx_errors += vport->err_stats.tx_errors;
+ stats->tx_dropped += vport->err_stats.tx_dropped;
+ stats->rx_dropped += vport->err_stats.rx_dropped;
+ stats->rx_over_err += vport->err_stats.rx_over_err;
+ stats->rx_crc_err += vport->err_stats.rx_crc_err;
+ stats->rx_frame_err += vport->err_stats.rx_frame_err;
+ stats->collisions += vport->err_stats.collisions;
+
+ spin_unlock_bh(&vport->stats_lock);
+
+ if (dev_statsp) {
+ stats->rx_errors += dev_statsp->rx_errors;
+ stats->tx_errors += dev_statsp->tx_errors;
+ stats->rx_dropped += dev_statsp->rx_dropped;
+ stats->tx_dropped += dev_statsp->tx_dropped;
+ stats->rx_over_err += dev_statsp->rx_over_err;
+ stats->rx_crc_err += dev_statsp->rx_crc_err;
+ stats->rx_frame_err += dev_statsp->rx_frame_err;
+ stats->collisions += dev_statsp->collisions;
+ }
+
+ for_each_possible_cpu(i) {
+ const struct vport_percpu_stats *percpu_stats;
+ struct vport_percpu_stats local_stats;
+ unsigned seqcount;
+
+ percpu_stats = per_cpu_ptr(vport->percpu_stats, i);
+
+ do {
+ seqcount = read_seqcount_begin(&percpu_stats->seqlock);
+ local_stats = *percpu_stats;
+ } while (read_seqcount_retry(&percpu_stats->seqlock, seqcount));
+
+ stats->rx_bytes += local_stats.rx_bytes;
+ stats->rx_packets += local_stats.rx_packets;
+ stats->tx_bytes += local_stats.tx_bytes;
+ stats->tx_packets += local_stats.tx_packets;
+ }
+
+ err = 0;
+ } else
+ err = -EOPNOTSUPP;
+
+out:
+ return err;
+}
+