CONFIGFS_ATTR(nvmet_ns_, device_nguid);
 
+static ssize_t nvmet_ns_ana_grpid_show(struct config_item *item, char *page)
+{
+       return sprintf(page, "%u\n", to_nvmet_ns(item)->anagrpid);
+}
+
+static ssize_t nvmet_ns_ana_grpid_store(struct config_item *item,
+               const char *page, size_t count)
+{
+       struct nvmet_ns *ns = to_nvmet_ns(item);
+       u32 oldgrpid, newgrpid;
+       int ret;
+
+       ret = kstrtou32(page, 0, &newgrpid);
+       if (ret)
+               return ret;
+
+       if (newgrpid < 1 || newgrpid > NVMET_MAX_ANAGRPS)
+               return -EINVAL;
+
+       down_write(&nvmet_ana_sem);
+       oldgrpid = ns->anagrpid;
+       nvmet_ana_group_enabled[newgrpid]++;
+       ns->anagrpid = newgrpid;
+       nvmet_ana_group_enabled[oldgrpid]--;
+       nvmet_ana_chgcnt++;
+       up_write(&nvmet_ana_sem);
+
+       nvmet_send_ana_event(ns->subsys, NULL);
+       return count;
+}
+
+CONFIGFS_ATTR(nvmet_ns_, ana_grpid);
+
 static ssize_t nvmet_ns_enable_show(struct config_item *item, char *page)
 {
        return sprintf(page, "%d\n", to_nvmet_ns(item)->enabled);
        &nvmet_ns_attr_device_path,
        &nvmet_ns_attr_device_nguid,
        &nvmet_ns_attr_device_uuid,
+       &nvmet_ns_attr_ana_grpid,
        &nvmet_ns_attr_enable,
        &nvmet_ns_attr_buffered_io,
        NULL,
        .ct_group_ops   = &nvmet_referral_group_ops,
 };
 
+static struct {
+       enum nvme_ana_state     state;
+       const char              *name;
+} nvmet_ana_state_names[] = {
+       { NVME_ANA_OPTIMIZED,           "optimized" },
+       { NVME_ANA_NONOPTIMIZED,        "non-optimized" },
+       { NVME_ANA_INACCESSIBLE,        "inaccessible" },
+       { NVME_ANA_PERSISTENT_LOSS,     "persistent-loss" },
+       { NVME_ANA_CHANGE,              "change" },
+};
+
+static ssize_t nvmet_ana_group_ana_state_show(struct config_item *item,
+               char *page)
+{
+       struct nvmet_ana_group *grp = to_ana_group(item);
+       enum nvme_ana_state state = grp->port->ana_state[grp->grpid];
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(nvmet_ana_state_names); i++) {
+               if (state != nvmet_ana_state_names[i].state)
+                       continue;
+               return sprintf(page, "%s\n", nvmet_ana_state_names[i].name);
+       }
+
+       return sprintf(page, "\n");
+}
+
+static ssize_t nvmet_ana_group_ana_state_store(struct config_item *item,
+               const char *page, size_t count)
+{
+       struct nvmet_ana_group *grp = to_ana_group(item);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(nvmet_ana_state_names); i++) {
+               if (sysfs_streq(page, nvmet_ana_state_names[i].name))
+                       goto found;
+       }
+
+       pr_err("Invalid value '%s' for ana_state\n", page);
+       return -EINVAL;
+
+found:
+       down_write(&nvmet_ana_sem);
+       grp->port->ana_state[grp->grpid] = nvmet_ana_state_names[i].state;
+       nvmet_ana_chgcnt++;
+       up_write(&nvmet_ana_sem);
+
+       nvmet_port_send_ana_event(grp->port);
+       return count;
+}
+
+CONFIGFS_ATTR(nvmet_ana_group_, ana_state);
+
+static struct configfs_attribute *nvmet_ana_group_attrs[] = {
+       &nvmet_ana_group_attr_ana_state,
+       NULL,
+};
+
+static void nvmet_ana_group_release(struct config_item *item)
+{
+       struct nvmet_ana_group *grp = to_ana_group(item);
+
+       if (grp == &grp->port->ana_default_group)
+               return;
+
+       down_write(&nvmet_ana_sem);
+       grp->port->ana_state[grp->grpid] = NVME_ANA_INACCESSIBLE;
+       nvmet_ana_group_enabled[grp->grpid]--;
+       up_write(&nvmet_ana_sem);
+
+       nvmet_port_send_ana_event(grp->port);
+       kfree(grp);
+}
+
+static struct configfs_item_operations nvmet_ana_group_item_ops = {
+       .release                = nvmet_ana_group_release,
+};
+
+static const struct config_item_type nvmet_ana_group_type = {
+       .ct_item_ops            = &nvmet_ana_group_item_ops,
+       .ct_attrs               = nvmet_ana_group_attrs,
+       .ct_owner               = THIS_MODULE,
+};
+
+static struct config_group *nvmet_ana_groups_make_group(
+               struct config_group *group, const char *name)
+{
+       struct nvmet_port *port = ana_groups_to_port(&group->cg_item);
+       struct nvmet_ana_group *grp;
+       u32 grpid;
+       int ret;
+
+       ret = kstrtou32(name, 0, &grpid);
+       if (ret)
+               goto out;
+
+       ret = -EINVAL;
+       if (grpid <= 1 || grpid > NVMET_MAX_ANAGRPS)
+               goto out;
+
+       ret = -ENOMEM;
+       grp = kzalloc(sizeof(*grp), GFP_KERNEL);
+       if (!grp)
+               goto out;
+       grp->port = port;
+       grp->grpid = grpid;
+
+       down_write(&nvmet_ana_sem);
+       nvmet_ana_group_enabled[grpid]++;
+       up_write(&nvmet_ana_sem);
+
+       nvmet_port_send_ana_event(grp->port);
+
+       config_group_init_type_name(&grp->group, name, &nvmet_ana_group_type);
+       return &grp->group;
+out:
+       return ERR_PTR(ret);
+}
+
+static struct configfs_group_operations nvmet_ana_groups_group_ops = {
+       .make_group             = nvmet_ana_groups_make_group,
+};
+
+static const struct config_item_type nvmet_ana_groups_type = {
+       .ct_group_ops           = &nvmet_ana_groups_group_ops,
+       .ct_owner               = THIS_MODULE,
+};
+
 /*
  * Ports definitions.
  */
 {
        struct nvmet_port *port;
        u16 portid;
+       u32 i;
 
        if (kstrtou16(name, 0, &portid))
                return ERR_PTR(-EINVAL);
                return ERR_PTR(-ENOMEM);
        }
 
-       port->ana_state[NVMET_DEFAULT_ANA_GRPID] = NVME_ANA_OPTIMIZED;
+       for (i = 1; i <= NVMET_MAX_ANAGRPS; i++) {
+               if (i == NVMET_DEFAULT_ANA_GRPID)
+                       port->ana_state[1] = NVME_ANA_OPTIMIZED;
+               else
+                       port->ana_state[i] = NVME_ANA_INACCESSIBLE;
+       }
 
        INIT_LIST_HEAD(&port->entry);
        INIT_LIST_HEAD(&port->subsystems);
                        "referrals", &nvmet_referrals_type);
        configfs_add_default_group(&port->referrals_group, &port->group);
 
+       config_group_init_type_name(&port->ana_groups_group,
+                       "ana_groups", &nvmet_ana_groups_type);
+       configfs_add_default_group(&port->ana_groups_group, &port->group);
+
+       port->ana_default_group.port = port;
+       port->ana_default_group.grpid = NVMET_DEFAULT_ANA_GRPID;
+       config_group_init_type_name(&port->ana_default_group.group,
+                       __stringify(NVMET_DEFAULT_ANA_GRPID),
+                       &nvmet_ana_group_type);
+       configfs_add_default_group(&port->ana_default_group.group,
+                       &port->ana_groups_group);
+
        return &port->group;
 }
 
 
 #define NVMET_ASYNC_EVENTS             4
 #define NVMET_ERROR_LOG_SLOTS          128
 
-
 /*
  * Supported optional AENs:
  */
 #define NVMET_AEN_CFG_OPTIONAL \
-       NVME_AEN_CFG_NS_ATTR
+       (NVME_AEN_CFG_NS_ATTR | NVME_AEN_CFG_ANA_CHANGE)
 
 /*
  * Plus mandatory SMART AENs (we'll never send them, but allow enabling them):
        struct completion       confirm_done;
 };
 
+struct nvmet_ana_group {
+       struct config_group     group;
+       struct nvmet_port       *port;
+       u32                     grpid;
+};
+
+static inline struct nvmet_ana_group *to_ana_group(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct nvmet_ana_group,
+                       group);
+}
+
 /**
  * struct nvmet_port - Common structure to keep port
  *                             information for the target.
        struct list_head                subsystems;
        struct config_group             referrals_group;
        struct list_head                referrals;
+       struct config_group             ana_groups_group;
+       struct nvmet_ana_group          ana_default_group;
        enum nvme_ana_state             *ana_state;
        void                            *priv;
        bool                            enabled;
                        group);
 }
 
+static inline struct nvmet_port *ana_groups_to_port(
+               struct config_item *item)
+{
+       return container_of(to_config_group(item), struct nvmet_port,
+                       ana_groups_group);
+}
+
 struct nvmet_ctrl {
        struct nvmet_subsys     *subsys;
        struct nvmet_cq         **cqs;
 struct nvmet_ns *nvmet_ns_alloc(struct nvmet_subsys *subsys, u32 nsid);
 void nvmet_ns_free(struct nvmet_ns *ns);
 
+void nvmet_send_ana_event(struct nvmet_subsys *subsys,
+               struct nvmet_port *port);
+void nvmet_port_send_ana_event(struct nvmet_port *port);
+
 int nvmet_register_transport(const struct nvmet_fabrics_ops *ops);
 void nvmet_unregister_transport(const struct nvmet_fabrics_ops *ops);
 
  * ANA Group 1 exists without manual intervention, has namespaces assigned to it
  * by default, and is available in an optimized state through all ports.
  */
-#define NVMET_MAX_ANAGRPS      1
+#define NVMET_MAX_ANAGRPS      128
 #define NVMET_DEFAULT_ANA_GRPID        1
 
 #define NVMET_KAS              10