diff --git a/ChangeLog b/ChangeLog index e88bb4c7d..13af513e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2012-06-22 Vladimir Serbinenko + + 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 * grub-core/net/net.c (receive_packets): Stop after 100 packets to let diff --git a/grub-core/net/http.c b/grub-core/net/http.c index be9dafacd..fc8f3babd 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -82,6 +82,7 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) 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); } @@ -156,6 +157,7 @@ http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)), 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); } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index dbfc9dba4..ae7340cb2 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -1309,7 +1309,7 @@ grub_net_fs_close (grub_file_t 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) @@ -1332,7 +1332,7 @@ receive_packets (struct grub_net_card *card) 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); @@ -1362,7 +1362,7 @@ grub_net_poll_cards (unsigned time, int *stop_condition) 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 (); } @@ -1376,7 +1376,7 @@ grub_net_poll_cards_idle_real (void) 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 (); } @@ -1417,12 +1417,19 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) 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; diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index abd5395d6..45a908c7d 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -102,6 +102,7 @@ typedef struct tftp_data 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; @@ -124,7 +125,7 @@ cmp (const void *a__, const void *b__) } 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]; @@ -144,8 +145,11 @@ ack (grub_net_udp_socket_t sock, grub_uint16_t block) 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 @@ -185,7 +189,7 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), } 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: @@ -195,9 +199,6 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), 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) @@ -223,6 +224,16 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), 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) @@ -233,6 +244,7 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), if (size < data->block_size) { file->device->net->eof = 1; + file->device->net->stall = 1; grub_net_udp_close (data->sock); data->sock = NULL; } @@ -435,11 +447,26 @@ tftp_close (struct grub_file *file) 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) diff --git a/include/grub/net.h b/include/grub/net.h index 1db05e806..3877451da 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -93,6 +93,7 @@ typedef struct grub_net_packets { grub_net_packet_t *first; grub_net_packet_t *last; + grub_size_t count; } grub_net_packets_t; #ifdef GRUB_MACHINE_EFI @@ -204,12 +205,16 @@ grub_net_put_packet (grub_net_packets_t *pkts, struct grub_net_buff *nb) 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 @@ -236,6 +241,7 @@ struct grub_net_app_protocol 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 @@ -247,6 +253,7 @@ 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);