On Wed, 2013-10-16 at 09:25 +0200, Hannes Reinecke wrote: > Referrals need an LBA map, which needs to be kept > consistent across all target port groups. So > instead of tying the map to the target port groups > I've implemented a single attribute containing the > entire map. > > Signed-off-by: Hannes Reinecke <hare@xxxxxxx> > --- > drivers/target/target_core_alua.c | 101 +++++++++++++++++++ > drivers/target/target_core_alua.h | 8 ++ > drivers/target/target_core_configfs.c | 171 +++++++++++++++++++++++++++++++++ > drivers/target/target_core_device.c | 1 + > drivers/target/target_core_transport.c | 28 +++++- > 5 files changed, 308 insertions(+), 1 deletion(-) > > diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c > index 8f66146..9dd01ff 100644 > --- a/drivers/target/target_core_alua.c > +++ b/drivers/target/target_core_alua.c > @@ -1340,6 +1340,107 @@ static int core_alua_set_tg_pt_secondary_state( > return 0; > } > > +struct t10_alua_lba_map * > +core_alua_allocate_lba_map(struct list_head *list, > + u64 first_lba, u64 last_lba) > +{ > + struct t10_alua_lba_map *lba_map; > + > + lba_map = kmem_cache_zalloc(t10_alua_lba_map_cache, GFP_KERNEL); > + if (!lba_map) { > + pr_err("Unable to allocate struct t10_alua_lba_map\n"); > + return ERR_PTR(-ENOMEM); > + } > + INIT_LIST_HEAD(&lba_map->lba_map_mem_list); > + lba_map->lba_map_first_lba = first_lba; > + lba_map->lba_map_last_lba = last_lba; > + > + list_add_tail(&lba_map->lba_map_list, list); > + return lba_map; > +} This list_add_tail needs to be protected, no..? > + > +int > +core_alua_allocate_lba_map_mem(struct t10_alua_lba_map *lba_map, > + int pg_id, int state) > +{ > + struct t10_alua_lba_map_member *lba_map_mem; > + > + list_for_each_entry(lba_map_mem, &lba_map->lba_map_mem_list, > + lba_map_mem_list) { > + if (lba_map_mem->lba_map_mem_alua_pg_id == pg_id) { > + pr_err("Duplicate pg_id %d in lba_map\n", pg_id); > + return -EINVAL; > + } > + } > + > + lba_map_mem = kmem_cache_zalloc(t10_alua_lba_map_mem_cache, GFP_KERNEL); > + if (!lba_map_mem) { > + pr_err("Unable to allocate struct t10_alua_lba_map_mem\n"); > + return -ENOMEM; > + } > + lba_map_mem->lba_map_mem_alua_state = state; > + lba_map_mem->lba_map_mem_alua_pg_id = pg_id; > + > + list_add_tail(&lba_map_mem->lba_map_mem_list, > + &lba_map->lba_map_mem_list); > + return 0; > +} Ditto here.. > + > +void > +core_alua_free_lba_map(struct list_head *lba_list) > +{ > + struct t10_alua_lba_map *lba_map, *lba_map_tmp; > + struct t10_alua_lba_map_member *lba_map_mem, *lba_map_mem_tmp; > + > + list_for_each_entry_safe(lba_map, lba_map_tmp, lba_list, > + lba_map_list) { > + list_for_each_entry_safe(lba_map_mem, lba_map_mem_tmp, > + &lba_map->lba_map_mem_list, > + lba_map_mem_list) { > + list_del(&lba_map_mem->lba_map_mem_list); > + kmem_cache_free(t10_alua_lba_map_mem_cache, > + lba_map_mem); > + } > + list_del(&lba_map->lba_map_list); > + kmem_cache_free(t10_alua_lba_map_cache, lba_map); > + } > +} And here.. > + > +void > +core_alua_set_lba_map(struct se_device *dev, struct list_head *lba_map_list, > + int segment_size, int segment_mult) > +{ > + struct list_head old_lba_map_list; > + struct t10_alua_tg_pt_gp *tg_pt_gp; > + int activate = 0, supported; > + > + INIT_LIST_HEAD(&old_lba_map_list); > + spin_lock(&dev->t10_alua.lba_map_lock); > + dev->t10_alua.lba_map_segment_size = segment_size; > + dev->t10_alua.lba_map_segment_multiplier = segment_mult; > + list_splice_init(&dev->t10_alua.lba_map_list, &old_lba_map_list); > + if (lba_map_list) { > + list_splice_init(lba_map_list, &dev->t10_alua.lba_map_list); > + activate = 1; > + } > + spin_unlock(&dev->t10_alua.lba_map_lock); > + spin_lock(&dev->t10_alua.tg_pt_gps_lock); > + list_for_each_entry(tg_pt_gp, &dev->t10_alua.tg_pt_gps_list, > + tg_pt_gp_list) { > + > + if (!tg_pt_gp->tg_pt_gp_valid_id) > + continue; > + supported = tg_pt_gp->tg_pt_gp_alua_supported_states; > + if (activate) > + supported |= ALUA_LBD_SUP; > + else > + supported &= ~ALUA_LBD_SUP; > + tg_pt_gp->tg_pt_gp_alua_supported_states = supported; > + } > + spin_unlock(&dev->t10_alua.tg_pt_gps_lock); > + core_alua_free_lba_map(&old_lba_map_list); > +} > + > struct t10_alua_lu_gp * > core_alua_allocate_lu_gp(const char *name, int def_group) > { > diff --git a/drivers/target/target_core_alua.h b/drivers/target/target_core_alua.h > index 47950cd..0a7d65e 100644 > --- a/drivers/target/target_core_alua.h > +++ b/drivers/target/target_core_alua.h > @@ -86,6 +86,8 @@ extern struct kmem_cache *t10_alua_lu_gp_cache; > extern struct kmem_cache *t10_alua_lu_gp_mem_cache; > extern struct kmem_cache *t10_alua_tg_pt_gp_cache; > extern struct kmem_cache *t10_alua_tg_pt_gp_mem_cache; > +extern struct kmem_cache *t10_alua_lba_map_cache; > +extern struct kmem_cache *t10_alua_lba_map_mem_cache; > > extern sense_reason_t target_emulate_report_target_port_groups(struct se_cmd *); > extern sense_reason_t target_emulate_set_target_port_groups(struct se_cmd *); > @@ -95,6 +97,12 @@ extern int core_alua_do_port_transition(struct t10_alua_tg_pt_gp *, > struct se_device *, struct se_port *, > struct se_node_acl *, int, int); > extern char *core_alua_dump_status(int); > +extern struct t10_alua_lba_map *core_alua_allocate_lba_map( > + struct list_head *, u64, u64); > +extern int core_alua_allocate_lba_map_mem(struct t10_alua_lba_map *, int, int); > +extern void core_alua_free_lba_map(struct list_head *); > +extern void core_alua_set_lba_map(struct se_device *, struct list_head *, > + int, int); > extern struct t10_alua_lu_gp *core_alua_allocate_lu_gp(const char *, int); > extern int core_alua_set_lu_gp_id(struct t10_alua_lu_gp *, u16); > extern void core_alua_free_lu_gp(struct t10_alua_lu_gp *); > diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c > index 172a54e..613cafb 100644 > --- a/drivers/target/target_core_configfs.c > +++ b/drivers/target/target_core_configfs.c > @@ -1741,6 +1741,176 @@ static struct target_core_configfs_attribute target_core_attr_dev_alua_lu_gp = { > .store = target_core_store_alua_lu_gp, > }; > > +static ssize_t target_core_show_dev_lba_map(void *p, char *page) > +{ > + struct se_device *dev = p; > + struct t10_alua_lba_map *map; > + struct t10_alua_lba_map_member *mem; > + char *b = page; > + int bl = 0; > + char state; > + > + spin_lock(&dev->t10_alua.lba_map_lock); > + if (!list_empty(&dev->t10_alua.lba_map_list)) > + bl += sprintf(b + bl, "%u %u\n", > + dev->t10_alua.lba_map_segment_size, > + dev->t10_alua.lba_map_segment_multiplier); > + list_for_each_entry(map, &dev->t10_alua.lba_map_list, lba_map_list) { > + bl += sprintf(b + bl, "%llu %llu", > + map->lba_map_first_lba, map->lba_map_last_lba); > + list_for_each_entry(mem, &map->lba_map_mem_list, > + lba_map_mem_list) { > + switch (mem->lba_map_mem_alua_state) { > + case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED: > + state = 'O'; > + break; > + case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED: > + state = 'A'; > + break; > + case ALUA_ACCESS_STATE_STANDBY: > + state = 'S'; > + break; > + case ALUA_ACCESS_STATE_UNAVAILABLE: > + state = 'U'; > + break; > + default: > + state = '.'; > + break; > + } > + bl += sprintf(b + bl, " %d:%c", > + mem->lba_map_mem_alua_pg_id, state); > + } > + bl += sprintf(b + bl, "\n"); > + } > + spin_unlock(&dev->t10_alua.lba_map_lock); > + return bl; > +} Unfortunately due to the existing limitations of configfs/sysfs attribute output, the writing to *page needs to be limited to PAGE_SIZE. > + > +static ssize_t target_core_store_dev_lba_map( > + void *p, > + const char *page, > + size_t count) > +{ > + struct se_device *dev = p; > + struct t10_alua_lba_map *lba_map = NULL; > + struct list_head lba_list; > + char *map_entries, *ptr; > + char state; > + int pg_num = -1, pg; > + int ret = 0, num = 0, pg_id, alua_state; > + unsigned long start_lba = -1, end_lba = -1; > + unsigned long segment_size = -1, segment_mult = -1; > + > + map_entries = kstrdup(page, GFP_KERNEL); > + if (!map_entries) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&lba_list); > + while ((ptr = strsep(&map_entries, "\n")) != NULL) { > + if (!*ptr) > + continue; > + > + if (num == 0) { > + if (sscanf(ptr, "%lu %lu\n", > + &segment_size, &segment_mult) != 2) { > + pr_err("Invalid line %d\n", num); > + ret = -EINVAL; > + break; > + } > + num++; > + continue; > + } > + if (sscanf(ptr, "%lu %lu", &start_lba, &end_lba) != 2) { > + pr_err("Invalid line %d\n", num); > + ret = -EINVAL; > + break; > + } > + ptr = strchr(ptr, ' '); > + if (!ptr) { > + pr_err("Invalid line %d, missing end lba\n", num); > + ret = -EINVAL; > + break; > + } > + ptr++; > + ptr = strchr(ptr, ' '); > + if (!ptr) { > + pr_err("Invalid line %d, missing state definitions\n", > + num); > + ret = -EINVAL; > + break; > + } > + ptr++; > + lba_map = core_alua_allocate_lba_map(&lba_list, > + start_lba, end_lba); > + if (IS_ERR(lba_map)) { > + ret = PTR_ERR(lba_map); > + break; > + } > + pg = 0; > + while (sscanf(ptr, "%d:%c", &pg_id, &state) == 2) { > + switch (state) { > + case 'O': > + alua_state = ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED; > + break; > + case 'A': > + alua_state = ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED; > + break; > + case 'S': > + alua_state = ALUA_ACCESS_STATE_STANDBY; > + break; > + case 'U': > + alua_state = ALUA_ACCESS_STATE_UNAVAILABLE; > + break; > + default: > + pr_err("Invalid ALUA state '%c'\n", state); > + ret = -EINVAL; > + goto out; > + } > + > + ret = core_alua_allocate_lba_map_mem(lba_map, > + pg_id, alua_state); > + if (ret) { > + pr_err("Invalid target descriptor %d:%c " > + "at line %d\n", > + pg_id, state, num); > + break; > + } > + pg++; > + ptr = strchr(ptr, ' '); > + if (ptr) > + ptr++; > + else > + break; > + } > + if (pg_num == -1) > + pg_num = pg; > + else if (pg != pg_num) { > + pr_err("Only %d from %d port groups definitions " > + "at line %d\n", pg, pg_num, num); > + ret = -EINVAL; > + break; > + } Btw, checkpatch complains about conditionals that don't have matching brackets on both code block, eg: if foo else { bar } > + num++; > + } > +out: > + if (ret) { > + core_alua_free_lba_map(&lba_list); > + count = ret; > + } else > + core_alua_set_lba_map(dev, &lba_list, > + segment_size, segment_mult); > + kfree(map_entries); > + return count; > +} > + > +static struct target_core_configfs_attribute target_core_attr_dev_lba_map = { > + .attr = { .ca_owner = THIS_MODULE, > + .ca_name = "lba_map", > + .ca_mode = S_IRUGO | S_IWUSR }, > + .show = target_core_show_dev_lba_map, > + .store = target_core_store_dev_lba_map, > +}; > + > static struct configfs_attribute *lio_core_dev_attrs[] = { > &target_core_attr_dev_info.attr, > &target_core_attr_dev_control.attr, > @@ -1748,6 +1918,7 @@ static struct configfs_attribute *lio_core_dev_attrs[] = { > &target_core_attr_dev_udev_path.attr, > &target_core_attr_dev_enable.attr, > &target_core_attr_dev_alua_lu_gp.attr, > + &target_core_attr_dev_lba_map.attr, > NULL, > }; > > diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c > index f71cc33..6db76af 100644 > --- a/drivers/target/target_core_device.c > +++ b/drivers/target/target_core_device.c > @@ -1578,6 +1578,7 @@ void target_free_device(struct se_device *dev) > } > > core_alua_free_lu_gp_mem(dev); > + core_alua_set_lba_map(dev, NULL, 0, 0); > core_scsi3_free_all_registrations(dev); > se_release_vpd_for_dev(dev); > > diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c > index 98bb7c4..e34d4b4 100644 > --- a/drivers/target/target_core_transport.c > +++ b/drivers/target/target_core_transport.c > @@ -63,6 +63,8 @@ struct kmem_cache *t10_alua_lu_gp_cache; > struct kmem_cache *t10_alua_lu_gp_mem_cache; > struct kmem_cache *t10_alua_tg_pt_gp_cache; > struct kmem_cache *t10_alua_tg_pt_gp_mem_cache; > +struct kmem_cache *t10_alua_lba_map_cache; > +struct kmem_cache *t10_alua_lba_map_mem_cache; > > static void transport_complete_task_attr(struct se_cmd *cmd); > static void transport_handle_queue_full(struct se_cmd *cmd, > @@ -129,14 +131,36 @@ int init_se_kmem_caches(void) > "mem_t failed\n"); > goto out_free_tg_pt_gp_cache; > } > + t10_alua_lba_map_cache = kmem_cache_create( > + "t10_alua_lba_map_cache", > + sizeof(struct t10_alua_lba_map), > + __alignof__(struct t10_alua_lba_map), 0, NULL); > + if (!t10_alua_lba_map_cache) { > + pr_err("kmem_cache_create() for t10_alua_lba_map_" > + "cache failed\n"); > + goto out_free_tg_pt_gp_mem_cache; > + } > + t10_alua_lba_map_mem_cache = kmem_cache_create( > + "t10_alua_lba_map_mem_cache", > + sizeof(struct t10_alua_lba_map_member), > + __alignof__(struct t10_alua_lba_map_member), 0, NULL); > + if (!t10_alua_lba_map_mem_cache) { > + pr_err("kmem_cache_create() for t10_alua_lba_map_mem_" > + "cache failed\n"); > + goto out_free_lba_map_cache; > + } > > target_completion_wq = alloc_workqueue("target_completion", > WQ_MEM_RECLAIM, 0); > if (!target_completion_wq) > - goto out_free_tg_pt_gp_mem_cache; > + goto out_free_lba_map_mem_cache; > > return 0; > > +out_free_lba_map_mem_cache: > + kmem_cache_destroy(t10_alua_lba_map_mem_cache); > +out_free_lba_map_cache: > + kmem_cache_destroy(t10_alua_lba_map_cache); > out_free_tg_pt_gp_mem_cache: > kmem_cache_destroy(t10_alua_tg_pt_gp_mem_cache); > out_free_tg_pt_gp_cache: > @@ -165,6 +189,8 @@ void release_se_kmem_caches(void) > kmem_cache_destroy(t10_alua_lu_gp_mem_cache); > kmem_cache_destroy(t10_alua_tg_pt_gp_cache); > kmem_cache_destroy(t10_alua_tg_pt_gp_mem_cache); > + kmem_cache_destroy(t10_alua_lba_map_cache); > + kmem_cache_destroy(t10_alua_lba_map_mem_cache); > } > > /* This code ensures unique mib indexes are handed out. */ -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html