162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Block device concurrent positioning ranges. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2021 Western Digital Corporation or its Affiliates. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/blkdev.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "blk.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic ssize_t 1562306a36Sopenharmony_ciblk_ia_range_sector_show(struct blk_independent_access_range *iar, 1662306a36Sopenharmony_ci char *buf) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci return sprintf(buf, "%llu\n", iar->sector); 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic ssize_t 2262306a36Sopenharmony_ciblk_ia_range_nr_sectors_show(struct blk_independent_access_range *iar, 2362306a36Sopenharmony_ci char *buf) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci return sprintf(buf, "%llu\n", iar->nr_sectors); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct blk_ia_range_sysfs_entry { 2962306a36Sopenharmony_ci struct attribute attr; 3062306a36Sopenharmony_ci ssize_t (*show)(struct blk_independent_access_range *iar, char *buf); 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct blk_ia_range_sysfs_entry blk_ia_range_sector_entry = { 3462306a36Sopenharmony_ci .attr = { .name = "sector", .mode = 0444 }, 3562306a36Sopenharmony_ci .show = blk_ia_range_sector_show, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic struct blk_ia_range_sysfs_entry blk_ia_range_nr_sectors_entry = { 3962306a36Sopenharmony_ci .attr = { .name = "nr_sectors", .mode = 0444 }, 4062306a36Sopenharmony_ci .show = blk_ia_range_nr_sectors_show, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct attribute *blk_ia_range_attrs[] = { 4462306a36Sopenharmony_ci &blk_ia_range_sector_entry.attr, 4562306a36Sopenharmony_ci &blk_ia_range_nr_sectors_entry.attr, 4662306a36Sopenharmony_ci NULL, 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ciATTRIBUTE_GROUPS(blk_ia_range); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic ssize_t blk_ia_range_sysfs_show(struct kobject *kobj, 5162306a36Sopenharmony_ci struct attribute *attr, char *buf) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct blk_ia_range_sysfs_entry *entry = 5462306a36Sopenharmony_ci container_of(attr, struct blk_ia_range_sysfs_entry, attr); 5562306a36Sopenharmony_ci struct blk_independent_access_range *iar = 5662306a36Sopenharmony_ci container_of(kobj, struct blk_independent_access_range, kobj); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return entry->show(iar, buf); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct sysfs_ops blk_ia_range_sysfs_ops = { 6262306a36Sopenharmony_ci .show = blk_ia_range_sysfs_show, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * Independent access range entries are not freed individually, but alltogether 6762306a36Sopenharmony_ci * with struct blk_independent_access_ranges and its array of ranges. Since 6862306a36Sopenharmony_ci * kobject_add() takes a reference on the parent kobject contained in 6962306a36Sopenharmony_ci * struct blk_independent_access_ranges, the array of independent access range 7062306a36Sopenharmony_ci * entries cannot be freed until kobject_del() is called for all entries. 7162306a36Sopenharmony_ci * So we do not need to do anything here, but still need this no-op release 7262306a36Sopenharmony_ci * operation to avoid complaints from the kobject code. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistatic void blk_ia_range_sysfs_nop_release(struct kobject *kobj) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const struct kobj_type blk_ia_range_ktype = { 7962306a36Sopenharmony_ci .sysfs_ops = &blk_ia_range_sysfs_ops, 8062306a36Sopenharmony_ci .default_groups = blk_ia_range_groups, 8162306a36Sopenharmony_ci .release = blk_ia_range_sysfs_nop_release, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * This will be executed only after all independent access range entries are 8662306a36Sopenharmony_ci * removed with kobject_del(), at which point, it is safe to free everything, 8762306a36Sopenharmony_ci * including the array of ranges. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic void blk_ia_ranges_sysfs_release(struct kobject *kobj) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct blk_independent_access_ranges *iars = 9262306a36Sopenharmony_ci container_of(kobj, struct blk_independent_access_ranges, kobj); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci kfree(iars); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic const struct kobj_type blk_ia_ranges_ktype = { 9862306a36Sopenharmony_ci .release = blk_ia_ranges_sysfs_release, 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/** 10262306a36Sopenharmony_ci * disk_register_independent_access_ranges - register with sysfs a set of 10362306a36Sopenharmony_ci * independent access ranges 10462306a36Sopenharmony_ci * @disk: Target disk 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * Register with sysfs a set of independent access ranges for @disk. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ciint disk_register_independent_access_ranges(struct gendisk *disk) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct blk_independent_access_ranges *iars = disk->ia_ranges; 11162306a36Sopenharmony_ci struct request_queue *q = disk->queue; 11262306a36Sopenharmony_ci int i, ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci lockdep_assert_held(&q->sysfs_dir_lock); 11562306a36Sopenharmony_ci lockdep_assert_held(&q->sysfs_lock); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (!iars) 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * At this point, iars is the new set of sector access ranges that needs 12262306a36Sopenharmony_ci * to be registered with sysfs. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci WARN_ON(iars->sysfs_registered); 12562306a36Sopenharmony_ci ret = kobject_init_and_add(&iars->kobj, &blk_ia_ranges_ktype, 12662306a36Sopenharmony_ci &disk->queue_kobj, "%s", 12762306a36Sopenharmony_ci "independent_access_ranges"); 12862306a36Sopenharmony_ci if (ret) { 12962306a36Sopenharmony_ci disk->ia_ranges = NULL; 13062306a36Sopenharmony_ci kobject_put(&iars->kobj); 13162306a36Sopenharmony_ci return ret; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci for (i = 0; i < iars->nr_ia_ranges; i++) { 13562306a36Sopenharmony_ci ret = kobject_init_and_add(&iars->ia_range[i].kobj, 13662306a36Sopenharmony_ci &blk_ia_range_ktype, &iars->kobj, 13762306a36Sopenharmony_ci "%d", i); 13862306a36Sopenharmony_ci if (ret) { 13962306a36Sopenharmony_ci while (--i >= 0) 14062306a36Sopenharmony_ci kobject_del(&iars->ia_range[i].kobj); 14162306a36Sopenharmony_ci kobject_del(&iars->kobj); 14262306a36Sopenharmony_ci kobject_put(&iars->kobj); 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci iars->sysfs_registered = true; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_civoid disk_unregister_independent_access_ranges(struct gendisk *disk) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct request_queue *q = disk->queue; 15562306a36Sopenharmony_ci struct blk_independent_access_ranges *iars = disk->ia_ranges; 15662306a36Sopenharmony_ci int i; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci lockdep_assert_held(&q->sysfs_dir_lock); 15962306a36Sopenharmony_ci lockdep_assert_held(&q->sysfs_lock); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!iars) 16262306a36Sopenharmony_ci return; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (iars->sysfs_registered) { 16562306a36Sopenharmony_ci for (i = 0; i < iars->nr_ia_ranges; i++) 16662306a36Sopenharmony_ci kobject_del(&iars->ia_range[i].kobj); 16762306a36Sopenharmony_ci kobject_del(&iars->kobj); 16862306a36Sopenharmony_ci kobject_put(&iars->kobj); 16962306a36Sopenharmony_ci } else { 17062306a36Sopenharmony_ci kfree(iars); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci disk->ia_ranges = NULL; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct blk_independent_access_range * 17762306a36Sopenharmony_cidisk_find_ia_range(struct blk_independent_access_ranges *iars, 17862306a36Sopenharmony_ci sector_t sector) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct blk_independent_access_range *iar; 18162306a36Sopenharmony_ci int i; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci for (i = 0; i < iars->nr_ia_ranges; i++) { 18462306a36Sopenharmony_ci iar = &iars->ia_range[i]; 18562306a36Sopenharmony_ci if (sector >= iar->sector && 18662306a36Sopenharmony_ci sector < iar->sector + iar->nr_sectors) 18762306a36Sopenharmony_ci return iar; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return NULL; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic bool disk_check_ia_ranges(struct gendisk *disk, 19462306a36Sopenharmony_ci struct blk_independent_access_ranges *iars) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct blk_independent_access_range *iar, *tmp; 19762306a36Sopenharmony_ci sector_t capacity = get_capacity(disk); 19862306a36Sopenharmony_ci sector_t sector = 0; 19962306a36Sopenharmony_ci int i; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (WARN_ON_ONCE(!iars->nr_ia_ranges)) 20262306a36Sopenharmony_ci return false; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * While sorting the ranges in increasing LBA order, check that the 20662306a36Sopenharmony_ci * ranges do not overlap, that there are no sector holes and that all 20762306a36Sopenharmony_ci * sectors belong to one range. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci for (i = 0; i < iars->nr_ia_ranges; i++) { 21062306a36Sopenharmony_ci tmp = disk_find_ia_range(iars, sector); 21162306a36Sopenharmony_ci if (!tmp || tmp->sector != sector) { 21262306a36Sopenharmony_ci pr_warn("Invalid non-contiguous independent access ranges\n"); 21362306a36Sopenharmony_ci return false; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci iar = &iars->ia_range[i]; 21762306a36Sopenharmony_ci if (tmp != iar) { 21862306a36Sopenharmony_ci swap(iar->sector, tmp->sector); 21962306a36Sopenharmony_ci swap(iar->nr_sectors, tmp->nr_sectors); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci sector += iar->nr_sectors; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (sector != capacity) { 22662306a36Sopenharmony_ci pr_warn("Independent access ranges do not match disk capacity\n"); 22762306a36Sopenharmony_ci return false; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return true; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic bool disk_ia_ranges_changed(struct gendisk *disk, 23462306a36Sopenharmony_ci struct blk_independent_access_ranges *new) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct blk_independent_access_ranges *old = disk->ia_ranges; 23762306a36Sopenharmony_ci int i; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!old) 24062306a36Sopenharmony_ci return true; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (old->nr_ia_ranges != new->nr_ia_ranges) 24362306a36Sopenharmony_ci return true; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (i = 0; i < old->nr_ia_ranges; i++) { 24662306a36Sopenharmony_ci if (new->ia_range[i].sector != old->ia_range[i].sector || 24762306a36Sopenharmony_ci new->ia_range[i].nr_sectors != old->ia_range[i].nr_sectors) 24862306a36Sopenharmony_ci return true; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return false; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/** 25562306a36Sopenharmony_ci * disk_alloc_independent_access_ranges - Allocate an independent access ranges 25662306a36Sopenharmony_ci * data structure 25762306a36Sopenharmony_ci * @disk: target disk 25862306a36Sopenharmony_ci * @nr_ia_ranges: Number of independent access ranges 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * Allocate a struct blk_independent_access_ranges structure with @nr_ia_ranges 26162306a36Sopenharmony_ci * access range descriptors. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistruct blk_independent_access_ranges * 26462306a36Sopenharmony_cidisk_alloc_independent_access_ranges(struct gendisk *disk, int nr_ia_ranges) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct blk_independent_access_ranges *iars; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci iars = kzalloc_node(struct_size(iars, ia_range, nr_ia_ranges), 26962306a36Sopenharmony_ci GFP_KERNEL, disk->queue->node); 27062306a36Sopenharmony_ci if (iars) 27162306a36Sopenharmony_ci iars->nr_ia_ranges = nr_ia_ranges; 27262306a36Sopenharmony_ci return iars; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(disk_alloc_independent_access_ranges); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * disk_set_independent_access_ranges - Set a disk independent access ranges 27862306a36Sopenharmony_ci * @disk: target disk 27962306a36Sopenharmony_ci * @iars: independent access ranges structure 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * Set the independent access ranges information of the request queue 28262306a36Sopenharmony_ci * of @disk to @iars. If @iars is NULL and the independent access ranges 28362306a36Sopenharmony_ci * structure already set is cleared. If there are no differences between 28462306a36Sopenharmony_ci * @iars and the independent access ranges structure already set, @iars 28562306a36Sopenharmony_ci * is freed. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_civoid disk_set_independent_access_ranges(struct gendisk *disk, 28862306a36Sopenharmony_ci struct blk_independent_access_ranges *iars) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct request_queue *q = disk->queue; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci mutex_lock(&q->sysfs_dir_lock); 29362306a36Sopenharmony_ci mutex_lock(&q->sysfs_lock); 29462306a36Sopenharmony_ci if (iars && !disk_check_ia_ranges(disk, iars)) { 29562306a36Sopenharmony_ci kfree(iars); 29662306a36Sopenharmony_ci iars = NULL; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci if (iars && !disk_ia_ranges_changed(disk, iars)) { 29962306a36Sopenharmony_ci kfree(iars); 30062306a36Sopenharmony_ci goto unlock; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * This may be called for a registered queue. E.g. during a device 30562306a36Sopenharmony_ci * revalidation. If that is the case, we need to unregister the old 30662306a36Sopenharmony_ci * set of independent access ranges and register the new set. If the 30762306a36Sopenharmony_ci * queue is not registered, registration of the device request queue 30862306a36Sopenharmony_ci * will register the independent access ranges. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci disk_unregister_independent_access_ranges(disk); 31162306a36Sopenharmony_ci disk->ia_ranges = iars; 31262306a36Sopenharmony_ci if (blk_queue_registered(q)) 31362306a36Sopenharmony_ci disk_register_independent_access_ranges(disk); 31462306a36Sopenharmony_ciunlock: 31562306a36Sopenharmony_ci mutex_unlock(&q->sysfs_lock); 31662306a36Sopenharmony_ci mutex_unlock(&q->sysfs_dir_lock); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(disk_set_independent_access_ranges); 319