return *(u64 *)_priv;
 }
 
+static void igc_xsk_request_launch_time(u64 launch_time, void *_priv)
+{
+       struct igc_metadata_request *meta_req = _priv;
+       struct igc_ring *tx_ring = meta_req->tx_ring;
+       __le32 launch_time_offset;
+       bool insert_empty = false;
+       bool first_flag = false;
+       u16 used_desc = 0;
+
+       if (!tx_ring->launchtime_enable)
+               return;
+
+       launch_time_offset = igc_tx_launchtime(tx_ring,
+                                              ns_to_ktime(launch_time),
+                                              &first_flag, &insert_empty);
+       if (insert_empty) {
+               /* Disregard the launch time request if the required empty frame
+                * fails to be inserted.
+                */
+               if (igc_insert_empty_frame(tx_ring))
+                       return;
+
+               meta_req->tx_buffer =
+                       &tx_ring->tx_buffer_info[tx_ring->next_to_use];
+               /* Inserting an empty packet requires two descriptors:
+                * one data descriptor and one context descriptor.
+                */
+               used_desc += 2;
+       }
+
+       /* Use one context descriptor to specify launch time and first flag. */
+       igc_tx_ctxtdesc(tx_ring, launch_time_offset, first_flag, 0, 0, 0);
+       used_desc += 1;
+
+       /* Update the number of used descriptors in this request */
+       meta_req->used_desc += used_desc;
+}
+
 const struct xsk_tx_metadata_ops igc_xsk_tx_metadata_ops = {
        .tmo_request_timestamp          = igc_xsk_request_timestamp,
        .tmo_fill_timestamp             = igc_xsk_fill_timestamp,
+       .tmo_request_launch_time        = igc_xsk_request_launch_time,
 };
 
 static void igc_xdp_xmit_zc(struct igc_ring *ring)
        ntu = ring->next_to_use;
        budget = igc_desc_unused(ring);
 
-       while (xsk_tx_peek_desc(pool, &xdp_desc) && budget--) {
+       /* Packets with launch time require one data descriptor and one context
+        * descriptor. When the launch time falls into the next Qbv cycle, we
+        * may need to insert an empty packet, which requires two more
+        * descriptors. Therefore, to be safe, we always ensure we have at least
+        * 4 descriptors available.
+        */
+       while (xsk_tx_peek_desc(pool, &xdp_desc) && budget >= 4) {
                struct igc_metadata_request meta_req;
                struct xsk_tx_metadata *meta = NULL;
                struct igc_tx_buffer *bi;
                meta_req.tx_ring = ring;
                meta_req.tx_buffer = bi;
                meta_req.meta = meta;
+               meta_req.used_desc = 0;
                xsk_tx_metadata_request(meta, &igc_xsk_tx_metadata_ops,
                                        &meta_req);
 
+               /* xsk_tx_metadata_request() may have updated next_to_use */
+               ntu = ring->next_to_use;
+
+               /* xsk_tx_metadata_request() may have updated Tx buffer info */
+               bi = meta_req.tx_buffer;
+
+               /* xsk_tx_metadata_request() may use a few descriptors */
+               budget -= meta_req.used_desc;
+
                tx_desc = IGC_TX_DESC(ring, ntu);
                tx_desc->read.cmd_type_len = cpu_to_le32(meta_req.cmd_type);
                tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
                ntu++;
                if (ntu == ring->count)
                        ntu = 0;
+
+               ring->next_to_use = ntu;
+               budget--;
        }
 
-       ring->next_to_use = ntu;
        if (tx_desc) {
                igc_flush_tx_descriptors(ring);
                xsk_tx_release(pool);