block: add I/O throttling algorithm
Signed-off-by: Zhi Yong Wu <wuzhy@linux.vnet.ibm.com> Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
		
							parent
							
								
									e9e6295b28
								
							
						
					
					
						commit
						98f90dba5e
					
				
							
								
								
									
										234
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										234
									
								
								block.c
									
									
									
									
									
								
							@ -74,6 +74,13 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
 | 
				
			|||||||
                                               bool is_write);
 | 
					                                               bool is_write);
 | 
				
			||||||
static void coroutine_fn bdrv_co_do_rw(void *opaque);
 | 
					static void coroutine_fn bdrv_co_do_rw(void *opaque);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors,
 | 
				
			||||||
 | 
					        bool is_write, double elapsed_time, uint64_t *wait);
 | 
				
			||||||
 | 
					static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write,
 | 
				
			||||||
 | 
					        double elapsed_time, uint64_t *wait);
 | 
				
			||||||
 | 
					static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors,
 | 
				
			||||||
 | 
					        bool is_write, int64_t *wait);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
 | 
					static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
 | 
				
			||||||
    QTAILQ_HEAD_INITIALIZER(bdrv_states);
 | 
					    QTAILQ_HEAD_INITIALIZER(bdrv_states);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -107,6 +114,24 @@ int is_windows_drive(const char *filename)
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* throttling disk I/O limits */
 | 
					/* throttling disk I/O limits */
 | 
				
			||||||
 | 
					void bdrv_io_limits_disable(BlockDriverState *bs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    bs->io_limits_enabled = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (qemu_co_queue_next(&bs->throttled_reqs));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (bs->block_timer) {
 | 
				
			||||||
 | 
					        qemu_del_timer(bs->block_timer);
 | 
				
			||||||
 | 
					        qemu_free_timer(bs->block_timer);
 | 
				
			||||||
 | 
					        bs->block_timer = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bs->slice_start = 0;
 | 
				
			||||||
 | 
					    bs->slice_end   = 0;
 | 
				
			||||||
 | 
					    bs->slice_time  = 0;
 | 
				
			||||||
 | 
					    memset(&bs->io_base, 0, sizeof(bs->io_base));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void bdrv_block_timer(void *opaque)
 | 
					static void bdrv_block_timer(void *opaque)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    BlockDriverState *bs = opaque;
 | 
					    BlockDriverState *bs = opaque;
 | 
				
			||||||
@ -136,6 +161,31 @@ bool bdrv_io_limits_enabled(BlockDriverState *bs)
 | 
				
			|||||||
         || io_limits->iops[BLOCK_IO_LIMIT_TOTAL];
 | 
					         || io_limits->iops[BLOCK_IO_LIMIT_TOTAL];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bdrv_io_limits_intercept(BlockDriverState *bs,
 | 
				
			||||||
 | 
					                                     bool is_write, int nb_sectors)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int64_t wait_time = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!qemu_co_queue_empty(&bs->throttled_reqs)) {
 | 
				
			||||||
 | 
					        qemu_co_queue_wait(&bs->throttled_reqs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* In fact, we hope to keep each request's timing, in FIFO mode. The next
 | 
				
			||||||
 | 
					     * throttled requests will not be dequeued until the current request is
 | 
				
			||||||
 | 
					     * allowed to be serviced. So if the current request still exceeds the
 | 
				
			||||||
 | 
					     * limits, it will be inserted to the head. All requests followed it will
 | 
				
			||||||
 | 
					     * be still in throttled_reqs queue.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (bdrv_exceed_io_limits(bs, nb_sectors, is_write, &wait_time)) {
 | 
				
			||||||
 | 
					        qemu_mod_timer(bs->block_timer,
 | 
				
			||||||
 | 
					                       wait_time + qemu_get_clock_ns(vm_clock));
 | 
				
			||||||
 | 
					        qemu_co_queue_wait_insert_head(&bs->throttled_reqs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    qemu_co_queue_next(&bs->throttled_reqs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* check if the path starts with "<protocol>:" */
 | 
					/* check if the path starts with "<protocol>:" */
 | 
				
			||||||
static int path_has_protocol(const char *path)
 | 
					static int path_has_protocol(const char *path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -718,6 +768,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
 | 
				
			|||||||
        bdrv_dev_change_media_cb(bs, true);
 | 
					        bdrv_dev_change_media_cb(bs, true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* throttling disk I/O limits */
 | 
				
			||||||
 | 
					    if (bs->io_limits_enabled) {
 | 
				
			||||||
 | 
					        bdrv_io_limits_enable(bs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unlink_and_fail:
 | 
					unlink_and_fail:
 | 
				
			||||||
@ -753,6 +808,11 @@ void bdrv_close(BlockDriverState *bs)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        bdrv_dev_change_media_cb(bs, false);
 | 
					        bdrv_dev_change_media_cb(bs, false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*throttling disk I/O limits*/
 | 
				
			||||||
 | 
					    if (bs->io_limits_enabled) {
 | 
				
			||||||
 | 
					        bdrv_io_limits_disable(bs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bdrv_close_all(void)
 | 
					void bdrv_close_all(void)
 | 
				
			||||||
@ -1298,6 +1358,11 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
 | 
				
			|||||||
        return -EIO;
 | 
					        return -EIO;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* throttling disk read I/O */
 | 
				
			||||||
 | 
					    if (bs->io_limits_enabled) {
 | 
				
			||||||
 | 
					        bdrv_io_limits_intercept(bs, false, nb_sectors);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
 | 
					    return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1328,6 +1393,11 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
 | 
				
			|||||||
        return -EIO;
 | 
					        return -EIO;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* throttling disk write I/O */
 | 
				
			||||||
 | 
					    if (bs->io_limits_enabled) {
 | 
				
			||||||
 | 
					        bdrv_io_limits_intercept(bs, true, nb_sectors);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
 | 
					    ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (bs->dirty_bitmap) {
 | 
					    if (bs->dirty_bitmap) {
 | 
				
			||||||
@ -2519,6 +2589,170 @@ void bdrv_aio_cancel(BlockDriverAIOCB *acb)
 | 
				
			|||||||
    acb->pool->cancel(acb);
 | 
					    acb->pool->cancel(acb);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* block I/O throttling */
 | 
				
			||||||
 | 
					static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors,
 | 
				
			||||||
 | 
					                 bool is_write, double elapsed_time, uint64_t *wait)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    uint64_t bps_limit = 0;
 | 
				
			||||||
 | 
					    double   bytes_limit, bytes_base, bytes_res;
 | 
				
			||||||
 | 
					    double   slice_time, wait_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) {
 | 
				
			||||||
 | 
					        bps_limit = bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL];
 | 
				
			||||||
 | 
					    } else if (bs->io_limits.bps[is_write]) {
 | 
				
			||||||
 | 
					        bps_limit = bs->io_limits.bps[is_write];
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (wait) {
 | 
				
			||||||
 | 
					            *wait = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    slice_time = bs->slice_end - bs->slice_start;
 | 
				
			||||||
 | 
					    slice_time /= (NANOSECONDS_PER_SECOND);
 | 
				
			||||||
 | 
					    bytes_limit = bps_limit * slice_time;
 | 
				
			||||||
 | 
					    bytes_base  = bs->nr_bytes[is_write] - bs->io_base.bytes[is_write];
 | 
				
			||||||
 | 
					    if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) {
 | 
				
			||||||
 | 
					        bytes_base += bs->nr_bytes[!is_write] - bs->io_base.bytes[!is_write];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* bytes_base: the bytes of data which have been read/written; and
 | 
				
			||||||
 | 
					     *             it is obtained from the history statistic info.
 | 
				
			||||||
 | 
					     * bytes_res: the remaining bytes of data which need to be read/written.
 | 
				
			||||||
 | 
					     * (bytes_base + bytes_res) / bps_limit: used to calcuate
 | 
				
			||||||
 | 
					     *             the total time for completing reading/writting all data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    bytes_res   = (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (bytes_base + bytes_res <= bytes_limit) {
 | 
				
			||||||
 | 
					        if (wait) {
 | 
				
			||||||
 | 
					            *wait = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Calc approx time to dispatch */
 | 
				
			||||||
 | 
					    wait_time = (bytes_base + bytes_res) / bps_limit - elapsed_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* When the I/O rate at runtime exceeds the limits,
 | 
				
			||||||
 | 
					     * bs->slice_end need to be extended in order that the current statistic
 | 
				
			||||||
 | 
					     * info can be kept until the timer fire, so it is increased and tuned
 | 
				
			||||||
 | 
					     * based on the result of experiment.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    bs->slice_time = wait_time * BLOCK_IO_SLICE_TIME * 10;
 | 
				
			||||||
 | 
					    bs->slice_end += bs->slice_time - 3 * BLOCK_IO_SLICE_TIME;
 | 
				
			||||||
 | 
					    if (wait) {
 | 
				
			||||||
 | 
					        *wait = wait_time * BLOCK_IO_SLICE_TIME * 10;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write,
 | 
				
			||||||
 | 
					                             double elapsed_time, uint64_t *wait)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    uint64_t iops_limit = 0;
 | 
				
			||||||
 | 
					    double   ios_limit, ios_base;
 | 
				
			||||||
 | 
					    double   slice_time, wait_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) {
 | 
				
			||||||
 | 
					        iops_limit = bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL];
 | 
				
			||||||
 | 
					    } else if (bs->io_limits.iops[is_write]) {
 | 
				
			||||||
 | 
					        iops_limit = bs->io_limits.iops[is_write];
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (wait) {
 | 
				
			||||||
 | 
					            *wait = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    slice_time = bs->slice_end - bs->slice_start;
 | 
				
			||||||
 | 
					    slice_time /= (NANOSECONDS_PER_SECOND);
 | 
				
			||||||
 | 
					    ios_limit  = iops_limit * slice_time;
 | 
				
			||||||
 | 
					    ios_base   = bs->nr_ops[is_write] - bs->io_base.ios[is_write];
 | 
				
			||||||
 | 
					    if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) {
 | 
				
			||||||
 | 
					        ios_base += bs->nr_ops[!is_write] - bs->io_base.ios[!is_write];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ios_base + 1 <= ios_limit) {
 | 
				
			||||||
 | 
					        if (wait) {
 | 
				
			||||||
 | 
					            *wait = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Calc approx time to dispatch */
 | 
				
			||||||
 | 
					    wait_time = (ios_base + 1) / iops_limit;
 | 
				
			||||||
 | 
					    if (wait_time > elapsed_time) {
 | 
				
			||||||
 | 
					        wait_time = wait_time - elapsed_time;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        wait_time = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bs->slice_time = wait_time * BLOCK_IO_SLICE_TIME * 10;
 | 
				
			||||||
 | 
					    bs->slice_end += bs->slice_time - 3 * BLOCK_IO_SLICE_TIME;
 | 
				
			||||||
 | 
					    if (wait) {
 | 
				
			||||||
 | 
					        *wait = wait_time * BLOCK_IO_SLICE_TIME * 10;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors,
 | 
				
			||||||
 | 
					                           bool is_write, int64_t *wait)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int64_t  now, max_wait;
 | 
				
			||||||
 | 
					    uint64_t bps_wait = 0, iops_wait = 0;
 | 
				
			||||||
 | 
					    double   elapsed_time;
 | 
				
			||||||
 | 
					    int      bps_ret, iops_ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    now = qemu_get_clock_ns(vm_clock);
 | 
				
			||||||
 | 
					    if ((bs->slice_start < now)
 | 
				
			||||||
 | 
					        && (bs->slice_end > now)) {
 | 
				
			||||||
 | 
					        bs->slice_end = now + bs->slice_time;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        bs->slice_time  =  5 * BLOCK_IO_SLICE_TIME;
 | 
				
			||||||
 | 
					        bs->slice_start = now;
 | 
				
			||||||
 | 
					        bs->slice_end   = now + bs->slice_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bs->io_base.bytes[is_write]  = bs->nr_bytes[is_write];
 | 
				
			||||||
 | 
					        bs->io_base.bytes[!is_write] = bs->nr_bytes[!is_write];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bs->io_base.ios[is_write]    = bs->nr_ops[is_write];
 | 
				
			||||||
 | 
					        bs->io_base.ios[!is_write]   = bs->nr_ops[!is_write];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elapsed_time  = now - bs->slice_start;
 | 
				
			||||||
 | 
					    elapsed_time  /= (NANOSECONDS_PER_SECOND);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bps_ret  = bdrv_exceed_bps_limits(bs, nb_sectors,
 | 
				
			||||||
 | 
					                                      is_write, elapsed_time, &bps_wait);
 | 
				
			||||||
 | 
					    iops_ret = bdrv_exceed_iops_limits(bs, is_write,
 | 
				
			||||||
 | 
					                                      elapsed_time, &iops_wait);
 | 
				
			||||||
 | 
					    if (bps_ret || iops_ret) {
 | 
				
			||||||
 | 
					        max_wait = bps_wait > iops_wait ? bps_wait : iops_wait;
 | 
				
			||||||
 | 
					        if (wait) {
 | 
				
			||||||
 | 
					            *wait = max_wait;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        now = qemu_get_clock_ns(vm_clock);
 | 
				
			||||||
 | 
					        if (bs->slice_end < now + max_wait) {
 | 
				
			||||||
 | 
					            bs->slice_end = now + max_wait;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (wait) {
 | 
				
			||||||
 | 
					        *wait = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**************************************************************/
 | 
					/**************************************************************/
 | 
				
			||||||
/* async block device emulation */
 | 
					/* async block device emulation */
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								block.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								block.h
									
									
									
									
									
								
							@ -100,6 +100,7 @@ void bdrv_info_stats(Monitor *mon, QObject **ret_data);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* disk I/O throttling */
 | 
					/* disk I/O throttling */
 | 
				
			||||||
void bdrv_io_limits_enable(BlockDriverState *bs);
 | 
					void bdrv_io_limits_enable(BlockDriverState *bs);
 | 
				
			||||||
 | 
					void bdrv_io_limits_disable(BlockDriverState *bs);
 | 
				
			||||||
bool bdrv_io_limits_enabled(BlockDriverState *bs);
 | 
					bool bdrv_io_limits_enabled(BlockDriverState *bs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bdrv_init(void);
 | 
					void bdrv_init(void);
 | 
				
			||||||
 | 
				
			|||||||
@ -39,6 +39,7 @@
 | 
				
			|||||||
#define BLOCK_IO_LIMIT_TOTAL    2
 | 
					#define BLOCK_IO_LIMIT_TOTAL    2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define BLOCK_IO_SLICE_TIME     100000000
 | 
					#define BLOCK_IO_SLICE_TIME     100000000
 | 
				
			||||||
 | 
					#define NANOSECONDS_PER_SECOND  1000000000.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define BLOCK_OPT_SIZE          "size"
 | 
					#define BLOCK_OPT_SIZE          "size"
 | 
				
			||||||
#define BLOCK_OPT_ENCRYPT       "encryption"
 | 
					#define BLOCK_OPT_ENCRYPT       "encryption"
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user