xref: /linux/drivers/scsi/cxlflash/lunmgt.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
265be2c79SMatthew R. Ochs /*
365be2c79SMatthew R. Ochs  * CXL Flash Device Driver
465be2c79SMatthew R. Ochs  *
565be2c79SMatthew R. Ochs  * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation
665be2c79SMatthew R. Ochs  *             Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation
765be2c79SMatthew R. Ochs  *
865be2c79SMatthew R. Ochs  * Copyright (C) 2015 IBM Corporation
965be2c79SMatthew R. Ochs  */
1065be2c79SMatthew R. Ochs 
1165be2c79SMatthew R. Ochs #include <asm/unaligned.h>
1265be2c79SMatthew R. Ochs 
13cd43c221SUma Krishnan #include <linux/interrupt.h>
14cd43c221SUma Krishnan #include <linux/pci.h>
15cd43c221SUma Krishnan 
1665be2c79SMatthew R. Ochs #include <scsi/scsi_host.h>
1765be2c79SMatthew R. Ochs #include <uapi/scsi/cxlflash_ioctl.h>
1865be2c79SMatthew R. Ochs 
1965be2c79SMatthew R. Ochs #include "sislite.h"
2065be2c79SMatthew R. Ochs #include "common.h"
212cb79266SMatthew R. Ochs #include "vlun.h"
2265be2c79SMatthew R. Ochs #include "superpipe.h"
2365be2c79SMatthew R. Ochs 
2465be2c79SMatthew R. Ochs /**
2565be2c79SMatthew R. Ochs  * create_local() - allocate and initialize a local LUN information structure
2665be2c79SMatthew R. Ochs  * @sdev:	SCSI device associated with LUN.
2765be2c79SMatthew R. Ochs  * @wwid:	World Wide Node Name for LUN.
2865be2c79SMatthew R. Ochs  *
2965be2c79SMatthew R. Ochs  * Return: Allocated local llun_info structure on success, NULL on failure
3065be2c79SMatthew R. Ochs  */
create_local(struct scsi_device * sdev,u8 * wwid)3165be2c79SMatthew R. Ochs static struct llun_info *create_local(struct scsi_device *sdev, u8 *wwid)
3265be2c79SMatthew R. Ochs {
33fb67d44dSMatthew R. Ochs 	struct cxlflash_cfg *cfg = shost_priv(sdev->host);
34fb67d44dSMatthew R. Ochs 	struct device *dev = &cfg->dev->dev;
3565be2c79SMatthew R. Ochs 	struct llun_info *lli = NULL;
3665be2c79SMatthew R. Ochs 
3765be2c79SMatthew R. Ochs 	lli = kzalloc(sizeof(*lli), GFP_KERNEL);
3865be2c79SMatthew R. Ochs 	if (unlikely(!lli)) {
39fb67d44dSMatthew R. Ochs 		dev_err(dev, "%s: could not allocate lli\n", __func__);
4065be2c79SMatthew R. Ochs 		goto out;
4165be2c79SMatthew R. Ochs 	}
4265be2c79SMatthew R. Ochs 
4365be2c79SMatthew R. Ochs 	lli->sdev = sdev;
4465be2c79SMatthew R. Ochs 	lli->host_no = sdev->host->host_no;
452cb79266SMatthew R. Ochs 	lli->in_table = false;
4665be2c79SMatthew R. Ochs 
4765be2c79SMatthew R. Ochs 	memcpy(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN);
4865be2c79SMatthew R. Ochs out:
4965be2c79SMatthew R. Ochs 	return lli;
5065be2c79SMatthew R. Ochs }
5165be2c79SMatthew R. Ochs 
5265be2c79SMatthew R. Ochs /**
5365be2c79SMatthew R. Ochs  * create_global() - allocate and initialize a global LUN information structure
5465be2c79SMatthew R. Ochs  * @sdev:	SCSI device associated with LUN.
5565be2c79SMatthew R. Ochs  * @wwid:	World Wide Node Name for LUN.
5665be2c79SMatthew R. Ochs  *
5765be2c79SMatthew R. Ochs  * Return: Allocated global glun_info structure on success, NULL on failure
5865be2c79SMatthew R. Ochs  */
create_global(struct scsi_device * sdev,u8 * wwid)5965be2c79SMatthew R. Ochs static struct glun_info *create_global(struct scsi_device *sdev, u8 *wwid)
6065be2c79SMatthew R. Ochs {
61fb67d44dSMatthew R. Ochs 	struct cxlflash_cfg *cfg = shost_priv(sdev->host);
62fb67d44dSMatthew R. Ochs 	struct device *dev = &cfg->dev->dev;
6365be2c79SMatthew R. Ochs 	struct glun_info *gli = NULL;
6465be2c79SMatthew R. Ochs 
6565be2c79SMatthew R. Ochs 	gli = kzalloc(sizeof(*gli), GFP_KERNEL);
6665be2c79SMatthew R. Ochs 	if (unlikely(!gli)) {
67fb67d44dSMatthew R. Ochs 		dev_err(dev, "%s: could not allocate gli\n", __func__);
6865be2c79SMatthew R. Ochs 		goto out;
6965be2c79SMatthew R. Ochs 	}
7065be2c79SMatthew R. Ochs 
7165be2c79SMatthew R. Ochs 	mutex_init(&gli->mutex);
7265be2c79SMatthew R. Ochs 	memcpy(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN);
7365be2c79SMatthew R. Ochs out:
7465be2c79SMatthew R. Ochs 	return gli;
7565be2c79SMatthew R. Ochs }
7665be2c79SMatthew R. Ochs 
7765be2c79SMatthew R. Ochs /**
781a47401bSMatthew R. Ochs  * lookup_local() - find a local LUN information structure by WWID
7965be2c79SMatthew R. Ochs  * @cfg:	Internal structure associated with the host.
8065be2c79SMatthew R. Ochs  * @wwid:	WWID associated with LUN.
8165be2c79SMatthew R. Ochs  *
8265be2c79SMatthew R. Ochs  * Return: Found local lun_info structure on success, NULL on failure
8365be2c79SMatthew R. Ochs  */
lookup_local(struct cxlflash_cfg * cfg,u8 * wwid)841a47401bSMatthew R. Ochs static struct llun_info *lookup_local(struct cxlflash_cfg *cfg, u8 *wwid)
8565be2c79SMatthew R. Ochs {
8665be2c79SMatthew R. Ochs 	struct llun_info *lli, *temp;
8765be2c79SMatthew R. Ochs 
8865be2c79SMatthew R. Ochs 	list_for_each_entry_safe(lli, temp, &cfg->lluns, list)
891a47401bSMatthew R. Ochs 		if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN))
9065be2c79SMatthew R. Ochs 			return lli;
9165be2c79SMatthew R. Ochs 
9265be2c79SMatthew R. Ochs 	return NULL;
9365be2c79SMatthew R. Ochs }
9465be2c79SMatthew R. Ochs 
9565be2c79SMatthew R. Ochs /**
9665be2c79SMatthew R. Ochs  * lookup_global() - find a global LUN information structure by WWID
9765be2c79SMatthew R. Ochs  * @wwid:	WWID associated with LUN.
9865be2c79SMatthew R. Ochs  *
9965be2c79SMatthew R. Ochs  * Return: Found global lun_info structure on success, NULL on failure
10065be2c79SMatthew R. Ochs  */
lookup_global(u8 * wwid)10165be2c79SMatthew R. Ochs static struct glun_info *lookup_global(u8 *wwid)
10265be2c79SMatthew R. Ochs {
10365be2c79SMatthew R. Ochs 	struct glun_info *gli, *temp;
10465be2c79SMatthew R. Ochs 
10565be2c79SMatthew R. Ochs 	list_for_each_entry_safe(gli, temp, &global.gluns, list)
10665be2c79SMatthew R. Ochs 		if (!memcmp(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN))
10765be2c79SMatthew R. Ochs 			return gli;
10865be2c79SMatthew R. Ochs 
10965be2c79SMatthew R. Ochs 	return NULL;
11065be2c79SMatthew R. Ochs }
11165be2c79SMatthew R. Ochs 
11265be2c79SMatthew R. Ochs /**
11365be2c79SMatthew R. Ochs  * find_and_create_lun() - find or create a local LUN information structure
11465be2c79SMatthew R. Ochs  * @sdev:	SCSI device associated with LUN.
11565be2c79SMatthew R. Ochs  * @wwid:	WWID associated with LUN.
11665be2c79SMatthew R. Ochs  *
11765be2c79SMatthew R. Ochs  * The LUN is kept both in a local list (per adapter) and in a global list
11865be2c79SMatthew R. Ochs  * (across all adapters). Certain attributes of the LUN are local to the
119fa4aa632SManoj Kumar  * adapter (such as index, port selection mask, etc.).
120fa4aa632SManoj Kumar  *
12165be2c79SMatthew R. Ochs  * The block allocation map is shared across all adapters (i.e. associated
12265be2c79SMatthew R. Ochs  * wih the global list). Since different attributes are associated with
12365be2c79SMatthew R. Ochs  * the per adapter and global entries, allocate two separate structures for each
12465be2c79SMatthew R. Ochs  * LUN (one local, one global).
12565be2c79SMatthew R. Ochs  *
12665be2c79SMatthew R. Ochs  * Keep a pointer back from the local to the global entry.
12765be2c79SMatthew R. Ochs  *
128fa4aa632SManoj Kumar  * This routine assumes the caller holds the global mutex.
129fa4aa632SManoj Kumar  *
13065be2c79SMatthew R. Ochs  * Return: Found/Allocated local lun_info structure on success, NULL on failure
13165be2c79SMatthew R. Ochs  */
find_and_create_lun(struct scsi_device * sdev,u8 * wwid)13265be2c79SMatthew R. Ochs static struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid)
13365be2c79SMatthew R. Ochs {
134fb67d44dSMatthew R. Ochs 	struct cxlflash_cfg *cfg = shost_priv(sdev->host);
135fb67d44dSMatthew R. Ochs 	struct device *dev = &cfg->dev->dev;
13665be2c79SMatthew R. Ochs 	struct llun_info *lli = NULL;
13765be2c79SMatthew R. Ochs 	struct glun_info *gli = NULL;
13865be2c79SMatthew R. Ochs 
13965be2c79SMatthew R. Ochs 	if (unlikely(!wwid))
14065be2c79SMatthew R. Ochs 		goto out;
14165be2c79SMatthew R. Ochs 
1421a47401bSMatthew R. Ochs 	lli = lookup_local(cfg, wwid);
14365be2c79SMatthew R. Ochs 	if (lli)
14465be2c79SMatthew R. Ochs 		goto out;
14565be2c79SMatthew R. Ochs 
14665be2c79SMatthew R. Ochs 	lli = create_local(sdev, wwid);
14765be2c79SMatthew R. Ochs 	if (unlikely(!lli))
14865be2c79SMatthew R. Ochs 		goto out;
14965be2c79SMatthew R. Ochs 
15065be2c79SMatthew R. Ochs 	gli = lookup_global(wwid);
15165be2c79SMatthew R. Ochs 	if (gli) {
15265be2c79SMatthew R. Ochs 		lli->parent = gli;
15365be2c79SMatthew R. Ochs 		list_add(&lli->list, &cfg->lluns);
15465be2c79SMatthew R. Ochs 		goto out;
15565be2c79SMatthew R. Ochs 	}
15665be2c79SMatthew R. Ochs 
15765be2c79SMatthew R. Ochs 	gli = create_global(sdev, wwid);
15865be2c79SMatthew R. Ochs 	if (unlikely(!gli)) {
15965be2c79SMatthew R. Ochs 		kfree(lli);
16065be2c79SMatthew R. Ochs 		lli = NULL;
16165be2c79SMatthew R. Ochs 		goto out;
16265be2c79SMatthew R. Ochs 	}
16365be2c79SMatthew R. Ochs 
16465be2c79SMatthew R. Ochs 	lli->parent = gli;
16565be2c79SMatthew R. Ochs 	list_add(&lli->list, &cfg->lluns);
16665be2c79SMatthew R. Ochs 
16765be2c79SMatthew R. Ochs 	list_add(&gli->list, &global.gluns);
16865be2c79SMatthew R. Ochs 
16965be2c79SMatthew R. Ochs out:
170fb67d44dSMatthew R. Ochs 	dev_dbg(dev, "%s: returning lli=%p, gli=%p\n", __func__, lli, gli);
17165be2c79SMatthew R. Ochs 	return lli;
17265be2c79SMatthew R. Ochs }
17365be2c79SMatthew R. Ochs 
17465be2c79SMatthew R. Ochs /**
17565be2c79SMatthew R. Ochs  * cxlflash_term_local_luns() - Delete all entries from local LUN list, free.
17665be2c79SMatthew R. Ochs  * @cfg:	Internal structure associated with the host.
17765be2c79SMatthew R. Ochs  */
cxlflash_term_local_luns(struct cxlflash_cfg * cfg)17865be2c79SMatthew R. Ochs void cxlflash_term_local_luns(struct cxlflash_cfg *cfg)
17965be2c79SMatthew R. Ochs {
18065be2c79SMatthew R. Ochs 	struct llun_info *lli, *temp;
18165be2c79SMatthew R. Ochs 
18265be2c79SMatthew R. Ochs 	mutex_lock(&global.mutex);
18365be2c79SMatthew R. Ochs 	list_for_each_entry_safe(lli, temp, &cfg->lluns, list) {
18465be2c79SMatthew R. Ochs 		list_del(&lli->list);
18565be2c79SMatthew R. Ochs 		kfree(lli);
18665be2c79SMatthew R. Ochs 	}
18765be2c79SMatthew R. Ochs 	mutex_unlock(&global.mutex);
18865be2c79SMatthew R. Ochs }
18965be2c79SMatthew R. Ochs 
19065be2c79SMatthew R. Ochs /**
19165be2c79SMatthew R. Ochs  * cxlflash_list_init() - initializes the global LUN list
19265be2c79SMatthew R. Ochs  */
cxlflash_list_init(void)19365be2c79SMatthew R. Ochs void cxlflash_list_init(void)
19465be2c79SMatthew R. Ochs {
19565be2c79SMatthew R. Ochs 	INIT_LIST_HEAD(&global.gluns);
19665be2c79SMatthew R. Ochs 	mutex_init(&global.mutex);
19765be2c79SMatthew R. Ochs 	global.err_page = NULL;
19865be2c79SMatthew R. Ochs }
19965be2c79SMatthew R. Ochs 
20065be2c79SMatthew R. Ochs /**
20165be2c79SMatthew R. Ochs  * cxlflash_term_global_luns() - frees resources associated with global LUN list
20265be2c79SMatthew R. Ochs  */
cxlflash_term_global_luns(void)20365be2c79SMatthew R. Ochs void cxlflash_term_global_luns(void)
20465be2c79SMatthew R. Ochs {
20565be2c79SMatthew R. Ochs 	struct glun_info *gli, *temp;
20665be2c79SMatthew R. Ochs 
20765be2c79SMatthew R. Ochs 	mutex_lock(&global.mutex);
20865be2c79SMatthew R. Ochs 	list_for_each_entry_safe(gli, temp, &global.gluns, list) {
20965be2c79SMatthew R. Ochs 		list_del(&gli->list);
2102cb79266SMatthew R. Ochs 		cxlflash_ba_terminate(&gli->blka.ba_lun);
21165be2c79SMatthew R. Ochs 		kfree(gli);
21265be2c79SMatthew R. Ochs 	}
21365be2c79SMatthew R. Ochs 	mutex_unlock(&global.mutex);
21465be2c79SMatthew R. Ochs }
21565be2c79SMatthew R. Ochs 
21665be2c79SMatthew R. Ochs /**
21765be2c79SMatthew R. Ochs  * cxlflash_manage_lun() - handles LUN management activities
21865be2c79SMatthew R. Ochs  * @sdev:	SCSI device associated with LUN.
219*28fc2bd2SArnd Bergmann  * @arg:	Manage ioctl data structure.
22065be2c79SMatthew R. Ochs  *
22165be2c79SMatthew R. Ochs  * This routine is used to notify the driver about a LUN's WWID and associate
22265be2c79SMatthew R. Ochs  * SCSI devices (sdev) with a global LUN instance. Additionally it serves to
22365be2c79SMatthew R. Ochs  * change a LUN's operating mode: legacy or superpipe.
22465be2c79SMatthew R. Ochs  *
22565be2c79SMatthew R. Ochs  * Return: 0 on success, -errno on failure
22665be2c79SMatthew R. Ochs  */
cxlflash_manage_lun(struct scsi_device * sdev,void * arg)227*28fc2bd2SArnd Bergmann int cxlflash_manage_lun(struct scsi_device *sdev, void *arg)
22865be2c79SMatthew R. Ochs {
229*28fc2bd2SArnd Bergmann 	struct dk_cxlflash_manage_lun *manage = arg;
230fb67d44dSMatthew R. Ochs 	struct cxlflash_cfg *cfg = shost_priv(sdev->host);
231fb67d44dSMatthew R. Ochs 	struct device *dev = &cfg->dev->dev;
23265be2c79SMatthew R. Ochs 	struct llun_info *lli = NULL;
233fb67d44dSMatthew R. Ochs 	int rc = 0;
23465be2c79SMatthew R. Ochs 	u64 flags = manage->hdr.flags;
23565be2c79SMatthew R. Ochs 	u32 chan = sdev->channel;
23665be2c79SMatthew R. Ochs 
237fa4aa632SManoj Kumar 	mutex_lock(&global.mutex);
23865be2c79SMatthew R. Ochs 	lli = find_and_create_lun(sdev, manage->wwid);
239fb67d44dSMatthew R. Ochs 	dev_dbg(dev, "%s: WWID=%016llx%016llx, flags=%016llx lli=%p\n",
2401a47401bSMatthew R. Ochs 		__func__, get_unaligned_be64(&manage->wwid[0]),
241fb67d44dSMatthew R. Ochs 		get_unaligned_be64(&manage->wwid[8]), manage->hdr.flags, lli);
24265be2c79SMatthew R. Ochs 	if (unlikely(!lli)) {
24365be2c79SMatthew R. Ochs 		rc = -ENOMEM;
24465be2c79SMatthew R. Ochs 		goto out;
24565be2c79SMatthew R. Ochs 	}
24665be2c79SMatthew R. Ochs 
24765be2c79SMatthew R. Ochs 	if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) {
2481a47401bSMatthew R. Ochs 		/*
2491a47401bSMatthew R. Ochs 		 * Update port selection mask based upon channel, store off LUN
2501a47401bSMatthew R. Ochs 		 * in unpacked, AFU-friendly format, and hang LUN reference in
2511a47401bSMatthew R. Ochs 		 * the sdev.
2521a47401bSMatthew R. Ochs 		 */
2538fa4f177SMatthew R. Ochs 		lli->port_sel |= CHAN2PORTMASK(chan);
25465be2c79SMatthew R. Ochs 		lli->lun_id[chan] = lun_to_lunid(sdev->lun);
25565be2c79SMatthew R. Ochs 		sdev->hostdata = lli;
25665be2c79SMatthew R. Ochs 	} else if (flags & DK_CXLFLASH_MANAGE_LUN_DISABLE_SUPERPIPE) {
25765be2c79SMatthew R. Ochs 		if (lli->parent->mode != MODE_NONE)
25865be2c79SMatthew R. Ochs 			rc = -EBUSY;
2591a47401bSMatthew R. Ochs 		else {
2608a260543SUma Krishnan 			/*
2618a260543SUma Krishnan 			 * Clean up local LUN for this port and reset table
2628a260543SUma Krishnan 			 * tracking when no more references exist.
2638a260543SUma Krishnan 			 */
26465be2c79SMatthew R. Ochs 			sdev->hostdata = NULL;
2658fa4f177SMatthew R. Ochs 			lli->port_sel &= ~CHAN2PORTMASK(chan);
2668a260543SUma Krishnan 			if (lli->port_sel == 0U)
2678a260543SUma Krishnan 				lli->in_table = false;
26865be2c79SMatthew R. Ochs 		}
2691a47401bSMatthew R. Ochs 	}
2701a47401bSMatthew R. Ochs 
271fb67d44dSMatthew R. Ochs 	dev_dbg(dev, "%s: port_sel=%08x chan=%u lun_id=%016llx\n",
272fb67d44dSMatthew R. Ochs 		__func__, lli->port_sel, chan, lli->lun_id[chan]);
27365be2c79SMatthew R. Ochs 
27465be2c79SMatthew R. Ochs out:
275fa4aa632SManoj Kumar 	mutex_unlock(&global.mutex);
276fb67d44dSMatthew R. Ochs 	dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc);
27765be2c79SMatthew R. Ochs 	return rc;
27865be2c79SMatthew R. Ochs }
279