From: Nabeel M Mohamed <nmeeramoh...@micron.com>

This adds utility routines to:

- Create and initialize a media class with an mpool volume
- Initialize and validate superblocks on all media class
  volumes
- Open and initialize all media class volumes
- Allocate metadata container 0 (MDC0) and update the
  superblock on capacity media class volume with metadata for
  accessing MDC0
- Create and initialize root MDC
- Initialize mpool descriptor and track the mapping between an
  mpool UUID and its descriptor in a rbtree

When an mpool is created, a pair of mlogs are instantiated with
well-known OIDs comprising the root MDC of the mpool. The root
MDC provides a location for mpool clients to store whatever
metadata they need for start-up.

Co-developed-by: Greg Becker <gbec...@micron.com>
Signed-off-by: Greg Becker <gbec...@micron.com>
Co-developed-by: Pierre Labat <pla...@micron.com>
Signed-off-by: Pierre Labat <pla...@micron.com>
Co-developed-by: John Groves <jgro...@micron.com>
Signed-off-by: John Groves <jgro...@micron.com>
Signed-off-by: Nabeel M Mohamed <nmeeramoh...@micron.com>
---
 drivers/mpool/mpcore.c | 987 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 987 insertions(+)
 create mode 100644 drivers/mpool/mpcore.c

diff --git a/drivers/mpool/mpcore.c b/drivers/mpool/mpcore.c
new file mode 100644
index 000000000000..246baedcdcec
--- /dev/null
+++ b/drivers/mpool/mpcore.c
@@ -0,0 +1,987 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015-2020 Micron Technology, Inc.  All rights reserved.
+ */
+
+/*
+ * Media pool (mpool) manager module.
+ *
+ * Defines functions to create and maintain mpools comprising multiple drives
+ * in multiple media classes used for storing mblocks and mlogs.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sort.h>
+#include <linux/slab.h>
+#include <linux/kref.h>
+#include <linux/rbtree.h>
+
+#include "mpool_ioctl.h"
+
+#include "mpool_printk.h"
+#include "assert.h"
+#include "uuid.h"
+
+#include "mp.h"
+#include "omf.h"
+#include "omf_if.h"
+#include "pd.h"
+#include "smap.h"
+#include "mclass.h"
+#include "pmd_obj.h"
+#include "mpcore.h"
+#include "sb.h"
+#include "upgrade.h"
+
+struct omf_devparm_descriptor;
+struct mpool_descriptor;
+
+/* Rbtree mapping mpool UUID to mpool descriptor node: uuid_to_mpdesc_rb */
+struct rb_root mpool_pools = { NULL };
+
+int uuid_to_mpdesc_insert(struct rb_root *root, struct mpool_descriptor *data)
+{
+       struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+       /* Figure out where to put new node */
+       while (*new) {
+               struct mpool_descriptor *this = rb_entry(*new, struct 
mpool_descriptor, pds_node);
+
+               int result = mpool_uuid_compare(&data->pds_poolid, 
&this->pds_poolid);
+
+               parent = *new;
+               if (result < 0)
+                       new = &((*new)->rb_left);
+               else if (result > 0)
+                       new = &((*new)->rb_right);
+               else
+                       return false;
+       }
+
+       /* Add new node and rebalance tree. */
+       rb_link_node(&data->pds_node, parent, new);
+       rb_insert_color(&data->pds_node, root);
+
+       return true;
+}
+
+static struct mpool_descriptor *
+uuid_to_mpdesc_search(struct rb_root *root, struct mpool_uuid *key_uuid)
+{
+       struct rb_node *node = root->rb_node;
+
+       while (node) {
+               struct mpool_descriptor *data = rb_entry(node, struct 
mpool_descriptor, pds_node);
+
+               int  result = mpool_uuid_compare(key_uuid, &data->pds_poolid);
+
+               if (result < 0)
+                       node = node->rb_left;
+               else if (result > 0)
+                       node = node->rb_right;
+               else
+                       return data;
+       }
+       return NULL;
+}
+
+int mpool_dev_sbwrite(struct mpool_descriptor *mp, struct mpool_dev_info *pd,
+                     struct omf_sb_descriptor *sbmdc0)
+{
+       struct omf_sb_descriptor *sb = NULL;
+       struct mc_parms mc_parms;
+       int rc;
+
+       if (mpool_pd_status_get(pd) != PD_STAT_ONLINE) {
+               rc = -EIO;
+               mp_pr_err("%s:%s unavailable or offline, status %d",
+                         rc, mp->pds_name, pd->pdi_name, 
mpool_pd_status_get(pd));
+               return rc;
+       }
+
+       sb = kzalloc(sizeof(struct omf_sb_descriptor), GFP_KERNEL);
+       if (!sb) {
+               rc = -ENOMEM;
+               mp_pr_err("mpool %s, writing superblock on drive %s, alloc of 
superblock descriptor failed %lu",
+                         rc, mp->pds_name, pd->pdi_name, sizeof(struct 
omf_sb_descriptor));
+               return rc;
+       }
+
+       /*
+        * Set superblock values common to all new drives in pool
+        * (new or extant)
+        */
+       sb->osb_magic = OMF_SB_MAGIC;
+       strlcpy((char *) sb->osb_name, mp->pds_name, sizeof(sb->osb_name));
+       sb->osb_vers = OMF_SB_DESC_VER_LAST;
+       mpool_uuid_copy(&sb->osb_poolid, &mp->pds_poolid);
+       sb->osb_gen = 1;
+
+       /* Set superblock values specific to this drive */
+       mpool_uuid_copy(&sb->osb_parm.odp_devid, &pd->pdi_devid);
+       sb->osb_parm.odp_devsz = pd->pdi_parm.dpr_devsz;
+       sb->osb_parm.odp_zonetot = pd->pdi_parm.dpr_zonetot;
+       mc_pd_prop2mc_parms(&pd->pdi_parm.dpr_prop, &mc_parms);
+       mc_parms2omf_devparm(&mc_parms, &sb->osb_parm);
+
+       if (sbmdc0)
+               sbutil_mdc0_copy(sb, sbmdc0);
+       else
+               sbutil_mdc0_clear(sb);
+
+       rc = sb_write_new(&pd->pdi_parm, sb);
+       if (rc) {
+               mp_pr_err("mpool %s, writing superblock on drive %s, write 
failed",
+                         rc, mp->pds_name, pd->pdi_name);
+       }
+
+       kfree(sb);
+       return rc;
+}
+
+/**
+ * mpool_mdc0_alloc() - Allocate space for the two MDC0 mlogs
+ * @mp:
+ * @sb:
+ *
+ * In the context of a mpool create, allocate space for the two MDC0 mlogs
+ *     and update the sb structure with the position of MDC0.
+ *
+ * Note: this function assumes that the media classes have already been
+ *     created.
+ */
+static int mpool_mdc0_alloc(struct mpool_descriptor *mp, struct 
omf_sb_descriptor *sb)
+{
+       struct mpool_dev_info *pd;
+       struct media_class *mc;
+       struct mpool_uuid uuid;
+       u64 zcnt, zonelen;
+       u32 cnt;
+       int rc;
+
+       sbutil_mdc0_clear(sb);
+
+       ASSERT(mp->pds_mdparm.md_mclass < MP_MED_NUMBER);
+
+       mc = &mp->pds_mc[mp->pds_mdparm.md_mclass];
+       if (mc->mc_pdmc < 0) {
+               rc = -ENOSPC;
+               mp_pr_err("%s: sb update memory image MDC0 information, not 
enough drives",
+                         rc, mp->pds_name);
+               return rc;
+       }
+
+       pd = &mp->pds_pdv[mc->mc_pdmc];
+
+       zonelen = (u64)pd->pdi_parm.dpr_zonepg << PAGE_SHIFT;
+       zcnt = 1 + ((mp->pds_params.mp_mdc0cap - 1) / zonelen);
+
+       cnt = sb_zones_for_sbs(&(pd->pdi_prop));
+       if (cnt < 1) {
+               rc = -EINVAL;
+               mp_pr_err("%s: sb MDC0, getting sb range failed for drive %s 
%u",
+                         rc, mp->pds_name, pd->pdi_name, cnt);
+               return rc;
+       }
+
+       if ((pd->pdi_zonetot - cnt) < zcnt * 2) {
+               rc = -ENOSPC;
+               mp_pr_err("%s: sb MDC0, no room for MDC0 on drive %s %lu %u 
%lu",
+                         rc, mp->pds_name, pd->pdi_name,
+                         (ulong)pd->pdi_zonetot, cnt, (ulong)zcnt);
+               return rc;
+       }
+
+       /*
+        * mdc0 log1/2 alloced on first 2 * zcnt zone's
+        */
+       rc = pd_zone_erase(&pd->pdi_parm, cnt, zcnt * 2, true);
+       if (rc) {
+               mp_pr_err("%s: sb MDC0, erase failed on %s %u %lu",
+                         rc, mp->pds_name, pd->pdi_name, cnt, (ulong)zcnt);
+               return rc;
+       }
+
+       /*
+        * Fill in common mdc0 log1/2 and drive info.
+        */
+       sb->osb_mdc01gen = 1;
+       sb->osb_mdc01desc.ol_zcnt = zcnt;
+       mpool_generate_uuid(&uuid);
+       mpool_uuid_copy(&sb->osb_mdc01uuid, &uuid);
+
+       sb->osb_mdc02gen = 2;
+       sb->osb_mdc02desc.ol_zcnt = zcnt;
+       mpool_generate_uuid(&uuid);
+       mpool_uuid_copy(&sb->osb_mdc02uuid, &uuid);
+
+       mpool_uuid_copy(&sb->osb_mdc01devid, &pd->pdi_devid);
+       sb->osb_mdc01desc.ol_zaddr = cnt;
+
+       mpool_uuid_copy(&sb->osb_mdc02devid, &pd->pdi_devid);
+       sb->osb_mdc02desc.ol_zaddr = cnt + zcnt;
+
+       mpool_uuid_copy(&sb->osb_mdc0dev.odp_devid, &pd->pdi_devid);
+       sb->osb_mdc0dev.odp_devsz = pd->pdi_parm.dpr_devsz;
+       sb->osb_mdc0dev.odp_zonetot = pd->pdi_parm.dpr_zonetot;
+       mc_parms2omf_devparm(&mc->mc_parms, &sb->osb_mdc0dev);
+
+       return 0;
+}
+
+int mpool_dev_sbwrite_newpool(struct mpool_descriptor *mp, struct 
omf_sb_descriptor *sbmdc0)
+{
+       struct mpool_dev_info *pd = NULL;
+       u64 pdh = 0;
+       int rc;
+
+       /* Alloc mdc0 and generate mdc0 info for superblocks */
+       rc = mpool_mdc0_alloc(mp, sbmdc0);
+       if (rc) {
+               mp_pr_err("%s: MDC0 allocation failed", rc, mp->pds_name);
+               return rc;
+       }
+
+       for (pdh = 0; pdh < mp->pds_pdvcnt; pdh++) {
+               pd = &mp->pds_pdv[pdh];
+
+               if (pd->pdi_mclass == mp->pds_mdparm.md_mclass)
+                       rc = mpool_dev_sbwrite(mp, pd, sbmdc0);
+               else
+                       rc = mpool_dev_sbwrite(mp, pd, NULL);
+               if (rc) {
+                       mp_pr_err("%s: sb write %s failed, %d %d", rc, 
mp->pds_name,
+                                 pd->pdi_name, pd->pdi_mclass, 
mp->pds_mdparm.md_mclass);
+                       break;
+               }
+       }
+
+       return rc;
+}
+
+int mpool_mdc0_sb2obj(struct mpool_descriptor *mp, struct omf_sb_descriptor 
*sb,
+                     struct pmd_layout **l1, struct pmd_layout **l2)
+{
+       int rc, i;
+
+       /* MDC0 mlog1 layout */
+       *l1 = pmd_layout_alloc(&sb->osb_mdc01uuid, MDC0_OBJID_LOG1, 
sb->osb_mdc01gen, 0,
+                              sb->osb_mdc01desc.ol_zcnt);
+       if (!*l1) {
+               *l1 = *l2 = NULL;
+
+               rc = -ENOMEM;
+               mp_pr_err("mpool %s, MDC0 mlog1 allocation failed", rc, 
mp->pds_name);
+               return rc;
+       }
+
+       (*l1)->eld_state = PMD_LYT_COMMITTED;
+
+       for (i = 0; i < mp->pds_pdvcnt; i++) {
+               if (mpool_uuid_compare(&mp->pds_pdv[i].pdi_devid, 
&sb->osb_mdc01devid) == 0) {
+                       (*l1)->eld_ld.ol_pdh = i;
+                       (*l1)->eld_ld.ol_zaddr = sb->osb_mdc01desc.ol_zaddr;
+                       break;
+               }
+       }
+
+       if (i >= mp->pds_pdvcnt) {
+               char uuid_str[40];
+
+               /* Should never happen */
+               pmd_obj_put(*l1);
+               *l1 = *l2 = NULL;
+
+               mpool_unparse_uuid(&sb->osb_mdc01devid, uuid_str);
+               rc = -ENOENT;
+               mp_pr_err("mpool %s, allocating MDC0 mlog1, can't find handle 
for pd uuid %s,",
+                         rc, mp->pds_name, uuid_str);
+
+               return rc;
+       }
+
+       /* MDC0 mlog2 layout */
+       *l2 = pmd_layout_alloc(&sb->osb_mdc02uuid, MDC0_OBJID_LOG2, 
sb->osb_mdc02gen, 0,
+                              sb->osb_mdc02desc.ol_zcnt);
+       if (!*l2) {
+               pmd_obj_put(*l1);
+
+               *l1 = *l2 = NULL;
+
+               rc = -ENOMEM;
+               mp_pr_err("mpool %s, MDC0 mlog2 allocation failed", rc, 
mp->pds_name);
+               return rc;
+       }
+
+       (*l2)->eld_state = PMD_LYT_COMMITTED;
+
+       for (i = 0; i < mp->pds_pdvcnt; i++) {
+               if (mpool_uuid_compare(&mp->pds_pdv[i].pdi_devid, 
&sb->osb_mdc02devid) == 0) {
+                       (*l2)->eld_ld.ol_pdh = i;
+                       (*l2)->eld_ld.ol_zaddr = sb->osb_mdc02desc.ol_zaddr;
+                       break;
+               }
+       }
+
+       if (i >= mp->pds_pdvcnt) {
+               char uuid_str[40];
+
+               /* Should never happen */
+               pmd_obj_put(*l1);
+               pmd_obj_put(*l2);
+               *l1 = *l2 = NULL;
+
+               mpool_unparse_uuid(&sb->osb_mdc02devid, uuid_str);
+               rc = -ENOENT;
+               mp_pr_err("mpool %s, allocating MDC0 mlog2, can't find handle 
for pd uuid %s",
+                         rc, mp->pds_name, uuid_str);
+
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * mpool_dev_check_new() - check if a drive is ready to be added in an mpool.
+ * @mp:
+ * @pd:
+ */
+int mpool_dev_check_new(struct mpool_descriptor *mp, struct mpool_dev_info *pd)
+{
+       int rval, rc;
+
+       if (mpool_pd_status_get(pd) != PD_STAT_ONLINE) {
+               rc = -EIO;
+               mp_pr_err("%s:%s unavailable or offline, status %d",
+                         rc, mp->pds_name, pd->pdi_name, 
mpool_pd_status_get(pd));
+               return rc;
+       }
+
+       /* Confirm drive does not contain mpool magic value */
+       rval = sb_magic_check(&pd->pdi_parm);
+       if (rval) {
+               if (rval < 0) {
+                       rc = rval;
+                       mp_pr_err("%s:%s read sb magic failed", rc, 
mp->pds_name, pd->pdi_name);
+                       return rc;
+               }
+
+               rc = -EBUSY;
+               mp_pr_err("%s:%s sb magic already exists", rc, mp->pds_name, 
pd->pdi_name);
+               return rc;
+       }
+
+       return 0;
+}
+
+int mpool_desc_pdmc_add(struct mpool_descriptor *mp, u16 pdh,
+                       struct omf_devparm_descriptor *omf_devparm, bool 
check_only)
+{
+       struct mpool_dev_info *pd = NULL;
+       struct media_class *mc;
+       struct mc_parms mc_parms;
+       int rc;
+
+       pd = &mp->pds_pdv[pdh];
+       if (omf_devparm == NULL)
+               mc_pd_prop2mc_parms(&pd->pdi_parm.dpr_prop, &mc_parms);
+       else
+               mc_omf_devparm2mc_parms(omf_devparm, &mc_parms);
+
+       if (!mclass_isvalid(mc_parms.mcp_classp)) {
+               rc = -EINVAL;
+               mp_pr_err("%s: media class %u of %s is undefined",  rc, 
mp->pds_name,
+                         mc_parms.mcp_classp, pd->pdi_name);
+               return rc;
+       }
+
+       /*
+        * Devices that do not support updatable sectors can't be included
+        * in an mpool. Do not check if in the context of an unavailable PD
+        * during activate, because it is impossible to determine the PD
+        * properties.
+        */
+       if ((omf_devparm == NULL) && !(pd->pdi_cmdopt & 
PD_CMD_SECTOR_UPDATABLE)) {
+               rc = -EINVAL;
+               mp_pr_err("%s: device %s sectors not updatable", rc, 
mp->pds_name, pd->pdi_name);
+               return rc;
+       }
+
+       mc = &mp->pds_mc[mc_parms.mcp_classp];
+       if (mc->mc_pdmc < 0) {
+               struct mc_smap_parms mcsp;
+
+               /*
+                * No media class corresponding to the PD class yet, create one.
+                */
+               rc = mc_smap_parms_get(&mp->pds_mc[mc_parms.mcp_classp], 
&mp->pds_params, &mcsp);
+               if (rc)
+                       return rc;
+
+               if (!check_only)
+                       mc_init_class(mc, &mc_parms, &mcsp);
+       } else {
+               rc = -EINVAL;
+               mp_pr_err("%s: add %s, only 1 device allowed per media class",
+                         rc, mp->pds_name, pd->pdi_name);
+               return rc;
+       }
+
+       if (check_only)
+               return 0;
+
+       mc->mc_pdmc = pdh;
+
+       return 0;
+}
+
+/**
+ * mpool_desc_init_newpool() - Create the media classes and add all the mpool 
PDs
+ * @mp:
+ * @flags: enum mp_mgmt_flags
+ *
+ * Called on mpool create.
+ * Create the media classes and add all the mpool PDs in their media class.
+ * Update the metadata media class in mp->pds_mdparm
+ *
+ * Note: the PD properties (pd->pdi_parm.dpr_prop) must be updated
+ * and correct when entering this function.
+ */
+int mpool_desc_init_newpool(struct mpool_descriptor *mp, u32 flags)
+{
+       u64 pdh = 0;
+       int rc;
+
+       if (!(flags & (1 << MP_FLAGS_FORCE))) {
+               rc = mpool_dev_check_new(mp, &mp->pds_pdv[pdh]);
+               if (rc)
+                       return rc;
+       }
+
+       /*
+        * Add drive in its media class. That may create the class
+        * if first drive of the class.
+        */
+       rc = mpool_desc_pdmc_add(mp, pdh, NULL, false);
+       if (rc) {
+               struct mpool_dev_info *pd __maybe_unused;
+
+               pd = &mp->pds_pdv[pdh];
+
+               mp_pr_err("mpool %s, mpool desc init, adding drive %s in a 
media class failed",
+                         rc, mp->pds_name, pd->pdi_name);
+               return rc;
+       }
+
+       mp->pds_mdparm.md_mclass = mp->pds_pdv[pdh].pdi_mclass;
+
+       return 0;
+}
+
+int mpool_dev_init_all(struct mpool_dev_info *pdv, u64 dcnt, char **dpaths,
+                      struct pd_prop *pd_prop)
+{
+       char *pdname;
+       int idx, rc;
+
+       if (dcnt == 0)
+               return -EINVAL;
+
+       for (rc = 0, idx = 0; idx < dcnt; idx++, pd_prop++) {
+               rc = pd_dev_open(dpaths[idx], &pdv[idx].pdi_parm, pd_prop);
+               if (rc) {
+                       mp_pr_err("opening device %s failed", rc, dpaths[idx]);
+                       break;
+               }
+
+               pdname = strrchr(dpaths[idx], '/');
+               pdname = pdname ? pdname + 1 : dpaths[idx];
+               strlcpy(pdv[idx].pdi_name, pdname, sizeof(pdv[idx].pdi_name));
+
+               mpool_pd_status_set(&pdv[idx], PD_STAT_ONLINE);
+       }
+
+       while (rc && idx-- > 0)
+               pd_dev_close(&pdv[idx].pdi_parm);
+
+       return rc;
+}
+
+void mpool_mdc_cap_init(struct mpool_descriptor *mp, struct mpool_dev_info *pd)
+{
+       u64 zonesz, defmbsz;
+
+       zonesz = (pd->pdi_zonepg << PAGE_SHIFT) >> 20;
+       defmbsz = MPOOL_MBSIZE_MB_DEFAULT;
+
+       if (mp->pds_params.mp_mdc0cap == 0) {
+               mp->pds_params.mp_mdc0cap = max_t(u64, defmbsz, zonesz);
+               mp->pds_params.mp_mdc0cap <<= 20;
+       }
+
+       if (mp->pds_params.mp_mdcncap == 0) {
+               mp->pds_params.mp_mdcncap = max_t(u64, zonesz, (256 / zonesz));
+               mp->pds_params.mp_mdcncap <<= 20;
+       }
+}
+
+/**
+ * mpool_desc_init_sb() - Read the super blocks of the PDs.
+ * @mp:
+ * @sbmdc0: output. MDC0 information stored in the super blocks.
+ * @flags:
+ *
+ * Adjust the discovered PD properties stored in pd->pdi_parm.dpr_prop with
+ * PD parameters from the super block. Some of discovered PD properties are
+ * default (like zone size) and need to be adjusted to what the PD actually
+ * use.
+ */
+int mpool_desc_init_sb(struct mpool_descriptor *mp, struct omf_sb_descriptor 
*sbmdc0,
+                      u32 flags, bool *mc_resize)
+{
+       struct omf_sb_descriptor *sb = NULL;
+       struct mpool_dev_info *pd = NULL;
+       u16 omf_ver = OMF_SB_DESC_UNDEF;
+       bool mdc0found = false;
+       bool force = ((flags & (1 << MP_FLAGS_FORCE)) != 0);
+       u8 pdh = 0;
+       int rc;
+
+       sb = kzalloc(sizeof(*sb), GFP_KERNEL);
+       if (!sb) {
+               rc = -ENOMEM;
+               mp_pr_err("sb desc alloc failed %lu", rc, (ulong)sizeof(*sb));
+               return rc;
+       }
+
+       for (pdh = 0; pdh < mp->pds_pdvcnt; pdh++) {
+               struct omf_devparm_descriptor *dparm;
+               bool resize = false;
+               int i;
+
+               pd = &mp->pds_pdv[pdh];
+               if (mpool_pd_status_get(pd) != PD_STAT_ONLINE) {
+                       rc = -EIO;
+                       mp_pr_err("pd %s unavailable or offline, status %d",
+                                 rc, pd->pdi_name, mpool_pd_status_get(pd));
+                       kfree(sb);
+                       return rc;
+               }
+
+               /*
+                * Read superblock; init and validate pool drive info
+                * from device parameters stored in the super block.
+                */
+               rc = sb_read(&pd->pdi_parm, sb, &omf_ver, force);
+               if (rc) {
+                       mp_pr_err("sb read from %s failed", rc, pd->pdi_name);
+                       kfree(sb);
+                       return rc;
+               }
+
+               if (!pdh) {
+                       size_t n __maybe_unused;
+
+                       /*
+                        * First drive; confirm pool not open; set pool-wide
+                        * properties
+                        */
+                       if (uuid_to_mpdesc_search(&mpool_pools, 
&sb->osb_poolid)) {
+                               char *uuid_str;
+
+                               uuid_str = kmalloc(MPOOL_UUID_STRING_LEN + 1, 
GFP_KERNEL);
+                               if (uuid_str)
+                                       mpool_unparse_uuid(&sb->osb_poolid, 
uuid_str);
+
+                               rc = -EBUSY;
+                               mp_pr_err("%s: mpool already activated, id %s, 
pd name %s",
+                                         rc, sb->osb_name, uuid_str, 
pd->pdi_name);
+                               kfree(sb);
+                               kfree(uuid_str);
+                               return rc;
+                       }
+                       mpool_uuid_copy(&mp->pds_poolid, &sb->osb_poolid);
+
+                       n = strlcpy(mp->pds_name, (char *)sb->osb_name, 
sizeof(mp->pds_name));
+                       ASSERT(n < sizeof(mp->pds_name));
+               } else {
+                       /* Second or later drive; validate pool-wide properties 
*/
+                       if (mpool_uuid_compare(&sb->osb_poolid, 
&mp->pds_poolid) != 0) {
+                               char *uuid_str1, *uuid_str2 = NULL;
+
+                               uuid_str1 = kmalloc(2 * (MPOOL_UUID_STRING_LEN 
+ 1), GFP_KERNEL);
+                               if (uuid_str1) {
+                                       uuid_str2 = uuid_str1 + 
MPOOL_UUID_STRING_LEN + 1;
+                                       mpool_unparse_uuid(&sb->osb_poolid, 
uuid_str1);
+                                       mpool_unparse_uuid(&mp->pds_poolid, 
uuid_str2);
+                               }
+
+                               rc = -EINVAL;
+                               mp_pr_err("%s: pd %s, mpool id %s different 
from prior id %s",
+                                         rc, mp->pds_name, pd->pdi_name, 
uuid_str1, uuid_str2);
+                               kfree(sb);
+                               kfree(uuid_str1);
+                               return rc;
+                       }
+               }
+
+               dparm = &sb->osb_parm;
+               if (!force && pd->pdi_devsz > dparm->odp_devsz) {
+                       mp_pr_info("%s: pd %s, discovered size %lu > on-media 
size %lu",
+                               mp->pds_name, pd->pdi_name,
+                               (ulong)pd->pdi_devsz, (ulong)dparm->odp_devsz);
+
+                       if ((flags & (1 << MP_FLAGS_RESIZE)) == 0) {
+                               pd->pdi_devsz = dparm->odp_devsz;
+                       } else {
+                               dparm->odp_devsz  = pd->pdi_devsz;
+                               dparm->odp_zonetot = pd->pdi_devsz / 
(pd->pdi_zonepg << PAGE_SHIFT);
+
+                               pd->pdi_zonetot = dparm->odp_zonetot;
+                               resize = true;
+                       }
+               }
+
+               /* Validate mdc0 info in superblock if present */
+               if (!sbutil_mdc0_isclear(sb)) {
+                       if (!force && !sbutil_mdc0_isvalid(sb)) {
+                               rc = -EINVAL;
+                               mp_pr_err("%s: pd %s, invalid sb MDC0",
+                                         rc, mp->pds_name, pd->pdi_name);
+                               kfree(sb);
+                               return rc;
+                       }
+
+                       dparm = &sb->osb_mdc0dev;
+                       if (resize) {
+                               ASSERT(pd->pdi_devsz > dparm->odp_devsz);
+
+                               dparm->odp_devsz = pd->pdi_devsz;
+                               dparm->odp_zonetot = pd->pdi_devsz / 
(pd->pdi_zonepg << PAGE_SHIFT);
+                       }
+
+                       sbutil_mdc0_copy(sbmdc0, sb);
+                       mdc0found = true;
+               }
+
+               /* Set drive info confirming devid is unique and zone parms 
match */
+               for (i = 0; i < pdh; i++) {
+                       if (mpool_uuid_compare(&mp->pds_pdv[i].pdi_devid,
+                                              &sb->osb_parm.odp_devid) == 0) {
+                               char *uuid_str;
+
+                               uuid_str = kmalloc(MPOOL_UUID_STRING_LEN + 1, 
GFP_KERNEL);
+                               if (uuid_str)
+                                       
mpool_unparse_uuid(&sb->osb_parm.odp_devid, uuid_str);
+                               rc = -EINVAL;
+                               mp_pr_err("%s: pd %s, duplicate devices, uuid 
%s",
+                                         rc, mp->pds_name, pd->pdi_name, 
uuid_str);
+                               kfree(uuid_str);
+                               kfree(sb);
+                               return rc;
+                       }
+               }
+
+               if (omf_ver > OMF_SB_DESC_VER_LAST) {
+                       rc = -EOPNOTSUPP;
+                       mp_pr_err("%s: unsupported sb version %d", rc, 
mp->pds_name, omf_ver);
+                       kfree(sb);
+                       return rc;
+               } else if (!force && (omf_ver < OMF_SB_DESC_VER_LAST || 
resize)) {
+                       if ((flags & (1 << MP_FLAGS_PERMIT_META_CONV)) == 0) {
+                               struct omf_mdcver *mdcver;
+                               char *buf1, *buf2 = NULL;
+
+                               /*
+                                * We have to get the permission from users
+                                * to update mpool meta data
+                                */
+                               mdcver = omf_sbver_to_mdcver(omf_ver);
+                               ASSERT(mdcver != NULL);
+
+                               buf1 = kmalloc(2 * MAX_MDCVERSTR, GFP_KERNEL);
+                               if (buf1) {
+                                       buf2 = buf1 + MAX_MDCVERSTR;
+                                       omfu_mdcver_to_str(mdcver, buf1, 
sizeof(buf1));
+                                       omfu_mdcver_to_str(omfu_mdcver_cur(), 
buf2, sizeof(buf2));
+                               }
+
+                               rc = -EPERM;
+                               mp_pr_err("%s: reqd sb upgrade from version %s 
(%s) to %s (%s)",
+                                         rc, mp->pds_name,
+                                         buf1, omfu_mdcver_comment(mdcver) ?: 
"",
+                                         buf2, 
omfu_mdcver_comment(omfu_mdcver_cur()));
+                               kfree(buf1);
+                               kfree(sb);
+                               return rc;
+                       }
+
+                       /* We need to overwrite the old version superblock on 
the device */
+                       rc = sb_write_update(&pd->pdi_parm, sb);
+                       if (rc) {
+                               mp_pr_err("%s: pd %s, failed to convert or 
overwrite mpool sb",
+                                         rc, mp->pds_name, pd->pdi_name);
+                               kfree(sb);
+                               return rc;
+                       }
+
+                       if (!resize)
+                               mp_pr_info("%s: pd %s, Convert mpool sb, oldv 
%d newv %d",
+                                          mp->pds_name, pd->pdi_name, omf_ver, 
sb->osb_vers);
+               }
+
+               mpool_uuid_copy(&pd->pdi_devid, &sb->osb_parm.odp_devid);
+
+               /* Add drive in its media class. Create the media class if not 
yet created. */
+               rc = mpool_desc_pdmc_add(mp, pdh, NULL, false);
+               if (rc) {
+                       mp_pr_err("%s: pd %s, adding drive in a media class 
failed",
+                                 rc, mp->pds_name, pd->pdi_name);
+
+                       kfree(sb);
+                       return rc;
+               }
+
+               /*
+                * Record the media class used by the MDC0 metadata.
+                */
+               if (mdc0found)
+                       mp->pds_mdparm.md_mclass = pd->pdi_mclass;
+
+               if (resize && mc_resize)
+                       mc_resize[pd->pdi_mclass] = resize;
+       }
+
+       if (!mdc0found) {
+               rc = -EINVAL;
+               mp_pr_err("%s: MDC0 not found", rc, mp->pds_name);
+               kfree(sb);
+               return rc;
+       }
+
+       kfree(sb);
+
+       return 0;
+}
+
+static int comp_func(const void *c1, const void *c2)
+{
+       return strcmp(*(char **)c1, *(char **)c2);
+}
+
+int check_for_dups(char **listv, int cnt, int *dup, int *offset)
+{
+       const char **sortedv;
+       const char *prev;
+       int rc, i;
+
+       *dup = 0;
+       *offset = -1;
+
+       if (0 == cnt || 1 == cnt)
+               return 0;
+
+       sortedv = kcalloc(cnt + 1, sizeof(char *), GFP_KERNEL);
+       if (!sortedv) {
+               rc = -ENOMEM;
+               mp_pr_err("kcalloc failed for %d paths, first path %s", rc, 
cnt, *listv);
+               return rc;
+       }
+
+       /* Make a shallow copy */
+       for (i = 0; i < cnt; i++)
+               sortedv[i] = listv[i];
+
+       sortedv[i] = NULL;
+
+       sort(sortedv, cnt, sizeof(char *), comp_func, NULL);
+
+       prev = sortedv[0];
+       for (i = 1; i < cnt; i++) {
+               if (strcmp(sortedv[i], prev) == 0) {
+                       mp_pr_info("path %s is duplicated", prev);
+                       *dup = 1;
+                       break;
+               }
+
+               prev = sortedv[i];
+       }
+
+       /* Find offset, prev points to first dup */
+       if (*dup) {
+               for (i = 0; i < cnt; i++) {
+                       if (prev == listv[i]) {
+                               *offset = i;
+                               break;
+                       }
+               }
+       }
+
+       kfree(sortedv);
+       return 0;
+}
+
+void fill_in_devprops(struct mpool_descriptor *mp, u64 pdh, struct 
mpool_devprops *dprop)
+{
+       struct mpool_dev_info *pd;
+       struct media_class *mc;
+       int rc;
+
+       pd = &mp->pds_pdv[pdh];
+       memcpy(dprop->pdp_devid.b, pd->pdi_devid.uuid, MPOOL_UUID_SIZE);
+
+       mc = &mp->pds_mc[pd->pdi_mclass];
+       dprop->pdp_mclassp = mc->mc_parms.mcp_classp;
+       dprop->pdp_status  = mpool_pd_status_get(pd);
+
+       rc = smap_drive_usage(mp, pdh, dprop);
+       if (rc) {
+               mp_pr_err("mpool %s, can't get drive usage, media class %d",
+                         rc, mp->pds_name, dprop->pdp_mclassp);
+       }
+}
+
+int mpool_desc_unavail_add(struct mpool_descriptor *mp, struct 
omf_devparm_descriptor *omf_devparm)
+{
+       struct mpool_dev_info *pd = NULL;
+       char uuid_str[40];
+       int rc;
+
+       mpool_unparse_uuid(&omf_devparm->odp_devid, uuid_str);
+
+       mp_pr_warn("Activating mpool %s, adding unavailable drive %s", 
mp->pds_name, uuid_str);
+
+       if (mp->pds_pdvcnt >= MPOOL_DRIVES_MAX) {
+               rc = -EINVAL;
+               mp_pr_err("Activating mpool %s, adding an unavailable drive, 
too many drives",
+                         rc, mp->pds_name);
+               return rc;
+       }
+
+       pd = &mp->pds_pdv[mp->pds_pdvcnt];
+
+       mpool_uuid_copy(&pd->pdi_devid, &omf_devparm->odp_devid);
+
+       /* Update the PD properties from the metadata record. */
+       mpool_pd_status_set(pd, PD_STAT_UNAVAIL);
+       pd_dev_set_unavail(&pd->pdi_parm, omf_devparm);
+
+       /* Add the PD in its media class. */
+       rc = mpool_desc_pdmc_add(mp, mp->pds_pdvcnt, omf_devparm, false);
+       if (rc)
+               return rc;
+
+       mp->pds_pdvcnt = mp->pds_pdvcnt + 1;
+
+       return 0;
+}
+
+int mpool_create_rmlogs(struct mpool_descriptor *mp, u64 mlog_cap)
+{
+       struct mlog_descriptor *ml_desc;
+       struct mlog_capacity mlcap = {
+               .lcp_captgt = mlog_cap,
+       };
+       struct mlog_props mlprops;
+       u64 root_mlog_id[2];
+       int rc, i;
+
+       mlog_lookup_rootids(&root_mlog_id[0], &root_mlog_id[1]);
+
+       for (i = 0; i < 2; ++i) {
+               rc = mlog_find_get(mp, root_mlog_id[i], 1, NULL, &ml_desc);
+               if (!rc) {
+                       mlog_put(ml_desc);
+                       continue;
+               }
+
+               if (rc != -ENOENT) {
+                       mp_pr_err("mpool %s, root mlog find 0x%lx failed",
+                                 rc, mp->pds_name, (ulong)root_mlog_id[i]);
+                       return rc;
+               }
+
+               rc = mlog_realloc(mp, root_mlog_id[i], &mlcap,
+                                 MP_MED_CAPACITY, &mlprops, &ml_desc);
+               if (rc) {
+                       mp_pr_err("mpool %s, root mlog realloc 0x%lx failed",
+                                 rc, mp->pds_name, (ulong)root_mlog_id[i]);
+                       return rc;
+               }
+
+               if (mlprops.lpr_objid != root_mlog_id[i]) {
+                       mlog_put(ml_desc);
+                       rc = -ENOENT;
+                       mp_pr_err("mpool %s, root mlog mismatch 0x%lx 0x%lx", 
rc,
+                                 mp->pds_name, (ulong)root_mlog_id[i], 
(ulong)mlprops.lpr_objid);
+                       return rc;
+               }
+
+               rc = mlog_commit(mp, ml_desc);
+               if (rc) {
+                       if (mlog_abort(mp, ml_desc))
+                               mlog_put(ml_desc);
+
+                       mp_pr_err("mpool %s, root mlog commit 0x%lx failed",
+                                 rc, mp->pds_name, (ulong)root_mlog_id[i]);
+                       return rc;
+               }
+
+               mlog_put(ml_desc);
+       }
+
+       return rc;
+}
+
+struct mpool_descriptor *mpool_desc_alloc(void)
+{
+       struct mpool_descriptor *mp;
+       int i;
+
+       mp = kzalloc(sizeof(*mp), GFP_KERNEL);
+       if (!mp)
+               return NULL;
+
+       init_rwsem(&mp->pds_pdvlock);
+
+       mutex_init(&mp->pds_oml_lock);
+       mp->pds_oml_root = RB_ROOT;
+
+       mp->pds_mdparm.md_mclass = MP_MED_INVALID;
+
+       mpcore_params_defaults(&mp->pds_params);
+
+       for (i = 0; i < MP_MED_NUMBER; i++)
+               mp->pds_mc[i].mc_pdmc = -1;
+
+       return mp;
+}
+
+/*
+ * remove mp from mpool_pools; close all dev; dealloc mp.
+ */
+void mpool_desc_free(struct mpool_descriptor *mp)
+{
+       struct mpool_descriptor *found_mp = NULL;
+       struct mpool_uuid uuid_zero;
+       int i;
+
+       mpool_uuid_clear(&uuid_zero);
+
+       /*
+        * Handle case where poolid and devid not in mappings
+        * which can happen when cleaning up from failed create/open.
+        */
+       found_mp = uuid_to_mpdesc_search(&mpool_pools, &mp->pds_poolid);
+       if (found_mp)
+               rb_erase(&found_mp->pds_node, &mpool_pools);
+
+       for (i = 0; i < mp->pds_pdvcnt; i++) {
+               if (mpool_pd_status_get(&mp->pds_pdv[i]) != PD_STAT_UNAVAIL)
+                       pd_dev_close(&mp->pds_pdv[i].pdi_parm);
+       }
+
+       kfree(mp);
+}
-- 
2.17.2

Reply via email to