ctl(4): Do hole-punching for UNMAP to file-backed LUNs

This adds support for SCSI UNMAP command to file-backed LUNs, if the
underlying file system has a non-zerofilling VOP_DEALLOCATE
implementation where some or all parts of the requested operation range
may be deallocated.

Sponsored by:	The FreeBSD Foundation
Reviewed by:	mav
Differential Revision:	https://reviews.freebsd.org/D31922
This commit is contained in:
Ka Ho Ng 2021-09-15 03:51:58 +08:00
parent 8cba2003e8
commit 49050613ef

View File

@ -3,13 +3,16 @@
*
* Copyright (c) 2003 Silicon Graphics International Corp.
* Copyright (c) 2009-2011 Spectra Logic Corporation
* Copyright (c) 2012 The FreeBSD Foundation
* Copyright (c) 2012,2021 The FreeBSD Foundation
* Copyright (c) 2014-2015 Alexander Motin <mav@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed by Edward Tomasz Napierala
* under sponsorship from the FreeBSD Foundation.
*
* Portions of this software were developed by Ka Ho Ng <khng@FreeBSD.org>
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -81,6 +84,7 @@ __FBSDID("$FreeBSD$");
#include <sys/nv.h>
#include <sys/dnv.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <geom/geom.h>
@ -245,6 +249,8 @@ static void ctl_be_block_gls_file(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio);
static uint64_t ctl_be_block_getattr_file(struct ctl_be_block_lun *be_lun,
const char *attrname);
static void ctl_be_block_unmap_file(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio);
static void ctl_be_block_flush_dev(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio);
static void ctl_be_block_unmap_dev(struct ctl_be_block_lun *be_lun,
@ -854,6 +860,84 @@ ctl_be_block_getattr_file(struct ctl_be_block_lun *be_lun, const char *attrname)
return (val);
}
static void
ctl_be_block_unmap_file(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio)
{
struct ctl_be_block_filedata *file_data;
union ctl_io *io;
struct ctl_ptr_len_flags *ptrlen;
struct scsi_unmap_desc *buf, *end;
struct mount *mp;
off_t off, len;
int error;
io = beio->io;
file_data = &be_lun->backend.file;
mp = NULL;
error = 0;
binuptime(&beio->ds_t0);
devstat_start_transaction(be_lun->disk_stats, &beio->ds_t0);
(void)vn_start_write(be_lun->vn, &mp, V_WAIT);
vn_lock(be_lun->vn, vn_lktype_write(mp, be_lun->vn) | LK_RETRY);
if (beio->io_offset == -1) {
beio->io_len = 0;
ptrlen = (struct ctl_ptr_len_flags *)
&io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
buf = (struct scsi_unmap_desc *)ptrlen->ptr;
end = buf + ptrlen->len / sizeof(*buf);
for (; buf < end; buf++) {
off = (off_t)scsi_8btou64(buf->lba) *
be_lun->cbe_lun.blocksize;
len = (off_t)scsi_4btoul(buf->length) *
be_lun->cbe_lun.blocksize;
beio->io_len += len;
error = vn_deallocate(be_lun->vn, &off, &len,
0, IO_NOMACCHECK | IO_NODELOCKED, file_data->cred,
NOCRED);
if (error != 0)
break;
}
} else {
/* WRITE_SAME */
off = beio->io_offset;
len = beio->io_len;
error = vn_deallocate(be_lun->vn, &off, &len, 0,
IO_NOMACCHECK | IO_NODELOCKED, file_data->cred, NOCRED);
}
VOP_UNLOCK(be_lun->vn);
vn_finished_write(mp);
mtx_lock(&be_lun->io_lock);
devstat_end_transaction(beio->lun->disk_stats, beio->io_len,
beio->ds_tag_type, beio->ds_trans_type,
/*now*/ NULL, /*then*/&beio->ds_t0);
mtx_unlock(&be_lun->io_lock);
/*
* If we got an error, set the sense data to "MEDIUM ERROR" and
* return the I/O to the user.
*/
switch (error) {
case 0:
ctl_set_success(&io->scsiio);
break;
case ENOSPC:
case EDQUOT:
ctl_set_space_alloc_fail(&io->scsiio);
break;
case EROFS:
case EACCES:
ctl_set_hw_write_protected(&io->scsiio);
break;
default:
ctl_set_medium_error(&io->scsiio, false);
}
ctl_complete_beio(beio);
}
static void
ctl_be_block_dispatch_zvol(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio)
@ -1804,6 +1888,7 @@ ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
struct vattr vattr;
off_t ps, pss, po, pos, us, uss, uo, uos;
int error;
long pconf;
cbe_lun = &be_lun->cbe_lun;
file_data = &be_lun->backend.file;
@ -1814,7 +1899,7 @@ ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
be_lun->lun_flush = ctl_be_block_flush_file;
be_lun->get_lba_status = ctl_be_block_gls_file;
be_lun->getattr = ctl_be_block_getattr_file;
be_lun->unmap = NULL;
be_lun->unmap = ctl_be_block_unmap_file;
cbe_lun->flags &= ~CTL_LUN_FLAG_UNMAP;
error = VOP_GETATTR(be_lun->vn, &vattr, curthread->td_ucred);
@ -1825,6 +1910,16 @@ ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
return (error);
}
error = VOP_PATHCONF(be_lun->vn, _PC_DEALLOC_PRESENT, &pconf);
if (error != 0) {
snprintf(req->error_str, sizeof(req->error_str),
"error calling VOP_PATHCONF() for file %s",
be_lun->dev_path);
return (error);
}
if (pconf == 1)
cbe_lun->flags |= CTL_LUN_FLAG_UNMAP;
file_data->cred = crhold(curthread->td_ucred);
if (params->lun_size_bytes != 0)
be_lun->size_bytes = params->lun_size_bytes;