Beispiel #1
0
static gint64 rm_hasher_buffered_read(RmHasher *hasher, GThreadPool *hashpipe,
                                      RmDigest *digest, char *path, gsize start_offset,
                                      gsize bytes_to_read) {
    FILE *fd = NULL;
    if(bytes_to_read == 0) {
        bytes_to_read = G_MAXSIZE;
    }

    gsize total_bytes_read = 0;

    if((fd = fopen(path, "rb")) == NULL) {
        rm_log_info("fopen(3) failed for %s: %s\n", path, g_strerror(errno));
        goto finish;
    }

    gint32 bytes_read = 0;

    rm_hasher_request_readahead(fileno(fd), start_offset, bytes_to_read);

    if(fseek(fd, start_offset, SEEK_SET) == -1) {
        rm_log_perror("fseek(3) failed");
        goto finish;
    }

    RmBuffer *buffer = rm_buffer_get(hasher->mem_pool);

    while((bytes_read =
               fread(buffer->data, 1, MIN(bytes_to_read, hasher->buf_size), fd)) > 0) {
        bytes_to_read -= bytes_read;

        buffer->len = bytes_read;
        buffer->digest = digest;
        buffer->user_data = NULL;

        rm_util_thread_pool_push(hashpipe, buffer);

        total_bytes_read += bytes_read;
        buffer = rm_buffer_get(hasher->mem_pool);
    }
    rm_buffer_release(buffer);

    if(ferror(fd) != 0) {
        rm_log_perror("fread(3) failed");
        if(total_bytes_read == bytes_to_read) {
            /* signal error to caller */
            total_bytes_read++;
        }
    }

finish:
    if(fd != NULL) {
        fclose(fd);
    }
    return total_bytes_read;
}
Beispiel #2
0
/** @brief Push an RmMDSDevice to the threadpool
 **/
void rm_mds_device_start(RmMDSDevice *device, RmMDS *mds) {
    rm_assert_gentle(device->threads == 0);

    device->threads = mds->threads_per_disk;
    g_mutex_lock(&device->lock);
    {
        for(int i = 0; i < mds->threads_per_disk; ++i) {
            rm_log_debug_line("Starting disk %" LLU " (pointer %p) thread #%i",
                              (RmOff)device->disk, device, i + 1);
            rm_util_thread_pool_push(mds->pool, device);
        }
    }
    g_mutex_unlock(&device->lock);
}
Beispiel #3
0
RmDigest *rm_hasher_task_finish(RmHasherTask *task) {
    /* get a dummy buffer to use to signal the hasher thread that this increment is
     * finished */
    RmHasher *hasher = task->hasher;
    RmBuffer *finisher = rm_buffer_get(task->hasher->mem_pool);
    finisher->digest = task->digest;
    finisher->len = 0;
    finisher->user_data = task;
    rm_util_thread_pool_push(task->hashpipe, finisher);

    if(hasher->return_queue) {
        return g_async_queue_pop(hasher->return_queue);
    } else {
        return NULL;
    }
}
Beispiel #4
0
/** @brief RmMDSDevice worker thread
 **/
static void rm_mds_factory(RmMDSDevice *device, RmMDS *mds) {
    /* rm_mds_factory processes tasks from device->task_list.
     * After completing one pass of the device, returns self to the
     * mds->pool threadpool. */
    gint processed = 0;
    g_mutex_lock(&device->lock);
    {
        /* check for empty queues - if so then wait a little while before giving up */
        if(!device->sorted_tasks && !device->unsorted_tasks && device->ref_count > 0) {
            /* timed wait for signal from rm_mds_push_task_impl() */
            gint64 end_time = g_get_monotonic_time() + MDS_EMPTYQUEUE_SLEEP_US;
            g_cond_wait_until(&device->cond, &device->lock, end_time);
        }

        /* sort and merge task lists */
        if(device->unsorted_tasks) {
            if(mds->prioritiser) {
                device->sorted_tasks = g_slist_concat(
                                           g_slist_sort_with_data(device->unsorted_tasks,
                                                   (GCompareDataFunc)rm_mds_compare,
                                                   (RmMDSSortFunc)mds->prioritiser),
                                           device->sorted_tasks);
            } else {
                device->sorted_tasks =
                    g_slist_concat(device->unsorted_tasks, device->sorted_tasks);
            }
            device->unsorted_tasks = NULL;
        }
    }
    g_mutex_unlock(&device->lock);

    /* process tasks from device->sorted_tasks */
    RmMDSTask *task = NULL;
    while(processed < mds->pass_quota && (task = rm_util_slist_pop(&device->sorted_tasks, &device->lock))) {
        if(mds->func(task->task_data, mds->user_data)) {
            /* task succeeded; update counters */
            ++processed;
        }
        rm_mds_task_free(task);
    }

    if(rm_mds_device_ref(device, 0) > 0) {
        /* return self to pool for further processing */
        if(processed == 0) {
            /* stalled queue; chill for a bit */
            g_usleep(1000);
        }
        rm_util_thread_pool_push(mds->pool, device);
    } else if(g_atomic_int_dec_and_test(&device->threads)) {
        /* free self and signal to rm_mds_free() */
        g_mutex_lock(&mds->lock);
        {
            rm_log_debug_line("Freeing device %" LLU " (pointer %p)", (RmOff)device->disk,
                              device);
            g_hash_table_remove(mds->disks, GINT_TO_POINTER(device->disk));
            rm_mds_device_free(device);
            g_cond_signal(&mds->cond);
        }
        g_mutex_unlock(&mds->lock);
    }
}
Beispiel #5
0
static gint64 rm_hasher_unbuffered_read(RmHasher *hasher, GThreadPool *hashpipe,
                                        RmDigest *digest, char *path, gint64 start_offset,
                                        gint64 bytes_to_read) {
    gint32 bytes_read = 0;
    gint64 total_bytes_read = 0;
    guint64 file_offset = start_offset;

    if(bytes_to_read == 0) {
        RmStat stat_buf;
        if(rm_sys_stat(path, &stat_buf) != -1) {
            bytes_to_read = MAX(stat_buf.st_size - start_offset, 0);
        }
    }

    /* how many buffers to read? */
    const gint16 N_BUFFERS = MIN(4, DIVIDE_CEIL(bytes_to_read, hasher->buf_size));
    struct iovec readvec[N_BUFFERS + 1];

    int fd = 0;

    fd = rm_sys_open(path, O_RDONLY);
    if(fd == -1) {
        rm_log_info("open(2) failed for %s: %s\n", path, g_strerror(errno));
        goto finish;
    }

    /* preadv() is beneficial for large files since it can cut the
     * number of syscall heavily.  I suggest N_BUFFERS=4 as good
     * compromise between memory and cpu.
     *
     * With 16 buffers: 43% cpu 33,871 total
     * With  8 buffers: 43% cpu 32,098 total
     * With  4 buffers: 42% cpu 32,091 total
     * With  2 buffers: 44% cpu 32,245 total
     * With  1 buffers: 45% cpu 34,491 total
     */

    /* Give the kernel scheduler some hints */
    rm_hasher_request_readahead(fd, start_offset, bytes_to_read);

    /* Initialize the buffers to begin with.
     * After a buffer is full, a new one is retrieved.
     */
    RmBuffer **buffers;
    buffers = g_slice_alloc(sizeof(*buffers) * N_BUFFERS);

    memset(readvec, 0, sizeof(readvec));
    for(int i = 0; i < N_BUFFERS; ++i) {
        /* buffer is one contignous memory block */
        buffers[i] = rm_buffer_get(hasher->mem_pool);
        readvec[i].iov_base = buffers[i]->data;
        readvec[i].iov_len = hasher->buf_size;
    }

    while((bytes_to_read == 0 || total_bytes_read < bytes_to_read) &&
          (bytes_read = rm_sys_preadv(fd, readvec, N_BUFFERS, file_offset)) > 0) {
        bytes_read =
            MIN(bytes_read, bytes_to_read - total_bytes_read); /* ignore over-reads */

        int blocks = DIVIDE_CEIL(bytes_read, hasher->buf_size);
        rm_assert_gentle(blocks <= N_BUFFERS);

        total_bytes_read += bytes_read;
        file_offset += bytes_read;

        for(int i = 0; i < blocks; ++i) {
            /* Get the RmBuffer from the datapointer */
            RmBuffer *buffer = buffers[i];
            buffer->len = MIN(hasher->buf_size, bytes_read - i * hasher->buf_size);
            buffer->digest = digest;
            buffer->user_data = NULL;

            /* Send it to the hasher */
            rm_util_thread_pool_push(hashpipe, buffer);

            /* Allocate a new buffer - hasher will release the old buffer */
            buffers[i] = rm_buffer_get(hasher->mem_pool);
            readvec[i].iov_base = buffers[i]->data;
            readvec[i].iov_len = hasher->buf_size;
        }
    }

    if(bytes_read == -1) {
        rm_log_perror("preadv failed");
    } else if(total_bytes_read != bytes_to_read) {
        rm_log_error_line(_("Something went wrong reading %s; expected %li bytes, "
                            "got %li; ignoring"),
                          path, (long int)bytes_to_read, (long int)total_bytes_read);
    }

    /* Release the rest of the buffers */
    for(int i = 0; i < N_BUFFERS; ++i) {
        rm_buffer_release(buffers[i]);
    }
    g_slice_free1(sizeof(*buffers) * N_BUFFERS, buffers);

finish:
    if(fd > 0) {
        rm_sys_close(fd);
    }

    return total_bytes_read;
}