@@ -1100,7 +1100,91 @@ static ssize_t btrfs_checksum_show(struct kobject *kobj,
crypto_shash_driver_name(fs_info->csum_shash[CSUM_DEFAULT]));
}
-BTRFS_ATTR(, checksum, btrfs_checksum_show);
+static const char csum_impl[][8] = {
+ [CSUM_DEFAULT] = "default",
+ [CSUM_GENERIC] = "generic",
+ [CSUM_ACCEL] = "accel",
+};
+
+static int select_checksum(struct btrfs_fs_info *fs_info, enum btrfs_csum_impl type)
+{
+ /* Already set */
+ if (fs_info->csum_shash[CSUM_DEFAULT] == fs_info->csum_shash[type])
+ return 0;
+
+ /* Allocate new if needed */
+ if (!fs_info->csum_shash[type]) {
+ const u16 csum_type = btrfs_super_csum_type(fs_info->super_copy);
+ const char *csum_driver = btrfs_super_csum_driver(csum_type);
+ struct crypto_shash *shash1, *tmp;
+ char full_name[32];
+ u32 mask = 0;
+
+ /*
+ * Generic must be requested explicitly, otherwise it could
+ * be accelerated implementation with highest priority.
+ */
+ scnprintf(full_name, sizeof(full_name), "%s%s", csum_driver,
+ (type == CSUM_GENERIC ? "-generic" : ""));
+
+ shash1 = crypto_alloc_shash(full_name, 0, mask);
+ if (IS_ERR(shash1))
+ return PTR_ERR(shash1);
+
+ /* Accelerated requested but generic found */
+ if (type != CSUM_GENERIC &&
+ strstr(crypto_shash_driver_name(shash1), "generic")) {
+ crypto_free_shash(shash1);
+ return -ENOENT;
+ }
+
+ tmp = cmpxchg(&fs_info->csum_shash[type], NULL, shash1);
+ if (tmp) {
+ /* Something raced in */
+ crypto_free_shash(shash1);
+ }
+ }
+
+ /* Select it */
+ fs_info->csum_shash[CSUM_DEFAULT] = fs_info->csum_shash[type];
+ return 0;
+}
+
+static bool strmatch(const char *buffer, const char *string);
+
+static ssize_t btrfs_checksum_store(struct kobject *kobj,
+ struct kobj_attribute *a,
+ const char *buf, size_t len)
+{
+ struct btrfs_fs_info *fs_info = to_fs_info(kobj);
+
+ if (!fs_info)
+ return -EPERM;
+
+ /* Pick the best available, generic or accelerated */
+ if (strmatch(buf, csum_impl[CSUM_DEFAULT])) {
+ if (fs_info->csum_shash[CSUM_ACCEL]) {
+ fs_info->csum_shash[CSUM_DEFAULT] =
+ fs_info->csum_shash[CSUM_ACCEL];
+ return len;
+ }
+ ASSERT(fs_info->csum_shash[CSUM_GENERIC]);
+ fs_info->csum_shash[CSUM_DEFAULT] = fs_info->csum_shash[CSUM_GENERIC];
+ return len;
+ }
+
+ for (int i = 1; i < CSUM_COUNT; i++) {
+ if (strmatch(buf, csum_impl[i])) {
+ int ret;
+
+ ret = select_checksum(fs_info, i);
+ return ret < 0 ? ret : len;
+ }
+ }
+
+ return -EINVAL;
+}
+BTRFS_ATTR_RW(, checksum, btrfs_checksum_show, btrfs_checksum_store);
static ssize_t btrfs_exclusive_operation_show(struct kobject *kobj,
struct kobj_attribute *a, char *buf)
The sysfs file /sys/fs/btrfs/FSID/checksum shows the filesystem checksum and the crypto module implementing it. In the scenario when there's only the default generic implementation available during mount it's selected, even if there's an unloaded module with accelerated version. This happens with sha256 that's often built-in so the crypto API may not trigger loading the modules and select the fastest implementation. Such filesystem could be left using in the generic for the whole time. Remount can't fix that and full umount/mount cycle may be impossible eg. for a root filesystem. Allow writing strings to the sysfs checksum file that will trigger loading the crypto shash again and check if the found module is the desired one. Possible values: - default - select whatever is considered default by crypto subsystem, either generic or accelerated - accel - try loading an accelerated implementation (must not contain "generic" in the name) - generic - load and select the generic implementation A typical scenario, assuming sha256 is built-in: $ mkfs.btrfs --csum sha256 $ lsmod | grep sha256 $ mount /dev /mnt $ cat ...FSID/checksum sha256 (sha256-generic) $ modprobe sha256 $ lsmod | grep sha256 sha256_ssse3 $ echo accel > ...FSID/checksum sha256 (sha256-ni) The crypto shash for all slots has the same lifetime as the mount, so it's not possible to unload the crypto module. Signed-off-by: David Sterba <dsterba@suse.com> --- fs/btrfs/sysfs.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-)