From: Hannes Reinecke <hare@suse.de>
To: "Nicholas A. Bellinger" <nab@linux-iscsi.org>
Cc: Nic Bellinger <nab@daterainc.com>,
Doug Gilber <dgilbert@interlog.com>,
target-devel@vger.kernel.org, linux-scsi@vger.kernel.org
Subject: Re: [PATCH 2/2] target_core_alua: Referrals configfs integration
Date: Thu, 17 Oct 2013 09:42:26 +0200 [thread overview]
Message-ID: <525F94E2.80909@suse.de> (raw)
In-Reply-To: <1381970210.19256.674.camel@haakon3.risingtidesystems.com>
On 10/17/2013 02:36 AM, Nicholas A. Bellinger wrote:
> 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@suse.de>
>> ---
>> 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..?
>
No. The current usage is that we first construct the mapping in
memory, and then switching maps in set_lba_map.
This way we only need to protect the switch itself, not the map
construction.
>> +
>> +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..
>
See above.
>> +
>> +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)
>> {
This is what I meant; I'm protecting the map switching, not the map
construction.
>> 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.
>
Okay, I'll be inserting the respective checks.
>> +
>> +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);>> + 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. */
>
>
>> + 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
> }
>
Ah. Of course.
Cheers,
Hannes
--
Dr. Hannes Reinecke zSeries & Storage
hare@suse.de +49 911 74053 688
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: J. Hawn, J. Guild, F. Imendörffer, HRB 16746 (AG Nürnberg)
prev parent reply other threads:[~2013-10-17 7:42 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-10-16 7:25 [PATCH 0/2] TCM Referrals support Hannes Reinecke
2013-10-16 7:25 ` [PATCH 1/2] target_core_alua: Referrals infrastructure Hannes Reinecke
2013-10-16 22:28 ` Nicholas A. Bellinger
2013-10-17 7:38 ` Hannes Reinecke
2013-10-16 7:25 ` [PATCH 2/2] target_core_alua: Referrals configfs integration Hannes Reinecke
2013-10-17 0:36 ` Nicholas A. Bellinger
2013-10-17 7:42 ` Hannes Reinecke [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=525F94E2.80909@suse.de \
--to=hare@suse.de \
--cc=dgilbert@interlog.com \
--cc=linux-scsi@vger.kernel.org \
--cc=nab@daterainc.com \
--cc=nab@linux-iscsi.org \
--cc=target-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).