+2012-06-22 Vladimir Serbinenko <phcoder@gmail.com>
+
+ Implement flow control for tftp.
+
+ * grub-core/net/net.c (receive_packets): Decrease the stop to 10
+ packets but stop only if stop condition is satisfied.
+ (grub_net_fs_read_real): Call packets_pulled after real read. Use
+ `stall' instead of `eof' as stop condition.
+ * grub-core/net/http.c (parse_line): Set `stall' on EOF.
+ (http_err): Likewise.
+ * grub-core/net/tftp.c (ack): Replace the first argument with data
+ instead of socket.
+ (tftp_receive): Stall if too many packets are in wait queue.
+ (tftp_packets_pulled): New function.
+ (grub_tftp_protocol): Set packets_pulled.
+ * include/grub/net.h (grub_net_packets): New field count.
+ (grub_net_put_packet): Increment count.
+ (grub_net_remove_packet): Likewise.
+ (grub_net_app_protocol): New field `packets_pulled'.
+ (grub_net): New field `stall'.
+
2012-06-22 Vladimir Serbinenko <phcoder@gmail.com>
* grub-core/net/net.c (receive_packets): Stop after 100 packets to let
if (data->chunk_rem == 0)
{
file->device->net->eof = 1;
+ file->device->net->stall = 1;
if (file->size == GRUB_FILE_SIZE_UNKNOWN)
file->size = have_ahead (file);
}
grub_free (data->current_line);
grub_free (data);
file->device->net->eof = 1;
+ file->device->net->stall = 1;
if (file->size == GRUB_FILE_SIZE_UNKNOWN)
file->size = have_ahead (file);
}
}
static void
-receive_packets (struct grub_net_card *card)
+receive_packets (struct grub_net_card *card, int *stop_condition)
{
int received = 0;
if (card->num_ifaces == 0)
and just mark them as used and not used. */
struct grub_net_buff *nb;
- if (received > 100)
+ if (received > 10 && stop_condition && *stop_condition)
break;
nb = card->driver->recv (card);
while ((grub_get_time_ms () - start_time) < time
&& (!stop_condition || !*stop_condition))
FOR_NET_CARDS (card)
- receive_packets (card);
+ receive_packets (card, stop_condition);
grub_net_tcp_retransmit ();
}
if (ctime < card->last_poll
|| ctime >= card->last_poll + card->idle_poll_delay_ms)
- receive_packets (card);
+ receive_packets (card, 0);
}
grub_net_tcp_retransmit ();
}
nb->data += amount;
if (!len)
- return total;
+ {
+ if (net->protocol->packets_pulled)
+ net->protocol->packets_pulled (file);
+ return total;
+ }
}
+ if (net->protocol->packets_pulled)
+ net->protocol->packets_pulled (file);
+
if (!net->eof)
{
try++;
- grub_net_poll_cards (GRUB_NET_INTERVAL, &net->eof);
+ grub_net_poll_cards (GRUB_NET_INTERVAL, &net->stall);
}
else
return total;
grub_uint64_t file_size;
grub_uint64_t block;
grub_uint32_t block_size;
+ grub_uint32_t ack_sent;
int have_oack;
struct grub_error_saved save_err;
grub_net_udp_socket_t sock;
}
static grub_err_t
-ack (grub_net_udp_socket_t sock, grub_uint16_t block)
+ack (tftp_data_t data, grub_uint16_t block)
{
struct tftphdr *tftph_ack;
grub_uint8_t nbdata[512];
tftph_ack->opcode = grub_cpu_to_be16 (TFTP_ACK);
tftph_ack->u.ack.block = block;
- err = grub_net_send_udp_packet (sock, &nb_ack);
- return err;
+ err = grub_net_send_udp_packet (data->sock, &nb_ack);
+ if (err)
+ return err;
+ data->ack_sent = block;
+ return GRUB_ERR_NONE;
}
static grub_err_t
}
data->block = 0;
grub_netbuff_free (nb);
- err = ack (data->sock, 0);
+ err = ack (data, 0);
grub_error_save (&data->save_err);
return GRUB_ERR_NONE;
case TFTP_DATA:
grub_dprintf ("tftp", "TFTP packet too small\n");
return GRUB_ERR_NONE;
}
- err = ack (data->sock, tftph->u.data.block);
- if (err)
- return err;
err = grub_priority_queue_push (data->pq, &nb);
if (err)
grub_priority_queue_pop (data->pq);
+ if (file->device->net->packs.count < 200)
+ err = ack (data, tftph->u.data.block);
+ else
+ {
+ file->device->net->stall = 1;
+ err = 0;
+ }
+ if (err)
+ return err;
+
err = grub_netbuff_pull (nb_top, sizeof (tftph->opcode) +
sizeof (tftph->u.data.block));
if (err)
if (size < data->block_size)
{
file->device->net->eof = 1;
+ file->device->net->stall = 1;
grub_net_udp_close (data->sock);
data->sock = NULL;
}
return GRUB_ERR_NONE;
}
+static grub_err_t
+tftp_packets_pulled (struct grub_file *file)
+{
+ tftp_data_t data = file->data;
+ if (file->device->net->packs.count >= 200)
+ return 0;
+
+ if (!file->device->net->eof)
+ file->device->net->stall = 0;
+ if (data->ack_sent >= data->block)
+ return 0;
+ return ack (data, data->block);
+}
+
static struct grub_net_app_protocol grub_tftp_protocol =
{
.name = "tftp",
.open = tftp_open,
- .close = tftp_close
+ .close = tftp_close,
+ .packets_pulled = tftp_packets_pulled
};
GRUB_MOD_INIT (tftp)
{
grub_net_packet_t *first;
grub_net_packet_t *last;
+ grub_size_t count;
} grub_net_packets_t;
#ifdef GRUB_MACHINE_EFI
else
pkts->first = pkts->last = n;
+ pkts->count++;
+
return GRUB_ERR_NONE;
}
static inline void
grub_net_remove_packet (grub_net_packet_t *pkt)
{
+ pkt->up->count--;
+
if (pkt->prev)
pkt->prev->next = pkt->next;
else
grub_err_t (*open) (struct grub_file *file, const char *filename);
grub_err_t (*seek) (struct grub_file *file, grub_off_t off);
grub_err_t (*close) (struct grub_file *file);
+ grub_err_t (*packets_pulled) (struct grub_file *file);
};
typedef struct grub_net
grub_off_t offset;
grub_fs_t fs;
int eof;
+ int stall;
} *grub_net_t;
extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name);