Skip to content

Instantly share code, notes, and snippets.

@evansus
Last active August 29, 2015 14:02
Show Gist options
  • Save evansus/ecdafd80570bd8b84683 to your computer and use it in GitHub Desktop.
Save evansus/ecdafd80570bd8b84683 to your computer and use it in GitHub Desktop.
Adds a new property com.apple.blocksize to zvols
diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h
index b220a41..a461c9a 100644
--- a/include/sys/fs/zfs.h
+++ b/include/sys/fs/zfs.h
@@ -145,6 +145,7 @@ typedef enum {
#ifdef __APPLE__
ZFS_PROP_APPLE_BROWSE,
ZFS_PROP_APPLE_IGNOREOWNER,
+ ZFS_PROP_APPLE_BLOCKSIZE,
#endif
#ifdef LINUX
ZFS_PROP_ACLTYPE,
@@ -788,6 +789,7 @@ typedef struct ddt_histogram {
#define ZVOL_DEFAULT_BLOCKSIZE 8192
+#define ZVOL_APPLE_BLOCKSIZE DEV_BSIZE
/*
* zvol ioctl to get dataset name
diff --git a/include/sys/zvol.h b/include/sys/zvol.h
index 46b0cae..21c99e0 100644
--- a/include/sys/zvol.h
+++ b/include/sys/zvol.h
@@ -55,6 +55,7 @@ typedef struct zvol_state {
char zv_name[MAXPATHLEN]; /* pool/dd name */
uint64_t zv_volsize; /* amount of space we advertise */
uint64_t zv_volblocksize; /* volume block size */
+ uint64_t zv_logicalblocksize; /* logical block size */
minor_t zv_minor; /* minor number */
uint8_t zv_min_bs; /* minimum addressable block shift */
uint8_t zv_flags; /* readonly, dumpified, etc. */
diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c
index 5aa2eac..8633209 100644
--- a/lib/libzfs/libzfs_dataset.c
+++ b/lib/libzfs/libzfs_dataset.c
@@ -1915,6 +1915,9 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
case ZFS_PROP_REFQUOTA:
case ZFS_PROP_RESERVATION:
case ZFS_PROP_REFRESERVATION:
+#ifdef __APPLE__
+ case ZFS_PROP_APPLE_BLOCKSIZE:
+#endif
*val = getprop_uint64(zhp, prop, source);
if (*source == NULL) {
diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c
index b83e9e9..73d802e 100644
--- a/module/zcommon/zfs_prop.c
+++ b/module/zcommon/zfs_prop.c
@@ -465,6 +465,11 @@ zfs_prop_init(void)
ZFS_TYPE_FILESYSTEM, "on | off", "COM.APPLE.BROWSE", boolean_table);
zprop_register_index(ZFS_PROP_APPLE_IGNOREOWNER, "com.apple.ignoreowner", 0, PROP_INHERIT,
ZFS_TYPE_FILESYSTEM, "on | off", "COM.APPLE.IGNOREOWNER", boolean_table);
+ zprop_register_number(ZFS_PROP_APPLE_BLOCKSIZE,
+ "com.apple.blocksize", ZVOL_APPLE_BLOCKSIZE,
+ PROP_INHERIT, ZFS_TYPE_VOLUME,
+ "512 to 128k, power of 2",
+ "COM.APPLE.BLOCKSIZE");
#endif
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index b0fc23e..544d26d 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -3097,6 +3097,18 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
return (SET_ERROR(EINVAL));
if ((error = nvlist_lookup_uint64(nvprops,
+ zfs_prop_to_name(ZFS_PROP_APPLE_BLOCKSIZE),
+ &volblocksize)) != 0) {
+ if ((error = zvol_check_volblocksize(
+ volblocksize)) != 0 ||
+ (error = zvol_check_volsize(volsize,
+ volblocksize)) != 0) {
+ return (error);
+ }
+ }
+
+ if ((error = nvlist_lookup_uint64(nvprops,
+
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
&volblocksize)) != 0 && error != ENOENT)
return (SET_ERROR(EINVAL));
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c
index 4722dbd..8b70a34 100644
--- a/module/zfs/zvol.c
+++ b/module/zfs/zvol.c
@@ -574,6 +574,11 @@ zvol_create_minor(const char *name)
// set here.
error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zv->zv_volsize);
+ error = dsl_prop_get_integer(zv->zv_name, zfs_prop_to_name(ZFS_PROP_APPLE_BLOCKSIZE), &zv->zv_logicalblocksize, NULL);
+
+ if (zv->zv_logicalblocksize == 0)
+ zv->zv_logicalblocksize = DEV_BSIZE;
+
dmu_objset_disown(os, FTAG);
zv->zv_objset = NULL;
diff --git a/module/zfs/zvolIO.cpp b/module/zfs/zvolIO.cpp
index 5e0df03..116e63f 100644
--- a/module/zfs/zvolIO.cpp
+++ b/module/zfs/zvolIO.cpp
@@ -1,6 +1,7 @@
#include <IOKit/IOLib.h>
#include <IOKit/IOBSD.h>
+#include <IOKit/IOKitKeys.h>
#include <sys/zfs_ioctl.h>
#include <sys/zfs_znode.h>
@@ -20,8 +21,6 @@
// Define the superclass
#define super IOBlockStorageDevice
-#define ZVOL_BSIZE DEV_BSIZE
-
OSDefineMetaClassAndStructors(net_lundman_zfs_zvol_device,IOBlockStorageDevice)
bool net_lundman_zfs_zvol_device::init(zvol_state_t *c_zv,
@@ -42,7 +41,9 @@ bool net_lundman_zfs_zvol_device::init(zvol_state_t *c_zv,
bool net_lundman_zfs_zvol_device::attach(IOService* provider)
{
OSDictionary * protocolCharacteristics = 0;
+ OSDictionary * deviceCharacteristics = 0;
OSString * dataString = 0;
+ OSNumber * dataNumber = 0;
if (super::attach(provider) == false)
return false;
@@ -52,19 +53,21 @@ bool net_lundman_zfs_zvol_device::attach(IOService* provider)
/*
* We want to set some additional properties for ZVOLs, in
- * particular, "Virtual Device", and type "File" (or is Internal better?)
- * Finally "Generic" type.
+ * particular, "Virtual Device", and type "File" (or is Internal better?)
+ * Finally "Generic" type.
+ *
+ * These properties are defined in *protocol* characteristics
*/
protocolCharacteristics = OSDictionary::withCapacity(3);
if (!protocolCharacteristics) {
- IOLog("failed to create dictionary for protocolCharacteristics.\n");
+ dprintf("failed to create dictionary for protocolCharacteristics.\n");
return true;
}
dataString = OSString::withCString(kIOPropertyPhysicalInterconnectTypeVirtual);
if (!dataString) {
- IOLog( "could not create interconnect type string\n" );
+ dprintf( "could not create interconnect type string\n" );
return true;
}
protocolCharacteristics->setObject(kIOPropertyPhysicalInterconnectTypeKey, dataString);
@@ -73,7 +76,7 @@ bool net_lundman_zfs_zvol_device::attach(IOService* provider)
dataString = OSString::withCString(kIOPropertyInterconnectFileKey);
if (!dataString) {
- IOLog( "PGPdiskDriver::createNub: could not create interconnect location string\n" );
+ dprintf( "PGPdiskDriver::createNub: could not create interconnect location string\n" );
return true;
}
protocolCharacteristics->setObject(kIOPropertyPhysicalInterconnectLocationKey, dataString);
@@ -84,9 +87,82 @@ bool net_lundman_zfs_zvol_device::attach(IOService* provider)
protocolCharacteristics->release();
protocolCharacteristics = 0;
- setProperty( kIOBlockStorageDeviceTypeKey, kIOBlockStorageDeviceTypeGeneric );
-
- return true;
+ /*
+ * We want to set some additional properties for ZVOLs, in
+ * particular, logical block size (volblocksize) of the
+ * underlying ZVOL, and 'physical' block size presented by
+ * the virtual disk. Also set physical bytes per sector.
+ *
+ * These properties are defined in *device* characteristics
+ */
+
+ deviceCharacteristics = OSDictionary::withCapacity(3);
+ if (!deviceCharacteristics) {
+ dprintf("failed to create dictionary for deviceCharacteristics.\n");
+ return true;
+ }
+
+ /* Set physical block size to 4k */
+ dataNumber = OSNumber::withNumber(8*DEV_BSIZE,
+ 8 * sizeof (uint64_t));
+
+ deviceCharacteristics->setObject(kIOPropertyPhysicalBlockSizeKey, dataNumber);
+ dprintf( "physicalBlockSize %llu\n", dataNumber->unsigned64BitValue());
+ dataNumber->release();
+ dataNumber = 0;
+
+ /* Set logical block size to zv->zv_logicalblocksize */
+ dataNumber = OSNumber::withNumber(zv->zv_logicalblocksize,
+ 8 * sizeof (uint64_t));
+
+ deviceCharacteristics->setObject(kIOPropertyLogicalBlockSizeKey, dataNumber);
+ dprintf( "logicalBlockSize %llu\n", dataNumber->unsigned64BitValue());
+ dataNumber->release();
+ dataNumber = 0;
+
+ /* Set physical bytes per sector to zv->zv_volblocksize */
+ dataNumber = OSNumber::withNumber((uint64_t)(
+ zv->zv_volblocksize),
+ 8 * sizeof (zv->zv_volblocksize));
+
+ deviceCharacteristics->setObject(kIOPropertyBytesPerPhysicalSectorKey, dataNumber);
+ dprintf( "physicalBytesPerSector %llu\n", dataNumber->unsigned64BitValue());
+ dataNumber->release();
+ dataNumber = 0;
+
+ /* Apply these characteristics */
+ setProperty( kIOPropertyDeviceCharacteristicsKey, deviceCharacteristics );
+ deviceCharacteristics->release();
+ deviceCharacteristics = 0;
+
+ /*
+ * Set transfer limits:
+ *
+ * Maximum transfer size (bytes)
+ * Maximum transfer block count
+ * Maximum transfer block size (bytes)
+ * Maximum transfer segment count
+ * Maximum transfer segment size (bytes)
+ * Minimum transfer segment size (bytes)
+ *
+ * We will need to establish safe
+ * defaults for all / per volblocksize
+ *
+ * Example: setProperty( kIOMinimumSegmentAlignmentByteCountKey, 4 );
+ */
+
+ setProperty(kIOMaximumByteCountWriteKey,
+ 8*zv->zv_logicalblocksize);
+ setProperty(kIOMaximumByteCountReadKey,
+ 8*zv->zv_logicalblocksize);
+
+ /*
+ * Finally "Generic" type, set as a device property.
+ */
+
+ setProperty( kIOBlockStorageDeviceTypeKey, kIOBlockStorageDeviceTypeGeneric );
+
+ return (true);
}
@@ -205,13 +281,13 @@ IOReturn net_lundman_zfs_zvol_device::doAsyncReadWrite(
}
// Ensure the start block being targeted is within the disk’s capacity.
- if ((block)*(ZVOL_BSIZE) >= zv->zv_volsize) {
+ if ((block)*(zv->zv_logicalblocksize) >= zv->zv_volsize) {
dprintf("asyncReadWrite start block outside volume\n");
return kIOReturnBadArgument;
}
// Shorten the read, if beyond the end
- if (((block + nblks)*(ZVOL_BSIZE)) > zv->zv_volsize) {
+ if (((block + nblks)*(zv->zv_logicalblocksize)) > zv->zv_volsize) {
dprintf("asyncReadWrite block shortening needed\n");
return kIOReturnBadArgument;
}
@@ -225,15 +301,15 @@ IOReturn net_lundman_zfs_zvol_device::doAsyncReadWrite(
dprintf("%s offset @block %llu numblocks %llu: blksz %llu\n",
direction == kIODirectionIn ? "Read" : "Write",
- block, nblks, (ZVOL_BSIZE));
+ block, nblks, (zv->zv_logicalblocksize));
//IOLog("getMediaBlockSize is %llu\n", m_provider->getMediaBlockSize());
// Perform the read or write operation through the transport driver.
- actualByteCount = (nblks*(ZVOL_BSIZE));
+ actualByteCount = (nblks*(zv->zv_logicalblocksize));
if (direction == kIODirectionIn) {
if (zvol_read_iokit(zv,
- (block*(ZVOL_BSIZE)),
+ (block*(zv->zv_logicalblocksize)),
actualByteCount,
(void *)buffer))
actualByteCount = 0;
@@ -241,14 +317,14 @@ IOReturn net_lundman_zfs_zvol_device::doAsyncReadWrite(
} else {
if (zvol_write_iokit(zv,
- (block*(ZVOL_BSIZE)),
+ (block*(zv->zv_logicalblocksize)),
actualByteCount,
(void *)buffer))
actualByteCount = 0;
}
- if (actualByteCount != nblks*(ZVOL_BSIZE))
+ if (actualByteCount != nblks*(zv->zv_logicalblocksize))
dprintf("Read/Write operation failed\n");
// Call the completion function.
@@ -290,14 +366,19 @@ char* net_lundman_zfs_zvol_device::getProductString(void)
IOReturn net_lundman_zfs_zvol_device::reportBlockSize(UInt64 *blockSize)
{
- *blockSize = (ZVOL_BSIZE);
- dprintf("reportBlockSize %llu\n", *blockSize);
- return kIOReturnSuccess;
+ *blockSize = (zv->zv_logicalblocksize);
+ dprintf("reportBlockSize %llu\n", *blockSize);
+ return kIOReturnSuccess;
}
IOReturn net_lundman_zfs_zvol_device::reportMaxValidBlock(UInt64 *maxBlock)
{
- *maxBlock = (zv->zv_volsize / (ZVOL_BSIZE))-1 ; //-1
+ if (zv->zv_logicalblocksize == 0 ) {
+ /* Avoid divide by zero */
+ return kIOReturnNotAttached;
+ }
+
+ *maxBlock = (zv->zv_volsize / (zv->zv_logicalblocksize)) - 1;
dprintf("reportMaxValidBlock %llu\n", *maxBlock);
return kIOReturnSuccess;
}
@@ -409,7 +490,10 @@ IOReturn net_lundman_zfs_zvol_device::reportWriteProtection(bool *isWriteProtec
IOReturn net_lundman_zfs_zvol_device::getWriteCacheState(bool *enabled)
{
dprintf("getCacheState\n");
- *enabled = true;
+ if (zv && (zv->zv_flags & ZVOL_RDONLY))
+ *enabled = false;
+ else
+ *enabled = true;
return kIOReturnSuccess;
}
@evansus
Copy link
Author

evansus commented Jun 1, 2014

On Mac OS X, block devices are addressable at 512, 1024, 2048, or 4096. The most useful of these are 512 (the default), and 4k.

This patch allows for an OS X-only compatible zvol that uses the 4k blocksize. The blocksize can actually be set to any power of two between 512 and 128k, however only the character devices can be used at blocksize settings higher than 4k.

For simplicity, since 512 and 4k are useable, but the other settings between 512-4k and above 4k are unlikely to benefit most users, I think the property should only accept these two options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment