+struct queue_stats_cbdata {
+ struct ofconn *ofconn;
+ struct ofpbuf *msg;
+ uint16_t port_no;
+};
+
+static void
+put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id,
+ const struct netdev_queue_stats *stats)
+{
+ struct ofp_queue_stats *reply;
+
+ reply = append_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg);
+ reply->port_no = htons(cbdata->port_no);
+ memset(reply->pad, 0, sizeof reply->pad);
+ reply->queue_id = htonl(queue_id);
+ reply->tx_bytes = htonll(stats->tx_bytes);
+ reply->tx_packets = htonll(stats->tx_packets);
+ reply->tx_errors = htonll(stats->tx_errors);
+}
+
+static void
+handle_queue_stats_dump_cb(uint32_t queue_id,
+ struct netdev_queue_stats *stats,
+ void *cbdata_)
+{
+ struct queue_stats_cbdata *cbdata = cbdata_;
+
+ put_queue_stats(cbdata, queue_id, stats);
+}
+
+static void
+handle_queue_stats_for_port(struct ofport *port, uint16_t port_no,
+ uint32_t queue_id,
+ struct queue_stats_cbdata *cbdata)
+{
+ cbdata->port_no = port_no;
+ if (queue_id == OFPQ_ALL) {
+ netdev_dump_queue_stats(port->netdev,
+ handle_queue_stats_dump_cb, cbdata);
+ } else {
+ struct netdev_queue_stats stats;
+
+ netdev_get_queue_stats(port->netdev, queue_id, &stats);
+ put_queue_stats(cbdata, queue_id, &stats);
+ }
+}
+
+static int
+handle_queue_stats_request(struct ofproto *ofproto, struct ofconn *ofconn,
+ const struct ofp_stats_request *osr,
+ size_t arg_size)
+{
+ struct ofp_queue_stats_request *qsr;
+ struct queue_stats_cbdata cbdata;
+ struct ofport *port;
+ unsigned int port_no;
+ uint32_t queue_id;
+
+ if (arg_size != sizeof *qsr) {
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+ }
+ qsr = (struct ofp_queue_stats_request *) osr->body;
+
+ COVERAGE_INC(ofproto_queue_req);
+
+ cbdata.ofconn = ofconn;
+ cbdata.msg = start_stats_reply(osr, 128);
+
+ port_no = ntohs(qsr->port_no);
+ queue_id = ntohl(qsr->queue_id);
+ if (port_no == OFPP_ALL) {
+ PORT_ARRAY_FOR_EACH (port, &ofproto->ports, port_no) {
+ handle_queue_stats_for_port(port, port_no, queue_id, &cbdata);
+ }
+ } else if (port_no < ofproto->max_ports) {
+ port = port_array_get(&ofproto->ports, port_no);
+ if (port) {
+ handle_queue_stats_for_port(port, port_no, queue_id, &cbdata);
+ }
+ } else {
+ ofpbuf_delete(cbdata.msg);
+ return ofp_mkerr(OFPET_QUEUE_OP_FAILED, OFPQOFC_BAD_PORT);
+ }
+ queue_tx(cbdata.msg, ofconn, ofconn->reply_counter);
+
+ return 0;
+}
+