X-Git-Url: https://git.kernel.dk/?a=blobdiff_plain;f=zbd.c;h=876246ed9f5d639123012de683e423e251e4cc85;hb=402f08876d93be12f18703acfadb603eec7a5791;hp=9b603b97b18344b5e17baf31f199371a8c5f11ff;hpb=63e3e40d47e1f318aa4d770d74a7b14d64867e1d;p=fio.git diff --git a/zbd.c b/zbd.c index 9b603b97..876246ed 100644 --- a/zbd.c +++ b/zbd.c @@ -186,11 +186,14 @@ static bool zbd_verify_bs(void) * size of @buf. * * Returns 0 upon success and a negative error code upon failure. + * If the zone report is empty, always assume an error (device problem) and + * return -EIO. */ static int read_zone_info(int fd, uint64_t start_sector, void *buf, unsigned int bufsz) { struct blk_zone_report *hdr = buf; + int ret; if (bufsz < sizeof(*hdr)) return -EINVAL; @@ -199,7 +202,12 @@ static int read_zone_info(int fd, uint64_t start_sector, hdr->nr_zones = (bufsz - sizeof(*hdr)) / sizeof(struct blk_zone); hdr->sector = start_sector; - return ioctl(fd, BLKREPORTZONE, hdr) >= 0 ? 0 : -errno; + ret = ioctl(fd, BLKREPORTZONE, hdr); + if (ret) + return -errno; + if (!hdr->nr_zones) + return -EIO; + return 0; } /* @@ -304,13 +312,23 @@ static int init_zone_info(struct thread_data *td, struct fio_file *f) { uint32_t nr_zones; struct fio_zone_info *p; - uint64_t zone_size; + uint64_t zone_size = td->o.zone_size; struct zoned_block_device_info *zbd_info = NULL; pthread_mutexattr_t attr; int i; - zone_size = td->o.zone_size; - assert(zone_size); + if (zone_size == 0) { + log_err("%s: Specifying the zone size is mandatory for regular block devices with --zonemode=zbd\n\n", + f->file_name); + return 1; + } + + if (zone_size < 512) { + log_err("%s: zone size must be at least 512 bytes for --zonemode=zbd\n\n", + f->file_name); + return 1; + } + nr_zones = (f->real_file_size + zone_size - 1) / zone_size; zbd_info = scalloc(1, sizeof(*zbd_info) + (nr_zones + 1) * sizeof(zbd_info->zone_info[0])); @@ -393,8 +411,8 @@ static int parse_zone_info(struct thread_data *td, struct fio_file *f) if (td->o.zone_size == 0) { td->o.zone_size = zone_size; } else if (td->o.zone_size != zone_size) { - log_info("fio: %s job parameter zonesize %llu does not match disk zone size %llu.\n", - f->file_name, (unsigned long long) td->o.zone_size, + log_err("fio: %s job parameter zonesize %llu does not match disk zone size %llu.\n", + f->file_name, (unsigned long long) td->o.zone_size, (unsigned long long) zone_size); ret = -EINVAL; goto close; @@ -418,8 +436,6 @@ static int parse_zone_info(struct thread_data *td, struct fio_file *f) p->start = z->start << 9; switch (z->cond) { case BLK_ZONE_COND_NOT_WP: - p->wp = p->start; - break; case BLK_ZONE_COND_FULL: p->wp = p->start + zone_size; break; @@ -475,7 +491,7 @@ out: * * Returns 0 upon success and a negative error code upon failure. */ -int zbd_create_zone_info(struct thread_data *td, struct fio_file *f) +static int zbd_create_zone_info(struct thread_data *td, struct fio_file *f) { enum blk_zoned_model zbd_model; int ret = 0; @@ -543,7 +559,7 @@ static int zbd_init_zone_info(struct thread_data *td, struct fio_file *file) ret = zbd_create_zone_info(td, file); if (ret < 0) - td_verror(td, -ret, "BLKREPORTZONE failed"); + td_verror(td, -ret, "zbd_create_zone_info() failed"); return ret; } @@ -555,18 +571,8 @@ int zbd_init(struct thread_data *td) for_each_file(td, f, i) { if (f->filetype != FIO_TYPE_BLOCK) continue; - if (td->o.zone_size && td->o.zone_size < 512) { - log_err("%s: zone size must be at least 512 bytes for --zonemode=zbd\n\n", - f->file_name); - return 1; - } - if (td->o.zone_size == 0 && - get_zbd_model(f->file_name) == ZBD_DM_NONE) { - log_err("%s: Specifying the zone size is mandatory for regular block devices with --zonemode=zbd\n\n", - f->file_name); + if (zbd_init_zone_info(td, f)) return 1; - } - zbd_init_zone_info(td, f); } if (!zbd_using_direct_io()) { @@ -927,8 +933,8 @@ static void zbd_close_zone(struct thread_data *td, const struct fio_file *f, * a multiple of the fio block size. The caller must neither hold z->mutex * nor f->zbd_info->mutex. Returns with z->mutex held upon success. */ -struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td, - struct io_u *io_u) +static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td, + struct io_u *io_u) { const uint32_t min_bs = td->o.min_bs[io_u->ddir]; const struct fio_file *f = io_u->file; @@ -1111,37 +1117,44 @@ zbd_find_zone(struct thread_data *td, struct io_u *io_u, return NULL; } - /** - * zbd_post_submit - update the write pointer and unlock the zone lock + * zbd_queue_io - update the write pointer of a sequential zone * @io_u: I/O unit - * @success: Whether or not the I/O unit has been executed successfully + * @success: Whether or not the I/O unit has been queued successfully + * @q: queueing status (busy, completed or queued). * - * For write and trim operations, update the write pointer of all affected - * zones. + * For write and trim operations, update the write pointer of the I/O unit + * target zone. */ -static void zbd_post_submit(const struct io_u *io_u, bool success) +static void zbd_queue_io(struct io_u *io_u, int q, bool success) { - struct zoned_block_device_info *zbd_info; + const struct fio_file *f = io_u->file; + struct zoned_block_device_info *zbd_info = f->zbd_info; struct fio_zone_info *z; uint32_t zone_idx; - uint64_t end, zone_end; + uint64_t zone_end; - zbd_info = io_u->file->zbd_info; if (!zbd_info) return; - zone_idx = zbd_zone_idx(io_u->file, io_u->offset); - end = io_u->offset + io_u->buflen; - z = &zbd_info->zone_info[zone_idx]; + zone_idx = zbd_zone_idx(f, io_u->offset); assert(zone_idx < zbd_info->nr_zones); + z = &zbd_info->zone_info[zone_idx]; + if (z->type != BLK_ZONE_TYPE_SEQWRITE_REQ) return; + if (!success) goto unlock; + + dprint(FD_ZBD, + "%s: queued I/O (%lld, %llu) for zone %u\n", + f->file_name, io_u->offset, io_u->buflen, zone_idx); + switch (io_u->ddir) { case DDIR_WRITE: - zone_end = min(end, (z + 1)->start); + zone_end = min((uint64_t)(io_u->offset + io_u->buflen), + (z + 1)->start); pthread_mutex_lock(&zbd_info->mutex); /* * z->wp > zone_end means that one or more I/O errors @@ -1158,10 +1171,42 @@ static void zbd_post_submit(const struct io_u *io_u, bool success) default: break; } + unlock: - pthread_mutex_unlock(&z->mutex); + if (!success || q != FIO_Q_QUEUED) { + /* BUSY or COMPLETED: unlock the zone */ + pthread_mutex_unlock(&z->mutex); + io_u->zbd_put_io = NULL; + } +} - zbd_check_swd(io_u->file); +/** + * zbd_put_io - Unlock an I/O unit target zone lock + * @io_u: I/O unit + */ +static void zbd_put_io(const struct io_u *io_u) +{ + const struct fio_file *f = io_u->file; + struct zoned_block_device_info *zbd_info = f->zbd_info; + struct fio_zone_info *z; + uint32_t zone_idx; + + if (!zbd_info) + return; + + zone_idx = zbd_zone_idx(f, io_u->offset); + assert(zone_idx < zbd_info->nr_zones); + z = &zbd_info->zone_info[zone_idx]; + + if (z->type != BLK_ZONE_TYPE_SEQWRITE_REQ) + return; + + dprint(FD_ZBD, + "%s: terminate I/O (%lld, %llu) for zone %u\n", + f->file_name, io_u->offset, io_u->buflen, zone_idx); + + assert(pthread_mutex_unlock(&z->mutex) == 0); + zbd_check_swd(f); } bool zbd_unaligned_write(int error_code) @@ -1216,7 +1261,21 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u) zbd_check_swd(f); - pthread_mutex_lock(&zb->mutex); + /* + * Lock the io_u target zone. The zone will be unlocked if io_u offset + * is changed or when io_u completes and zbd_put_io() executed. + * To avoid multiple jobs doing asynchronous I/Os from deadlocking each + * other waiting for zone locks when building an io_u batch, first + * only trylock the zone. If the zone is already locked by another job, + * process the currently queued I/Os so that I/O progress is made and + * zones unlocked. + */ + if (pthread_mutex_trylock(&zb->mutex) != 0) { + if (!td_ioengine_flagged(td, FIO_SYNCIO)) + io_u_quiesce(td); + pthread_mutex_lock(&zb->mutex); + } + switch (io_u->ddir) { case DDIR_READ: if (td->runstate == TD_VERIFYING) { @@ -1354,8 +1413,10 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u) accept: assert(zb); assert(zb->cond != BLK_ZONE_COND_OFFLINE); - assert(!io_u->post_submit); - io_u->post_submit = zbd_post_submit; + assert(!io_u->zbd_queue_io); + assert(!io_u->zbd_put_io); + io_u->zbd_queue_io = zbd_queue_io; + io_u->zbd_put_io = zbd_put_io; return io_u_accept; eof: