+
+ if (error && error != EOPNOTSUPP) {
+ VLOG_DBG_RL(&rl, "failed to set tx/rx queue for network device %s:"
+ "%s", netdev_get_name(netdev), ovs_strerror(error));
+ }
+
+ return error;
+}
+
+/* Sends 'buffers' on 'netdev'. Returns 0 if successful (for every packet),
+ * otherwise a positive errno value. Returns EAGAIN without blocking if
+ * at least one the packets cannot be queued immediately. Returns EMSGSIZE
+ * if a partial packet was transmitted or if a packet is too big or too small
+ * to transmit on the device.
+ *
+ * If the function returns a non-zero value, some of the packets might have
+ * been sent anyway.
+ *
+ * If 'may_steal' is false, the caller retains ownership of all the packets.
+ * If 'may_steal' is true, the caller transfers ownership of all the packets
+ * to the network device, regardless of success.
+ *
+ * The network device is expected to maintain one or more packet
+ * transmission queues, so that the caller does not ordinarily have to
+ * do additional queuing of packets. 'qid' specifies the queue to use
+ * and can be ignored if the implementation does not support multiple
+ * queues.
+ *
+ * Some network devices may not implement support for this function. In such
+ * cases this function will always return EOPNOTSUPP. */
+int
+netdev_send(struct netdev *netdev, int qid, struct dp_packet **buffers,
+ int cnt, bool may_steal)
+{
+ if (!netdev->netdev_class->send) {
+ if (may_steal) {
+ for (int i = 0; i < cnt; i++) {
+ dp_packet_delete(buffers[i]);
+ }
+ }
+ return EOPNOTSUPP;
+ }
+
+ int error = netdev->netdev_class->send(netdev, qid, buffers, cnt,
+ may_steal);