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