void rate_sleep(struct rate *r) { struct timespec now, til; /* what clock to use depends on whether clock_nanosleep() is available */ #if HAVE_CLOCK_NANOSLEEP static const clockid_t rate_clock = CLOCK_MONOTONIC; #else static const clockid_t rate_clock = CLOCK_REALTIME; #endif if (r == NULL) return; /* update the event counter */ r->count += 1; /* fetch the current time */ clock_gettime(rate_clock, &now); /* special case: if this is the first call to rate_sleep(), * calculate when the next tick will be. this is a little bit more * accurate than calculating it in rate_init(). */ if (r->count == 1) { r->start = now; r->next_tick = calc_next_tick(&now, &r->period); } /* adjust the rate and period every 'freq' events. * skip the first window of 'freq' events. * disabled if 'freq' is 0. */ if (r->freq != 0 && (r->count % r->freq) == 0 && r->count > r->freq) adjust_rate(r, &now); /* 'til', amount of time remaining until the next tick */ til = r->next_tick; my_timespec_sub(&now, &til); /* if 'til' is in the past, don't bother sleeping */ if (ts_nanos(&til) > 0) { /* do the sleep */ #if HAVE_CLOCK_NANOSLEEP clock_nanosleep(rate_clock, TIMER_ABSTIME, &r->next_tick, NULL); #else struct timespec rel; rel = r->next_tick; my_timespec_sub(&now, &rel); my_nanosleep(&rel); #endif /* re-fetch the current time */ clock_gettime(rate_clock, &now); } /* calculate the next tick */ r->next_tick = calc_next_tick(&now, &r->period); }
/** * Performs the Transfer phase for a particular file * Returns 1 if at least one client finished, 0 if all are dropped or aborted */ int transfer_phase(struct finfo_t *finfo) { unsigned char *packet, *encpacket, *data; char path[MAXPATHNAME]; struct uftp_h *header; struct fileseg_h *fileseg; int max_time, alldone, numbytes, sent_blocks, current_naks; unsigned int pass, section, numnaks, block; struct timeval start_time, last_sent, current_sent; int64_t avgwait, waitcnt, overage, tdiff; f_offset_t offset, curr_offset; int file, i; if (finfo->file_id != 0) { // First check to see if all clients are already done for this file. // This can happen on a restart when the file finished on the // last attempt and responded to the FILEINFO with a COMPLETE for (i = 0, alldone = 1; (i < destcount) && alldone; i++) { alldone = alldone && ((destlist[i].status == DEST_DONE) || (client_error(i)) || (destlist[i].clientcnt != -1)); } if (alldone) { gettimeofday(&start_time, NULL); print_status(finfo, start_time); return 1; } } // If rate is -1, use 100Mbps for purpose of calculating max time max_time = (int)floor(((double)weight / 100) * ((double)finfo->size / (((rate == -1) ? 100000 : rate) / 8) / 1024)); if (max_time < min_time) { max_time = min_time; } if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) { log(0, 0, "Maximum file transfer time: %d seconds", max_time); snprintf(path, sizeof(path), "%s%c%s", finfo->basedir, PATH_SEP, finfo->filename); if ((file = open(path, OPENREAD, 0)) == -1) { syserror(0, 0, "Error opening file"); return 1; } } else { // At end of group, all non-errored client are DEST_DONE from the // last file, so reset them to DEST_ACTIVE to get the final COMPLETE. for (i = 0; i < destcount; i++) { if (!client_error(i)) { destlist[i].status = DEST_ACTIVE; } } } packet = calloc(mtu, 1); encpacket = calloc(mtu, 1); if ((packet == NULL) || (encpacket == NULL)) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)packet; fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h)); data = (unsigned char *)fileseg + sizeof(struct fileseg_h); set_uftp_header(header, FILESEG, finfo, &receive_dest); pass = 1; alldone = 0; gettimeofday(&start_time, NULL); do { avgwait = 0; waitcnt = 0; numnaks = 0; overage = 0; section = 1; curr_offset = 0; sent_blocks = 0; gettimeofday(&last_sent, NULL); if (finfo->file_id != 0) { log(0, 0, "Sending file...pass %d", pass); lseek_func(file, 0, SEEK_SET); } else { log(0, 0, "Finishing group"); } fileseg->func = FILESEG; fileseg->file_id = htons(finfo->file_id); fileseg->pass = pass; fileseg->section = htons(section); for (block = 0; block < finfo->blocks; block++) { // If all clients received this file partially on a prior attempt // and it's the first pass, request NAKs for all sections // right away and don't send any data packets. if (((pass == 1) || finfo->naklist[block]) && !((pass == 1) && finfo->partial)) { if (diff_sec(last_sent, start_time) > max_time) { log0(0, 0, "Max file transfer time exceeded"); send_abort(finfo, "Max file transfer time exceeded", &receive_dest, NULL, (keytype != KEY_NONE), !quit_on_error); alldone = 1; for (i = 0; i < destcount; i++) { if (quit_on_error || ((destlist[i].status == DEST_ACTIVE) && destlist[i].clientcnt == -1 )) { destlist[i].status = DEST_ABORT; } } break; } // On the first pass, go straight through the file. // On later passes, seek to the next packet. if (pass != 1) { log4(0, 0, "Resending %d", block); if (!seek_block(file, block, &offset, curr_offset)) { continue; } } if ((numbytes = read(file, data, blocksize)) == -1) { syserror(0, 0, "read failed"); continue; } if (pass != 1) { curr_offset = offset + numbytes; } // Keep track of how long we really slept compared to how // long we expected to sleep. If we went over, subtract the // time over from the next sleep time. This way we maintain // the proper average sleep time. This can result in multiple // packets going out at once, potentially losing packets. if (packet_wait > overage) { usleep(packet_wait - (int32_t)overage); } gettimeofday(¤t_sent, NULL); tdiff = diff_usec(current_sent, last_sent); avgwait += tdiff; waitcnt++; if (packet_wait) overage += tdiff - packet_wait; last_sent = current_sent; fileseg->seq_num = htonl(block); send_data(finfo, packet, numbytes, encpacket); sent_blocks++; } if ((block % (blocksize * 8) == (blocksize * 8) - 1) || (block == finfo->blocks - 1)) { if ((pass == 1) || sent_blocks) { current_naks = get_naks(finfo, pass, section, &alldone); numnaks += current_naks; if ((rate != -1) && (cc_count > 0)) { if (!read_cc_config(cc_config)) { log1(0, 0, "Error rereading congestion control " "config, using prior values"); } adjust_rate(current_naks, sent_blocks); } overage = 0; if (alldone) break; } sent_blocks = 0; gettimeofday(&last_sent, NULL); fileseg->section = htons(++section); } } if ((finfo->size == 0) && !alldone) { // If it's the end of the group, or an empty file, a DONE was // never sent, so send it now numnaks += get_naks(finfo, pass, section, &alldone); } if (finfo->file_id != 0) { log(0, 0, "Average wait time = %.2f us", (waitcnt == 0) ? 0 : (float)avgwait / waitcnt); log(0, 0, "Received %d distinct NAKs for pass %d", numnaks, pass); } pass++; } while (!alldone); if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) { close(file); } print_status(finfo, start_time); free(packet); free(encpacket); for (i = 0; i < destcount; i++) { if (quit_on_error) { // Check to see that all finished if ((destlist[i].status != DEST_DONE) && (destlist[i].clientcnt == -1)) { return 0; } } else { // Check to see if at least one finished if (destlist[i].status == DEST_DONE) { return 1; } } } if (quit_on_error) { return 1; } else { return 0; } }