xref: /spdk/lib/nvmf/subsystem.c (revision b43c2605f17c995370bc9629019bc73dc4324d45)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2016 Intel Corporation. All rights reserved.
365757521SEvgeniy Kochetov  *   Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved.
497385af1SAlexey Marchuk  *   Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5f8296a99SDaniel Verkamp  */
6f8296a99SDaniel Verkamp 
7b961d9ccSBen Walker #include "spdk/stdinc.h"
8f8296a99SDaniel Verkamp 
9f8296a99SDaniel Verkamp #include "nvmf_internal.h"
10e3d9e24eSDaniel Verkamp #include "transport.h"
111e6ffa03SBen Walker 
12d2b9b2afSShuhei Matsumoto #include "spdk/assert.h"
1314451d76SDaniel Verkamp #include "spdk/likely.h"
14f8296a99SDaniel Verkamp #include "spdk/string.h"
15f8296a99SDaniel Verkamp #include "spdk/trace.h"
16f8296a99SDaniel Verkamp #include "spdk/nvmf_spec.h"
174c06ce9bSDaniel Verkamp #include "spdk/uuid.h"
18196d4f70SChangpeng Liu #include "spdk/json.h"
191edc5f00SChangpeng Liu #include "spdk/file.h"
20a4d132cdSJim Harris #include "spdk/bit_array.h"
21a36785dfSDennis Maisenbacher #include "spdk/bdev.h"
22f8296a99SDaniel Verkamp 
23fa796437SJim Harris #define __SPDK_BDEV_MODULE_ONLY
24bcff4c89SBen Walker #include "spdk/bdev_module.h"
254e8e97c8STomasz Zawadzki #include "spdk/log.h"
26a1a47b55SSeth Howell #include "spdk_internal/utf.h"
273a0f6244SJim Harris #include "spdk_internal/usdt.h"
28d27b24c9SDaniel Verkamp 
2914032a98SGregory Shapiro #define MODEL_NUMBER_DEFAULT "SPDK bdev Controller"
30af07b82fSBen Walker #define NVMF_SUBSYSTEM_DEFAULT_NAMESPACES 32
3114032a98SGregory Shapiro 
32b21fad1aSSeth Howell /*
33b21fad1aSSeth Howell  * States for parsing valid domains in NQNs according to RFC 1034
34b21fad1aSSeth Howell  */
35b21fad1aSSeth Howell enum spdk_nvmf_nqn_domain_states {
36b21fad1aSSeth Howell 	/* First character of a domain must be a letter */
37b21fad1aSSeth Howell 	SPDK_NVMF_DOMAIN_ACCEPT_LETTER = 0,
38b21fad1aSSeth Howell 
39b21fad1aSSeth Howell 	/* Subsequent characters can be any of letter, digit, or hyphen */
40b21fad1aSSeth Howell 	SPDK_NVMF_DOMAIN_ACCEPT_LDH = 1,
41b21fad1aSSeth Howell 
42b21fad1aSSeth Howell 	/* A domain label must end with either a letter or digit */
43b21fad1aSSeth Howell 	SPDK_NVMF_DOMAIN_ACCEPT_ANY = 2
44b21fad1aSSeth Howell };
45b21fad1aSSeth Howell 
4697385af1SAlexey Marchuk static int _nvmf_subsystem_destroy(struct spdk_nvmf_subsystem *subsystem);
4797385af1SAlexey Marchuk 
488cad9604SDaniel Verkamp /* Returns true if is a valid ASCII string as defined by the NVMe spec */
498cad9604SDaniel Verkamp static bool
5063c90491SSeth Howell nvmf_valid_ascii_string(const void *buf, size_t size)
518cad9604SDaniel Verkamp {
528cad9604SDaniel Verkamp 	const uint8_t *str = buf;
538cad9604SDaniel Verkamp 	size_t i;
548cad9604SDaniel Verkamp 
558cad9604SDaniel Verkamp 	for (i = 0; i < size; i++) {
568cad9604SDaniel Verkamp 		if (str[i] < 0x20 || str[i] > 0x7E) {
578cad9604SDaniel Verkamp 			return false;
588cad9604SDaniel Verkamp 		}
598cad9604SDaniel Verkamp 	}
608cad9604SDaniel Verkamp 
618cad9604SDaniel Verkamp 	return true;
628cad9604SDaniel Verkamp }
638cad9604SDaniel Verkamp 
64552f6517SKonrad Sztyber bool
65552f6517SKonrad Sztyber nvmf_nqn_is_valid(const char *nqn)
66c04b2968SDaniel Verkamp {
67c04b2968SDaniel Verkamp 	size_t len;
684c06ce9bSDaniel Verkamp 	struct spdk_uuid uuid_value;
699689e6ccSDaniel Verkamp 	uint32_t i;
70a1a47b55SSeth Howell 	int bytes_consumed;
719689e6ccSDaniel Verkamp 	uint32_t domain_label_length;
72b21fad1aSSeth Howell 	char *reverse_domain_end;
739689e6ccSDaniel Verkamp 	uint32_t reverse_domain_end_index;
74b21fad1aSSeth Howell 	enum spdk_nvmf_nqn_domain_states domain_state = SPDK_NVMF_DOMAIN_ACCEPT_LETTER;
75c04b2968SDaniel Verkamp 
76b21fad1aSSeth Howell 	/* Check for length requirements */
77c04b2968SDaniel Verkamp 	len = strlen(nqn);
785f3841f0SDaniel Verkamp 	if (len > SPDK_NVMF_NQN_MAX_LEN) {
795f3841f0SDaniel Verkamp 		SPDK_ERRLOG("Invalid NQN \"%s\": length %zu > max %d\n", nqn, len, SPDK_NVMF_NQN_MAX_LEN);
80c04b2968SDaniel Verkamp 		return false;
81c04b2968SDaniel Verkamp 	}
82c04b2968SDaniel Verkamp 
83b21fad1aSSeth Howell 	/* The nqn must be at least as long as SPDK_NVMF_NQN_MIN_LEN to contain the necessary prefix. */
84b21fad1aSSeth Howell 	if (len < SPDK_NVMF_NQN_MIN_LEN) {
85b21fad1aSSeth Howell 		SPDK_ERRLOG("Invalid NQN \"%s\": length %zu < min %d\n", nqn, len, SPDK_NVMF_NQN_MIN_LEN);
86b21fad1aSSeth Howell 		return false;
87b21fad1aSSeth Howell 	}
88b21fad1aSSeth Howell 
89b21fad1aSSeth Howell 	/* Check for discovery controller nqn */
90b21fad1aSSeth Howell 	if (!strcmp(nqn, SPDK_NVMF_DISCOVERY_NQN)) {
91b21fad1aSSeth Howell 		return true;
92b21fad1aSSeth Howell 	}
93b21fad1aSSeth Howell 
94b21fad1aSSeth Howell 	/* Check for equality with the generic nqn structure of the form "nqn.2014-08.org.nvmexpress:uuid:11111111-2222-3333-4444-555555555555" */
95b21fad1aSSeth Howell 	if (!strncmp(nqn, SPDK_NVMF_NQN_UUID_PRE, SPDK_NVMF_NQN_UUID_PRE_LEN)) {
96b21fad1aSSeth Howell 		if (len != SPDK_NVMF_NQN_UUID_PRE_LEN + SPDK_NVMF_UUID_STRING_LEN) {
97b21fad1aSSeth Howell 			SPDK_ERRLOG("Invalid NQN \"%s\": uuid is not the correct length\n", nqn);
98b21fad1aSSeth Howell 			return false;
99b21fad1aSSeth Howell 		}
100b21fad1aSSeth Howell 
1014c06ce9bSDaniel Verkamp 		if (spdk_uuid_parse(&uuid_value, &nqn[SPDK_NVMF_NQN_UUID_PRE_LEN])) {
102b21fad1aSSeth Howell 			SPDK_ERRLOG("Invalid NQN \"%s\": uuid is not formatted correctly\n", nqn);
103b21fad1aSSeth Howell 			return false;
104b21fad1aSSeth Howell 		}
105b21fad1aSSeth Howell 		return true;
106b21fad1aSSeth Howell 	}
107b21fad1aSSeth Howell 
108b21fad1aSSeth Howell 	/* If the nqn does not match the uuid structure, the next several checks validate the form "nqn.yyyy-mm.reverse.domain:user-string" */
109b21fad1aSSeth Howell 
110df70bc15SDaniel Verkamp 	if (strncmp(nqn, "nqn.", 4) != 0) {
111c04b2968SDaniel Verkamp 		SPDK_ERRLOG("Invalid NQN \"%s\": NQN must begin with \"nqn.\".\n", nqn);
112c04b2968SDaniel Verkamp 		return false;
113c04b2968SDaniel Verkamp 	}
114c04b2968SDaniel Verkamp 
115b21fad1aSSeth Howell 	/* Check for yyyy-mm. */
116c04b2968SDaniel Verkamp 	if (!(isdigit(nqn[4]) && isdigit(nqn[5]) && isdigit(nqn[6]) && isdigit(nqn[7]) &&
117c04b2968SDaniel Verkamp 	      nqn[8] == '-' && isdigit(nqn[9]) && isdigit(nqn[10]) && nqn[11] == '.')) {
118c04b2968SDaniel Verkamp 		SPDK_ERRLOG("Invalid date code in NQN \"%s\"\n", nqn);
119c04b2968SDaniel Verkamp 		return false;
120c04b2968SDaniel Verkamp 	}
121c04b2968SDaniel Verkamp 
122b21fad1aSSeth Howell 	reverse_domain_end = strchr(nqn, ':');
123b21fad1aSSeth Howell 	if (reverse_domain_end != NULL && (reverse_domain_end_index = reverse_domain_end - nqn) < len - 1) {
124b21fad1aSSeth Howell 	} else {
125b21fad1aSSeth Howell 		SPDK_ERRLOG("Invalid NQN \"%s\". NQN must contain user specified name with a ':' as a prefix.\n",
126b21fad1aSSeth Howell 			    nqn);
127b21fad1aSSeth Howell 		return false;
128b21fad1aSSeth Howell 	}
129b21fad1aSSeth Howell 
130b21fad1aSSeth Howell 	/* Check for valid reverse domain */
131b21fad1aSSeth Howell 	domain_label_length = 0;
132b21fad1aSSeth Howell 	for (i = 12; i < reverse_domain_end_index; i++) {
133b21fad1aSSeth Howell 		if (domain_label_length > SPDK_DOMAIN_LABEL_MAX_LEN) {
134b21fad1aSSeth Howell 			SPDK_ERRLOG("Invalid domain name in NQN \"%s\". At least one Label is too long.\n", nqn);
135b21fad1aSSeth Howell 			return false;
136b21fad1aSSeth Howell 		}
137b21fad1aSSeth Howell 
138b21fad1aSSeth Howell 		switch (domain_state) {
139b21fad1aSSeth Howell 
140b21fad1aSSeth Howell 		case SPDK_NVMF_DOMAIN_ACCEPT_LETTER: {
141b21fad1aSSeth Howell 			if (isalpha(nqn[i])) {
142b21fad1aSSeth Howell 				domain_state = SPDK_NVMF_DOMAIN_ACCEPT_ANY;
143b21fad1aSSeth Howell 				domain_label_length++;
144b21fad1aSSeth Howell 				break;
145b21fad1aSSeth Howell 			} else {
146b21fad1aSSeth Howell 				SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must start with a letter.\n", nqn);
147b21fad1aSSeth Howell 				return false;
148b21fad1aSSeth Howell 			}
149b21fad1aSSeth Howell 		}
150b21fad1aSSeth Howell 
151b21fad1aSSeth Howell 		case SPDK_NVMF_DOMAIN_ACCEPT_LDH: {
152b21fad1aSSeth Howell 			if (isalpha(nqn[i]) || isdigit(nqn[i])) {
153b21fad1aSSeth Howell 				domain_state = SPDK_NVMF_DOMAIN_ACCEPT_ANY;
154b21fad1aSSeth Howell 				domain_label_length++;
155b21fad1aSSeth Howell 				break;
156b21fad1aSSeth Howell 			} else if (nqn[i] == '-') {
157b21fad1aSSeth Howell 				if (i == reverse_domain_end_index - 1) {
158b21fad1aSSeth Howell 					SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must end with an alphanumeric symbol.\n",
159b21fad1aSSeth Howell 						    nqn);
160b21fad1aSSeth Howell 					return false;
161b21fad1aSSeth Howell 				}
162b21fad1aSSeth Howell 				domain_state = SPDK_NVMF_DOMAIN_ACCEPT_LDH;
163b21fad1aSSeth Howell 				domain_label_length++;
164b21fad1aSSeth Howell 				break;
165b21fad1aSSeth Howell 			} else if (nqn[i] == '.') {
166b21fad1aSSeth Howell 				SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must end with an alphanumeric symbol.\n",
167b21fad1aSSeth Howell 					    nqn);
168b21fad1aSSeth Howell 				return false;
169b21fad1aSSeth Howell 			} else {
170b21fad1aSSeth Howell 				SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must contain only [a-z,A-Z,0-9,'-','.'].\n",
171b21fad1aSSeth Howell 					    nqn);
172b21fad1aSSeth Howell 				return false;
173b21fad1aSSeth Howell 			}
174b21fad1aSSeth Howell 		}
175b21fad1aSSeth Howell 
176b21fad1aSSeth Howell 		case SPDK_NVMF_DOMAIN_ACCEPT_ANY: {
177b21fad1aSSeth Howell 			if (isalpha(nqn[i]) || isdigit(nqn[i])) {
178b21fad1aSSeth Howell 				domain_state = SPDK_NVMF_DOMAIN_ACCEPT_ANY;
179b21fad1aSSeth Howell 				domain_label_length++;
180b21fad1aSSeth Howell 				break;
181b21fad1aSSeth Howell 			} else if (nqn[i] == '-') {
182b21fad1aSSeth Howell 				if (i == reverse_domain_end_index - 1) {
183b21fad1aSSeth Howell 					SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must end with an alphanumeric symbol.\n",
184b21fad1aSSeth Howell 						    nqn);
185b21fad1aSSeth Howell 					return false;
186b21fad1aSSeth Howell 				}
187b21fad1aSSeth Howell 				domain_state = SPDK_NVMF_DOMAIN_ACCEPT_LDH;
188b21fad1aSSeth Howell 				domain_label_length++;
189b21fad1aSSeth Howell 				break;
190b21fad1aSSeth Howell 			} else if (nqn[i] == '.') {
191b21fad1aSSeth Howell 				domain_state = SPDK_NVMF_DOMAIN_ACCEPT_LETTER;
192b21fad1aSSeth Howell 				domain_label_length = 0;
193b21fad1aSSeth Howell 				break;
194b21fad1aSSeth Howell 			} else {
195b21fad1aSSeth Howell 				SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must contain only [a-z,A-Z,0-9,'-','.'].\n",
196b21fad1aSSeth Howell 					    nqn);
197b21fad1aSSeth Howell 				return false;
198b21fad1aSSeth Howell 			}
199b21fad1aSSeth Howell 		}
200b21fad1aSSeth Howell 		}
201b21fad1aSSeth Howell 	}
202a1a47b55SSeth Howell 
203a1a47b55SSeth Howell 	i = reverse_domain_end_index + 1;
204a1a47b55SSeth Howell 	while (i < len) {
205a1a47b55SSeth Howell 		bytes_consumed = utf8_valid(&nqn[i], &nqn[len]);
206a1a47b55SSeth Howell 		if (bytes_consumed <= 0) {
207a1a47b55SSeth Howell 			SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must contain only valid utf-8.\n", nqn);
208a1a47b55SSeth Howell 			return false;
209a1a47b55SSeth Howell 		}
210a1a47b55SSeth Howell 
211a1a47b55SSeth Howell 		i += bytes_consumed;
212a1a47b55SSeth Howell 	}
213c04b2968SDaniel Verkamp 	return true;
214c04b2968SDaniel Verkamp }
215c04b2968SDaniel Verkamp 
2161e337a1eSSeth Howell static void subsystem_state_change_on_pg(struct spdk_io_channel_iter *i);
2171e337a1eSSeth Howell 
218f8296a99SDaniel Verkamp struct spdk_nvmf_subsystem *
21995ac75aaSBen Walker spdk_nvmf_subsystem_create(struct spdk_nvmf_tgt *tgt,
2204addb5c8SBen Walker 			   const char *nqn,
2213cbed2edSBen Walker 			   enum spdk_nvmf_subtype type,
2227b3c6fbdSBen Walker 			   uint32_t num_ns)
223f8296a99SDaniel Verkamp {
224f8296a99SDaniel Verkamp 	struct spdk_nvmf_subsystem	*subsystem;
225ea134c5cSBen Walker 	uint32_t			sid;
226f8296a99SDaniel Verkamp 
227524e8fc9SDaniel Verkamp 	if (spdk_nvmf_tgt_find_subsystem(tgt, nqn)) {
228524e8fc9SDaniel Verkamp 		SPDK_ERRLOG("Subsystem NQN '%s' already exists\n", nqn);
229524e8fc9SDaniel Verkamp 		return NULL;
230524e8fc9SDaniel Verkamp 	}
231524e8fc9SDaniel Verkamp 
232552f6517SKonrad Sztyber 	if (!nvmf_nqn_is_valid(nqn)) {
2336e4e6cb3SAbhineet Pandey 		SPDK_ERRLOG("Subsystem NQN '%s' is invalid\n", nqn);
234c04b2968SDaniel Verkamp 		return NULL;
235c04b2968SDaniel Verkamp 	}
236c04b2968SDaniel Verkamp 
2377efdf905SSlawomir Ptak 	if (type == SPDK_NVMF_SUBTYPE_DISCOVERY_CURRENT ||
2387efdf905SSlawomir Ptak 	    type == SPDK_NVMF_SUBTYPE_DISCOVERY) {
239af07b82fSBen Walker 		if (num_ns != 0) {
24002f088bbSDaniel Verkamp 			SPDK_ERRLOG("Discovery subsystem cannot have namespaces.\n");
24102f088bbSDaniel Verkamp 			return NULL;
24202f088bbSDaniel Verkamp 		}
243af07b82fSBen Walker 	} else if (num_ns == 0) {
244af07b82fSBen Walker 		num_ns = NVMF_SUBSYSTEM_DEFAULT_NAMESPACES;
245af07b82fSBen Walker 	}
24602f088bbSDaniel Verkamp 
247ea134c5cSBen Walker 	/* Find a free subsystem id (sid) */
248779059a2SJim Harris 	sid = spdk_bit_array_find_first_clear(tgt->subsystem_ids, 0);
249779059a2SJim Harris 	if (sid == UINT32_MAX) {
2506e4e6cb3SAbhineet Pandey 		SPDK_ERRLOG("No free subsystem IDs are available for subsystem creation\n");
251ea134c5cSBen Walker 		return NULL;
252ea134c5cSBen Walker 	}
253f8296a99SDaniel Verkamp 	subsystem = calloc(1, sizeof(struct spdk_nvmf_subsystem));
254f8296a99SDaniel Verkamp 	if (subsystem == NULL) {
2556e4e6cb3SAbhineet Pandey 		SPDK_ERRLOG("Subsystem memory allocation failed\n");
256f8296a99SDaniel Verkamp 		return NULL;
257f8296a99SDaniel Verkamp 	}
258f8296a99SDaniel Verkamp 
2597346be69SZiye Yang 	subsystem->thread = spdk_get_thread();
26095ac75aaSBen Walker 	subsystem->state = SPDK_NVMF_SUBSYSTEM_INACTIVE;
2614addb5c8SBen Walker 	subsystem->tgt = tgt;
262ea134c5cSBen Walker 	subsystem->id = sid;
2633cbed2edSBen Walker 	subsystem->subtype = type;
26402f088bbSDaniel Verkamp 	subsystem->max_nsid = num_ns;
265ef14e9b9SAbhineet Pandey 	subsystem->next_cntlid = 1;
266be6a01efSJonathan Teh 	subsystem->min_cntlid = NVMF_MIN_CNTLID;
267be6a01efSJonathan Teh 	subsystem->max_cntlid = NVMF_MAX_CNTLID;
2683cbed2edSBen Walker 	snprintf(subsystem->subnqn, sizeof(subsystem->subnqn), "%s", nqn);
269d67119d8SBen Walker 	pthread_mutex_init(&subsystem->mutex, NULL);
270683d4a4cSBen Walker 	TAILQ_INIT(&subsystem->listeners);
2712b9d85c4SBen Walker 	TAILQ_INIT(&subsystem->hosts);
27203788f93SBen Walker 	TAILQ_INIT(&subsystem->ctrlrs);
2732881f7f6SKonrad Sztyber 	TAILQ_INIT(&subsystem->state_changes);
274a4d132cdSJim Harris 	subsystem->used_listener_ids = spdk_bit_array_create(NVMF_MAX_LISTENERS_PER_SUBSYSTEM);
275a4d132cdSJim Harris 	if (subsystem->used_listener_ids == NULL) {
276a4d132cdSJim Harris 		pthread_mutex_destroy(&subsystem->mutex);
277a4d132cdSJim Harris 		free(subsystem);
2786e4e6cb3SAbhineet Pandey 		SPDK_ERRLOG("Listener id array memory allocation failed\n");
279a4d132cdSJim Harris 		return NULL;
280a4d132cdSJim Harris 	}
281d6a499feSBen Walker 
28202f088bbSDaniel Verkamp 	if (num_ns != 0) {
2838af4b6c4SDaniel Verkamp 		subsystem->ns = calloc(num_ns, sizeof(struct spdk_nvmf_ns *));
28402f088bbSDaniel Verkamp 		if (subsystem->ns == NULL) {
28502f088bbSDaniel Verkamp 			SPDK_ERRLOG("Namespace memory allocation failed\n");
2862475faffSGangCao 			pthread_mutex_destroy(&subsystem->mutex);
287a4d132cdSJim Harris 			spdk_bit_array_free(&subsystem->used_listener_ids);
28802f088bbSDaniel Verkamp 			free(subsystem);
28902f088bbSDaniel Verkamp 			return NULL;
29002f088bbSDaniel Verkamp 		}
29107bfc3cbSShuhei Matsumoto 		subsystem->ana_group = calloc(num_ns, sizeof(uint32_t));
29207bfc3cbSShuhei Matsumoto 		if (subsystem->ana_group == NULL) {
29307bfc3cbSShuhei Matsumoto 			SPDK_ERRLOG("ANA group memory allocation failed\n");
29407bfc3cbSShuhei Matsumoto 			pthread_mutex_destroy(&subsystem->mutex);
29507bfc3cbSShuhei Matsumoto 			free(subsystem->ns);
296a4d132cdSJim Harris 			spdk_bit_array_free(&subsystem->used_listener_ids);
29707bfc3cbSShuhei Matsumoto 			free(subsystem);
29807bfc3cbSShuhei Matsumoto 			return NULL;
29907bfc3cbSShuhei Matsumoto 		}
30002f088bbSDaniel Verkamp 	}
30102f088bbSDaniel Verkamp 
3022eacfd87SBen Walker 	memset(subsystem->sn, '0', sizeof(subsystem->sn) - 1);
303ca44fd69SJim Harris 	subsystem->sn[sizeof(subsystem->sn) - 1] = '\0';
3042eacfd87SBen Walker 
30514032a98SGregory Shapiro 	snprintf(subsystem->mn, sizeof(subsystem->mn), "%s",
30614032a98SGregory Shapiro 		 MODEL_NUMBER_DEFAULT);
30714032a98SGregory Shapiro 
308779059a2SJim Harris 	spdk_bit_array_set(tgt->subsystem_ids, sid);
3090aef7f2aSJim Harris 	RB_INSERT(subsystem_tree, &tgt->subsystems, subsystem);
310f8296a99SDaniel Verkamp 
311d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE1(nvmf_subsystem_create, subsystem->subnqn);
312d11601e8SKrzysztof Karas 
313f8296a99SDaniel Verkamp 	return subsystem;
314f8296a99SDaniel Verkamp }
315f8296a99SDaniel Verkamp 
316e8841656SKonrad Sztyber static void
317e8841656SKonrad Sztyber nvmf_host_free(struct spdk_nvmf_host *host)
318e8841656SKonrad Sztyber {
319e8841656SKonrad Sztyber 	spdk_keyring_put_key(host->dhchap_key);
320aa13730dSKonrad Sztyber 	spdk_keyring_put_key(host->dhchap_ctrlr_key);
321e8841656SKonrad Sztyber 	free(host);
322e8841656SKonrad Sztyber }
323e8841656SKonrad Sztyber 
324d67119d8SBen Walker /* Must hold subsystem->mutex while calling this function */
3256285e36eSBen Walker static void
32661d85773SSeth Howell nvmf_subsystem_remove_host(struct spdk_nvmf_subsystem *subsystem, struct spdk_nvmf_host *host)
3276285e36eSBen Walker {
3286285e36eSBen Walker 	TAILQ_REMOVE(&subsystem->hosts, host, link);
329e8841656SKonrad Sztyber 	nvmf_host_free(host);
3306285e36eSBen Walker }
3316285e36eSBen Walker 
3324cd6544dSZiye Yang static void
3334cd6544dSZiye Yang _nvmf_subsystem_remove_listener(struct spdk_nvmf_subsystem *subsystem,
334c79d4eefSJacek Kalwas 				struct spdk_nvmf_subsystem_listener *listener,
3352167c68dSJan Kryl 				bool stop)
3364cd6544dSZiye Yang {
3374cd6544dSZiye Yang 	struct spdk_nvmf_transport *transport;
3381c81d1afSKonrad Sztyber 	struct spdk_nvmf_ctrlr *ctrlr;
3394cd6544dSZiye Yang 
3402167c68dSJan Kryl 	if (stop) {
3416d8f1fc6SJacek Kalwas 		transport = spdk_nvmf_tgt_get_transport(subsystem->tgt, listener->trid->trstring);
3424cd6544dSZiye Yang 		if (transport != NULL) {
3436d8f1fc6SJacek Kalwas 			spdk_nvmf_transport_stop_listen(transport, listener->trid);
3444cd6544dSZiye Yang 		}
3452167c68dSJan Kryl 	}
3464cd6544dSZiye Yang 
3471c81d1afSKonrad Sztyber 	TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) {
3481c81d1afSKonrad Sztyber 		if (ctrlr->listener == listener) {
3491c81d1afSKonrad Sztyber 			ctrlr->listener = NULL;
3501c81d1afSKonrad Sztyber 		}
3511c81d1afSKonrad Sztyber 	}
3521c81d1afSKonrad Sztyber 
3534cd6544dSZiye Yang 	TAILQ_REMOVE(&subsystem->listeners, listener, link);
354001db1eaSkyuho.son 	if (spdk_nvmf_subsystem_is_discovery(listener->subsystem)) {
355001db1eaSkyuho.son 		nvmf_tgt_update_mdns_prr(listener->subsystem->tgt);
356001db1eaSkyuho.son 	}
3575a8c76d9SAbhineet Pandey 	spdk_nvmf_send_discovery_log_notice(listener->subsystem->tgt, NULL);
358785d10b5SShuhei Matsumoto 	free(listener->ana_state);
359a4d132cdSJim Harris 	spdk_bit_array_clear(subsystem->used_listener_ids, listener->id);
360248c547dSKarl Bonde Torp 	free(listener->opts.sock_impl);
3614cd6544dSZiye Yang 	free(listener);
3624cd6544dSZiye Yang }
3634cd6544dSZiye Yang 
36497385af1SAlexey Marchuk static void
36597385af1SAlexey Marchuk _nvmf_subsystem_destroy_msg(void *cb_arg)
366f8296a99SDaniel Verkamp {
36797385af1SAlexey Marchuk 	struct spdk_nvmf_subsystem *subsystem = cb_arg;
36897385af1SAlexey Marchuk 
36997385af1SAlexey Marchuk 	_nvmf_subsystem_destroy(subsystem);
37097385af1SAlexey Marchuk }
37197385af1SAlexey Marchuk 
37297385af1SAlexey Marchuk static int
37397385af1SAlexey Marchuk _nvmf_subsystem_destroy(struct spdk_nvmf_subsystem *subsystem)
37497385af1SAlexey Marchuk {
3752881f7f6SKonrad Sztyber 	struct nvmf_subsystem_state_change_ctx *ctx;
37695ac75aaSBen Walker 	struct spdk_nvmf_ns		*ns;
37797385af1SAlexey Marchuk 	nvmf_subsystem_destroy_cb	async_destroy_cb = NULL;
37897385af1SAlexey Marchuk 	void				*async_destroy_cb_arg = NULL;
37997385af1SAlexey Marchuk 	int				rc;
380cf199a3fSBen Walker 
38197385af1SAlexey Marchuk 	if (!TAILQ_EMPTY(&subsystem->ctrlrs)) {
38297385af1SAlexey Marchuk 		SPDK_DEBUGLOG(nvmf, "subsystem %p %s has active controllers\n", subsystem, subsystem->subnqn);
38397385af1SAlexey Marchuk 		subsystem->async_destroy = true;
38497385af1SAlexey Marchuk 		rc = spdk_thread_send_msg(subsystem->thread, _nvmf_subsystem_destroy_msg, subsystem);
38597385af1SAlexey Marchuk 		if (rc) {
38697385af1SAlexey Marchuk 			SPDK_ERRLOG("Failed to send thread msg, rc %d\n", rc);
38797385af1SAlexey Marchuk 			assert(0);
38897385af1SAlexey Marchuk 			return rc;
3895dcf922cSZiye Yang 		}
39097385af1SAlexey Marchuk 		return -EINPROGRESS;
391f8296a99SDaniel Verkamp 	}
3921ad2c3eaSBen Walker 
3938af4b6c4SDaniel Verkamp 	ns = spdk_nvmf_subsystem_get_first_ns(subsystem);
3948af4b6c4SDaniel Verkamp 	while (ns != NULL) {
3958af4b6c4SDaniel Verkamp 		struct spdk_nvmf_ns *next_ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns);
3968af4b6c4SDaniel Verkamp 
39788da8a91SBen Walker 		spdk_nvmf_subsystem_remove_ns(subsystem, ns->opts.nsid);
3988af4b6c4SDaniel Verkamp 		ns = next_ns;
39995ac75aaSBen Walker 	}
40095ac75aaSBen Walker 
4012881f7f6SKonrad Sztyber 	while ((ctx = TAILQ_FIRST(&subsystem->state_changes))) {
4022881f7f6SKonrad Sztyber 		SPDK_WARNLOG("subsystem %s has pending state change requests\n", subsystem->subnqn);
4032881f7f6SKonrad Sztyber 		TAILQ_REMOVE(&subsystem->state_changes, ctx, link);
4042881f7f6SKonrad Sztyber 		if (ctx->cb_fn != NULL) {
4052881f7f6SKonrad Sztyber 			ctx->cb_fn(subsystem, ctx->cb_arg, -ECANCELED);
4062881f7f6SKonrad Sztyber 		}
4072881f7f6SKonrad Sztyber 		free(ctx);
4082881f7f6SKonrad Sztyber 	}
4092881f7f6SKonrad Sztyber 
41095ac75aaSBen Walker 	free(subsystem->ns);
41107bfc3cbSShuhei Matsumoto 	free(subsystem->ana_group);
41295ac75aaSBen Walker 
4130aef7f2aSJim Harris 	RB_REMOVE(subsystem_tree, &subsystem->tgt->subsystems, subsystem);
414779059a2SJim Harris 	assert(spdk_bit_array_get(subsystem->tgt->subsystem_ids, subsystem->id) == true);
415779059a2SJim Harris 	spdk_bit_array_clear(subsystem->tgt->subsystem_ids, subsystem->id);
41695ac75aaSBen Walker 
417d67119d8SBen Walker 	pthread_mutex_destroy(&subsystem->mutex);
418d67119d8SBen Walker 
419a4d132cdSJim Harris 	spdk_bit_array_free(&subsystem->used_listener_ids);
420a4d132cdSJim Harris 
42197385af1SAlexey Marchuk 	if (subsystem->async_destroy) {
42297385af1SAlexey Marchuk 		async_destroy_cb = subsystem->async_destroy_cb;
42397385af1SAlexey Marchuk 		async_destroy_cb_arg = subsystem->async_destroy_cb_arg;
42495ac75aaSBen Walker 	}
42595ac75aaSBen Walker 
42697385af1SAlexey Marchuk 	free(subsystem);
42797385af1SAlexey Marchuk 
42897385af1SAlexey Marchuk 	if (async_destroy_cb) {
42997385af1SAlexey Marchuk 		async_destroy_cb(async_destroy_cb_arg);
43097385af1SAlexey Marchuk 	}
43197385af1SAlexey Marchuk 
43297385af1SAlexey Marchuk 	return 0;
43397385af1SAlexey Marchuk }
43497385af1SAlexey Marchuk 
435a36785dfSDennis Maisenbacher static struct spdk_nvmf_ns *
436a36785dfSDennis Maisenbacher _nvmf_subsystem_get_first_zoned_ns(struct spdk_nvmf_subsystem *subsystem)
437a36785dfSDennis Maisenbacher {
438a36785dfSDennis Maisenbacher 	struct spdk_nvmf_ns *ns = spdk_nvmf_subsystem_get_first_ns(subsystem);
439a36785dfSDennis Maisenbacher 	while (ns != NULL) {
440a36785dfSDennis Maisenbacher 		if (ns->csi == SPDK_NVME_CSI_ZNS) {
441a36785dfSDennis Maisenbacher 			return ns;
442a36785dfSDennis Maisenbacher 		}
443a36785dfSDennis Maisenbacher 		ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns);
444a36785dfSDennis Maisenbacher 	}
445a36785dfSDennis Maisenbacher 	return NULL;
446a36785dfSDennis Maisenbacher }
447a36785dfSDennis Maisenbacher 
44897385af1SAlexey Marchuk int
44997385af1SAlexey Marchuk spdk_nvmf_subsystem_destroy(struct spdk_nvmf_subsystem *subsystem, nvmf_subsystem_destroy_cb cpl_cb,
45097385af1SAlexey Marchuk 			    void *cpl_cb_arg)
45197385af1SAlexey Marchuk {
45297385af1SAlexey Marchuk 	struct spdk_nvmf_host *host, *host_tmp;
4536803032dSKrzysztof Karas 	struct spdk_nvmf_transport *transport;
45497385af1SAlexey Marchuk 
45597385af1SAlexey Marchuk 	if (!subsystem) {
45697385af1SAlexey Marchuk 		return -EINVAL;
45797385af1SAlexey Marchuk 	}
45897385af1SAlexey Marchuk 
459d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE1(nvmf_subsystem_destroy, subsystem->subnqn);
460d11601e8SKrzysztof Karas 
46197385af1SAlexey Marchuk 	assert(spdk_get_thread() == subsystem->thread);
46297385af1SAlexey Marchuk 
46397385af1SAlexey Marchuk 	if (subsystem->state != SPDK_NVMF_SUBSYSTEM_INACTIVE) {
464c60cb1a8SChangpeng Liu 		SPDK_ERRLOG("Subsystem can only be destroyed in inactive state, %s state %d\n",
465c60cb1a8SChangpeng Liu 			    subsystem->subnqn, subsystem->state);
46697385af1SAlexey Marchuk 		return -EAGAIN;
46797385af1SAlexey Marchuk 	}
46897385af1SAlexey Marchuk 	if (subsystem->destroying) {
46997385af1SAlexey Marchuk 		SPDK_ERRLOG("Subsystem destruction is already started\n");
47097385af1SAlexey Marchuk 		assert(0);
47197385af1SAlexey Marchuk 		return -EALREADY;
47297385af1SAlexey Marchuk 	}
47397385af1SAlexey Marchuk 
47497385af1SAlexey Marchuk 	subsystem->destroying = true;
47597385af1SAlexey Marchuk 
47697385af1SAlexey Marchuk 	SPDK_DEBUGLOG(nvmf, "subsystem is %p %s\n", subsystem, subsystem->subnqn);
47797385af1SAlexey Marchuk 
47897385af1SAlexey Marchuk 	nvmf_subsystem_remove_all_listeners(subsystem, false);
47997385af1SAlexey Marchuk 
48097385af1SAlexey Marchuk 	pthread_mutex_lock(&subsystem->mutex);
48197385af1SAlexey Marchuk 
48297385af1SAlexey Marchuk 	TAILQ_FOREACH_SAFE(host, &subsystem->hosts, link, host_tmp) {
4836803032dSKrzysztof Karas 		for (transport = spdk_nvmf_transport_get_first(subsystem->tgt); transport;
4846803032dSKrzysztof Karas 		     transport = spdk_nvmf_transport_get_next(transport)) {
4856803032dSKrzysztof Karas 			if (transport->ops->subsystem_remove_host) {
4866803032dSKrzysztof Karas 				transport->ops->subsystem_remove_host(transport, subsystem, host->nqn);
4876803032dSKrzysztof Karas 			}
4886803032dSKrzysztof Karas 		}
48997385af1SAlexey Marchuk 		nvmf_subsystem_remove_host(subsystem, host);
49097385af1SAlexey Marchuk 	}
49197385af1SAlexey Marchuk 
49297385af1SAlexey Marchuk 	pthread_mutex_unlock(&subsystem->mutex);
49397385af1SAlexey Marchuk 
49497385af1SAlexey Marchuk 	subsystem->async_destroy_cb = cpl_cb;
49597385af1SAlexey Marchuk 	subsystem->async_destroy_cb_arg = cpl_cb_arg;
49697385af1SAlexey Marchuk 
49797385af1SAlexey Marchuk 	return _nvmf_subsystem_destroy(subsystem);
49897385af1SAlexey Marchuk }
4993f190fe1SSeth Howell 
5003f190fe1SSeth Howell /* we have to use the typedef in the function declaration to appease astyle. */
5013f190fe1SSeth Howell typedef enum spdk_nvmf_subsystem_state spdk_nvmf_subsystem_state_t;
5023f190fe1SSeth Howell 
5033f190fe1SSeth Howell static spdk_nvmf_subsystem_state_t
5043f190fe1SSeth Howell nvmf_subsystem_get_intermediate_state(enum spdk_nvmf_subsystem_state current_state,
5053f190fe1SSeth Howell 				      enum spdk_nvmf_subsystem_state requested_state)
5063f190fe1SSeth Howell {
5073f190fe1SSeth Howell 	switch (requested_state) {
5083f190fe1SSeth Howell 	case SPDK_NVMF_SUBSYSTEM_INACTIVE:
5093f190fe1SSeth Howell 		return SPDK_NVMF_SUBSYSTEM_DEACTIVATING;
5103f190fe1SSeth Howell 	case SPDK_NVMF_SUBSYSTEM_ACTIVE:
5113f190fe1SSeth Howell 		if (current_state == SPDK_NVMF_SUBSYSTEM_PAUSED) {
5123f190fe1SSeth Howell 			return SPDK_NVMF_SUBSYSTEM_RESUMING;
5133f190fe1SSeth Howell 		} else {
5143f190fe1SSeth Howell 			return SPDK_NVMF_SUBSYSTEM_ACTIVATING;
5153f190fe1SSeth Howell 		}
5163f190fe1SSeth Howell 	case SPDK_NVMF_SUBSYSTEM_PAUSED:
5173f190fe1SSeth Howell 		return SPDK_NVMF_SUBSYSTEM_PAUSING;
5183f190fe1SSeth Howell 	default:
5193f190fe1SSeth Howell 		assert(false);
5203f190fe1SSeth Howell 		return SPDK_NVMF_SUBSYSTEM_NUM_STATES;
5213f190fe1SSeth Howell 	}
5223f190fe1SSeth Howell }
5233f190fe1SSeth Howell 
52495ac75aaSBen Walker static int
52563c90491SSeth Howell nvmf_subsystem_set_state(struct spdk_nvmf_subsystem *subsystem,
52695ac75aaSBen Walker 			 enum spdk_nvmf_subsystem_state state)
52795ac75aaSBen Walker {
52895ac75aaSBen Walker 	enum spdk_nvmf_subsystem_state actual_old_state, expected_old_state;
529644fc120SRichael Zhuang 	bool exchanged;
53095ac75aaSBen Walker 
53195ac75aaSBen Walker 	switch (state) {
53295ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_INACTIVE:
53395ac75aaSBen Walker 		expected_old_state = SPDK_NVMF_SUBSYSTEM_DEACTIVATING;
53495ac75aaSBen Walker 		break;
53595ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_ACTIVATING:
53695ac75aaSBen Walker 		expected_old_state = SPDK_NVMF_SUBSYSTEM_INACTIVE;
53795ac75aaSBen Walker 		break;
53895ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_ACTIVE:
53995ac75aaSBen Walker 		expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVATING;
54095ac75aaSBen Walker 		break;
54195ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_PAUSING:
54295ac75aaSBen Walker 		expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVE;
54395ac75aaSBen Walker 		break;
54495ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_PAUSED:
54595ac75aaSBen Walker 		expected_old_state = SPDK_NVMF_SUBSYSTEM_PAUSING;
54695ac75aaSBen Walker 		break;
54795ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_RESUMING:
54895ac75aaSBen Walker 		expected_old_state = SPDK_NVMF_SUBSYSTEM_PAUSED;
54995ac75aaSBen Walker 		break;
55095ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_DEACTIVATING:
55195ac75aaSBen Walker 		expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVE;
55295ac75aaSBen Walker 		break;
55395ac75aaSBen Walker 	default:
55495ac75aaSBen Walker 		assert(false);
55595ac75aaSBen Walker 		return -1;
55695ac75aaSBen Walker 	}
55795ac75aaSBen Walker 
558644fc120SRichael Zhuang 	actual_old_state = expected_old_state;
559644fc120SRichael Zhuang 	exchanged = __atomic_compare_exchange_n(&subsystem->state, &actual_old_state, state, false,
560644fc120SRichael Zhuang 						__ATOMIC_RELAXED, __ATOMIC_RELAXED);
561644fc120SRichael Zhuang 	if (spdk_unlikely(exchanged == false)) {
56295ac75aaSBen Walker 		if (actual_old_state == SPDK_NVMF_SUBSYSTEM_RESUMING &&
56395ac75aaSBen Walker 		    state == SPDK_NVMF_SUBSYSTEM_ACTIVE) {
56495ac75aaSBen Walker 			expected_old_state = SPDK_NVMF_SUBSYSTEM_RESUMING;
56595ac75aaSBen Walker 		}
566da01835dSGangCao 		/* This is for the case when activating the subsystem fails. */
567da01835dSGangCao 		if (actual_old_state == SPDK_NVMF_SUBSYSTEM_ACTIVATING &&
568da01835dSGangCao 		    state == SPDK_NVMF_SUBSYSTEM_DEACTIVATING) {
569da01835dSGangCao 			expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVATING;
570da01835dSGangCao 		}
5711e337a1eSSeth Howell 		/* This is for the case when resuming the subsystem fails. */
5721e337a1eSSeth Howell 		if (actual_old_state == SPDK_NVMF_SUBSYSTEM_RESUMING &&
5731e337a1eSSeth Howell 		    state == SPDK_NVMF_SUBSYSTEM_PAUSING) {
5741e337a1eSSeth Howell 			expected_old_state = SPDK_NVMF_SUBSYSTEM_RESUMING;
5751e337a1eSSeth Howell 		}
576fa949b0cSChangpeng Liu 		/* This is for the case when stopping paused subsystem */
577fa949b0cSChangpeng Liu 		if (actual_old_state == SPDK_NVMF_SUBSYSTEM_PAUSED &&
578fa949b0cSChangpeng Liu 		    state == SPDK_NVMF_SUBSYSTEM_DEACTIVATING) {
579fa949b0cSChangpeng Liu 			expected_old_state = SPDK_NVMF_SUBSYSTEM_PAUSED;
580fa949b0cSChangpeng Liu 		}
581644fc120SRichael Zhuang 		actual_old_state = expected_old_state;
582644fc120SRichael Zhuang 		__atomic_compare_exchange_n(&subsystem->state, &actual_old_state, state, false,
583644fc120SRichael Zhuang 					    __ATOMIC_RELAXED, __ATOMIC_RELAXED);
58495ac75aaSBen Walker 	}
58595ac75aaSBen Walker 	assert(actual_old_state == expected_old_state);
58695ac75aaSBen Walker 	return actual_old_state - expected_old_state;
58795ac75aaSBen Walker }
58895ac75aaSBen Walker 
5892881f7f6SKonrad Sztyber static void nvmf_subsystem_do_state_change(struct nvmf_subsystem_state_change_ctx *ctx);
5902881f7f6SKonrad Sztyber 
59195ac75aaSBen Walker static void
592a516cd4bSKonrad Sztyber _nvmf_subsystem_state_change_complete(void *_ctx)
59320764873SKonrad Sztyber {
5942881f7f6SKonrad Sztyber 	struct nvmf_subsystem_state_change_ctx *next, *ctx = _ctx;
59520764873SKonrad Sztyber 	struct spdk_nvmf_subsystem *subsystem = ctx->subsystem;
59620764873SKonrad Sztyber 
59715a7b105SKonrad Sztyber 	pthread_mutex_lock(&subsystem->mutex);
5982881f7f6SKonrad Sztyber 	assert(TAILQ_FIRST(&subsystem->state_changes) == ctx);
5992881f7f6SKonrad Sztyber 	TAILQ_REMOVE(&subsystem->state_changes, ctx, link);
6002881f7f6SKonrad Sztyber 	next = TAILQ_FIRST(&subsystem->state_changes);
60115a7b105SKonrad Sztyber 	pthread_mutex_unlock(&subsystem->mutex);
60215a7b105SKonrad Sztyber 
60320764873SKonrad Sztyber 	if (ctx->cb_fn != NULL) {
604a516cd4bSKonrad Sztyber 		ctx->cb_fn(subsystem, ctx->cb_arg, ctx->status);
60520764873SKonrad Sztyber 	}
60620764873SKonrad Sztyber 	free(ctx);
6072881f7f6SKonrad Sztyber 
6082881f7f6SKonrad Sztyber 	if (next != NULL) {
6092881f7f6SKonrad Sztyber 		nvmf_subsystem_do_state_change(next);
6102881f7f6SKonrad Sztyber 	}
61120764873SKonrad Sztyber }
61220764873SKonrad Sztyber 
61320764873SKonrad Sztyber static void
614a516cd4bSKonrad Sztyber nvmf_subsystem_state_change_complete(struct nvmf_subsystem_state_change_ctx *ctx, int status)
615a516cd4bSKonrad Sztyber {
616a516cd4bSKonrad Sztyber 	ctx->status = status;
617a516cd4bSKonrad Sztyber 	spdk_thread_exec_msg(ctx->thread, _nvmf_subsystem_state_change_complete, ctx);
618a516cd4bSKonrad Sztyber }
619a516cd4bSKonrad Sztyber 
620a516cd4bSKonrad Sztyber static void
6211e337a1eSSeth Howell subsystem_state_change_revert_done(struct spdk_io_channel_iter *i, int status)
6221e337a1eSSeth Howell {
6238de19363SKonrad Sztyber 	struct nvmf_subsystem_state_change_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
6241e337a1eSSeth Howell 
6251e337a1eSSeth Howell 	/* Nothing to be done here if the state setting fails, we are just screwed. */
6261e337a1eSSeth Howell 	if (nvmf_subsystem_set_state(ctx->subsystem, ctx->requested_state)) {
6271e337a1eSSeth Howell 		SPDK_ERRLOG("Unable to revert the subsystem state after operation failure.\n");
6281e337a1eSSeth Howell 	}
6291e337a1eSSeth Howell 
6301e337a1eSSeth Howell 	/* return a failure here. This function only exists in an error path. */
63120764873SKonrad Sztyber 	nvmf_subsystem_state_change_complete(ctx, -1);
6321e337a1eSSeth Howell }
6331e337a1eSSeth Howell 
6341e337a1eSSeth Howell static void
63595ac75aaSBen Walker subsystem_state_change_done(struct spdk_io_channel_iter *i, int status)
63695ac75aaSBen Walker {
6378de19363SKonrad Sztyber 	struct nvmf_subsystem_state_change_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
6381e337a1eSSeth Howell 	enum spdk_nvmf_subsystem_state intermediate_state;
63995ac75aaSBen Walker 
6403a0f6244SJim Harris 	SPDK_DTRACE_PROBE4(nvmf_subsystem_change_state_done, ctx->subsystem->subnqn,
6413a0f6244SJim Harris 			   ctx->requested_state, ctx->original_state, status);
6423a0f6244SJim Harris 
64395ac75aaSBen Walker 	if (status == 0) {
64463c90491SSeth Howell 		status = nvmf_subsystem_set_state(ctx->subsystem, ctx->requested_state);
64595ac75aaSBen Walker 		if (status) {
64695ac75aaSBen Walker 			status = -1;
64795ac75aaSBen Walker 		}
64895ac75aaSBen Walker 	}
64995ac75aaSBen Walker 
6501e337a1eSSeth Howell 	if (status) {
6511e337a1eSSeth Howell 		intermediate_state = nvmf_subsystem_get_intermediate_state(ctx->requested_state,
6521e337a1eSSeth Howell 				     ctx->original_state);
6531e337a1eSSeth Howell 		assert(intermediate_state != SPDK_NVMF_SUBSYSTEM_NUM_STATES);
6541e337a1eSSeth Howell 
6551e337a1eSSeth Howell 		if (nvmf_subsystem_set_state(ctx->subsystem, intermediate_state)) {
6561e337a1eSSeth Howell 			goto out;
6571e337a1eSSeth Howell 		}
6581e337a1eSSeth Howell 		ctx->requested_state = ctx->original_state;
6591e337a1eSSeth Howell 		spdk_for_each_channel(ctx->subsystem->tgt,
6601e337a1eSSeth Howell 				      subsystem_state_change_on_pg,
6611e337a1eSSeth Howell 				      ctx,
6621e337a1eSSeth Howell 				      subsystem_state_change_revert_done);
6631e337a1eSSeth Howell 		return;
6641e337a1eSSeth Howell 	}
6651e337a1eSSeth Howell 
6661e337a1eSSeth Howell out:
66720764873SKonrad Sztyber 	nvmf_subsystem_state_change_complete(ctx, status);
66895ac75aaSBen Walker }
66995ac75aaSBen Walker 
67095ac75aaSBen Walker static void
671f2b22d68SSeth Howell subsystem_state_change_continue(void *ctx, int status)
672f2b22d68SSeth Howell {
673f2b22d68SSeth Howell 	struct spdk_io_channel_iter *i = ctx;
6748de19363SKonrad Sztyber 	struct nvmf_subsystem_state_change_ctx *_ctx __attribute__((unused));
6753a0f6244SJim Harris 
6763a0f6244SJim Harris 	_ctx = spdk_io_channel_iter_get_ctx(i);
6773a0f6244SJim Harris 	SPDK_DTRACE_PROBE3(nvmf_pg_change_state_done, _ctx->subsystem->subnqn,
6783a0f6244SJim Harris 			   _ctx->requested_state, spdk_thread_get_id(spdk_get_thread()));
6793a0f6244SJim Harris 
680f2b22d68SSeth Howell 	spdk_for_each_channel_continue(i, status);
681f2b22d68SSeth Howell }
682f2b22d68SSeth Howell 
683f2b22d68SSeth Howell static void
68495ac75aaSBen Walker subsystem_state_change_on_pg(struct spdk_io_channel_iter *i)
68595ac75aaSBen Walker {
6868de19363SKonrad Sztyber 	struct nvmf_subsystem_state_change_ctx *ctx;
68795ac75aaSBen Walker 	struct spdk_io_channel *ch;
68895ac75aaSBen Walker 	struct spdk_nvmf_poll_group *group;
68995ac75aaSBen Walker 
69095ac75aaSBen Walker 	ctx = spdk_io_channel_iter_get_ctx(i);
69195ac75aaSBen Walker 	ch = spdk_io_channel_iter_get_channel(i);
69295ac75aaSBen Walker 	group = spdk_io_channel_get_ctx(ch);
69395ac75aaSBen Walker 
6943a0f6244SJim Harris 	SPDK_DTRACE_PROBE3(nvmf_pg_change_state, ctx->subsystem->subnqn,
6953a0f6244SJim Harris 			   ctx->requested_state, spdk_thread_get_id(spdk_get_thread()));
69695ac75aaSBen Walker 	switch (ctx->requested_state) {
69795ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_INACTIVE:
6989cb21ad6SSeth Howell 		nvmf_poll_group_remove_subsystem(group, ctx->subsystem, subsystem_state_change_continue, i);
69995ac75aaSBen Walker 		break;
70095ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_ACTIVE:
70195ac75aaSBen Walker 		if (ctx->subsystem->state == SPDK_NVMF_SUBSYSTEM_ACTIVATING) {
7029cb21ad6SSeth Howell 			nvmf_poll_group_add_subsystem(group, ctx->subsystem, subsystem_state_change_continue, i);
70395ac75aaSBen Walker 		} else if (ctx->subsystem->state == SPDK_NVMF_SUBSYSTEM_RESUMING) {
7049cb21ad6SSeth Howell 			nvmf_poll_group_resume_subsystem(group, ctx->subsystem, subsystem_state_change_continue, i);
70595ac75aaSBen Walker 		}
70695ac75aaSBen Walker 		break;
70795ac75aaSBen Walker 	case SPDK_NVMF_SUBSYSTEM_PAUSED:
708312a9d60SBen Walker 		nvmf_poll_group_pause_subsystem(group, ctx->subsystem, ctx->nsid, subsystem_state_change_continue,
709312a9d60SBen Walker 						i);
71095ac75aaSBen Walker 		break;
71195ac75aaSBen Walker 	default:
71295ac75aaSBen Walker 		assert(false);
71395ac75aaSBen Walker 		break;
71495ac75aaSBen Walker 	}
71595ac75aaSBen Walker }
71695ac75aaSBen Walker 
717ca48edfeSKonrad Sztyber static void
718ca48edfeSKonrad Sztyber nvmf_subsystem_do_state_change(struct nvmf_subsystem_state_change_ctx *ctx)
719ca48edfeSKonrad Sztyber {
720ca48edfeSKonrad Sztyber 	struct spdk_nvmf_subsystem *subsystem = ctx->subsystem;
721ca48edfeSKonrad Sztyber 	enum spdk_nvmf_subsystem_state intermediate_state;
722ca48edfeSKonrad Sztyber 	int rc;
723ca48edfeSKonrad Sztyber 
724ca48edfeSKonrad Sztyber 	SPDK_DTRACE_PROBE3(nvmf_subsystem_change_state, subsystem->subnqn,
725ca48edfeSKonrad Sztyber 			   ctx->requested_state, subsystem->state);
726ca48edfeSKonrad Sztyber 
727ca48edfeSKonrad Sztyber 	/* If we are already in the requested state, just call the callback immediately. */
728ca48edfeSKonrad Sztyber 	if (subsystem->state == ctx->requested_state) {
729ca48edfeSKonrad Sztyber 		nvmf_subsystem_state_change_complete(ctx, 0);
730ca48edfeSKonrad Sztyber 		return;
731ca48edfeSKonrad Sztyber 	}
732ca48edfeSKonrad Sztyber 
733ca48edfeSKonrad Sztyber 	intermediate_state = nvmf_subsystem_get_intermediate_state(subsystem->state,
734ca48edfeSKonrad Sztyber 			     ctx->requested_state);
735ca48edfeSKonrad Sztyber 	assert(intermediate_state != SPDK_NVMF_SUBSYSTEM_NUM_STATES);
736ca48edfeSKonrad Sztyber 
737ca48edfeSKonrad Sztyber 	ctx->original_state = subsystem->state;
738ca48edfeSKonrad Sztyber 	rc = nvmf_subsystem_set_state(subsystem, intermediate_state);
739ca48edfeSKonrad Sztyber 	if (rc) {
740ca48edfeSKonrad Sztyber 		nvmf_subsystem_state_change_complete(ctx, -1);
741ca48edfeSKonrad Sztyber 		return;
742ca48edfeSKonrad Sztyber 	}
743ca48edfeSKonrad Sztyber 
744ca48edfeSKonrad Sztyber 	spdk_for_each_channel(subsystem->tgt,
745ca48edfeSKonrad Sztyber 			      subsystem_state_change_on_pg,
746ca48edfeSKonrad Sztyber 			      ctx,
747ca48edfeSKonrad Sztyber 			      subsystem_state_change_done);
748ca48edfeSKonrad Sztyber }
749ca48edfeSKonrad Sztyber 
750ca48edfeSKonrad Sztyber 
751d346b9c5SBen Walker static int
75263c90491SSeth Howell nvmf_subsystem_state_change(struct spdk_nvmf_subsystem *subsystem,
753312a9d60SBen Walker 			    uint32_t nsid,
75495ac75aaSBen Walker 			    enum spdk_nvmf_subsystem_state requested_state,
75595ac75aaSBen Walker 			    spdk_nvmf_subsystem_state_change_done cb_fn,
75695ac75aaSBen Walker 			    void *cb_arg)
75795ac75aaSBen Walker {
758b625b3d6SKonrad Sztyber 	struct nvmf_subsystem_state_change_ctx *ctx;
759a516cd4bSKonrad Sztyber 	struct spdk_thread *thread;
76095ac75aaSBen Walker 
761a516cd4bSKonrad Sztyber 	thread = spdk_get_thread();
762a516cd4bSKonrad Sztyber 	if (thread == NULL) {
763a516cd4bSKonrad Sztyber 		return -EINVAL;
764a516cd4bSKonrad Sztyber 	}
765a516cd4bSKonrad Sztyber 
766fa88b41eSKonrad Sztyber 	ctx = calloc(1, sizeof(*ctx));
767fa88b41eSKonrad Sztyber 	if (!ctx) {
768fa88b41eSKonrad Sztyber 		return -ENOMEM;
769fa88b41eSKonrad Sztyber 	}
770fa88b41eSKonrad Sztyber 
771fa88b41eSKonrad Sztyber 	ctx->subsystem = subsystem;
772fa88b41eSKonrad Sztyber 	ctx->nsid = nsid;
773fa88b41eSKonrad Sztyber 	ctx->requested_state = requested_state;
774fa88b41eSKonrad Sztyber 	ctx->cb_fn = cb_fn;
775fa88b41eSKonrad Sztyber 	ctx->cb_arg = cb_arg;
776a516cd4bSKonrad Sztyber 	ctx->thread = thread;
777fa88b41eSKonrad Sztyber 
77815a7b105SKonrad Sztyber 	pthread_mutex_lock(&subsystem->mutex);
7792881f7f6SKonrad Sztyber 	TAILQ_INSERT_TAIL(&subsystem->state_changes, ctx, link);
780b625b3d6SKonrad Sztyber 	if (ctx != TAILQ_FIRST(&subsystem->state_changes)) {
78115a7b105SKonrad Sztyber 		pthread_mutex_unlock(&subsystem->mutex);
7822881f7f6SKonrad Sztyber 		return 0;
7838ff8bf07SSeth Howell 	}
78415a7b105SKonrad Sztyber 	pthread_mutex_unlock(&subsystem->mutex);
78515a7b105SKonrad Sztyber 
786ca48edfeSKonrad Sztyber 	nvmf_subsystem_do_state_change(ctx);
787d346b9c5SBen Walker 
788d346b9c5SBen Walker 	return 0;
78995ac75aaSBen Walker }
79095ac75aaSBen Walker 
791d346b9c5SBen Walker int
79295ac75aaSBen Walker spdk_nvmf_subsystem_start(struct spdk_nvmf_subsystem *subsystem,
79395ac75aaSBen Walker 			  spdk_nvmf_subsystem_state_change_done cb_fn,
79495ac75aaSBen Walker 			  void *cb_arg)
79595ac75aaSBen Walker {
796312a9d60SBen Walker 	return nvmf_subsystem_state_change(subsystem, 0, SPDK_NVMF_SUBSYSTEM_ACTIVE, cb_fn, cb_arg);
79795ac75aaSBen Walker }
79895ac75aaSBen Walker 
799d346b9c5SBen Walker int
80095ac75aaSBen Walker spdk_nvmf_subsystem_stop(struct spdk_nvmf_subsystem *subsystem,
80195ac75aaSBen Walker 			 spdk_nvmf_subsystem_state_change_done cb_fn,
80295ac75aaSBen Walker 			 void *cb_arg)
80395ac75aaSBen Walker {
804312a9d60SBen Walker 	return nvmf_subsystem_state_change(subsystem, 0, SPDK_NVMF_SUBSYSTEM_INACTIVE, cb_fn, cb_arg);
80595ac75aaSBen Walker }
80695ac75aaSBen Walker 
807d346b9c5SBen Walker int
80895ac75aaSBen Walker spdk_nvmf_subsystem_pause(struct spdk_nvmf_subsystem *subsystem,
809312a9d60SBen Walker 			  uint32_t nsid,
81095ac75aaSBen Walker 			  spdk_nvmf_subsystem_state_change_done cb_fn,
81195ac75aaSBen Walker 			  void *cb_arg)
81295ac75aaSBen Walker {
813312a9d60SBen Walker 	return nvmf_subsystem_state_change(subsystem, nsid, SPDK_NVMF_SUBSYSTEM_PAUSED, cb_fn, cb_arg);
81495ac75aaSBen Walker }
81595ac75aaSBen Walker 
816d346b9c5SBen Walker int
81795ac75aaSBen Walker spdk_nvmf_subsystem_resume(struct spdk_nvmf_subsystem *subsystem,
81895ac75aaSBen Walker 			   spdk_nvmf_subsystem_state_change_done cb_fn,
81995ac75aaSBen Walker 			   void *cb_arg)
82095ac75aaSBen Walker {
821312a9d60SBen Walker 	return nvmf_subsystem_state_change(subsystem, 0, SPDK_NVMF_SUBSYSTEM_ACTIVE, cb_fn, cb_arg);
822f8296a99SDaniel Verkamp }
823f8296a99SDaniel Verkamp 
82433376dd1SBen Walker struct spdk_nvmf_subsystem *
82533376dd1SBen Walker spdk_nvmf_subsystem_get_first(struct spdk_nvmf_tgt *tgt)
82633376dd1SBen Walker {
8270aef7f2aSJim Harris 	return RB_MIN(subsystem_tree, &tgt->subsystems);
82833376dd1SBen Walker }
82933376dd1SBen Walker 
83033376dd1SBen Walker struct spdk_nvmf_subsystem *
83133376dd1SBen Walker spdk_nvmf_subsystem_get_next(struct spdk_nvmf_subsystem *subsystem)
83233376dd1SBen Walker {
83333376dd1SBen Walker 	if (!subsystem) {
83433376dd1SBen Walker 		return NULL;
83533376dd1SBen Walker 	}
83633376dd1SBen Walker 
8370aef7f2aSJim Harris 	return RB_NEXT(subsystem_tree, &tgt->subsystems, subsystem);
83833376dd1SBen Walker }
83945f04c89SBen Walker 
8404e347247SJim Harris static int
8414e347247SJim Harris nvmf_ns_add_host(struct spdk_nvmf_ns *ns, const char *hostnqn)
8424e347247SJim Harris {
8434e347247SJim Harris 	struct spdk_nvmf_host *host;
8444e347247SJim Harris 
8454e347247SJim Harris 	host = calloc(1, sizeof(*host));
8464e347247SJim Harris 	if (!host) {
8474e347247SJim Harris 		return -ENOMEM;
8484e347247SJim Harris 	}
8494e347247SJim Harris 	snprintf(host->nqn, sizeof(host->nqn), "%s", hostnqn);
8504e347247SJim Harris 	TAILQ_INSERT_HEAD(&ns->hosts, host, link);
8514e347247SJim Harris 	return 0;
8524e347247SJim Harris }
8534e347247SJim Harris 
854d37555b4SJonas Pfefferle static void
855d37555b4SJonas Pfefferle nvmf_ns_remove_host(struct spdk_nvmf_ns *ns, struct spdk_nvmf_host *host)
856d37555b4SJonas Pfefferle {
857d37555b4SJonas Pfefferle 	TAILQ_REMOVE(&ns->hosts, host, link);
858d37555b4SJonas Pfefferle 	free(host);
859d37555b4SJonas Pfefferle }
860d37555b4SJonas Pfefferle 
861d37555b4SJonas Pfefferle static void
862d37555b4SJonas Pfefferle _async_event_ns_notice(void *_ctrlr)
863d37555b4SJonas Pfefferle {
864d37555b4SJonas Pfefferle 	struct spdk_nvmf_ctrlr *ctrlr = _ctrlr;
865d37555b4SJonas Pfefferle 
866d37555b4SJonas Pfefferle 	nvmf_ctrlr_async_event_ns_notice(ctrlr);
867d37555b4SJonas Pfefferle }
868d37555b4SJonas Pfefferle 
869d37555b4SJonas Pfefferle static void
870d37555b4SJonas Pfefferle send_async_event_ns_notice(struct spdk_nvmf_ctrlr *ctrlr)
871d37555b4SJonas Pfefferle {
872d37555b4SJonas Pfefferle 	spdk_thread_send_msg(ctrlr->thread, _async_event_ns_notice, ctrlr);
873d37555b4SJonas Pfefferle }
874d37555b4SJonas Pfefferle 
875d37555b4SJonas Pfefferle static int
876d37555b4SJonas Pfefferle nvmf_ns_visible(struct spdk_nvmf_subsystem *subsystem,
877d37555b4SJonas Pfefferle 		uint32_t nsid,
878d37555b4SJonas Pfefferle 		const char *hostnqn,
879d37555b4SJonas Pfefferle 		bool visible)
880d37555b4SJonas Pfefferle {
881d37555b4SJonas Pfefferle 	struct spdk_nvmf_ns *ns;
882d37555b4SJonas Pfefferle 	struct spdk_nvmf_ctrlr *ctrlr;
883d37555b4SJonas Pfefferle 	struct spdk_nvmf_host *host;
8844e347247SJim Harris 	int rc;
885d37555b4SJonas Pfefferle 
886d37555b4SJonas Pfefferle 	if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE ||
887d37555b4SJonas Pfefferle 	      subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) {
888d37555b4SJonas Pfefferle 		assert(false);
889d37555b4SJonas Pfefferle 		return -1;
890d37555b4SJonas Pfefferle 	}
891d37555b4SJonas Pfefferle 
892d37555b4SJonas Pfefferle 	if (hostnqn == NULL || !nvmf_nqn_is_valid(hostnqn)) {
893d37555b4SJonas Pfefferle 		return -EINVAL;
894d37555b4SJonas Pfefferle 	}
895d37555b4SJonas Pfefferle 
896d37555b4SJonas Pfefferle 	if (nsid == 0 || nsid > subsystem->max_nsid) {
897d37555b4SJonas Pfefferle 		return -EINVAL;
898d37555b4SJonas Pfefferle 	}
899d37555b4SJonas Pfefferle 
900d37555b4SJonas Pfefferle 	ns = subsystem->ns[nsid - 1];
901d37555b4SJonas Pfefferle 	if (!ns) {
902d37555b4SJonas Pfefferle 		return -ENOENT;
903d37555b4SJonas Pfefferle 	}
904d37555b4SJonas Pfefferle 
905d37555b4SJonas Pfefferle 	if (ns->always_visible) {
906d37555b4SJonas Pfefferle 		/* No individual host control */
907d37555b4SJonas Pfefferle 		return -EPERM;
908d37555b4SJonas Pfefferle 	}
909d37555b4SJonas Pfefferle 
910d37555b4SJonas Pfefferle 	/* Save host info to use for any future controllers. */
911d37555b4SJonas Pfefferle 	host = nvmf_ns_find_host(ns, hostnqn);
912d37555b4SJonas Pfefferle 	if (visible && host == NULL) {
9134e347247SJim Harris 		rc = nvmf_ns_add_host(ns, hostnqn);
9144e347247SJim Harris 		if (rc) {
9154e347247SJim Harris 			return rc;
916d37555b4SJonas Pfefferle 		}
917d37555b4SJonas Pfefferle 	} else if (!visible && host != NULL) {
918d37555b4SJonas Pfefferle 		nvmf_ns_remove_host(ns, host);
919d37555b4SJonas Pfefferle 	}
920d37555b4SJonas Pfefferle 
921d37555b4SJonas Pfefferle 	/* Also apply to existing controllers. */
922d37555b4SJonas Pfefferle 	TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) {
923d37555b4SJonas Pfefferle 		if (strcmp(hostnqn, ctrlr->hostnqn) ||
924bfd014b5SKonrad Sztyber 		    nvmf_ctrlr_ns_is_visible(ctrlr, nsid) == visible) {
925d37555b4SJonas Pfefferle 			continue;
926d37555b4SJonas Pfefferle 		}
927bfd014b5SKonrad Sztyber 		nvmf_ctrlr_ns_set_visible(ctrlr, nsid, visible);
928d37555b4SJonas Pfefferle 		send_async_event_ns_notice(ctrlr);
929d37555b4SJonas Pfefferle 		nvmf_ctrlr_ns_changed(ctrlr, nsid);
930d37555b4SJonas Pfefferle 	}
931d37555b4SJonas Pfefferle 
932d37555b4SJonas Pfefferle 	return 0;
933d37555b4SJonas Pfefferle }
934d37555b4SJonas Pfefferle 
935d37555b4SJonas Pfefferle int
936d37555b4SJonas Pfefferle spdk_nvmf_ns_add_host(struct spdk_nvmf_subsystem *subsystem,
937d37555b4SJonas Pfefferle 		      uint32_t nsid,
938d37555b4SJonas Pfefferle 		      const char *hostnqn,
939d37555b4SJonas Pfefferle 		      uint32_t flags)
940d37555b4SJonas Pfefferle {
941d37555b4SJonas Pfefferle 	SPDK_DTRACE_PROBE4(spdk_nvmf_ns_add_host,
942d37555b4SJonas Pfefferle 			   subsystem->subnqn,
943d37555b4SJonas Pfefferle 			   nsid,
944d37555b4SJonas Pfefferle 			   hostnqn,
945d37555b4SJonas Pfefferle 			   flags);
946d37555b4SJonas Pfefferle 	return nvmf_ns_visible(subsystem, nsid, hostnqn, true);
947d37555b4SJonas Pfefferle }
948d37555b4SJonas Pfefferle 
949d37555b4SJonas Pfefferle int
950d37555b4SJonas Pfefferle spdk_nvmf_ns_remove_host(struct spdk_nvmf_subsystem *subsystem,
951d37555b4SJonas Pfefferle 			 uint32_t nsid,
952d37555b4SJonas Pfefferle 			 const char *hostnqn,
953d37555b4SJonas Pfefferle 			 uint32_t flags)
954d37555b4SJonas Pfefferle {
955d37555b4SJonas Pfefferle 	SPDK_DTRACE_PROBE4(spdk_nvmf_ns_remove_host,
956d37555b4SJonas Pfefferle 			   subsystem->subnqn,
957d37555b4SJonas Pfefferle 			   nsid,
958d37555b4SJonas Pfefferle 			   hostnqn,
959d37555b4SJonas Pfefferle 			   flags);
960d37555b4SJonas Pfefferle 	return nvmf_ns_visible(subsystem, nsid, hostnqn, false);
961d37555b4SJonas Pfefferle }
962d37555b4SJonas Pfefferle 
963d67119d8SBen Walker /* Must hold subsystem->mutex while calling this function */
9646285e36eSBen Walker static struct spdk_nvmf_host *
96561d85773SSeth Howell nvmf_subsystem_find_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn)
9666285e36eSBen Walker {
9676285e36eSBen Walker 	struct spdk_nvmf_host *host = NULL;
9686285e36eSBen Walker 
9696285e36eSBen Walker 	TAILQ_FOREACH(host, &subsystem->hosts, link) {
9706285e36eSBen Walker 		if (strcmp(hostnqn, host->nqn) == 0) {
9716285e36eSBen Walker 			return host;
9726285e36eSBen Walker 		}
9736285e36eSBen Walker 	}
9746285e36eSBen Walker 
9756285e36eSBen Walker 	return NULL;
9766285e36eSBen Walker }
9776285e36eSBen Walker 
97845f04c89SBen Walker int
97984e4df8cSKonrad Sztyber spdk_nvmf_subsystem_add_host_ext(struct spdk_nvmf_subsystem *subsystem,
98084e4df8cSKonrad Sztyber 				 const char *hostnqn, struct spdk_nvmf_host_opts *opts)
98145f04c89SBen Walker {
98245f04c89SBen Walker 	struct spdk_nvmf_host *host;
9836803032dSKrzysztof Karas 	struct spdk_nvmf_transport *transport;
9843f4f8ad0SKonrad Sztyber 	struct spdk_key *key;
9856803032dSKrzysztof Karas 	int rc;
98645f04c89SBen Walker 
987552f6517SKonrad Sztyber 	if (!nvmf_nqn_is_valid(hostnqn)) {
988be774bf6SBen Walker 		return -EINVAL;
989be774bf6SBen Walker 	}
990be774bf6SBen Walker 
991d67119d8SBen Walker 	pthread_mutex_lock(&subsystem->mutex);
99245f04c89SBen Walker 
99361d85773SSeth Howell 	if (nvmf_subsystem_find_host(subsystem, hostnqn)) {
9946285e36eSBen Walker 		/* This subsystem already allows the specified host. */
995d67119d8SBen Walker 		pthread_mutex_unlock(&subsystem->mutex);
996dca0da83SKonrad Sztyber 		return -EINVAL;
9976285e36eSBen Walker 	}
9986285e36eSBen Walker 
99945f04c89SBen Walker 	host = calloc(1, sizeof(*host));
100045f04c89SBen Walker 	if (!host) {
1001d67119d8SBen Walker 		pthread_mutex_unlock(&subsystem->mutex);
1002be774bf6SBen Walker 		return -ENOMEM;
100345f04c89SBen Walker 	}
100412d6dce2SShuhei Matsumoto 
10053f4f8ad0SKonrad Sztyber 	key = SPDK_GET_FIELD(opts, dhchap_key, NULL);
10063f4f8ad0SKonrad Sztyber 	if (key != NULL) {
1007f74bf0d0SKonrad Sztyber 		if (!nvmf_auth_is_supported()) {
1008f74bf0d0SKonrad Sztyber 			SPDK_ERRLOG("NVMe in-band authentication is unsupported\n");
1009f74bf0d0SKonrad Sztyber 			pthread_mutex_unlock(&subsystem->mutex);
1010e8841656SKonrad Sztyber 			nvmf_host_free(host);
1011f74bf0d0SKonrad Sztyber 			return -EINVAL;
1012f74bf0d0SKonrad Sztyber 		}
10133f4f8ad0SKonrad Sztyber 		host->dhchap_key = spdk_key_dup(key);
10143f4f8ad0SKonrad Sztyber 		if (host->dhchap_key == NULL) {
10153f4f8ad0SKonrad Sztyber 			pthread_mutex_unlock(&subsystem->mutex);
1016e8841656SKonrad Sztyber 			nvmf_host_free(host);
10173f4f8ad0SKonrad Sztyber 			return -EINVAL;
10183f4f8ad0SKonrad Sztyber 		}
1019aa13730dSKonrad Sztyber 		key = SPDK_GET_FIELD(opts, dhchap_ctrlr_key, NULL);
1020aa13730dSKonrad Sztyber 		if (key != NULL) {
1021aa13730dSKonrad Sztyber 			host->dhchap_ctrlr_key = spdk_key_dup(key);
1022aa13730dSKonrad Sztyber 			if (host->dhchap_ctrlr_key == NULL) {
1023aa13730dSKonrad Sztyber 				pthread_mutex_unlock(&subsystem->mutex);
1024aa13730dSKonrad Sztyber 				nvmf_host_free(host);
1025aa13730dSKonrad Sztyber 				return -EINVAL;
1026aa13730dSKonrad Sztyber 			}
1027aa13730dSKonrad Sztyber 		}
1028aa13730dSKonrad Sztyber 	} else if (SPDK_GET_FIELD(opts, dhchap_ctrlr_key, NULL) != NULL) {
1029aa13730dSKonrad Sztyber 		SPDK_ERRLOG("DH-HMAC-CHAP controller key requires host key to be set\n");
1030aa13730dSKonrad Sztyber 		pthread_mutex_unlock(&subsystem->mutex);
1031aa13730dSKonrad Sztyber 		nvmf_host_free(host);
1032aa13730dSKonrad Sztyber 		return -EINVAL;
10333f4f8ad0SKonrad Sztyber 	}
10343f4f8ad0SKonrad Sztyber 
103512d6dce2SShuhei Matsumoto 	snprintf(host->nqn, sizeof(host->nqn), "%s", hostnqn);
103645f04c89SBen Walker 
1037d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE2(nvmf_subsystem_add_host, subsystem->subnqn, host->nqn);
1038d11601e8SKrzysztof Karas 
103945f04c89SBen Walker 	TAILQ_INSERT_HEAD(&subsystem->hosts, host, link);
1040d67119d8SBen Walker 
104159f3cdacSJim Harris 	if (!TAILQ_EMPTY(&subsystem->listeners)) {
10425a8c76d9SAbhineet Pandey 		spdk_nvmf_send_discovery_log_notice(subsystem->tgt, hostnqn);
104359f3cdacSJim Harris 	}
104445f04c89SBen Walker 
10456803032dSKrzysztof Karas 	for (transport = spdk_nvmf_transport_get_first(subsystem->tgt); transport;
10466803032dSKrzysztof Karas 	     transport = spdk_nvmf_transport_get_next(transport)) {
10476803032dSKrzysztof Karas 		if (transport->ops->subsystem_add_host) {
104884e4df8cSKonrad Sztyber 			rc = transport->ops->subsystem_add_host(transport, subsystem, hostnqn,
104984e4df8cSKonrad Sztyber 								SPDK_GET_FIELD(opts, params, NULL));
10506803032dSKrzysztof Karas 			if (rc) {
10516803032dSKrzysztof Karas 				SPDK_ERRLOG("Unable to add host to %s transport\n", transport->ops->name);
10526803032dSKrzysztof Karas 				/* Remove this host from all transports we've managed to add it to. */
10536803032dSKrzysztof Karas 				pthread_mutex_unlock(&subsystem->mutex);
10546803032dSKrzysztof Karas 				spdk_nvmf_subsystem_remove_host(subsystem, hostnqn);
10556803032dSKrzysztof Karas 				return rc;
10566803032dSKrzysztof Karas 			}
10576803032dSKrzysztof Karas 		}
10586803032dSKrzysztof Karas 	}
10596803032dSKrzysztof Karas 
1060d67119d8SBen Walker 	pthread_mutex_unlock(&subsystem->mutex);
1061d67119d8SBen Walker 
106245f04c89SBen Walker 	return 0;
106345f04c89SBen Walker }
106445f04c89SBen Walker 
1065be774bf6SBen Walker int
106684e4df8cSKonrad Sztyber spdk_nvmf_subsystem_add_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn,
106784e4df8cSKonrad Sztyber 			     const struct spdk_json_val *params)
106884e4df8cSKonrad Sztyber {
106984e4df8cSKonrad Sztyber 	struct spdk_nvmf_host_opts opts = {};
107084e4df8cSKonrad Sztyber 
107184e4df8cSKonrad Sztyber 	opts.size = SPDK_SIZEOF(&opts, params);
107284e4df8cSKonrad Sztyber 	opts.params = params;
107384e4df8cSKonrad Sztyber 
107484e4df8cSKonrad Sztyber 	return spdk_nvmf_subsystem_add_host_ext(subsystem, hostnqn, &opts);
107584e4df8cSKonrad Sztyber }
107684e4df8cSKonrad Sztyber 
107784e4df8cSKonrad Sztyber int
10786285e36eSBen Walker spdk_nvmf_subsystem_remove_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn)
10796285e36eSBen Walker {
10806285e36eSBen Walker 	struct spdk_nvmf_host *host;
10816803032dSKrzysztof Karas 	struct spdk_nvmf_transport *transport;
10826285e36eSBen Walker 
1083d67119d8SBen Walker 	pthread_mutex_lock(&subsystem->mutex);
10846285e36eSBen Walker 
108561d85773SSeth Howell 	host = nvmf_subsystem_find_host(subsystem, hostnqn);
10866285e36eSBen Walker 	if (host == NULL) {
1087d67119d8SBen Walker 		pthread_mutex_unlock(&subsystem->mutex);
10886285e36eSBen Walker 		return -ENOENT;
10896285e36eSBen Walker 	}
10906285e36eSBen Walker 
1091d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE2(nvmf_subsystem_remove_host, subsystem->subnqn, host->nqn);
1092d11601e8SKrzysztof Karas 
109361d85773SSeth Howell 	nvmf_subsystem_remove_host(subsystem, host);
10944c1757ffSPeng Lian 
10954c1757ffSPeng Lian 	if (!TAILQ_EMPTY(&subsystem->listeners)) {
10965a8c76d9SAbhineet Pandey 		spdk_nvmf_send_discovery_log_notice(subsystem->tgt, hostnqn);
10974c1757ffSPeng Lian 	}
10984c1757ffSPeng Lian 
10996803032dSKrzysztof Karas 	for (transport = spdk_nvmf_transport_get_first(subsystem->tgt); transport;
11006803032dSKrzysztof Karas 	     transport = spdk_nvmf_transport_get_next(transport)) {
11016803032dSKrzysztof Karas 		if (transport->ops->subsystem_remove_host) {
11026803032dSKrzysztof Karas 			transport->ops->subsystem_remove_host(transport, subsystem, hostnqn);
11036803032dSKrzysztof Karas 		}
11046803032dSKrzysztof Karas 	}
11056803032dSKrzysztof Karas 
1106d67119d8SBen Walker 	pthread_mutex_unlock(&subsystem->mutex);
1107d67119d8SBen Walker 
11086285e36eSBen Walker 	return 0;
11096285e36eSBen Walker }
11106285e36eSBen Walker 
11118a4b7226SKonrad Sztyber int
11128a4b7226SKonrad Sztyber spdk_nvmf_subsystem_set_keys(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn,
11138a4b7226SKonrad Sztyber 			     struct spdk_nvmf_subsystem_key_opts *opts)
11148a4b7226SKonrad Sztyber {
11158a4b7226SKonrad Sztyber 	struct spdk_nvmf_host *host;
11168a4b7226SKonrad Sztyber 	struct spdk_key *key, *ckey;
11178a4b7226SKonrad Sztyber 
11188a4b7226SKonrad Sztyber 	if (!nvmf_auth_is_supported()) {
11198a4b7226SKonrad Sztyber 		SPDK_ERRLOG("NVMe in-band authentication is unsupported\n");
11208a4b7226SKonrad Sztyber 		return -EINVAL;
11218a4b7226SKonrad Sztyber 	}
11228a4b7226SKonrad Sztyber 
11238a4b7226SKonrad Sztyber 	pthread_mutex_lock(&subsystem->mutex);
11248a4b7226SKonrad Sztyber 	host = nvmf_subsystem_find_host(subsystem, hostnqn);
11258a4b7226SKonrad Sztyber 	if (host == NULL) {
11268a4b7226SKonrad Sztyber 		pthread_mutex_unlock(&subsystem->mutex);
11278a4b7226SKonrad Sztyber 		return -EINVAL;
11288a4b7226SKonrad Sztyber 	}
11298a4b7226SKonrad Sztyber 
11308a4b7226SKonrad Sztyber 	if (SPDK_GET_FIELD(opts, dhchap_key, host->dhchap_key) == NULL &&
11318a4b7226SKonrad Sztyber 	    SPDK_GET_FIELD(opts, dhchap_ctrlr_key, host->dhchap_ctrlr_key) != NULL) {
11328a4b7226SKonrad Sztyber 		SPDK_ERRLOG("DH-HMAC-CHAP controller key requires host key to be set\n");
11338a4b7226SKonrad Sztyber 		pthread_mutex_unlock(&subsystem->mutex);
11348a4b7226SKonrad Sztyber 		return -EINVAL;
11358a4b7226SKonrad Sztyber 	}
11368a4b7226SKonrad Sztyber 	key = SPDK_GET_FIELD(opts, dhchap_key, NULL);
11378a4b7226SKonrad Sztyber 	if (key != NULL) {
11388a4b7226SKonrad Sztyber 		key = spdk_key_dup(key);
11398a4b7226SKonrad Sztyber 		if (key == NULL) {
11408a4b7226SKonrad Sztyber 			pthread_mutex_unlock(&subsystem->mutex);
11418a4b7226SKonrad Sztyber 			return -EINVAL;
11428a4b7226SKonrad Sztyber 		}
11438a4b7226SKonrad Sztyber 	}
11448a4b7226SKonrad Sztyber 	ckey = SPDK_GET_FIELD(opts, dhchap_ctrlr_key, NULL);
11458a4b7226SKonrad Sztyber 	if (ckey != NULL) {
11468a4b7226SKonrad Sztyber 		ckey = spdk_key_dup(ckey);
11478a4b7226SKonrad Sztyber 		if (ckey == NULL) {
11488a4b7226SKonrad Sztyber 			pthread_mutex_unlock(&subsystem->mutex);
11498a4b7226SKonrad Sztyber 			spdk_keyring_put_key(key);
11508a4b7226SKonrad Sztyber 			return -EINVAL;
11518a4b7226SKonrad Sztyber 		}
11528a4b7226SKonrad Sztyber 	}
11538a4b7226SKonrad Sztyber 	if (SPDK_FIELD_VALID(opts, dhchap_key)) {
11548a4b7226SKonrad Sztyber 		spdk_keyring_put_key(host->dhchap_key);
11558a4b7226SKonrad Sztyber 		host->dhchap_key = key;
11568a4b7226SKonrad Sztyber 	}
11578a4b7226SKonrad Sztyber 	if (SPDK_FIELD_VALID(opts, dhchap_ctrlr_key)) {
11588a4b7226SKonrad Sztyber 		spdk_keyring_put_key(host->dhchap_ctrlr_key);
11598a4b7226SKonrad Sztyber 		host->dhchap_ctrlr_key = ckey;
11608a4b7226SKonrad Sztyber 	}
11618a4b7226SKonrad Sztyber 	pthread_mutex_unlock(&subsystem->mutex);
11628a4b7226SKonrad Sztyber 
11638a4b7226SKonrad Sztyber 	return 0;
11648a4b7226SKonrad Sztyber }
11658a4b7226SKonrad Sztyber 
11665efa3d61SBen Walker struct nvmf_subsystem_disconnect_host_ctx {
11675efa3d61SBen Walker 	struct spdk_nvmf_subsystem		*subsystem;
11685efa3d61SBen Walker 	char					*hostnqn;
11695efa3d61SBen Walker 	spdk_nvmf_tgt_subsystem_listen_done_fn	cb_fn;
11705efa3d61SBen Walker 	void					*cb_arg;
11715efa3d61SBen Walker };
11725efa3d61SBen Walker 
11735efa3d61SBen Walker static void
11745efa3d61SBen Walker nvmf_subsystem_disconnect_host_fini(struct spdk_io_channel_iter *i, int status)
11755efa3d61SBen Walker {
11765efa3d61SBen Walker 	struct nvmf_subsystem_disconnect_host_ctx *ctx;
11775efa3d61SBen Walker 
11785efa3d61SBen Walker 	ctx = spdk_io_channel_iter_get_ctx(i);
11795efa3d61SBen Walker 
11805efa3d61SBen Walker 	if (ctx->cb_fn) {
11815efa3d61SBen Walker 		ctx->cb_fn(ctx->cb_arg, status);
11825efa3d61SBen Walker 	}
11835efa3d61SBen Walker 	free(ctx->hostnqn);
11845efa3d61SBen Walker 	free(ctx);
11855efa3d61SBen Walker }
11865efa3d61SBen Walker 
11875efa3d61SBen Walker static void
11885efa3d61SBen Walker nvmf_subsystem_disconnect_qpairs_by_host(struct spdk_io_channel_iter *i)
11895efa3d61SBen Walker {
11905efa3d61SBen Walker 	struct nvmf_subsystem_disconnect_host_ctx *ctx;
11915efa3d61SBen Walker 	struct spdk_nvmf_poll_group *group;
11925efa3d61SBen Walker 	struct spdk_io_channel *ch;
11935efa3d61SBen Walker 	struct spdk_nvmf_qpair *qpair, *tmp_qpair;
11945efa3d61SBen Walker 	struct spdk_nvmf_ctrlr *ctrlr;
11955efa3d61SBen Walker 
11965efa3d61SBen Walker 	ctx = spdk_io_channel_iter_get_ctx(i);
11975efa3d61SBen Walker 	ch = spdk_io_channel_iter_get_channel(i);
11985efa3d61SBen Walker 	group = spdk_io_channel_get_ctx(ch);
11995efa3d61SBen Walker 
12005efa3d61SBen Walker 	TAILQ_FOREACH_SAFE(qpair, &group->qpairs, link, tmp_qpair) {
12015efa3d61SBen Walker 		ctrlr = qpair->ctrlr;
12025efa3d61SBen Walker 
12035efa3d61SBen Walker 		if (ctrlr == NULL || ctrlr->subsys != ctx->subsystem) {
12045efa3d61SBen Walker 			continue;
12055efa3d61SBen Walker 		}
12065efa3d61SBen Walker 
12075efa3d61SBen Walker 		if (strncmp(ctrlr->hostnqn, ctx->hostnqn, sizeof(ctrlr->hostnqn)) == 0) {
12085efa3d61SBen Walker 			/* Right now this does not wait for the queue pairs to actually disconnect. */
1209608b54a2SKonrad Sztyber 			spdk_nvmf_qpair_disconnect(qpair);
12105efa3d61SBen Walker 		}
12115efa3d61SBen Walker 	}
12125efa3d61SBen Walker 	spdk_for_each_channel_continue(i, 0);
12135efa3d61SBen Walker }
12145efa3d61SBen Walker 
12155efa3d61SBen Walker int
12165efa3d61SBen Walker spdk_nvmf_subsystem_disconnect_host(struct spdk_nvmf_subsystem *subsystem,
12175efa3d61SBen Walker 				    const char *hostnqn,
12185efa3d61SBen Walker 				    spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn,
12195efa3d61SBen Walker 				    void *cb_arg)
12205efa3d61SBen Walker {
12215efa3d61SBen Walker 	struct nvmf_subsystem_disconnect_host_ctx *ctx;
12225efa3d61SBen Walker 
12235efa3d61SBen Walker 	ctx = calloc(1, sizeof(struct nvmf_subsystem_disconnect_host_ctx));
12245efa3d61SBen Walker 	if (ctx == NULL) {
12255efa3d61SBen Walker 		return -ENOMEM;
12265efa3d61SBen Walker 	}
12275efa3d61SBen Walker 
12285efa3d61SBen Walker 	ctx->hostnqn = strdup(hostnqn);
1229e01a3f1cSZhiqiang Liu 	if (ctx->hostnqn == NULL) {
1230e01a3f1cSZhiqiang Liu 		free(ctx);
1231e01a3f1cSZhiqiang Liu 		return -ENOMEM;
1232e01a3f1cSZhiqiang Liu 	}
1233e01a3f1cSZhiqiang Liu 
1234e01a3f1cSZhiqiang Liu 	ctx->subsystem = subsystem;
12355efa3d61SBen Walker 	ctx->cb_fn = cb_fn;
12365efa3d61SBen Walker 	ctx->cb_arg = cb_arg;
12375efa3d61SBen Walker 
12385efa3d61SBen Walker 	spdk_for_each_channel(subsystem->tgt, nvmf_subsystem_disconnect_qpairs_by_host, ctx,
12395efa3d61SBen Walker 			      nvmf_subsystem_disconnect_host_fini);
12405efa3d61SBen Walker 
12415efa3d61SBen Walker 	return 0;
12425efa3d61SBen Walker }
12435efa3d61SBen Walker 
12446285e36eSBen Walker int
1245a2db49a1SDaniel Verkamp spdk_nvmf_subsystem_set_allow_any_host(struct spdk_nvmf_subsystem *subsystem, bool allow_any_host)
1246a2db49a1SDaniel Verkamp {
1247*b43c2605SAnkit Kumar 	if (subsystem->allow_any_host == allow_any_host) {
1248*b43c2605SAnkit Kumar 		return 0;
1249*b43c2605SAnkit Kumar 	}
1250*b43c2605SAnkit Kumar 
1251eda91a7bSBen Walker 	pthread_mutex_lock(&subsystem->mutex);
1252c06b0c79SKonrad Sztyber 	subsystem->allow_any_host = allow_any_host;
125359f3cdacSJim Harris 	if (!TAILQ_EMPTY(&subsystem->listeners)) {
12545a8c76d9SAbhineet Pandey 		spdk_nvmf_send_discovery_log_notice(subsystem->tgt, NULL);
125559f3cdacSJim Harris 	}
1256eda91a7bSBen Walker 	pthread_mutex_unlock(&subsystem->mutex);
1257be774bf6SBen Walker 
1258be774bf6SBen Walker 	return 0;
1259a2db49a1SDaniel Verkamp }
1260a2db49a1SDaniel Verkamp 
1261a2db49a1SDaniel Verkamp bool
1262a2db49a1SDaniel Verkamp spdk_nvmf_subsystem_get_allow_any_host(const struct spdk_nvmf_subsystem *subsystem)
1263a2db49a1SDaniel Verkamp {
1264eda91a7bSBen Walker 	bool allow_any_host;
1265eda91a7bSBen Walker 	struct spdk_nvmf_subsystem *sub;
1266eda91a7bSBen Walker 
1267eda91a7bSBen Walker 	/* Technically, taking the mutex modifies data in the subsystem. But the const
1268eda91a7bSBen Walker 	 * is still important to convey that this doesn't mutate any other data. Cast
1269eda91a7bSBen Walker 	 * it away to work around this. */
1270eda91a7bSBen Walker 	sub = (struct spdk_nvmf_subsystem *)subsystem;
1271eda91a7bSBen Walker 
1272eda91a7bSBen Walker 	pthread_mutex_lock(&sub->mutex);
1273c06b0c79SKonrad Sztyber 	allow_any_host = sub->allow_any_host;
1274eda91a7bSBen Walker 	pthread_mutex_unlock(&sub->mutex);
1275eda91a7bSBen Walker 
1276eda91a7bSBen Walker 	return allow_any_host;
1277a2db49a1SDaniel Verkamp }
1278a2db49a1SDaniel Verkamp 
127945f04c89SBen Walker bool
128045f04c89SBen Walker spdk_nvmf_subsystem_host_allowed(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn)
128145f04c89SBen Walker {
1282d67119d8SBen Walker 	bool allowed;
1283d67119d8SBen Walker 
128445f04c89SBen Walker 	if (!hostnqn) {
128545f04c89SBen Walker 		return false;
128645f04c89SBen Walker 	}
128745f04c89SBen Walker 
1288eda91a7bSBen Walker 	pthread_mutex_lock(&subsystem->mutex);
1289eda91a7bSBen Walker 
1290c06b0c79SKonrad Sztyber 	if (subsystem->allow_any_host) {
1291eda91a7bSBen Walker 		pthread_mutex_unlock(&subsystem->mutex);
129245f04c89SBen Walker 		return true;
129345f04c89SBen Walker 	}
129445f04c89SBen Walker 
1295d67119d8SBen Walker 	allowed =  nvmf_subsystem_find_host(subsystem, hostnqn) != NULL;
1296d67119d8SBen Walker 	pthread_mutex_unlock(&subsystem->mutex);
1297d67119d8SBen Walker 
1298d67119d8SBen Walker 	return allowed;
129945f04c89SBen Walker }
130045f04c89SBen Walker 
13010a6bb8caSKonrad Sztyber bool
13020a6bb8caSKonrad Sztyber nvmf_subsystem_host_auth_required(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn)
13030a6bb8caSKonrad Sztyber {
13040a6bb8caSKonrad Sztyber 	struct spdk_nvmf_host *host;
13050a6bb8caSKonrad Sztyber 	bool status;
13060a6bb8caSKonrad Sztyber 
13070a6bb8caSKonrad Sztyber 	pthread_mutex_lock(&subsystem->mutex);
13080a6bb8caSKonrad Sztyber 	host = nvmf_subsystem_find_host(subsystem, hostnqn);
13090a6bb8caSKonrad Sztyber 	status = host != NULL && host->dhchap_key != NULL;
13100a6bb8caSKonrad Sztyber 	pthread_mutex_unlock(&subsystem->mutex);
13110a6bb8caSKonrad Sztyber 
13120a6bb8caSKonrad Sztyber 	return status;
13130a6bb8caSKonrad Sztyber }
13140a6bb8caSKonrad Sztyber 
13152b14ffc3SKonrad Sztyber struct spdk_key *
1316e622de2cSKonrad Sztyber nvmf_subsystem_get_dhchap_key(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn,
1317e622de2cSKonrad Sztyber 			      enum nvmf_auth_key_type type)
13182b14ffc3SKonrad Sztyber {
13192b14ffc3SKonrad Sztyber 	struct spdk_nvmf_host *host;
13202b14ffc3SKonrad Sztyber 	struct spdk_key *key = NULL;
13212b14ffc3SKonrad Sztyber 
13222b14ffc3SKonrad Sztyber 	pthread_mutex_lock(&subsystem->mutex);
13232b14ffc3SKonrad Sztyber 	host = nvmf_subsystem_find_host(subsystem, hostnqn);
1324e622de2cSKonrad Sztyber 	if (host != NULL) {
1325e622de2cSKonrad Sztyber 		switch (type) {
1326e622de2cSKonrad Sztyber 		case NVMF_AUTH_KEY_HOST:
1327e622de2cSKonrad Sztyber 			key = host->dhchap_key;
1328e622de2cSKonrad Sztyber 			break;
1329e622de2cSKonrad Sztyber 		case NVMF_AUTH_KEY_CTRLR:
1330e622de2cSKonrad Sztyber 			key = host->dhchap_ctrlr_key;
1331e622de2cSKonrad Sztyber 			break;
1332e622de2cSKonrad Sztyber 		}
1333e622de2cSKonrad Sztyber 		if (key != NULL) {
1334e622de2cSKonrad Sztyber 			key = spdk_key_dup(key);
1335e622de2cSKonrad Sztyber 		}
13362b14ffc3SKonrad Sztyber 	}
13372b14ffc3SKonrad Sztyber 	pthread_mutex_unlock(&subsystem->mutex);
13382b14ffc3SKonrad Sztyber 
13392b14ffc3SKonrad Sztyber 	return key;
13402b14ffc3SKonrad Sztyber }
13412b14ffc3SKonrad Sztyber 
134245f04c89SBen Walker struct spdk_nvmf_host *
134345f04c89SBen Walker spdk_nvmf_subsystem_get_first_host(struct spdk_nvmf_subsystem *subsystem)
134445f04c89SBen Walker {
134545f04c89SBen Walker 	return TAILQ_FIRST(&subsystem->hosts);
134645f04c89SBen Walker }
134745f04c89SBen Walker 
134845f04c89SBen Walker 
134945f04c89SBen Walker struct spdk_nvmf_host *
135045f04c89SBen Walker spdk_nvmf_subsystem_get_next_host(struct spdk_nvmf_subsystem *subsystem,
135145f04c89SBen Walker 				  struct spdk_nvmf_host *prev_host)
135245f04c89SBen Walker {
135345f04c89SBen Walker 	return TAILQ_NEXT(prev_host, link);
135445f04c89SBen Walker }
135545f04c89SBen Walker 
135645f04c89SBen Walker const char *
1357fb712988SJacek Kalwas spdk_nvmf_host_get_nqn(const struct spdk_nvmf_host *host)
135845f04c89SBen Walker {
135945f04c89SBen Walker 	return host->nqn;
136045f04c89SBen Walker }
136145f04c89SBen Walker 
1362c79d4eefSJacek Kalwas struct spdk_nvmf_subsystem_listener *
13639cb21ad6SSeth Howell nvmf_subsystem_find_listener(struct spdk_nvmf_subsystem *subsystem,
13646ad3a5ceSDaniel Verkamp 			     const struct spdk_nvme_transport_id *trid)
13656ad3a5ceSDaniel Verkamp {
1366c79d4eefSJacek Kalwas 	struct spdk_nvmf_subsystem_listener *listener;
13676ad3a5ceSDaniel Verkamp 
13686ad3a5ceSDaniel Verkamp 	TAILQ_FOREACH(listener, &subsystem->listeners, link) {
13696d8f1fc6SJacek Kalwas 		if (spdk_nvme_transport_id_compare(listener->trid, trid) == 0) {
13706ad3a5ceSDaniel Verkamp 			return listener;
13716ad3a5ceSDaniel Verkamp 		}
13726ad3a5ceSDaniel Verkamp 	}
13736ad3a5ceSDaniel Verkamp 
13746ad3a5ceSDaniel Verkamp 	return NULL;
13756ad3a5ceSDaniel Verkamp }
13766ad3a5ceSDaniel Verkamp 
137740529e5dSBen Walker /**
137840529e5dSBen Walker  * Function to be called once the target is listening.
137940529e5dSBen Walker  *
138040529e5dSBen Walker  * \param ctx Context argument passed to this function.
138140529e5dSBen Walker  * \param status 0 if it completed successfully, or negative errno if it failed.
138240529e5dSBen Walker  */
138340529e5dSBen Walker static void
138440529e5dSBen Walker _nvmf_subsystem_add_listener_done(void *ctx, int status)
138540529e5dSBen Walker {
138640529e5dSBen Walker 	struct spdk_nvmf_subsystem_listener *listener = ctx;
138740529e5dSBen Walker 
138840529e5dSBen Walker 	if (status) {
138940529e5dSBen Walker 		listener->cb_fn(listener->cb_arg, status);
139040529e5dSBen Walker 		free(listener);
139140529e5dSBen Walker 		return;
139240529e5dSBen Walker 	}
139340529e5dSBen Walker 
139440529e5dSBen Walker 	TAILQ_INSERT_HEAD(&listener->subsystem->listeners, listener, link);
1395001db1eaSkyuho.son 
1396001db1eaSkyuho.son 	if (spdk_nvmf_subsystem_is_discovery(listener->subsystem)) {
1397001db1eaSkyuho.son 		status = nvmf_tgt_update_mdns_prr(listener->subsystem->tgt);
1398001db1eaSkyuho.son 		if (status) {
1399001db1eaSkyuho.son 			TAILQ_REMOVE(&listener->subsystem->listeners, listener, link);
1400001db1eaSkyuho.son 			listener->cb_fn(listener->cb_arg, status);
1401001db1eaSkyuho.son 			free(listener);
1402001db1eaSkyuho.son 			return;
1403001db1eaSkyuho.son 		}
1404001db1eaSkyuho.son 	}
1405001db1eaSkyuho.son 
14065a8c76d9SAbhineet Pandey 	spdk_nvmf_send_discovery_log_notice(listener->subsystem->tgt, NULL);
140740529e5dSBen Walker 	listener->cb_fn(listener->cb_arg, status);
140840529e5dSBen Walker }
140940529e5dSBen Walker 
14106fc8c8c2SBen Walker void
14117662387cSKrzysztof Karas spdk_nvmf_subsystem_listener_opts_init(struct spdk_nvmf_listener_opts *opts, size_t size)
14127662387cSKrzysztof Karas {
14137662387cSKrzysztof Karas 	if (opts == NULL) {
14147662387cSKrzysztof Karas 		SPDK_ERRLOG("opts should not be NULL\n");
14157662387cSKrzysztof Karas 		assert(false);
14167662387cSKrzysztof Karas 		return;
14177662387cSKrzysztof Karas 	}
14187662387cSKrzysztof Karas 	if (size == 0) {
14197662387cSKrzysztof Karas 		SPDK_ERRLOG("size should not be zero\n");
14207662387cSKrzysztof Karas 		assert(false);
14217662387cSKrzysztof Karas 		return;
14227662387cSKrzysztof Karas 	}
14237662387cSKrzysztof Karas 
14247662387cSKrzysztof Karas 	memset(opts, 0, size);
14257662387cSKrzysztof Karas 	opts->opts_size = size;
14267662387cSKrzysztof Karas 
14277662387cSKrzysztof Karas #define FIELD_OK(field) \
14287662387cSKrzysztof Karas 	offsetof(struct spdk_nvmf_listener_opts, field) + sizeof(opts->field) <= size
14297662387cSKrzysztof Karas 
14307662387cSKrzysztof Karas #define SET_FIELD(field, value) \
14317662387cSKrzysztof Karas 	if (FIELD_OK(field)) { \
14327662387cSKrzysztof Karas 		opts->field = value; \
14337662387cSKrzysztof Karas 	} \
14347662387cSKrzysztof Karas 
14357662387cSKrzysztof Karas 	SET_FIELD(secure_channel, false);
1436ecbceb22SMarcin Spiewak 	SET_FIELD(ana_state, SPDK_NVME_ANA_OPTIMIZED_STATE);
1437248c547dSKarl Bonde Torp 	SET_FIELD(sock_impl, NULL);
14387662387cSKrzysztof Karas 
14397662387cSKrzysztof Karas #undef FIELD_OK
14407662387cSKrzysztof Karas #undef SET_FIELD
14417662387cSKrzysztof Karas }
14427662387cSKrzysztof Karas 
14437662387cSKrzysztof Karas static int
14447662387cSKrzysztof Karas listener_opts_copy(struct spdk_nvmf_listener_opts *src, struct spdk_nvmf_listener_opts *dst)
14457662387cSKrzysztof Karas {
14467662387cSKrzysztof Karas 	if (src->opts_size == 0) {
14477662387cSKrzysztof Karas 		SPDK_ERRLOG("source structure size should not be zero\n");
14487662387cSKrzysztof Karas 		assert(false);
14497662387cSKrzysztof Karas 		return -EINVAL;
14507662387cSKrzysztof Karas 	}
14517662387cSKrzysztof Karas 
14527662387cSKrzysztof Karas 	memset(dst, 0, sizeof(*dst));
14537662387cSKrzysztof Karas 	dst->opts_size = src->opts_size;
14547662387cSKrzysztof Karas 
14557662387cSKrzysztof Karas #define FIELD_OK(field) \
14567662387cSKrzysztof Karas 	offsetof(struct spdk_nvmf_listener_opts, field) + sizeof(src->field) <= src->opts_size
14577662387cSKrzysztof Karas 
14587662387cSKrzysztof Karas #define SET_FIELD(field) \
14597662387cSKrzysztof Karas 	if (FIELD_OK(field)) { \
14607662387cSKrzysztof Karas 		dst->field = src->field; \
14617662387cSKrzysztof Karas 	} \
14627662387cSKrzysztof Karas 
14637662387cSKrzysztof Karas 	SET_FIELD(secure_channel);
1464ecbceb22SMarcin Spiewak 	SET_FIELD(ana_state);
1465248c547dSKarl Bonde Torp 	SET_FIELD(sock_impl);
14667662387cSKrzysztof Karas 	/* We should not remove this statement, but need to update the assert statement
14677662387cSKrzysztof Karas 	 * if we add a new field, and also add a corresponding SET_FIELD statement. */
1468248c547dSKarl Bonde Torp 	SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_listener_opts) == 24, "Incorrect size");
14697662387cSKrzysztof Karas 
14707662387cSKrzysztof Karas #undef SET_FIELD
14717662387cSKrzysztof Karas #undef FIELD_OK
14727662387cSKrzysztof Karas 
14737662387cSKrzysztof Karas 	return 0;
14747662387cSKrzysztof Karas }
14757662387cSKrzysztof Karas 
14767662387cSKrzysztof Karas static void
14777662387cSKrzysztof Karas _nvmf_subsystem_add_listener(struct spdk_nvmf_subsystem *subsystem,
14786fc8c8c2SBen Walker 			     struct spdk_nvme_transport_id *trid,
14796fc8c8c2SBen Walker 			     spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn,
14807662387cSKrzysztof Karas 			     void *cb_arg, struct spdk_nvmf_listener_opts *opts)
14814cdd929bSGangCao {
148265cac5fbSDaniel Verkamp 	struct spdk_nvmf_transport *transport;
1483c79d4eefSJacek Kalwas 	struct spdk_nvmf_subsystem_listener *listener;
14846d8f1fc6SJacek Kalwas 	struct spdk_nvmf_listener *tr_listener;
1485785d10b5SShuhei Matsumoto 	uint32_t i;
1486a4d132cdSJim Harris 	uint32_t id;
14878387e97fSJacek Kalwas 	int rc = 0;
14884cdd929bSGangCao 
14896fc8c8c2SBen Walker 	assert(cb_fn != NULL);
14906fc8c8c2SBen Walker 
1491be774bf6SBen Walker 	if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE ||
1492be774bf6SBen Walker 	      subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) {
14936fc8c8c2SBen Walker 		cb_fn(cb_arg, -EAGAIN);
14946fc8c8c2SBen Walker 		return;
1495be774bf6SBen Walker 	}
1496be774bf6SBen Walker 
14979cb21ad6SSeth Howell 	if (nvmf_subsystem_find_listener(subsystem, trid)) {
14986ad3a5ceSDaniel Verkamp 		/* Listener already exists in this subsystem */
14996fc8c8c2SBen Walker 		cb_fn(cb_arg, 0);
15006fc8c8c2SBen Walker 		return;
15016ad3a5ceSDaniel Verkamp 	}
15026ad3a5ceSDaniel Verkamp 
1503b397546eSSeth Howell 	transport = spdk_nvmf_tgt_get_transport(subsystem->tgt, trid->trstring);
1504de8ac98bSJacek Kalwas 	if (!transport) {
1505de8ac98bSJacek Kalwas 		SPDK_ERRLOG("Unable to find %s transport. The transport must be created first also make sure it is properly registered.\n",
1506de8ac98bSJacek Kalwas 			    trid->trstring);
15076fc8c8c2SBen Walker 		cb_fn(cb_arg, -EINVAL);
15086fc8c8c2SBen Walker 		return;
150965cac5fbSDaniel Verkamp 	}
151065cac5fbSDaniel Verkamp 
15119cb21ad6SSeth Howell 	tr_listener = nvmf_transport_find_listener(transport, trid);
15126d8f1fc6SJacek Kalwas 	if (!tr_listener) {
15136d8f1fc6SJacek Kalwas 		SPDK_ERRLOG("Cannot find transport listener for %s\n", trid->traddr);
15146fc8c8c2SBen Walker 		cb_fn(cb_arg, -EINVAL);
15156fc8c8c2SBen Walker 		return;
15166d8f1fc6SJacek Kalwas 	}
15176d8f1fc6SJacek Kalwas 
1518683d4a4cSBen Walker 	listener = calloc(1, sizeof(*listener));
1519683d4a4cSBen Walker 	if (!listener) {
15206fc8c8c2SBen Walker 		cb_fn(cb_arg, -ENOMEM);
15216fc8c8c2SBen Walker 		return;
15222641c31aSChangpeng Liu 	}
15232641c31aSChangpeng Liu 
15246d8f1fc6SJacek Kalwas 	listener->trid = &tr_listener->trid;
152565cac5fbSDaniel Verkamp 	listener->transport = transport;
152640529e5dSBen Walker 	listener->cb_fn = cb_fn;
152740529e5dSBen Walker 	listener->cb_arg = cb_arg;
152840529e5dSBen Walker 	listener->subsystem = subsystem;
1529785d10b5SShuhei Matsumoto 	listener->ana_state = calloc(subsystem->max_nsid, sizeof(enum spdk_nvme_ana_state));
1530785d10b5SShuhei Matsumoto 	if (!listener->ana_state) {
1531785d10b5SShuhei Matsumoto 		free(listener);
1532785d10b5SShuhei Matsumoto 		cb_fn(cb_arg, -ENOMEM);
1533785d10b5SShuhei Matsumoto 		return;
1534785d10b5SShuhei Matsumoto 	}
1535785d10b5SShuhei Matsumoto 
15367662387cSKrzysztof Karas 	spdk_nvmf_subsystem_listener_opts_init(&listener->opts, sizeof(listener->opts));
15377662387cSKrzysztof Karas 	if (opts != NULL) {
15387662387cSKrzysztof Karas 		rc = listener_opts_copy(opts, &listener->opts);
15397662387cSKrzysztof Karas 		if (rc) {
15407662387cSKrzysztof Karas 			SPDK_ERRLOG("Unable to copy listener options\n");
15417662387cSKrzysztof Karas 			free(listener->ana_state);
15427662387cSKrzysztof Karas 			free(listener);
15437662387cSKrzysztof Karas 			cb_fn(cb_arg, -EINVAL);
15447662387cSKrzysztof Karas 			return;
15457662387cSKrzysztof Karas 		}
15467662387cSKrzysztof Karas 	}
15477662387cSKrzysztof Karas 
1548a4d132cdSJim Harris 	id = spdk_bit_array_find_first_clear(subsystem->used_listener_ids, 0);
1549a4d132cdSJim Harris 	if (id == UINT32_MAX) {
1550a4d132cdSJim Harris 		SPDK_ERRLOG("Cannot add any more listeners\n");
1551a4d132cdSJim Harris 		free(listener->ana_state);
1552248c547dSKarl Bonde Torp 		free(listener->opts.sock_impl);
1553a4d132cdSJim Harris 		free(listener);
1554a4d132cdSJim Harris 		cb_fn(cb_arg, -EINVAL);
1555a4d132cdSJim Harris 		return;
1556a4d132cdSJim Harris 	}
1557a4d132cdSJim Harris 
1558a4d132cdSJim Harris 	spdk_bit_array_set(subsystem->used_listener_ids, id);
1559a4d132cdSJim Harris 	listener->id = id;
1560a4d132cdSJim Harris 
1561785d10b5SShuhei Matsumoto 	for (i = 0; i < subsystem->max_nsid; i++) {
1562ecbceb22SMarcin Spiewak 		listener->ana_state[i] = listener->opts.ana_state;
1563785d10b5SShuhei Matsumoto 	}
15644cdd929bSGangCao 
156540529e5dSBen Walker 	if (transport->ops->listen_associate != NULL) {
15668387e97fSJacek Kalwas 		rc = transport->ops->listen_associate(transport, subsystem, trid);
156740529e5dSBen Walker 	}
15688387e97fSJacek Kalwas 
1569d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE4(nvmf_subsystem_add_listener, subsystem->subnqn, listener->trid->trtype,
1570d11601e8SKrzysztof Karas 			   listener->trid->traddr, listener->trid->trsvcid);
1571d11601e8SKrzysztof Karas 
15728387e97fSJacek Kalwas 	_nvmf_subsystem_add_listener_done(listener, rc);
15732b9d85c4SBen Walker }
15742b9d85c4SBen Walker 
15757662387cSKrzysztof Karas void
15767662387cSKrzysztof Karas spdk_nvmf_subsystem_add_listener(struct spdk_nvmf_subsystem *subsystem,
15777662387cSKrzysztof Karas 				 struct spdk_nvme_transport_id *trid,
15787662387cSKrzysztof Karas 				 spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn,
15797662387cSKrzysztof Karas 				 void *cb_arg)
15807662387cSKrzysztof Karas {
15817662387cSKrzysztof Karas 	_nvmf_subsystem_add_listener(subsystem, trid, cb_fn, cb_arg, NULL);
15827662387cSKrzysztof Karas }
15837662387cSKrzysztof Karas 
15847662387cSKrzysztof Karas void
15857662387cSKrzysztof Karas spdk_nvmf_subsystem_add_listener_ext(struct spdk_nvmf_subsystem *subsystem,
15867662387cSKrzysztof Karas 				     struct spdk_nvme_transport_id *trid,
15877662387cSKrzysztof Karas 				     spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn,
15887662387cSKrzysztof Karas 				     void *cb_arg, struct spdk_nvmf_listener_opts *opts)
15897662387cSKrzysztof Karas {
15907662387cSKrzysztof Karas 	_nvmf_subsystem_add_listener(subsystem, trid, cb_fn, cb_arg, opts);
15917662387cSKrzysztof Karas }
15927662387cSKrzysztof Karas 
159354bfde6aSDaniel Verkamp int
159454bfde6aSDaniel Verkamp spdk_nvmf_subsystem_remove_listener(struct spdk_nvmf_subsystem *subsystem,
159554bfde6aSDaniel Verkamp 				    const struct spdk_nvme_transport_id *trid)
159654bfde6aSDaniel Verkamp {
1597c79d4eefSJacek Kalwas 	struct spdk_nvmf_subsystem_listener *listener;
159854bfde6aSDaniel Verkamp 
159954bfde6aSDaniel Verkamp 	if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE ||
160054bfde6aSDaniel Verkamp 	      subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) {
160154bfde6aSDaniel Verkamp 		return -EAGAIN;
160254bfde6aSDaniel Verkamp 	}
160354bfde6aSDaniel Verkamp 
16049cb21ad6SSeth Howell 	listener = nvmf_subsystem_find_listener(subsystem, trid);
160554bfde6aSDaniel Verkamp 	if (listener == NULL) {
160654bfde6aSDaniel Verkamp 		return -ENOENT;
160754bfde6aSDaniel Verkamp 	}
160854bfde6aSDaniel Verkamp 
1609d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE4(nvmf_subsystem_remove_listener, subsystem->subnqn, listener->trid->trtype,
1610d11601e8SKrzysztof Karas 			   listener->trid->traddr, listener->trid->trsvcid);
1611d11601e8SKrzysztof Karas 
16122167c68dSJan Kryl 	_nvmf_subsystem_remove_listener(subsystem, listener, false);
161354bfde6aSDaniel Verkamp 
161454bfde6aSDaniel Verkamp 	return 0;
161554bfde6aSDaniel Verkamp }
161654bfde6aSDaniel Verkamp 
16172167c68dSJan Kryl void
16189cb21ad6SSeth Howell nvmf_subsystem_remove_all_listeners(struct spdk_nvmf_subsystem *subsystem,
16192167c68dSJan Kryl 				    bool stop)
16202167c68dSJan Kryl {
1621c79d4eefSJacek Kalwas 	struct spdk_nvmf_subsystem_listener *listener, *listener_tmp;
16222167c68dSJan Kryl 
16232167c68dSJan Kryl 	TAILQ_FOREACH_SAFE(listener, &subsystem->listeners, link, listener_tmp) {
16242167c68dSJan Kryl 		_nvmf_subsystem_remove_listener(subsystem, listener, stop);
16252167c68dSJan Kryl 	}
16262167c68dSJan Kryl }
16272167c68dSJan Kryl 
16284cdd929bSGangCao bool
16294cdd929bSGangCao spdk_nvmf_subsystem_listener_allowed(struct spdk_nvmf_subsystem *subsystem,
16306913a864SJacek Kalwas 				     const struct spdk_nvme_transport_id *trid)
16314cdd929bSGangCao {
1632c79d4eefSJacek Kalwas 	struct spdk_nvmf_subsystem_listener *listener;
16334cdd929bSGangCao 
1634683d4a4cSBen Walker 	TAILQ_FOREACH(listener, &subsystem->listeners, link) {
16356d8f1fc6SJacek Kalwas 		if (spdk_nvme_transport_id_compare(listener->trid, trid) == 0) {
16364cdd929bSGangCao 			return true;
16374cdd929bSGangCao 		}
16384cdd929bSGangCao 	}
16394cdd929bSGangCao 
164025bc221cSJim Harris 	if (!strcmp(subsystem->subnqn, SPDK_NVMF_DISCOVERY_NQN)) {
164125bc221cSJim Harris 		SPDK_WARNLOG("Allowing connection to discovery subsystem on %s/%s/%s, "
164225bc221cSJim Harris 			     "even though this listener was not added to the discovery "
164325bc221cSJim Harris 			     "subsystem.  This behavior is deprecated and will be removed "
164425bc221cSJim Harris 			     "in a future release.\n",
164525bc221cSJim Harris 			     spdk_nvme_transport_id_trtype_str(trid->trtype), trid->traddr, trid->trsvcid);
164625bc221cSJim Harris 		return true;
164725bc221cSJim Harris 	}
164825bc221cSJim Harris 
16494cdd929bSGangCao 	return false;
16504cdd929bSGangCao }
16514cdd929bSGangCao 
1652c79d4eefSJacek Kalwas struct spdk_nvmf_subsystem_listener *
1653683d4a4cSBen Walker spdk_nvmf_subsystem_get_first_listener(struct spdk_nvmf_subsystem *subsystem)
1654683d4a4cSBen Walker {
1655683d4a4cSBen Walker 	return TAILQ_FIRST(&subsystem->listeners);
1656683d4a4cSBen Walker }
1657683d4a4cSBen Walker 
1658c79d4eefSJacek Kalwas struct spdk_nvmf_subsystem_listener *
1659683d4a4cSBen Walker spdk_nvmf_subsystem_get_next_listener(struct spdk_nvmf_subsystem *subsystem,
1660c79d4eefSJacek Kalwas 				      struct spdk_nvmf_subsystem_listener *prev_listener)
1661683d4a4cSBen Walker {
1662683d4a4cSBen Walker 	return TAILQ_NEXT(prev_listener, link);
1663683d4a4cSBen Walker }
1664683d4a4cSBen Walker 
1665683d4a4cSBen Walker const struct spdk_nvme_transport_id *
1666c79d4eefSJacek Kalwas spdk_nvmf_subsystem_listener_get_trid(struct spdk_nvmf_subsystem_listener *listener)
1667683d4a4cSBen Walker {
16686d8f1fc6SJacek Kalwas 	return listener->trid;
1669683d4a4cSBen Walker }
1670683d4a4cSBen Walker 
16715e25bbddSAnil Veerabhadrappa void
16725e25bbddSAnil Veerabhadrappa spdk_nvmf_subsystem_allow_any_listener(struct spdk_nvmf_subsystem *subsystem,
16735e25bbddSAnil Veerabhadrappa 				       bool allow_any_listener)
16745e25bbddSAnil Veerabhadrappa {
16755e4e4bc4SBen Walker 	subsystem->flags.allow_any_listener = allow_any_listener;
16765e25bbddSAnil Veerabhadrappa }
16775e25bbddSAnil Veerabhadrappa 
16780165cd07SSlawomir Ptak bool
16790165cd07SSlawomir Ptak spdk_nvmf_subsystem_any_listener_allowed(struct spdk_nvmf_subsystem *subsystem)
16800165cd07SSlawomir Ptak {
16810165cd07SSlawomir Ptak 	return subsystem->flags.allow_any_listener;
16820165cd07SSlawomir Ptak }
16835e25bbddSAnil Veerabhadrappa 
16844ca87a01SSeth Howell struct subsystem_update_ns_ctx {
16854ca87a01SSeth Howell 	struct spdk_nvmf_subsystem *subsystem;
16864ca87a01SSeth Howell 
16874ca87a01SSeth Howell 	spdk_nvmf_subsystem_state_change_done cb_fn;
16884ca87a01SSeth Howell 	void *cb_arg;
16894ca87a01SSeth Howell };
16904ca87a01SSeth Howell 
16914ca87a01SSeth Howell static void
16924ca87a01SSeth Howell subsystem_update_ns_done(struct spdk_io_channel_iter *i, int status)
16934ca87a01SSeth Howell {
16944ca87a01SSeth Howell 	struct subsystem_update_ns_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
16954ca87a01SSeth Howell 
16964ca87a01SSeth Howell 	if (ctx->cb_fn) {
16974ca87a01SSeth Howell 		ctx->cb_fn(ctx->subsystem, ctx->cb_arg, status);
16984ca87a01SSeth Howell 	}
16994ca87a01SSeth Howell 	free(ctx);
17004ca87a01SSeth Howell }
17014ca87a01SSeth Howell 
17024ca87a01SSeth Howell static void
17034ca87a01SSeth Howell subsystem_update_ns_on_pg(struct spdk_io_channel_iter *i)
17044ca87a01SSeth Howell {
17054ca87a01SSeth Howell 	int rc;
17064ca87a01SSeth Howell 	struct subsystem_update_ns_ctx *ctx;
17074ca87a01SSeth Howell 	struct spdk_nvmf_poll_group *group;
17084ca87a01SSeth Howell 	struct spdk_nvmf_subsystem *subsystem;
17094ca87a01SSeth Howell 
17104ca87a01SSeth Howell 	ctx = spdk_io_channel_iter_get_ctx(i);
17114ca87a01SSeth Howell 	group = spdk_io_channel_get_ctx(spdk_io_channel_iter_get_channel(i));
17124ca87a01SSeth Howell 	subsystem = ctx->subsystem;
17134ca87a01SSeth Howell 
17149cb21ad6SSeth Howell 	rc = nvmf_poll_group_update_subsystem(group, subsystem);
17154ca87a01SSeth Howell 	spdk_for_each_channel_continue(i, rc);
17164ca87a01SSeth Howell }
17174ca87a01SSeth Howell 
17184ca87a01SSeth Howell static int
1719d0fe26b2SArtur Paszkiewicz nvmf_subsystem_update_ns(struct spdk_nvmf_subsystem *subsystem,
1720d0fe26b2SArtur Paszkiewicz 			 spdk_nvmf_subsystem_state_change_done cb_fn, void *cb_arg)
17214ca87a01SSeth Howell {
1722d0fe26b2SArtur Paszkiewicz 	struct subsystem_update_ns_ctx *ctx;
1723d0fe26b2SArtur Paszkiewicz 
1724d0fe26b2SArtur Paszkiewicz 	ctx = calloc(1, sizeof(*ctx));
1725d0fe26b2SArtur Paszkiewicz 	if (ctx == NULL) {
1726d0fe26b2SArtur Paszkiewicz 		SPDK_ERRLOG("Can't alloc subsystem poll group update context\n");
1727d0fe26b2SArtur Paszkiewicz 		return -ENOMEM;
1728d0fe26b2SArtur Paszkiewicz 	}
1729d0fe26b2SArtur Paszkiewicz 	ctx->subsystem = subsystem;
1730d0fe26b2SArtur Paszkiewicz 	ctx->cb_fn = cb_fn;
1731d0fe26b2SArtur Paszkiewicz 	ctx->cb_arg = cb_arg;
1732d0fe26b2SArtur Paszkiewicz 
17334ca87a01SSeth Howell 	spdk_for_each_channel(subsystem->tgt,
17344ca87a01SSeth Howell 			      subsystem_update_ns_on_pg,
17354ca87a01SSeth Howell 			      ctx,
1736d0fe26b2SArtur Paszkiewicz 			      subsystem_update_ns_done);
17374ca87a01SSeth Howell 	return 0;
17384ca87a01SSeth Howell }
17394ca87a01SSeth Howell 
17400f15edeeSChangpeng Liu static void
174163c90491SSeth Howell nvmf_subsystem_ns_changed(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid)
17420f15edeeSChangpeng Liu {
17430f15edeeSChangpeng Liu 	struct spdk_nvmf_ctrlr *ctrlr;
17440f15edeeSChangpeng Liu 
17450f15edeeSChangpeng Liu 	TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) {
1746d37555b4SJonas Pfefferle 		if (nvmf_ctrlr_ns_is_visible(ctrlr, nsid)) {
17479cb21ad6SSeth Howell 			nvmf_ctrlr_ns_changed(ctrlr, nsid);
17480f15edeeSChangpeng Liu 		}
17490f15edeeSChangpeng Liu 	}
1750d37555b4SJonas Pfefferle }
17510f15edeeSChangpeng Liu 
17528dd1cd21SBen Walker static uint32_t nvmf_ns_reservation_clear_all_registrants(struct spdk_nvmf_ns *ns);
17533bb303d9SJacek Kalwas 
175488da8a91SBen Walker int
175588da8a91SBen Walker spdk_nvmf_subsystem_remove_ns(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid)
17567f5864beSCunyin Chang {
17572e26faa9SJacek Kalwas 	struct spdk_nvmf_transport *transport;
17587f5864beSCunyin Chang 	struct spdk_nvmf_ns *ns;
1759d37555b4SJonas Pfefferle 	struct spdk_nvmf_host *host, *tmp;
1760d37555b4SJonas Pfefferle 	struct spdk_nvmf_ctrlr *ctrlr;
17617f5864beSCunyin Chang 
17622d224c0dSJim Harris 	if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE ||
17632d224c0dSJim Harris 	      subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) {
17642d224c0dSJim Harris 		assert(false);
17657f5864beSCunyin Chang 		return -1;
17667f5864beSCunyin Chang 	}
17677f5864beSCunyin Chang 
17682d224c0dSJim Harris 	if (nsid == 0 || nsid > subsystem->max_nsid) {
1769be774bf6SBen Walker 		return -1;
1770be774bf6SBen Walker 	}
1771be774bf6SBen Walker 
17728af4b6c4SDaniel Verkamp 	ns = subsystem->ns[nsid - 1];
17738af4b6c4SDaniel Verkamp 	if (!ns) {
17747f5864beSCunyin Chang 		return -1;
17757f5864beSCunyin Chang 	}
17767f5864beSCunyin Chang 
17778af4b6c4SDaniel Verkamp 	subsystem->ns[nsid - 1] = NULL;
17788af4b6c4SDaniel Verkamp 
177907bfc3cbSShuhei Matsumoto 	assert(ns->anagrpid - 1 < subsystem->max_nsid);
178007bfc3cbSShuhei Matsumoto 	assert(subsystem->ana_group[ns->anagrpid - 1] > 0);
178107bfc3cbSShuhei Matsumoto 
178207bfc3cbSShuhei Matsumoto 	subsystem->ana_group[ns->anagrpid - 1]--;
178307bfc3cbSShuhei Matsumoto 
1784d37555b4SJonas Pfefferle 	TAILQ_FOREACH_SAFE(host, &ns->hosts, link, tmp) {
1785d37555b4SJonas Pfefferle 		nvmf_ns_remove_host(ns, host);
1786d37555b4SJonas Pfefferle 	}
1787d37555b4SJonas Pfefferle 
17883bb303d9SJacek Kalwas 	free(ns->ptpl_file);
17893bb303d9SJacek Kalwas 	nvmf_ns_reservation_clear_all_registrants(ns);
1790ff458be8SChangpeng Liu 	spdk_bdev_module_release_bdev(ns->bdev);
179195ac75aaSBen Walker 	spdk_bdev_close(ns->desc);
17928af4b6c4SDaniel Verkamp 	free(ns);
179395ac75aaSBen Walker 
1794037d5165SAnkit Kumar 	if (subsystem->fdp_supported && !spdk_nvmf_subsystem_get_first_ns(subsystem)) {
1795037d5165SAnkit Kumar 		subsystem->fdp_supported = false;
1796037d5165SAnkit Kumar 		SPDK_DEBUGLOG(nvmf, "Subsystem with id: %u doesn't have FDP capability.\n",
1797037d5165SAnkit Kumar 			      subsystem->id);
1798037d5165SAnkit Kumar 	}
1799037d5165SAnkit Kumar 
18002e26faa9SJacek Kalwas 	for (transport = spdk_nvmf_transport_get_first(subsystem->tgt); transport;
18012e26faa9SJacek Kalwas 	     transport = spdk_nvmf_transport_get_next(transport)) {
18022e26faa9SJacek Kalwas 		if (transport->ops->subsystem_remove_ns) {
18032e26faa9SJacek Kalwas 			transport->ops->subsystem_remove_ns(transport, subsystem, nsid);
18042e26faa9SJacek Kalwas 		}
18052e26faa9SJacek Kalwas 	}
18062e26faa9SJacek Kalwas 
180763c90491SSeth Howell 	nvmf_subsystem_ns_changed(subsystem, nsid);
1808763ab888SChangpeng Liu 
1809d37555b4SJonas Pfefferle 	TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) {
1810bfd014b5SKonrad Sztyber 		nvmf_ctrlr_ns_set_visible(ctrlr, nsid, false);
1811d37555b4SJonas Pfefferle 	}
1812d37555b4SJonas Pfefferle 
18137f5864beSCunyin Chang 	return 0;
18147f5864beSCunyin Chang }
18157f5864beSCunyin Chang 
18168ff8bf07SSeth Howell struct subsystem_ns_change_ctx {
18178ff8bf07SSeth Howell 	struct spdk_nvmf_subsystem		*subsystem;
18188ff8bf07SSeth Howell 	spdk_nvmf_subsystem_state_change_done	cb_fn;
18198ff8bf07SSeth Howell 	uint32_t				nsid;
18208ff8bf07SSeth Howell };
18218ff8bf07SSeth Howell 
18227f5864beSCunyin Chang static void
182363c90491SSeth Howell _nvmf_ns_hot_remove(struct spdk_nvmf_subsystem *subsystem,
18247358fb6fSBen Walker 		    void *cb_arg, int status)
1825bdcb0d70SCunyin Chang {
18268ff8bf07SSeth Howell 	struct subsystem_ns_change_ctx *ctx = cb_arg;
182788da8a91SBen Walker 	int rc;
1828bdcb0d70SCunyin Chang 
18298ff8bf07SSeth Howell 	rc = spdk_nvmf_subsystem_remove_ns(subsystem, ctx->nsid);
183088da8a91SBen Walker 	if (rc != 0) {
183188da8a91SBen Walker 		SPDK_ERRLOG("Failed to make changes to NVME-oF subsystem with id: %u\n", subsystem->id);
183288da8a91SBen Walker 	}
183388da8a91SBen Walker 
1834de02db63SGangCao 	rc = spdk_nvmf_subsystem_resume(subsystem, NULL, NULL);
1835de02db63SGangCao 	if (rc != 0) {
1836de02db63SGangCao 		SPDK_ERRLOG("Failed to resume NVME-oF subsystem with id: %u\n", subsystem->id);
1837de02db63SGangCao 	}
18388ff8bf07SSeth Howell 
18398ff8bf07SSeth Howell 	free(ctx);
18408ff8bf07SSeth Howell }
18418ff8bf07SSeth Howell 
18428ff8bf07SSeth Howell static void
18438ff8bf07SSeth Howell nvmf_ns_change_msg(void *ns_ctx)
18448ff8bf07SSeth Howell {
18458ff8bf07SSeth Howell 	struct subsystem_ns_change_ctx *ctx = ns_ctx;
18468ff8bf07SSeth Howell 	int rc;
18478ff8bf07SSeth Howell 
1848d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE2(nvmf_ns_change, ctx->nsid, ctx->subsystem->subnqn);
1849d11601e8SKrzysztof Karas 
1850312a9d60SBen Walker 	rc = spdk_nvmf_subsystem_pause(ctx->subsystem, ctx->nsid, ctx->cb_fn, ctx);
18518ff8bf07SSeth Howell 	if (rc) {
18528ff8bf07SSeth Howell 		if (rc == -EBUSY) {
18538ff8bf07SSeth Howell 			/* Try again, this is not a permanent situation. */
18548ff8bf07SSeth Howell 			spdk_thread_send_msg(spdk_get_thread(), nvmf_ns_change_msg, ctx);
18558ff8bf07SSeth Howell 		} else {
18568ff8bf07SSeth Howell 			free(ctx);
18578ff8bf07SSeth Howell 			SPDK_ERRLOG("Unable to pause subsystem to process namespace removal!\n");
18588ff8bf07SSeth Howell 		}
18598ff8bf07SSeth Howell 	}
1860bdcb0d70SCunyin Chang }
1861bdcb0d70SCunyin Chang 
1862bdcb0d70SCunyin Chang static void
186363c90491SSeth Howell nvmf_ns_hot_remove(void *remove_ctx)
1864bdcb0d70SCunyin Chang {
1865bdcb0d70SCunyin Chang 	struct spdk_nvmf_ns *ns = remove_ctx;
18668ff8bf07SSeth Howell 	struct subsystem_ns_change_ctx *ns_ctx;
18677358fb6fSBen Walker 	int rc;
1868bdcb0d70SCunyin Chang 
18698ff8bf07SSeth Howell 	/* We have to allocate a new context because this op
18708ff8bf07SSeth Howell 	 * is asynchronous and we could lose the ns in the middle.
18718ff8bf07SSeth Howell 	 */
18728ff8bf07SSeth Howell 	ns_ctx = calloc(1, sizeof(struct subsystem_ns_change_ctx));
18738ff8bf07SSeth Howell 	if (!ns_ctx) {
18748ff8bf07SSeth Howell 		SPDK_ERRLOG("Unable to allocate context to process namespace removal!\n");
18758ff8bf07SSeth Howell 		return;
18768ff8bf07SSeth Howell 	}
18778ff8bf07SSeth Howell 
18788ff8bf07SSeth Howell 	ns_ctx->subsystem = ns->subsystem;
18798ff8bf07SSeth Howell 	ns_ctx->nsid = ns->opts.nsid;
18808ff8bf07SSeth Howell 	ns_ctx->cb_fn = _nvmf_ns_hot_remove;
18818ff8bf07SSeth Howell 
1882312a9d60SBen Walker 	rc = spdk_nvmf_subsystem_pause(ns->subsystem, ns_ctx->nsid, _nvmf_ns_hot_remove, ns_ctx);
18837358fb6fSBen Walker 	if (rc) {
18848ff8bf07SSeth Howell 		if (rc == -EBUSY) {
18858ff8bf07SSeth Howell 			/* Try again, this is not a permanent situation. */
18868ff8bf07SSeth Howell 			spdk_thread_send_msg(spdk_get_thread(), nvmf_ns_change_msg, ns_ctx);
18878ff8bf07SSeth Howell 		} else {
18887358fb6fSBen Walker 			SPDK_ERRLOG("Unable to pause subsystem to process namespace removal!\n");
18898ff8bf07SSeth Howell 			free(ns_ctx);
18908ff8bf07SSeth Howell 		}
18917358fb6fSBen Walker 	}
1892bdcb0d70SCunyin Chang }
1893bdcb0d70SCunyin Chang 
189465757521SEvgeniy Kochetov static void
189563c90491SSeth Howell _nvmf_ns_resize(struct spdk_nvmf_subsystem *subsystem, void *cb_arg, int status)
18963dfbf1fcSEvgeniy Kochetov {
18978ff8bf07SSeth Howell 	struct subsystem_ns_change_ctx *ctx = cb_arg;
18983dfbf1fcSEvgeniy Kochetov 
18998ff8bf07SSeth Howell 	nvmf_subsystem_ns_changed(subsystem, ctx->nsid);
1900de02db63SGangCao 	if (spdk_nvmf_subsystem_resume(subsystem, NULL, NULL) != 0) {
1901de02db63SGangCao 		SPDK_ERRLOG("Failed to resume NVME-oF subsystem with id: %u\n", subsystem->id);
1902de02db63SGangCao 	}
19038ff8bf07SSeth Howell 
19048ff8bf07SSeth Howell 	free(ctx);
19053dfbf1fcSEvgeniy Kochetov }
19063dfbf1fcSEvgeniy Kochetov 
19073dfbf1fcSEvgeniy Kochetov static void
190863c90491SSeth Howell nvmf_ns_resize(void *event_ctx)
19093dfbf1fcSEvgeniy Kochetov {
19103dfbf1fcSEvgeniy Kochetov 	struct spdk_nvmf_ns *ns = event_ctx;
19118ff8bf07SSeth Howell 	struct subsystem_ns_change_ctx *ns_ctx;
19123dfbf1fcSEvgeniy Kochetov 	int rc;
19133dfbf1fcSEvgeniy Kochetov 
19148ff8bf07SSeth Howell 	/* We have to allocate a new context because this op
19158ff8bf07SSeth Howell 	 * is asynchronous and we could lose the ns in the middle.
19168ff8bf07SSeth Howell 	 */
19178ff8bf07SSeth Howell 	ns_ctx = calloc(1, sizeof(struct subsystem_ns_change_ctx));
19188ff8bf07SSeth Howell 	if (!ns_ctx) {
19198ff8bf07SSeth Howell 		SPDK_ERRLOG("Unable to allocate context to process namespace removal!\n");
19208ff8bf07SSeth Howell 		return;
19218ff8bf07SSeth Howell 	}
19228ff8bf07SSeth Howell 
19238ff8bf07SSeth Howell 	ns_ctx->subsystem = ns->subsystem;
19248ff8bf07SSeth Howell 	ns_ctx->nsid = ns->opts.nsid;
19258ff8bf07SSeth Howell 	ns_ctx->cb_fn = _nvmf_ns_resize;
19268ff8bf07SSeth Howell 
1927ab0a3f8fSJim Harris 	/* Specify 0 for the nsid here, because we do not need to pause the namespace.
1928ab0a3f8fSJim Harris 	 * Namespaces can only be resized bigger, so there is no need to quiesce I/O.
1929ab0a3f8fSJim Harris 	 */
1930ab0a3f8fSJim Harris 	rc = spdk_nvmf_subsystem_pause(ns->subsystem, 0, _nvmf_ns_resize, ns_ctx);
19313dfbf1fcSEvgeniy Kochetov 	if (rc) {
19328ff8bf07SSeth Howell 		if (rc == -EBUSY) {
19338ff8bf07SSeth Howell 			/* Try again, this is not a permanent situation. */
19348ff8bf07SSeth Howell 			spdk_thread_send_msg(spdk_get_thread(), nvmf_ns_change_msg, ns_ctx);
1935c4a1b343SAlexey Marchuk 		} else {
19363dfbf1fcSEvgeniy Kochetov 			SPDK_ERRLOG("Unable to pause subsystem to process namespace resize!\n");
19378ff8bf07SSeth Howell 			free(ns_ctx);
19383dfbf1fcSEvgeniy Kochetov 		}
19393dfbf1fcSEvgeniy Kochetov 	}
1940c4a1b343SAlexey Marchuk }
19413dfbf1fcSEvgeniy Kochetov 
19423dfbf1fcSEvgeniy Kochetov static void
194363c90491SSeth Howell nvmf_ns_event(enum spdk_bdev_event_type type,
194465757521SEvgeniy Kochetov 	      struct spdk_bdev *bdev,
194565757521SEvgeniy Kochetov 	      void *event_ctx)
194665757521SEvgeniy Kochetov {
19472172c432STomasz Zawadzki 	SPDK_DEBUGLOG(nvmf, "Bdev event: type %d, name %s, subsystem_id %d, ns_id %d\n",
194865757521SEvgeniy Kochetov 		      type,
1949fa796437SJim Harris 		      spdk_bdev_get_name(bdev),
195065757521SEvgeniy Kochetov 		      ((struct spdk_nvmf_ns *)event_ctx)->subsystem->id,
195165757521SEvgeniy Kochetov 		      ((struct spdk_nvmf_ns *)event_ctx)->nsid);
195265757521SEvgeniy Kochetov 
195365757521SEvgeniy Kochetov 	switch (type) {
195465757521SEvgeniy Kochetov 	case SPDK_BDEV_EVENT_REMOVE:
195563c90491SSeth Howell 		nvmf_ns_hot_remove(event_ctx);
195665757521SEvgeniy Kochetov 		break;
19573dfbf1fcSEvgeniy Kochetov 	case SPDK_BDEV_EVENT_RESIZE:
195863c90491SSeth Howell 		nvmf_ns_resize(event_ctx);
19593dfbf1fcSEvgeniy Kochetov 		break;
196065757521SEvgeniy Kochetov 	default:
196165757521SEvgeniy Kochetov 		SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
196265757521SEvgeniy Kochetov 		break;
196365757521SEvgeniy Kochetov 	}
196465757521SEvgeniy Kochetov }
196565757521SEvgeniy Kochetov 
1966250d342bSDaniel Verkamp void
1967250d342bSDaniel Verkamp spdk_nvmf_ns_opts_get_defaults(struct spdk_nvmf_ns_opts *opts, size_t opts_size)
1968250d342bSDaniel Verkamp {
1969d2b9b2afSShuhei Matsumoto 	if (!opts) {
1970d2b9b2afSShuhei Matsumoto 		SPDK_ERRLOG("opts should not be NULL.\n");
1971d2b9b2afSShuhei Matsumoto 		return;
1972d2b9b2afSShuhei Matsumoto 	}
1973d2b9b2afSShuhei Matsumoto 
1974d2b9b2afSShuhei Matsumoto 	if (!opts_size) {
1975d2b9b2afSShuhei Matsumoto 		SPDK_ERRLOG("opts_size should not be zero.\n");
1976d2b9b2afSShuhei Matsumoto 		return;
1977d2b9b2afSShuhei Matsumoto 	}
1978d2b9b2afSShuhei Matsumoto 
1979250d342bSDaniel Verkamp 	memset(opts, 0, opts_size);
1980d2b9b2afSShuhei Matsumoto 	opts->opts_size = opts_size;
1981d2b9b2afSShuhei Matsumoto 
1982d2b9b2afSShuhei Matsumoto #define FIELD_OK(field) \
1983d2b9b2afSShuhei Matsumoto 	offsetof(struct spdk_nvmf_ns_opts, field) + sizeof(opts->field) <= opts_size
1984d2b9b2afSShuhei Matsumoto 
1985d2b9b2afSShuhei Matsumoto #define SET_FIELD(field, value) \
1986d2b9b2afSShuhei Matsumoto 	if (FIELD_OK(field)) { \
1987d2b9b2afSShuhei Matsumoto 		opts->field = value; \
1988d2b9b2afSShuhei Matsumoto 	} \
1989d2b9b2afSShuhei Matsumoto 
1990d2b9b2afSShuhei Matsumoto 	/* All current fields are set to 0 by default. */
1991d2b9b2afSShuhei Matsumoto 	SET_FIELD(nsid, 0);
1992d2b9b2afSShuhei Matsumoto 	if (FIELD_OK(nguid)) {
1993d2b9b2afSShuhei Matsumoto 		memset(opts->nguid, 0, sizeof(opts->nguid));
1994d2b9b2afSShuhei Matsumoto 	}
1995d2b9b2afSShuhei Matsumoto 	if (FIELD_OK(eui64)) {
1996d2b9b2afSShuhei Matsumoto 		memset(opts->eui64, 0, sizeof(opts->eui64));
1997d2b9b2afSShuhei Matsumoto 	}
1998d2b9b2afSShuhei Matsumoto 	if (FIELD_OK(uuid)) {
199995a367d6SArtur Paszkiewicz 		spdk_uuid_set_null(&opts->uuid);
2000d2b9b2afSShuhei Matsumoto 	}
200107bfc3cbSShuhei Matsumoto 	SET_FIELD(anagrpid, 0);
20025e952d84SJacek Kalwas 	SET_FIELD(transport_specific, NULL);
2003afdec00eSShuhei Matsumoto 	SET_FIELD(hide_metadata, false);
2004d2b9b2afSShuhei Matsumoto 
2005d2b9b2afSShuhei Matsumoto #undef FIELD_OK
2006d2b9b2afSShuhei Matsumoto #undef SET_FIELD
2007d2b9b2afSShuhei Matsumoto }
2008d2b9b2afSShuhei Matsumoto 
2009d2b9b2afSShuhei Matsumoto static void
2010d2b9b2afSShuhei Matsumoto nvmf_ns_opts_copy(struct spdk_nvmf_ns_opts *opts,
2011d2b9b2afSShuhei Matsumoto 		  const struct spdk_nvmf_ns_opts *user_opts,
2012d2b9b2afSShuhei Matsumoto 		  size_t opts_size)
2013d2b9b2afSShuhei Matsumoto {
2014d2b9b2afSShuhei Matsumoto #define FIELD_OK(field)	\
2015d2b9b2afSShuhei Matsumoto 	offsetof(struct spdk_nvmf_ns_opts, field) + sizeof(opts->field) <= user_opts->opts_size
2016d2b9b2afSShuhei Matsumoto 
2017d2b9b2afSShuhei Matsumoto #define SET_FIELD(field) \
2018d2b9b2afSShuhei Matsumoto 	if (FIELD_OK(field)) { \
2019d2b9b2afSShuhei Matsumoto 		opts->field = user_opts->field;	\
2020d2b9b2afSShuhei Matsumoto 	} \
2021d2b9b2afSShuhei Matsumoto 
2022d2b9b2afSShuhei Matsumoto 	SET_FIELD(nsid);
2023d2b9b2afSShuhei Matsumoto 	if (FIELD_OK(nguid)) {
2024d2b9b2afSShuhei Matsumoto 		memcpy(opts->nguid, user_opts->nguid, sizeof(opts->nguid));
2025d2b9b2afSShuhei Matsumoto 	}
2026d2b9b2afSShuhei Matsumoto 	if (FIELD_OK(eui64)) {
2027d2b9b2afSShuhei Matsumoto 		memcpy(opts->eui64, user_opts->eui64, sizeof(opts->eui64));
2028d2b9b2afSShuhei Matsumoto 	}
2029d2b9b2afSShuhei Matsumoto 	if (FIELD_OK(uuid)) {
203095a367d6SArtur Paszkiewicz 		spdk_uuid_copy(&opts->uuid, &user_opts->uuid);
2031d2b9b2afSShuhei Matsumoto 	}
203207bfc3cbSShuhei Matsumoto 	SET_FIELD(anagrpid);
2033d37555b4SJonas Pfefferle 	SET_FIELD(no_auto_visible);
20345e952d84SJacek Kalwas 	SET_FIELD(transport_specific);
2035afdec00eSShuhei Matsumoto 	SET_FIELD(hide_metadata);
2036d2b9b2afSShuhei Matsumoto 
2037d2b9b2afSShuhei Matsumoto 	opts->opts_size = user_opts->opts_size;
2038d2b9b2afSShuhei Matsumoto 
2039d2b9b2afSShuhei Matsumoto 	/* We should not remove this statement, but need to update the assert statement
2040d2b9b2afSShuhei Matsumoto 	 * if we add a new field, and also add a corresponding SET_FIELD statement.
2041d2b9b2afSShuhei Matsumoto 	 */
2042afdec00eSShuhei Matsumoto 	SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_ns_opts) == 73, "Incorrect size");
2043d2b9b2afSShuhei Matsumoto 
2044d2b9b2afSShuhei Matsumoto #undef FIELD_OK
2045d2b9b2afSShuhei Matsumoto #undef SET_FIELD
2046250d342bSDaniel Verkamp }
2047250d342bSDaniel Verkamp 
2048ff458be8SChangpeng Liu /* Dummy bdev module used to to claim bdevs. */
2049ff458be8SChangpeng Liu static struct spdk_bdev_module ns_bdev_module = {
2050ff458be8SChangpeng Liu 	.name	= "NVMe-oF Target",
2051ff458be8SChangpeng Liu };
2052ff458be8SChangpeng Liu 
205309e8e884SArtur Paszkiewicz static int nvmf_ns_reservation_update(const struct spdk_nvmf_ns *ns,
205409e8e884SArtur Paszkiewicz 				      const struct spdk_nvmf_reservation_info *info);
205509e8e884SArtur Paszkiewicz static int nvmf_ns_reservation_load(const struct spdk_nvmf_ns *ns,
20565bbe0c0bSArtur Paszkiewicz 				    struct spdk_nvmf_reservation_info *info);
20578dd1cd21SBen Walker static int nvmf_ns_reservation_restore(struct spdk_nvmf_ns *ns,
20588dd1cd21SBen Walker 				       struct spdk_nvmf_reservation_info *info);
20591edc5f00SChangpeng Liu 
2060415a0d2bSKonrad Sztyber bool
2061415a0d2bSKonrad Sztyber nvmf_subsystem_zone_append_supported(struct spdk_nvmf_subsystem *subsystem)
2062415a0d2bSKonrad Sztyber {
2063415a0d2bSKonrad Sztyber 	struct spdk_nvmf_ns *ns;
2064415a0d2bSKonrad Sztyber 
2065415a0d2bSKonrad Sztyber 	for (ns = spdk_nvmf_subsystem_get_first_ns(subsystem);
2066415a0d2bSKonrad Sztyber 	     ns != NULL;
2067415a0d2bSKonrad Sztyber 	     ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns)) {
2068415a0d2bSKonrad Sztyber 		if (spdk_bdev_is_zoned(ns->bdev) &&
2069415a0d2bSKonrad Sztyber 		    spdk_bdev_io_type_supported(ns->bdev, SPDK_BDEV_IO_TYPE_ZONE_APPEND)) {
2070415a0d2bSKonrad Sztyber 			return true;
2071415a0d2bSKonrad Sztyber 		}
2072415a0d2bSKonrad Sztyber 	}
2073415a0d2bSKonrad Sztyber 
2074415a0d2bSKonrad Sztyber 	return false;
2075415a0d2bSKonrad Sztyber }
2076415a0d2bSKonrad Sztyber 
2077f65731c4SDaniel Verkamp uint32_t
2078a22d8a53SShuhei Matsumoto spdk_nvmf_subsystem_add_ns_ext(struct spdk_nvmf_subsystem *subsystem, const char *bdev_name,
20793ec06180SChangpeng Liu 			       const struct spdk_nvmf_ns_opts *user_opts, size_t opts_size,
20803ec06180SChangpeng Liu 			       const char *ptpl_file)
2081b4d9cca1SCunyin Chang {
20822e26faa9SJacek Kalwas 	struct spdk_nvmf_transport *transport;
2083250d342bSDaniel Verkamp 	struct spdk_nvmf_ns_opts opts;
2084afdec00eSShuhei Matsumoto 	struct spdk_bdev_open_opts open_opts = {};
2085037d5165SAnkit Kumar 	struct spdk_nvmf_ns *ns, *first_ns;
2086d37555b4SJonas Pfefferle 	struct spdk_nvmf_ctrlr *ctrlr;
20871edc5f00SChangpeng Liu 	struct spdk_nvmf_reservation_info info = {0};
208857d174ffSJim Harris 	int rc;
2089a36785dfSDennis Maisenbacher 	bool zone_append_supported;
2090a36785dfSDennis Maisenbacher 	uint64_t max_zone_append_size_kib;
2091b4d9cca1SCunyin Chang 
2092be774bf6SBen Walker 	if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE ||
2093be774bf6SBen Walker 	      subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) {
2094be774bf6SBen Walker 		return 0;
2095be774bf6SBen Walker 	}
209695ac75aaSBen Walker 
2097250d342bSDaniel Verkamp 	spdk_nvmf_ns_opts_get_defaults(&opts, sizeof(opts));
2098250d342bSDaniel Verkamp 	if (user_opts) {
2099d2b9b2afSShuhei Matsumoto 		nvmf_ns_opts_copy(&opts, user_opts, opts_size);
2100250d342bSDaniel Verkamp 	}
2101250d342bSDaniel Verkamp 
2102250d342bSDaniel Verkamp 	if (opts.nsid == SPDK_NVME_GLOBAL_NS_TAG) {
2103250d342bSDaniel Verkamp 		SPDK_ERRLOG("Invalid NSID %" PRIu32 "\n", opts.nsid);
21048011d8c0SDaniel Verkamp 		return 0;
21058011d8c0SDaniel Verkamp 	}
21068011d8c0SDaniel Verkamp 
2107250d342bSDaniel Verkamp 	if (opts.nsid == 0) {
2108eb387189SDaniel Verkamp 		/*
2109eb387189SDaniel Verkamp 		 * NSID not specified - find a free index.
2110eb387189SDaniel Verkamp 		 *
21118d3fdcabSAnkit Kumar 		 * If no free slots are found, return error.
2112eb387189SDaniel Verkamp 		 */
2113eb387189SDaniel Verkamp 		for (opts.nsid = 1; opts.nsid <= subsystem->max_nsid; opts.nsid++) {
21149cb21ad6SSeth Howell 			if (_nvmf_subsystem_get_ns(subsystem, opts.nsid) == NULL) {
2115f65731c4SDaniel Verkamp 				break;
2116b4d9cca1SCunyin Chang 			}
2117f65731c4SDaniel Verkamp 		}
21188d3fdcabSAnkit Kumar 		if (opts.nsid > subsystem->max_nsid) {
21198d3fdcabSAnkit Kumar 			SPDK_ERRLOG("No free namespace slot available in the subsystem\n");
2120f65731c4SDaniel Verkamp 			return 0;
2121f65731c4SDaniel Verkamp 		}
21228d3fdcabSAnkit Kumar 	}
2123ed61bf79SDaniel Verkamp 
21241e481d04SBen Walker 	if (opts.nsid > subsystem->max_nsid) {
2125af07b82fSBen Walker 		SPDK_ERRLOG("NSID greater than maximum not allowed\n");
2126eb387189SDaniel Verkamp 		return 0;
2127eb387189SDaniel Verkamp 	}
2128eb387189SDaniel Verkamp 
21298d3fdcabSAnkit Kumar 	if (_nvmf_subsystem_get_ns(subsystem, opts.nsid)) {
21308d3fdcabSAnkit Kumar 		SPDK_ERRLOG("Requested NSID %" PRIu32 " already in use\n", opts.nsid);
21318d3fdcabSAnkit Kumar 		return 0;
21328d3fdcabSAnkit Kumar 	}
21338d3fdcabSAnkit Kumar 
213407bfc3cbSShuhei Matsumoto 	if (opts.anagrpid == 0) {
213507bfc3cbSShuhei Matsumoto 		opts.anagrpid = opts.nsid;
213607bfc3cbSShuhei Matsumoto 	}
213707bfc3cbSShuhei Matsumoto 
213807bfc3cbSShuhei Matsumoto 	if (opts.anagrpid > subsystem->max_nsid) {
213907bfc3cbSShuhei Matsumoto 		SPDK_ERRLOG("ANAGRPID greater than maximum NSID not allowed\n");
214007bfc3cbSShuhei Matsumoto 		return 0;
214107bfc3cbSShuhei Matsumoto 	}
214207bfc3cbSShuhei Matsumoto 
21438af4b6c4SDaniel Verkamp 	ns = calloc(1, sizeof(*ns));
21448af4b6c4SDaniel Verkamp 	if (ns == NULL) {
21458af4b6c4SDaniel Verkamp 		SPDK_ERRLOG("Namespace allocation failed\n");
21468af4b6c4SDaniel Verkamp 		return 0;
21478af4b6c4SDaniel Verkamp 	}
21488af4b6c4SDaniel Verkamp 
2149d37555b4SJonas Pfefferle 	TAILQ_INIT(&ns->hosts);
2150d37555b4SJonas Pfefferle 	ns->always_visible = !opts.no_auto_visible;
2151d37555b4SJonas Pfefferle 	TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) {
2152214b0826SKonrad Sztyber 		nvmf_ctrlr_ns_set_visible(ctrlr, opts.nsid, ns->always_visible);
2153d37555b4SJonas Pfefferle 	}
2154d37555b4SJonas Pfefferle 
2155afdec00eSShuhei Matsumoto 	spdk_bdev_open_opts_init(&open_opts, sizeof(open_opts));
2156afdec00eSShuhei Matsumoto 	open_opts.hide_metadata = opts.hide_metadata;
2157afdec00eSShuhei Matsumoto 
2158afdec00eSShuhei Matsumoto 	rc = spdk_bdev_open_ext_v2(bdev_name, true, nvmf_ns_event, ns, &open_opts, &ns->desc);
215957d174ffSJim Harris 	if (rc != 0) {
216057d174ffSJim Harris 		SPDK_ERRLOG("Subsystem %s: bdev %s cannot be opened, error=%d\n",
2161a22d8a53SShuhei Matsumoto 			    subsystem->subnqn, bdev_name, rc);
21628af4b6c4SDaniel Verkamp 		free(ns);
2163f65731c4SDaniel Verkamp 		return 0;
2164ed61bf79SDaniel Verkamp 	}
2165a22d8a53SShuhei Matsumoto 
2166a22d8a53SShuhei Matsumoto 	ns->bdev = spdk_bdev_desc_get_bdev(ns->desc);
2167a22d8a53SShuhei Matsumoto 
2168b09de013SShuhei Matsumoto 	if (spdk_bdev_desc_get_md_size(ns->desc) != 0) {
2169b09de013SShuhei Matsumoto 		if (!spdk_bdev_desc_is_md_interleaved(ns->desc)) {
2170a22d8a53SShuhei Matsumoto 			SPDK_ERRLOG("Can't attach bdev with separate metadata.\n");
2171a22d8a53SShuhei Matsumoto 			spdk_bdev_close(ns->desc);
2172a22d8a53SShuhei Matsumoto 			free(ns);
2173a22d8a53SShuhei Matsumoto 			return 0;
2174a22d8a53SShuhei Matsumoto 		}
2175a22d8a53SShuhei Matsumoto 
2176b09de013SShuhei Matsumoto 		if (spdk_bdev_desc_get_md_size(ns->desc) > SPDK_BDEV_MAX_INTERLEAVED_MD_SIZE) {
21776abb4764SChangpeng Liu 			SPDK_ERRLOG("Maximum supported interleaved md size %u, current md size %u\n",
2178b09de013SShuhei Matsumoto 				    SPDK_BDEV_MAX_INTERLEAVED_MD_SIZE,
2179b09de013SShuhei Matsumoto 				    spdk_bdev_desc_get_md_size(ns->desc));
21806abb4764SChangpeng Liu 			spdk_bdev_close(ns->desc);
21816abb4764SChangpeng Liu 			free(ns);
21826abb4764SChangpeng Liu 			return 0;
21836abb4764SChangpeng Liu 		}
21846abb4764SChangpeng Liu 	}
21856abb4764SChangpeng Liu 
2186a22d8a53SShuhei Matsumoto 	rc = spdk_bdev_module_claim_bdev(ns->bdev, ns->desc, &ns_bdev_module);
2187ff458be8SChangpeng Liu 	if (rc != 0) {
2188ff458be8SChangpeng Liu 		spdk_bdev_close(ns->desc);
2189ff458be8SChangpeng Liu 		free(ns);
2190ff458be8SChangpeng Liu 		return 0;
2191ff458be8SChangpeng Liu 	}
2192a22d8a53SShuhei Matsumoto 
2193f220d590SKonrad Sztyber 	ns->passthru_nsid = spdk_bdev_get_nvme_nsid(ns->bdev);
2194f220d590SKonrad Sztyber 	if (subsystem->passthrough && ns->passthru_nsid == 0) {
219560241941SKarl Bonde Torp 		SPDK_ERRLOG("Only bdev_nvme namespaces can be added to a passthrough subsystem.\n");
219660241941SKarl Bonde Torp 		goto err;
2197c156f3e1SKarl Bonde Torp 	}
2198c156f3e1SKarl Bonde Torp 
21995818b42fSmatthewb 	/* Cache the zcopy capability of the bdev device */
22005818b42fSmatthewb 	ns->zcopy = spdk_bdev_io_type_supported(ns->bdev, SPDK_BDEV_IO_TYPE_ZCOPY);
22015818b42fSmatthewb 
220295a367d6SArtur Paszkiewicz 	if (spdk_uuid_is_null(&opts.uuid)) {
2203a22d8a53SShuhei Matsumoto 		opts.uuid = *spdk_bdev_get_uuid(ns->bdev);
2204a22d8a53SShuhei Matsumoto 	}
2205a22d8a53SShuhei Matsumoto 
220603ac99d1SJacek Kalwas 	/* if nguid descriptor is supported by bdev module (nvme) then uuid = nguid */
220703ac99d1SJacek Kalwas 	if (spdk_mem_all_zero(opts.nguid, sizeof(opts.nguid))) {
220803ac99d1SJacek Kalwas 		SPDK_STATIC_ASSERT(sizeof(opts.nguid) == sizeof(opts.uuid), "size mismatch");
220903ac99d1SJacek Kalwas 		memcpy(opts.nguid, spdk_bdev_get_uuid(ns->bdev), sizeof(opts.nguid));
221003ac99d1SJacek Kalwas 	}
221103ac99d1SJacek Kalwas 
2212a36785dfSDennis Maisenbacher 	if (spdk_bdev_is_zoned(ns->bdev)) {
2213a36785dfSDennis Maisenbacher 		SPDK_DEBUGLOG(nvmf, "The added namespace is backed by a zoned block device.\n");
2214a36785dfSDennis Maisenbacher 		ns->csi = SPDK_NVME_CSI_ZNS;
2215a36785dfSDennis Maisenbacher 
2216a36785dfSDennis Maisenbacher 		zone_append_supported = spdk_bdev_io_type_supported(ns->bdev,
2217a36785dfSDennis Maisenbacher 					SPDK_BDEV_IO_TYPE_ZONE_APPEND);
2218b09de013SShuhei Matsumoto 		max_zone_append_size_kib = spdk_bdev_get_max_zone_append_size(ns->bdev) *
2219b09de013SShuhei Matsumoto 					   spdk_bdev_desc_get_block_size(ns->desc);
2220a36785dfSDennis Maisenbacher 
2221a36785dfSDennis Maisenbacher 		if (_nvmf_subsystem_get_first_zoned_ns(subsystem) != NULL &&
2222415a0d2bSKonrad Sztyber 		    (nvmf_subsystem_zone_append_supported(subsystem) != zone_append_supported ||
2223a36785dfSDennis Maisenbacher 		     subsystem->max_zone_append_size_kib != max_zone_append_size_kib)) {
2224a36785dfSDennis Maisenbacher 			SPDK_ERRLOG("Namespaces with different zone append support or different zone append size are not allowed.\n");
2225ed7654d2SArtur Paszkiewicz 			goto err;
2226a36785dfSDennis Maisenbacher 		}
2227a36785dfSDennis Maisenbacher 
2228a36785dfSDennis Maisenbacher 		subsystem->max_zone_append_size_kib = max_zone_append_size_kib;
2229a36785dfSDennis Maisenbacher 	}
2230a36785dfSDennis Maisenbacher 
2231037d5165SAnkit Kumar 	first_ns = spdk_nvmf_subsystem_get_first_ns(subsystem);
2232037d5165SAnkit Kumar 	if (!first_ns) {
2233037d5165SAnkit Kumar 		if (spdk_bdev_get_nvme_ctratt(ns->bdev).bits.fdps) {
2234037d5165SAnkit Kumar 			SPDK_DEBUGLOG(nvmf, "Subsystem with id: %u has FDP capability.\n",
2235037d5165SAnkit Kumar 				      subsystem->id);
2236037d5165SAnkit Kumar 			subsystem->fdp_supported = true;
2237037d5165SAnkit Kumar 		}
2238037d5165SAnkit Kumar 	} else {
2239037d5165SAnkit Kumar 		if (spdk_bdev_get_nvme_ctratt(first_ns->bdev).bits.fdps !=
2240037d5165SAnkit Kumar 		    spdk_bdev_get_nvme_ctratt(ns->bdev).bits.fdps) {
2241037d5165SAnkit Kumar 			SPDK_ERRLOG("Subsystem with id: %u can%s FDP namespace.\n", subsystem->id,
2242037d5165SAnkit Kumar 				    spdk_bdev_get_nvme_ctratt(first_ns->bdev).bits.fdps ? " only add" : "not add");
2243037d5165SAnkit Kumar 			goto err;
2244037d5165SAnkit Kumar 		}
2245037d5165SAnkit Kumar 	}
2246037d5165SAnkit Kumar 
2247a22d8a53SShuhei Matsumoto 	ns->opts = opts;
2248a22d8a53SShuhei Matsumoto 	ns->subsystem = subsystem;
2249250d342bSDaniel Verkamp 	subsystem->ns[opts.nsid - 1] = ns;
2250a9c30bccSChangpeng Liu 	ns->nsid = opts.nsid;
225107bfc3cbSShuhei Matsumoto 	ns->anagrpid = opts.anagrpid;
225207bfc3cbSShuhei Matsumoto 	subsystem->ana_group[ns->anagrpid - 1]++;
2253bc1d0b91SChangpeng Liu 	TAILQ_INIT(&ns->registrants);
22543ec06180SChangpeng Liu 	if (ptpl_file) {
2255ed7654d2SArtur Paszkiewicz 		ns->ptpl_file = strdup(ptpl_file);
2256ed7654d2SArtur Paszkiewicz 		if (!ns->ptpl_file) {
2257ed7654d2SArtur Paszkiewicz 			SPDK_ERRLOG("Namespace ns->ptpl_file allocation failed\n");
2258ed7654d2SArtur Paszkiewicz 			goto err;
2259ed7654d2SArtur Paszkiewicz 		}
226009e8e884SArtur Paszkiewicz 	}
2261ed7654d2SArtur Paszkiewicz 
226209e8e884SArtur Paszkiewicz 	if (nvmf_ns_is_ptpl_capable(ns)) {
226309e8e884SArtur Paszkiewicz 		rc = nvmf_ns_reservation_load(ns, &info);
22640773385eSArtur Paszkiewicz 		if (rc) {
22650773385eSArtur Paszkiewicz 			SPDK_ERRLOG("Subsystem load reservation failed\n");
22660773385eSArtur Paszkiewicz 			goto err;
22670773385eSArtur Paszkiewicz 		}
22680773385eSArtur Paszkiewicz 
22691edc5f00SChangpeng Liu 		rc = nvmf_ns_reservation_restore(ns, &info);
22701edc5f00SChangpeng Liu 		if (rc) {
22711edc5f00SChangpeng Liu 			SPDK_ERRLOG("Subsystem restore reservation failed\n");
2272ed7654d2SArtur Paszkiewicz 			goto err;
22731edc5f00SChangpeng Liu 		}
22741edc5f00SChangpeng Liu 	}
22753ec06180SChangpeng Liu 
22762e26faa9SJacek Kalwas 	for (transport = spdk_nvmf_transport_get_first(subsystem->tgt); transport;
22772e26faa9SJacek Kalwas 	     transport = spdk_nvmf_transport_get_next(transport)) {
22782e26faa9SJacek Kalwas 		if (transport->ops->subsystem_add_ns) {
22792e26faa9SJacek Kalwas 			rc = transport->ops->subsystem_add_ns(transport, subsystem, ns);
22802e26faa9SJacek Kalwas 			if (rc) {
22812e26faa9SJacek Kalwas 				SPDK_ERRLOG("Namespace attachment is not allowed by %s transport\n", transport->ops->name);
2282ed7654d2SArtur Paszkiewicz 				nvmf_ns_reservation_clear_all_registrants(ns);
2283ed7654d2SArtur Paszkiewicz 				goto err;
22842e26faa9SJacek Kalwas 			}
22852e26faa9SJacek Kalwas 		}
22862e26faa9SJacek Kalwas 	}
22872e26faa9SJacek Kalwas 
22885e952d84SJacek Kalwas 	/* JSON value obj is freed before sending the response. Set NULL to prevent usage of dangling pointer. */
22895e952d84SJacek Kalwas 	ns->opts.transport_specific = NULL;
22905e952d84SJacek Kalwas 
22912172c432STomasz Zawadzki 	SPDK_DEBUGLOG(nvmf, "Subsystem %s: bdev %s assigned nsid %" PRIu32 "\n",
2292f65731c4SDaniel Verkamp 		      spdk_nvmf_subsystem_get_nqn(subsystem),
2293a22d8a53SShuhei Matsumoto 		      bdev_name,
2294250d342bSDaniel Verkamp 		      opts.nsid);
2295f65731c4SDaniel Verkamp 
229663c90491SSeth Howell 	nvmf_subsystem_ns_changed(subsystem, opts.nsid);
22970f15edeeSChangpeng Liu 
2298d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE2(nvmf_subsystem_add_ns, subsystem->subnqn, ns->nsid);
2299d11601e8SKrzysztof Karas 
2300250d342bSDaniel Verkamp 	return opts.nsid;
2301ed7654d2SArtur Paszkiewicz err:
2302242eb6b4SZhiqiang Liu 	subsystem->ns[opts.nsid - 1] = NULL;
2303242eb6b4SZhiqiang Liu 	spdk_bdev_module_release_bdev(ns->bdev);
2304242eb6b4SZhiqiang Liu 	spdk_bdev_close(ns->desc);
2305ed7654d2SArtur Paszkiewicz 	free(ns->ptpl_file);
2306242eb6b4SZhiqiang Liu 	free(ns);
2307242eb6b4SZhiqiang Liu 
2308d11601e8SKrzysztof Karas 	return 0;
2309b4d9cca1SCunyin Chang }
23101e6ffa03SBen Walker 
2311bc06e05aSShuhei Matsumoto int
2312bc06e05aSShuhei Matsumoto spdk_nvmf_subsystem_set_ns_ana_group(struct spdk_nvmf_subsystem *subsystem,
2313bc06e05aSShuhei Matsumoto 				     uint32_t nsid, uint32_t anagrpid)
2314bc06e05aSShuhei Matsumoto {
2315bc06e05aSShuhei Matsumoto 	struct spdk_nvmf_ns *ns;
2316bc06e05aSShuhei Matsumoto 
2317bc06e05aSShuhei Matsumoto 	if (anagrpid > subsystem->max_nsid) {
2318bc06e05aSShuhei Matsumoto 		SPDK_ERRLOG("ANAGRPID greater than maximum NSID not allowed\n");
2319bc06e05aSShuhei Matsumoto 		return -1;
2320bc06e05aSShuhei Matsumoto 	}
2321bc06e05aSShuhei Matsumoto 
2322bc06e05aSShuhei Matsumoto 	if (anagrpid == 0) {
2323bc06e05aSShuhei Matsumoto 		SPDK_ERRLOG("Zero is not allowed to ANAGRPID\n");
2324bc06e05aSShuhei Matsumoto 		return -1;
2325bc06e05aSShuhei Matsumoto 	}
2326bc06e05aSShuhei Matsumoto 
2327bc06e05aSShuhei Matsumoto 	if (nsid == 0 || nsid > subsystem->max_nsid) {
2328bc06e05aSShuhei Matsumoto 		return -1;
2329bc06e05aSShuhei Matsumoto 	}
2330bc06e05aSShuhei Matsumoto 
2331bc06e05aSShuhei Matsumoto 	ns = subsystem->ns[nsid - 1];
2332bc06e05aSShuhei Matsumoto 	if (!ns) {
2333bc06e05aSShuhei Matsumoto 		return -1;
2334bc06e05aSShuhei Matsumoto 	}
2335bc06e05aSShuhei Matsumoto 
2336bc06e05aSShuhei Matsumoto 	assert(ns->anagrpid - 1 < subsystem->max_nsid);
2337bc06e05aSShuhei Matsumoto 
2338bc06e05aSShuhei Matsumoto 	assert(subsystem->ana_group[ns->anagrpid - 1] > 0);
2339bc06e05aSShuhei Matsumoto 
2340bc06e05aSShuhei Matsumoto 	subsystem->ana_group[ns->anagrpid - 1]--;
2341bc06e05aSShuhei Matsumoto 
2342bc06e05aSShuhei Matsumoto 	subsystem->ana_group[anagrpid - 1]++;
2343bc06e05aSShuhei Matsumoto 
2344bc06e05aSShuhei Matsumoto 	ns->anagrpid = anagrpid;
2345bc06e05aSShuhei Matsumoto 	ns->opts.anagrpid = anagrpid;
2346bc06e05aSShuhei Matsumoto 
2347bc06e05aSShuhei Matsumoto 	nvmf_subsystem_ns_changed(subsystem, nsid);
2348bc06e05aSShuhei Matsumoto 
2349bc06e05aSShuhei Matsumoto 	return 0;
2350bc06e05aSShuhei Matsumoto }
2351bc06e05aSShuhei Matsumoto 
235214451d76SDaniel Verkamp static uint32_t
235363c90491SSeth Howell nvmf_subsystem_get_next_allocated_nsid(struct spdk_nvmf_subsystem *subsystem,
235414451d76SDaniel Verkamp 				       uint32_t prev_nsid)
235514451d76SDaniel Verkamp {
235614451d76SDaniel Verkamp 	uint32_t nsid;
235714451d76SDaniel Verkamp 
235814451d76SDaniel Verkamp 	if (prev_nsid >= subsystem->max_nsid) {
235914451d76SDaniel Verkamp 		return 0;
236014451d76SDaniel Verkamp 	}
236114451d76SDaniel Verkamp 
236214451d76SDaniel Verkamp 	for (nsid = prev_nsid + 1; nsid <= subsystem->max_nsid; nsid++) {
23638af4b6c4SDaniel Verkamp 		if (subsystem->ns[nsid - 1]) {
236414451d76SDaniel Verkamp 			return nsid;
236514451d76SDaniel Verkamp 		}
236614451d76SDaniel Verkamp 	}
236714451d76SDaniel Verkamp 
236814451d76SDaniel Verkamp 	return 0;
236914451d76SDaniel Verkamp }
237014451d76SDaniel Verkamp 
237114451d76SDaniel Verkamp struct spdk_nvmf_ns *
237214451d76SDaniel Verkamp spdk_nvmf_subsystem_get_first_ns(struct spdk_nvmf_subsystem *subsystem)
237314451d76SDaniel Verkamp {
237414451d76SDaniel Verkamp 	uint32_t first_nsid;
237514451d76SDaniel Verkamp 
237663c90491SSeth Howell 	first_nsid = nvmf_subsystem_get_next_allocated_nsid(subsystem, 0);
23779cb21ad6SSeth Howell 	return _nvmf_subsystem_get_ns(subsystem, first_nsid);
237814451d76SDaniel Verkamp }
237914451d76SDaniel Verkamp 
238014451d76SDaniel Verkamp struct spdk_nvmf_ns *
238114451d76SDaniel Verkamp spdk_nvmf_subsystem_get_next_ns(struct spdk_nvmf_subsystem *subsystem,
238214451d76SDaniel Verkamp 				struct spdk_nvmf_ns *prev_ns)
238314451d76SDaniel Verkamp {
238414451d76SDaniel Verkamp 	uint32_t next_nsid;
238514451d76SDaniel Verkamp 
238663c90491SSeth Howell 	next_nsid = nvmf_subsystem_get_next_allocated_nsid(subsystem, prev_ns->opts.nsid);
23879cb21ad6SSeth Howell 	return _nvmf_subsystem_get_ns(subsystem, next_nsid);
238814451d76SDaniel Verkamp }
238914451d76SDaniel Verkamp 
239014451d76SDaniel Verkamp struct spdk_nvmf_ns *
239114451d76SDaniel Verkamp spdk_nvmf_subsystem_get_ns(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid)
239214451d76SDaniel Verkamp {
23939cb21ad6SSeth Howell 	return _nvmf_subsystem_get_ns(subsystem, nsid);
239414451d76SDaniel Verkamp }
239514451d76SDaniel Verkamp 
239614451d76SDaniel Verkamp uint32_t
239714451d76SDaniel Verkamp spdk_nvmf_ns_get_id(const struct spdk_nvmf_ns *ns)
239814451d76SDaniel Verkamp {
2399250d342bSDaniel Verkamp 	return ns->opts.nsid;
240014451d76SDaniel Verkamp }
240114451d76SDaniel Verkamp 
240214451d76SDaniel Verkamp struct spdk_bdev *
240314451d76SDaniel Verkamp spdk_nvmf_ns_get_bdev(struct spdk_nvmf_ns *ns)
240414451d76SDaniel Verkamp {
240514451d76SDaniel Verkamp 	return ns->bdev;
240614451d76SDaniel Verkamp }
240714451d76SDaniel Verkamp 
2408250d342bSDaniel Verkamp void
2409250d342bSDaniel Verkamp spdk_nvmf_ns_get_opts(const struct spdk_nvmf_ns *ns, struct spdk_nvmf_ns_opts *opts,
2410250d342bSDaniel Verkamp 		      size_t opts_size)
2411250d342bSDaniel Verkamp {
2412250d342bSDaniel Verkamp 	memset(opts, 0, opts_size);
2413250d342bSDaniel Verkamp 	memcpy(opts, &ns->opts, spdk_min(sizeof(ns->opts), opts_size));
2414250d342bSDaniel Verkamp }
2415250d342bSDaniel Verkamp 
24167b2a6b05SDaniel Verkamp const char *
24177b2a6b05SDaniel Verkamp spdk_nvmf_subsystem_get_sn(const struct spdk_nvmf_subsystem *subsystem)
24187b2a6b05SDaniel Verkamp {
241914451d76SDaniel Verkamp 	return subsystem->sn;
24207b2a6b05SDaniel Verkamp }
24217b2a6b05SDaniel Verkamp 
24221e6ffa03SBen Walker int
24231e6ffa03SBen Walker spdk_nvmf_subsystem_set_sn(struct spdk_nvmf_subsystem *subsystem, const char *sn)
24241e6ffa03SBen Walker {
2425bbfcb094SDaniel Verkamp 	size_t len, max_len;
2426bbfcb094SDaniel Verkamp 
242714451d76SDaniel Verkamp 	max_len = sizeof(subsystem->sn) - 1;
2428bbfcb094SDaniel Verkamp 	len = strlen(sn);
2429bbfcb094SDaniel Verkamp 	if (len > max_len) {
24302172c432STomasz Zawadzki 		SPDK_DEBUGLOG(nvmf, "Invalid sn \"%s\": length %zu > max %zu\n",
2431bbfcb094SDaniel Verkamp 			      sn, len, max_len);
2432bbfcb094SDaniel Verkamp 		return -1;
2433bbfcb094SDaniel Verkamp 	}
2434bbfcb094SDaniel Verkamp 
243563c90491SSeth Howell 	if (!nvmf_valid_ascii_string(sn, len)) {
24362172c432STomasz Zawadzki 		SPDK_DEBUGLOG(nvmf, "Non-ASCII sn\n");
24372172c432STomasz Zawadzki 		SPDK_LOGDUMP(nvmf, "sn", sn, len);
24388cad9604SDaniel Verkamp 		return -1;
24398cad9604SDaniel Verkamp 	}
24408cad9604SDaniel Verkamp 
244114451d76SDaniel Verkamp 	snprintf(subsystem->sn, sizeof(subsystem->sn), "%s", sn);
24421e6ffa03SBen Walker 
24431e6ffa03SBen Walker 	return 0;
24441e6ffa03SBen Walker }
24453cbed2edSBen Walker 
24463cbed2edSBen Walker const char *
244714032a98SGregory Shapiro spdk_nvmf_subsystem_get_mn(const struct spdk_nvmf_subsystem *subsystem)
244814032a98SGregory Shapiro {
244914032a98SGregory Shapiro 	return subsystem->mn;
245014032a98SGregory Shapiro }
245114032a98SGregory Shapiro 
245214032a98SGregory Shapiro int
245314032a98SGregory Shapiro spdk_nvmf_subsystem_set_mn(struct spdk_nvmf_subsystem *subsystem, const char *mn)
245414032a98SGregory Shapiro {
245514032a98SGregory Shapiro 	size_t len, max_len;
245614032a98SGregory Shapiro 
245714032a98SGregory Shapiro 	if (mn == NULL) {
245814032a98SGregory Shapiro 		mn = MODEL_NUMBER_DEFAULT;
245914032a98SGregory Shapiro 	}
246014032a98SGregory Shapiro 	max_len = sizeof(subsystem->mn) - 1;
246114032a98SGregory Shapiro 	len = strlen(mn);
246214032a98SGregory Shapiro 	if (len > max_len) {
24632172c432STomasz Zawadzki 		SPDK_DEBUGLOG(nvmf, "Invalid mn \"%s\": length %zu > max %zu\n",
246414032a98SGregory Shapiro 			      mn, len, max_len);
246514032a98SGregory Shapiro 		return -1;
246614032a98SGregory Shapiro 	}
246714032a98SGregory Shapiro 
246863c90491SSeth Howell 	if (!nvmf_valid_ascii_string(mn, len)) {
24692172c432STomasz Zawadzki 		SPDK_DEBUGLOG(nvmf, "Non-ASCII mn\n");
24702172c432STomasz Zawadzki 		SPDK_LOGDUMP(nvmf, "mn", mn, len);
247114032a98SGregory Shapiro 		return -1;
247214032a98SGregory Shapiro 	}
247314032a98SGregory Shapiro 
247414032a98SGregory Shapiro 	snprintf(subsystem->mn, sizeof(subsystem->mn), "%s", mn);
247514032a98SGregory Shapiro 
247614032a98SGregory Shapiro 	return 0;
247714032a98SGregory Shapiro }
247814032a98SGregory Shapiro 
247914032a98SGregory Shapiro const char *
2480fb712988SJacek Kalwas spdk_nvmf_subsystem_get_nqn(const struct spdk_nvmf_subsystem *subsystem)
24813cbed2edSBen Walker {
24823cbed2edSBen Walker 	return subsystem->subnqn;
24833cbed2edSBen Walker }
24843cbed2edSBen Walker 
24858dd1cd21SBen Walker /* We have to use the typedef in the function declaration to appease astyle. */
24868dd1cd21SBen Walker typedef enum spdk_nvmf_subtype spdk_nvmf_subtype_t;
24878dd1cd21SBen Walker 
24888dd1cd21SBen Walker spdk_nvmf_subtype_t
24898dd1cd21SBen Walker spdk_nvmf_subsystem_get_type(struct spdk_nvmf_subsystem *subsystem)
24903cbed2edSBen Walker {
24913cbed2edSBen Walker 	return subsystem->subtype;
24923cbed2edSBen Walker }
249321ea2901SBen Walker 
24943fa22056SMichael Haeuptle uint32_t
24953fa22056SMichael Haeuptle spdk_nvmf_subsystem_get_max_nsid(struct spdk_nvmf_subsystem *subsystem)
24963fa22056SMichael Haeuptle {
24973fa22056SMichael Haeuptle 	return subsystem->max_nsid;
24983fa22056SMichael Haeuptle }
24993fa22056SMichael Haeuptle 
2500be6a01efSJonathan Teh int
2501096538c8SAbhineet Pandey spdk_nvmf_subsystem_set_cntlid_range(struct spdk_nvmf_subsystem *subsystem,
2502be6a01efSJonathan Teh 				     uint16_t min_cntlid, uint16_t max_cntlid)
2503be6a01efSJonathan Teh {
2504be6a01efSJonathan Teh 	if (subsystem->state != SPDK_NVMF_SUBSYSTEM_INACTIVE) {
2505be6a01efSJonathan Teh 		return -EAGAIN;
2506be6a01efSJonathan Teh 	}
2507be6a01efSJonathan Teh 
2508be6a01efSJonathan Teh 	if (min_cntlid > max_cntlid) {
2509be6a01efSJonathan Teh 		return -EINVAL;
2510be6a01efSJonathan Teh 	}
2511be6a01efSJonathan Teh 	/* The spec reserves cntlid values in the range FFF0h to FFFFh. */
2512be6a01efSJonathan Teh 	if (min_cntlid < NVMF_MIN_CNTLID || min_cntlid > NVMF_MAX_CNTLID ||
2513be6a01efSJonathan Teh 	    max_cntlid < NVMF_MIN_CNTLID || max_cntlid > NVMF_MAX_CNTLID) {
2514be6a01efSJonathan Teh 		return -EINVAL;
2515be6a01efSJonathan Teh 	}
2516be6a01efSJonathan Teh 	subsystem->min_cntlid = min_cntlid;
2517be6a01efSJonathan Teh 	subsystem->max_cntlid = max_cntlid;
2518ef14e9b9SAbhineet Pandey 	if (subsystem->next_cntlid < min_cntlid || subsystem->next_cntlid > max_cntlid) {
2519ef14e9b9SAbhineet Pandey 		subsystem->next_cntlid = min_cntlid;
2520be6a01efSJonathan Teh 	}
2521be6a01efSJonathan Teh 
2522be6a01efSJonathan Teh 	return 0;
2523be6a01efSJonathan Teh }
2524be6a01efSJonathan Teh 
2525a1a770d0SMichael Haeuptle uint16_t
252663c90491SSeth Howell nvmf_subsystem_gen_cntlid(struct spdk_nvmf_subsystem *subsystem)
252721ea2901SBen Walker {
2528f4a4ddd8SDaniel Verkamp 	int count;
2529ef14e9b9SAbhineet Pandey 	uint16_t cntlid;
253021ea2901SBen Walker 
253121ea2901SBen Walker 	/*
2532be6a01efSJonathan Teh 	 * In the worst case, we might have to try all CNTLID values between min_cntlid and max_cntlid
253321ea2901SBen Walker 	 * before we find one that is unused (or find that all values are in use).
253421ea2901SBen Walker 	 */
2535be6a01efSJonathan Teh 	for (count = 0; count < subsystem->max_cntlid - subsystem->min_cntlid + 1; count++) {
2536ef14e9b9SAbhineet Pandey 		cntlid = subsystem->next_cntlid;
253721ea2901SBen Walker 		subsystem->next_cntlid++;
2538ef14e9b9SAbhineet Pandey 
2539be6a01efSJonathan Teh 		if (subsystem->next_cntlid > subsystem->max_cntlid) {
2540be6a01efSJonathan Teh 			subsystem->next_cntlid = subsystem->min_cntlid;
254121ea2901SBen Walker 		}
254221ea2901SBen Walker 
254321ea2901SBen Walker 		/* Check if a controller with this cntlid currently exists. */
2544ef14e9b9SAbhineet Pandey 		if (nvmf_subsystem_get_ctrlr(subsystem, cntlid) == NULL) {
2545f4a4ddd8SDaniel Verkamp 			/* Found unused cntlid */
2546ef14e9b9SAbhineet Pandey 			return cntlid;
254721ea2901SBen Walker 		}
254821ea2901SBen Walker 	}
254921ea2901SBen Walker 
255021ea2901SBen Walker 	/* All valid cntlid values are in use. */
255121ea2901SBen Walker 	return 0xFFFF;
255221ea2901SBen Walker }
255321ea2901SBen Walker 
2554f4a4ddd8SDaniel Verkamp int
25559cb21ad6SSeth Howell nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem, struct spdk_nvmf_ctrlr *ctrlr)
2556f4a4ddd8SDaniel Verkamp {
25572a6c2c28SChangpeng Liu 
25582a6c2c28SChangpeng Liu 	if (ctrlr->dynamic_ctrlr) {
255963c90491SSeth Howell 		ctrlr->cntlid = nvmf_subsystem_gen_cntlid(subsystem);
2560f4a4ddd8SDaniel Verkamp 		if (ctrlr->cntlid == 0xFFFF) {
2561f4a4ddd8SDaniel Verkamp 			/* Unable to get a cntlid */
2562f4a4ddd8SDaniel Verkamp 			SPDK_ERRLOG("Reached max simultaneous ctrlrs\n");
2563f4a4ddd8SDaniel Verkamp 			return -EBUSY;
2564f4a4ddd8SDaniel Verkamp 		}
25652a6c2c28SChangpeng Liu 	} else if (nvmf_subsystem_get_ctrlr(subsystem, ctrlr->cntlid) != NULL) {
25662a6c2c28SChangpeng Liu 		SPDK_ERRLOG("Ctrlr with cntlid %u already exist\n", ctrlr->cntlid);
25672a6c2c28SChangpeng Liu 		return -EEXIST;
25682a6c2c28SChangpeng Liu 	}
2569f4a4ddd8SDaniel Verkamp 
2570f4a4ddd8SDaniel Verkamp 	TAILQ_INSERT_TAIL(&subsystem->ctrlrs, ctrlr, link);
2571f4a4ddd8SDaniel Verkamp 
2572d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE3(nvmf_subsystem_add_ctrlr, subsystem->subnqn, ctrlr, ctrlr->hostnqn);
2573d11601e8SKrzysztof Karas 
2574f4a4ddd8SDaniel Verkamp 	return 0;
2575f4a4ddd8SDaniel Verkamp }
2576f4a4ddd8SDaniel Verkamp 
2577f4a4ddd8SDaniel Verkamp void
25789cb21ad6SSeth Howell nvmf_subsystem_remove_ctrlr(struct spdk_nvmf_subsystem *subsystem,
2579f4a4ddd8SDaniel Verkamp 			    struct spdk_nvmf_ctrlr *ctrlr)
2580f4a4ddd8SDaniel Verkamp {
2581d11601e8SKrzysztof Karas 	SPDK_DTRACE_PROBE3(nvmf_subsystem_remove_ctrlr, subsystem->subnqn, ctrlr, ctrlr->hostnqn);
2582d11601e8SKrzysztof Karas 
258397385af1SAlexey Marchuk 	assert(spdk_get_thread() == subsystem->thread);
2584f4a4ddd8SDaniel Verkamp 	assert(subsystem == ctrlr->subsys);
2585478f6524SAlexey Marchuk 	SPDK_DEBUGLOG(nvmf, "remove ctrlr %p id 0x%x from subsys %p %s\n", ctrlr, ctrlr->cntlid, subsystem,
2586478f6524SAlexey Marchuk 		      subsystem->subnqn);
2587f4a4ddd8SDaniel Verkamp 	TAILQ_REMOVE(&subsystem->ctrlrs, ctrlr, link);
2588f4a4ddd8SDaniel Verkamp }
2589f4a4ddd8SDaniel Verkamp 
2590f4a4ddd8SDaniel Verkamp struct spdk_nvmf_ctrlr *
25919cb21ad6SSeth Howell nvmf_subsystem_get_ctrlr(struct spdk_nvmf_subsystem *subsystem, uint16_t cntlid)
2592f4a4ddd8SDaniel Verkamp {
2593f4a4ddd8SDaniel Verkamp 	struct spdk_nvmf_ctrlr *ctrlr;
2594f4a4ddd8SDaniel Verkamp 
2595f4a4ddd8SDaniel Verkamp 	TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) {
2596f4a4ddd8SDaniel Verkamp 		if (ctrlr->cntlid == cntlid) {
2597f4a4ddd8SDaniel Verkamp 			return ctrlr;
2598f4a4ddd8SDaniel Verkamp 		}
2599f4a4ddd8SDaniel Verkamp 	}
2600f4a4ddd8SDaniel Verkamp 
2601f4a4ddd8SDaniel Verkamp 	return NULL;
260221ea2901SBen Walker }
26036546fd10SDaniel Verkamp 
26046546fd10SDaniel Verkamp uint32_t
26056546fd10SDaniel Verkamp spdk_nvmf_subsystem_get_max_namespaces(const struct spdk_nvmf_subsystem *subsystem)
26066546fd10SDaniel Verkamp {
2607af07b82fSBen Walker 	return subsystem->max_nsid;
26086546fd10SDaniel Verkamp }
2609bc1d0b91SChangpeng Liu 
2610be6a01efSJonathan Teh uint16_t
2611be6a01efSJonathan Teh spdk_nvmf_subsystem_get_min_cntlid(const struct spdk_nvmf_subsystem *subsystem)
2612be6a01efSJonathan Teh {
2613be6a01efSJonathan Teh 	return subsystem->min_cntlid;
2614be6a01efSJonathan Teh }
2615be6a01efSJonathan Teh 
2616be6a01efSJonathan Teh uint16_t
2617be6a01efSJonathan Teh spdk_nvmf_subsystem_get_max_cntlid(const struct spdk_nvmf_subsystem *subsystem)
2618be6a01efSJonathan Teh {
2619be6a01efSJonathan Teh 	return subsystem->max_cntlid;
2620be6a01efSJonathan Teh }
2621be6a01efSJonathan Teh 
26221edc5f00SChangpeng Liu struct _nvmf_ns_registrant {
26231edc5f00SChangpeng Liu 	uint64_t		rkey;
26241edc5f00SChangpeng Liu 	char			*host_uuid;
26251edc5f00SChangpeng Liu };
26261edc5f00SChangpeng Liu 
26271edc5f00SChangpeng Liu struct _nvmf_ns_registrants {
26281edc5f00SChangpeng Liu 	size_t				num_regs;
26291edc5f00SChangpeng Liu 	struct _nvmf_ns_registrant	reg[SPDK_NVMF_MAX_NUM_REGISTRANTS];
26301edc5f00SChangpeng Liu };
26311edc5f00SChangpeng Liu 
26321edc5f00SChangpeng Liu struct _nvmf_ns_reservation {
26331edc5f00SChangpeng Liu 	bool					ptpl_activated;
26341edc5f00SChangpeng Liu 	enum spdk_nvme_reservation_type		rtype;
26351edc5f00SChangpeng Liu 	uint64_t				crkey;
26361edc5f00SChangpeng Liu 	char					*bdev_uuid;
26371edc5f00SChangpeng Liu 	char					*holder_uuid;
26381edc5f00SChangpeng Liu 	struct _nvmf_ns_registrants		regs;
26391edc5f00SChangpeng Liu };
26401edc5f00SChangpeng Liu 
26411edc5f00SChangpeng Liu static const struct spdk_json_object_decoder nvmf_ns_pr_reg_decoders[] = {
26421edc5f00SChangpeng Liu 	{"rkey", offsetof(struct _nvmf_ns_registrant, rkey), spdk_json_decode_uint64},
26431edc5f00SChangpeng Liu 	{"host_uuid", offsetof(struct _nvmf_ns_registrant, host_uuid), spdk_json_decode_string},
26441edc5f00SChangpeng Liu };
26451edc5f00SChangpeng Liu 
26461edc5f00SChangpeng Liu static int
26471edc5f00SChangpeng Liu nvmf_decode_ns_pr_reg(const struct spdk_json_val *val, void *out)
26481edc5f00SChangpeng Liu {
26491edc5f00SChangpeng Liu 	struct _nvmf_ns_registrant *reg = out;
26501edc5f00SChangpeng Liu 
26511edc5f00SChangpeng Liu 	return spdk_json_decode_object(val, nvmf_ns_pr_reg_decoders,
26521edc5f00SChangpeng Liu 				       SPDK_COUNTOF(nvmf_ns_pr_reg_decoders), reg);
26531edc5f00SChangpeng Liu }
26541edc5f00SChangpeng Liu 
26551edc5f00SChangpeng Liu static int
26561edc5f00SChangpeng Liu nvmf_decode_ns_pr_regs(const struct spdk_json_val *val, void *out)
26571edc5f00SChangpeng Liu {
26581edc5f00SChangpeng Liu 	struct _nvmf_ns_registrants *regs = out;
26591edc5f00SChangpeng Liu 
26601edc5f00SChangpeng Liu 	return spdk_json_decode_array(val, nvmf_decode_ns_pr_reg, regs->reg,
26611edc5f00SChangpeng Liu 				      SPDK_NVMF_MAX_NUM_REGISTRANTS, &regs->num_regs,
26621edc5f00SChangpeng Liu 				      sizeof(struct _nvmf_ns_registrant));
26631edc5f00SChangpeng Liu }
26641edc5f00SChangpeng Liu 
26651edc5f00SChangpeng Liu static const struct spdk_json_object_decoder nvmf_ns_pr_decoders[] = {
26661edc5f00SChangpeng Liu 	{"ptpl", offsetof(struct _nvmf_ns_reservation, ptpl_activated), spdk_json_decode_bool, true},
26671edc5f00SChangpeng Liu 	{"rtype", offsetof(struct _nvmf_ns_reservation, rtype), spdk_json_decode_uint32, true},
26681edc5f00SChangpeng Liu 	{"crkey", offsetof(struct _nvmf_ns_reservation, crkey), spdk_json_decode_uint64, true},
26691edc5f00SChangpeng Liu 	{"bdev_uuid", offsetof(struct _nvmf_ns_reservation, bdev_uuid), spdk_json_decode_string},
26701edc5f00SChangpeng Liu 	{"holder_uuid", offsetof(struct _nvmf_ns_reservation, holder_uuid), spdk_json_decode_string, true},
26711edc5f00SChangpeng Liu 	{"registrants", offsetof(struct _nvmf_ns_reservation, regs), nvmf_decode_ns_pr_regs},
26721edc5f00SChangpeng Liu };
26731edc5f00SChangpeng Liu 
26741edc5f00SChangpeng Liu static int
267509e8e884SArtur Paszkiewicz nvmf_ns_reservation_load_json(const struct spdk_nvmf_ns *ns,
267609e8e884SArtur Paszkiewicz 			      struct spdk_nvmf_reservation_info *info)
26771edc5f00SChangpeng Liu {
26781edc5f00SChangpeng Liu 	size_t json_size;
26791edc5f00SChangpeng Liu 	ssize_t values_cnt, rc;
26801edc5f00SChangpeng Liu 	void *json = NULL, *end;
26811edc5f00SChangpeng Liu 	struct spdk_json_val *values = NULL;
26821edc5f00SChangpeng Liu 	struct _nvmf_ns_reservation res = {};
26835bbe0c0bSArtur Paszkiewicz 	const char *file = ns->ptpl_file;
26841edc5f00SChangpeng Liu 	uint32_t i;
26851edc5f00SChangpeng Liu 
2686fc3e26a5SMichael Piszczek 	/* It's not an error if the file does not exist */
2687fc3e26a5SMichael Piszczek 	if (access(file, F_OK) != 0) {
2688fc3e26a5SMichael Piszczek 		SPDK_DEBUGLOG(nvmf, "File %s does not exist\n", file);
2689fc3e26a5SMichael Piszczek 		return 0;
2690fc3e26a5SMichael Piszczek 	}
2691fc3e26a5SMichael Piszczek 
26921edc5f00SChangpeng Liu 	/* Load all persist file contents into a local buffer */
26935434834aSKrzysztof Karas 	json = spdk_posix_file_load_from_name(file, &json_size);
26941edc5f00SChangpeng Liu 	if (!json) {
2695fc3e26a5SMichael Piszczek 		SPDK_ERRLOG("Load persist file %s failed\n", file);
26961edc5f00SChangpeng Liu 		return -ENOMEM;
26971edc5f00SChangpeng Liu 	}
26981edc5f00SChangpeng Liu 
26991edc5f00SChangpeng Liu 	rc = spdk_json_parse(json, json_size, NULL, 0, &end, 0);
27001edc5f00SChangpeng Liu 	if (rc < 0) {
27011edc5f00SChangpeng Liu 		SPDK_NOTICELOG("Parsing JSON configuration failed (%zd)\n", rc);
27021edc5f00SChangpeng Liu 		goto exit;
27031edc5f00SChangpeng Liu 	}
27041edc5f00SChangpeng Liu 
27051edc5f00SChangpeng Liu 	values_cnt = rc;
27061edc5f00SChangpeng Liu 	values = calloc(values_cnt, sizeof(struct spdk_json_val));
27071edc5f00SChangpeng Liu 	if (values == NULL) {
27081edc5f00SChangpeng Liu 		goto exit;
27091edc5f00SChangpeng Liu 	}
27101edc5f00SChangpeng Liu 
27111edc5f00SChangpeng Liu 	rc = spdk_json_parse(json, json_size, values, values_cnt, &end, 0);
27121edc5f00SChangpeng Liu 	if (rc != values_cnt) {
27131edc5f00SChangpeng Liu 		SPDK_ERRLOG("Parsing JSON configuration failed (%zd)\n", rc);
27141edc5f00SChangpeng Liu 		goto exit;
27151edc5f00SChangpeng Liu 	}
27161edc5f00SChangpeng Liu 
27171edc5f00SChangpeng Liu 	/* Decode json */
27181edc5f00SChangpeng Liu 	if (spdk_json_decode_object(values, nvmf_ns_pr_decoders,
27191edc5f00SChangpeng Liu 				    SPDK_COUNTOF(nvmf_ns_pr_decoders),
27201edc5f00SChangpeng Liu 				    &res)) {
27211edc5f00SChangpeng Liu 		SPDK_ERRLOG("Invalid objects in the persist file %s\n", file);
27221edc5f00SChangpeng Liu 		rc = -EINVAL;
27231edc5f00SChangpeng Liu 		goto exit;
27241edc5f00SChangpeng Liu 	}
27251edc5f00SChangpeng Liu 
27267b74274fSChangpeng Liu 	if (res.regs.num_regs > SPDK_NVMF_MAX_NUM_REGISTRANTS) {
27277b74274fSChangpeng Liu 		SPDK_ERRLOG("Can only support up to %u registrants\n", SPDK_NVMF_MAX_NUM_REGISTRANTS);
27287b74274fSChangpeng Liu 		rc = -ERANGE;
27297b74274fSChangpeng Liu 		goto exit;
27307b74274fSChangpeng Liu 	}
27317b74274fSChangpeng Liu 
27321edc5f00SChangpeng Liu 	rc = 0;
27331edc5f00SChangpeng Liu 	info->ptpl_activated = res.ptpl_activated;
27341edc5f00SChangpeng Liu 	info->rtype = res.rtype;
27351edc5f00SChangpeng Liu 	info->crkey = res.crkey;
27361edc5f00SChangpeng Liu 	snprintf(info->bdev_uuid, sizeof(info->bdev_uuid), "%s", res.bdev_uuid);
27371edc5f00SChangpeng Liu 	snprintf(info->holder_uuid, sizeof(info->holder_uuid), "%s", res.holder_uuid);
27381edc5f00SChangpeng Liu 	info->num_regs = res.regs.num_regs;
27391edc5f00SChangpeng Liu 	for (i = 0; i < res.regs.num_regs; i++) {
27401edc5f00SChangpeng Liu 		info->registrants[i].rkey = res.regs.reg[i].rkey;
27411edc5f00SChangpeng Liu 		snprintf(info->registrants[i].host_uuid, sizeof(info->registrants[i].host_uuid), "%s",
27421edc5f00SChangpeng Liu 			 res.regs.reg[i].host_uuid);
27431edc5f00SChangpeng Liu 	}
27441edc5f00SChangpeng Liu 
27451edc5f00SChangpeng Liu exit:
27461edc5f00SChangpeng Liu 	free(json);
27471edc5f00SChangpeng Liu 	free(values);
27481edc5f00SChangpeng Liu 	free(res.bdev_uuid);
27491edc5f00SChangpeng Liu 	free(res.holder_uuid);
27501edc5f00SChangpeng Liu 	for (i = 0; i < res.regs.num_regs; i++) {
27511edc5f00SChangpeng Liu 		free(res.regs.reg[i].host_uuid);
27521edc5f00SChangpeng Liu 	}
27531edc5f00SChangpeng Liu 
27541edc5f00SChangpeng Liu 	return rc;
27551edc5f00SChangpeng Liu }
27561edc5f00SChangpeng Liu 
27578dd1cd21SBen Walker static bool nvmf_ns_reservation_all_registrants_type(struct spdk_nvmf_ns *ns);
27581edc5f00SChangpeng Liu 
27591edc5f00SChangpeng Liu static int
27601edc5f00SChangpeng Liu nvmf_ns_reservation_restore(struct spdk_nvmf_ns *ns, struct spdk_nvmf_reservation_info *info)
27611edc5f00SChangpeng Liu {
27621edc5f00SChangpeng Liu 	uint32_t i;
27631edc5f00SChangpeng Liu 	struct spdk_nvmf_registrant *reg, *holder = NULL;
27641edc5f00SChangpeng Liu 	struct spdk_uuid bdev_uuid, holder_uuid;
2765159fa94aSMao Jiang 	bool rkey_flag = false;
27661edc5f00SChangpeng Liu 
27672172c432STomasz Zawadzki 	SPDK_DEBUGLOG(nvmf, "NSID %u, PTPL %u, Number of registrants %u\n",
27681edc5f00SChangpeng Liu 		      ns->nsid, info->ptpl_activated, info->num_regs);
27691edc5f00SChangpeng Liu 
27701edc5f00SChangpeng Liu 	/* it's not an error */
27711edc5f00SChangpeng Liu 	if (!info->ptpl_activated || !info->num_regs) {
27721edc5f00SChangpeng Liu 		return 0;
27731edc5f00SChangpeng Liu 	}
27741edc5f00SChangpeng Liu 
2775159fa94aSMao Jiang 	/* Check info->crkey exist or not in info->registrants[i].rkey */
2776159fa94aSMao Jiang 	for (i = 0; i < info->num_regs; i++) {
2777159fa94aSMao Jiang 		if (info->crkey == info->registrants[i].rkey) {
2778159fa94aSMao Jiang 			rkey_flag = true;
2779159fa94aSMao Jiang 		}
2780159fa94aSMao Jiang 	}
27818b61f516SArtur Paszkiewicz 	if (!rkey_flag && info->crkey != 0) {
2782159fa94aSMao Jiang 		return -EINVAL;
2783159fa94aSMao Jiang 	}
2784159fa94aSMao Jiang 
27851edc5f00SChangpeng Liu 	spdk_uuid_parse(&bdev_uuid, info->bdev_uuid);
27861edc5f00SChangpeng Liu 	if (spdk_uuid_compare(&bdev_uuid, spdk_bdev_get_uuid(ns->bdev))) {
27871edc5f00SChangpeng Liu 		SPDK_ERRLOG("Existing bdev UUID is not same with configuration file\n");
27881edc5f00SChangpeng Liu 		return -EINVAL;
27891edc5f00SChangpeng Liu 	}
27901edc5f00SChangpeng Liu 
27911edc5f00SChangpeng Liu 	ns->crkey = info->crkey;
27921edc5f00SChangpeng Liu 	ns->rtype = info->rtype;
27931edc5f00SChangpeng Liu 	ns->ptpl_activated = info->ptpl_activated;
27941edc5f00SChangpeng Liu 	spdk_uuid_parse(&holder_uuid, info->holder_uuid);
27951edc5f00SChangpeng Liu 
27962172c432STomasz Zawadzki 	SPDK_DEBUGLOG(nvmf, "Bdev UUID %s\n", info->bdev_uuid);
27971edc5f00SChangpeng Liu 	if (info->rtype) {
27982172c432STomasz Zawadzki 		SPDK_DEBUGLOG(nvmf, "Holder UUID %s, RTYPE %u, RKEY 0x%"PRIx64"\n",
27991edc5f00SChangpeng Liu 			      info->holder_uuid, info->rtype, info->crkey);
28001edc5f00SChangpeng Liu 	}
28011edc5f00SChangpeng Liu 
28021edc5f00SChangpeng Liu 	for (i = 0; i < info->num_regs; i++) {
28031edc5f00SChangpeng Liu 		reg = calloc(1, sizeof(*reg));
28041edc5f00SChangpeng Liu 		if (!reg) {
28051edc5f00SChangpeng Liu 			return -ENOMEM;
28061edc5f00SChangpeng Liu 		}
28071edc5f00SChangpeng Liu 		spdk_uuid_parse(&reg->hostid, info->registrants[i].host_uuid);
28081edc5f00SChangpeng Liu 		reg->rkey = info->registrants[i].rkey;
28091edc5f00SChangpeng Liu 		TAILQ_INSERT_TAIL(&ns->registrants, reg, link);
28108b61f516SArtur Paszkiewicz 		if (info->crkey != 0 && !spdk_uuid_compare(&holder_uuid, &reg->hostid)) {
28111edc5f00SChangpeng Liu 			holder = reg;
28121edc5f00SChangpeng Liu 		}
28132172c432STomasz Zawadzki 		SPDK_DEBUGLOG(nvmf, "Registrant RKEY 0x%"PRIx64", Host UUID %s\n",
28141edc5f00SChangpeng Liu 			      info->registrants[i].rkey, info->registrants[i].host_uuid);
28151edc5f00SChangpeng Liu 	}
28161edc5f00SChangpeng Liu 
28171edc5f00SChangpeng Liu 	if (nvmf_ns_reservation_all_registrants_type(ns)) {
28181edc5f00SChangpeng Liu 		ns->holder = TAILQ_FIRST(&ns->registrants);
28191edc5f00SChangpeng Liu 	} else {
28201edc5f00SChangpeng Liu 		ns->holder = holder;
28211edc5f00SChangpeng Liu 	}
28221edc5f00SChangpeng Liu 
28231edc5f00SChangpeng Liu 	return 0;
28241edc5f00SChangpeng Liu }
28251edc5f00SChangpeng Liu 
2826196d4f70SChangpeng Liu static int
282763c90491SSeth Howell nvmf_ns_json_write_cb(void *cb_ctx, const void *data, size_t size)
2828196d4f70SChangpeng Liu {
2829196d4f70SChangpeng Liu 	char *file = cb_ctx;
2830196d4f70SChangpeng Liu 	size_t rc;
2831196d4f70SChangpeng Liu 	FILE *fd;
2832196d4f70SChangpeng Liu 
2833196d4f70SChangpeng Liu 	fd = fopen(file, "w");
2834196d4f70SChangpeng Liu 	if (!fd) {
2835196d4f70SChangpeng Liu 		SPDK_ERRLOG("Can't open file %s for write\n", file);
2836196d4f70SChangpeng Liu 		return -ENOENT;
2837196d4f70SChangpeng Liu 	}
2838196d4f70SChangpeng Liu 	rc = fwrite(data, 1, size, fd);
2839196d4f70SChangpeng Liu 	fclose(fd);
2840196d4f70SChangpeng Liu 
2841196d4f70SChangpeng Liu 	return rc == size ? 0 : -1;
2842196d4f70SChangpeng Liu }
2843196d4f70SChangpeng Liu 
2844196d4f70SChangpeng Liu static int
284509e8e884SArtur Paszkiewicz nvmf_ns_reservation_update_json(const struct spdk_nvmf_ns *ns,
28465bbe0c0bSArtur Paszkiewicz 				const struct spdk_nvmf_reservation_info *info)
2847196d4f70SChangpeng Liu {
28485bbe0c0bSArtur Paszkiewicz 	const char *file = ns->ptpl_file;
2849196d4f70SChangpeng Liu 	struct spdk_json_write_ctx *w;
2850196d4f70SChangpeng Liu 	uint32_t i;
2851196d4f70SChangpeng Liu 	int rc = 0;
2852196d4f70SChangpeng Liu 
285363c90491SSeth Howell 	w = spdk_json_write_begin(nvmf_ns_json_write_cb, (void *)file, 0);
2854196d4f70SChangpeng Liu 	if (w == NULL) {
2855196d4f70SChangpeng Liu 		return -ENOMEM;
2856196d4f70SChangpeng Liu 	}
2857196d4f70SChangpeng Liu 	/* clear the configuration file */
2858196d4f70SChangpeng Liu 	if (!info->ptpl_activated) {
2859196d4f70SChangpeng Liu 		goto exit;
2860196d4f70SChangpeng Liu 	}
2861196d4f70SChangpeng Liu 
2862196d4f70SChangpeng Liu 	spdk_json_write_object_begin(w);
2863196d4f70SChangpeng Liu 	spdk_json_write_named_bool(w, "ptpl", info->ptpl_activated);
2864196d4f70SChangpeng Liu 	spdk_json_write_named_uint32(w, "rtype", info->rtype);
2865196d4f70SChangpeng Liu 	spdk_json_write_named_uint64(w, "crkey", info->crkey);
2866196d4f70SChangpeng Liu 	spdk_json_write_named_string(w, "bdev_uuid", info->bdev_uuid);
2867196d4f70SChangpeng Liu 	spdk_json_write_named_string(w, "holder_uuid", info->holder_uuid);
2868196d4f70SChangpeng Liu 
2869196d4f70SChangpeng Liu 	spdk_json_write_named_array_begin(w, "registrants");
2870196d4f70SChangpeng Liu 	for (i = 0; i < info->num_regs; i++) {
2871196d4f70SChangpeng Liu 		spdk_json_write_object_begin(w);
2872196d4f70SChangpeng Liu 		spdk_json_write_named_uint64(w, "rkey", info->registrants[i].rkey);
2873196d4f70SChangpeng Liu 		spdk_json_write_named_string(w, "host_uuid", info->registrants[i].host_uuid);
2874196d4f70SChangpeng Liu 		spdk_json_write_object_end(w);
2875196d4f70SChangpeng Liu 	}
2876196d4f70SChangpeng Liu 	spdk_json_write_array_end(w);
2877196d4f70SChangpeng Liu 	spdk_json_write_object_end(w);
2878196d4f70SChangpeng Liu 
2879196d4f70SChangpeng Liu exit:
2880196d4f70SChangpeng Liu 	rc = spdk_json_write_end(w);
2881196d4f70SChangpeng Liu 	return rc;
2882196d4f70SChangpeng Liu }
2883196d4f70SChangpeng Liu 
2884196d4f70SChangpeng Liu static int
2885196d4f70SChangpeng Liu nvmf_ns_update_reservation_info(struct spdk_nvmf_ns *ns)
2886196d4f70SChangpeng Liu {
2887196d4f70SChangpeng Liu 	struct spdk_nvmf_reservation_info info;
2888196d4f70SChangpeng Liu 	struct spdk_nvmf_registrant *reg, *tmp;
2889196d4f70SChangpeng Liu 	uint32_t i = 0;
2890196d4f70SChangpeng Liu 
2891196d4f70SChangpeng Liu 	assert(ns != NULL);
2892196d4f70SChangpeng Liu 
2893c6a78e83SArtur Paszkiewicz 	if (!ns->bdev || !nvmf_ns_is_ptpl_capable(ns)) {
2894196d4f70SChangpeng Liu 		return 0;
2895196d4f70SChangpeng Liu 	}
2896196d4f70SChangpeng Liu 
2897196d4f70SChangpeng Liu 	memset(&info, 0, sizeof(info));
2898196d4f70SChangpeng Liu 	spdk_uuid_fmt_lower(info.bdev_uuid, sizeof(info.bdev_uuid), spdk_bdev_get_uuid(ns->bdev));
2899196d4f70SChangpeng Liu 
2900196d4f70SChangpeng Liu 	if (ns->rtype) {
2901196d4f70SChangpeng Liu 		info.rtype = ns->rtype;
2902196d4f70SChangpeng Liu 		info.crkey = ns->crkey;
2903196d4f70SChangpeng Liu 		if (!nvmf_ns_reservation_all_registrants_type(ns)) {
2904196d4f70SChangpeng Liu 			assert(ns->holder != NULL);
2905196d4f70SChangpeng Liu 			spdk_uuid_fmt_lower(info.holder_uuid, sizeof(info.holder_uuid), &ns->holder->hostid);
2906196d4f70SChangpeng Liu 		}
2907196d4f70SChangpeng Liu 	}
2908196d4f70SChangpeng Liu 
2909196d4f70SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, tmp) {
2910196d4f70SChangpeng Liu 		spdk_uuid_fmt_lower(info.registrants[i].host_uuid, sizeof(info.registrants[i].host_uuid),
2911196d4f70SChangpeng Liu 				    &reg->hostid);
2912196d4f70SChangpeng Liu 		info.registrants[i++].rkey = reg->rkey;
2913196d4f70SChangpeng Liu 	}
2914196d4f70SChangpeng Liu 
2915196d4f70SChangpeng Liu 	info.num_regs = i;
2916196d4f70SChangpeng Liu 	info.ptpl_activated = ns->ptpl_activated;
2917196d4f70SChangpeng Liu 
29185bbe0c0bSArtur Paszkiewicz 	return nvmf_ns_reservation_update(ns, &info);
2919196d4f70SChangpeng Liu }
2920196d4f70SChangpeng Liu 
2921bc1d0b91SChangpeng Liu static struct spdk_nvmf_registrant *
2922bc1d0b91SChangpeng Liu nvmf_ns_reservation_get_registrant(struct spdk_nvmf_ns *ns,
2923bc1d0b91SChangpeng Liu 				   struct spdk_uuid *uuid)
2924bc1d0b91SChangpeng Liu {
2925bc1d0b91SChangpeng Liu 	struct spdk_nvmf_registrant *reg, *tmp;
2926bc1d0b91SChangpeng Liu 
2927bc1d0b91SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, tmp) {
292868bb3995SChangpeng Liu 		if (!spdk_uuid_compare(&reg->hostid, uuid)) {
2929bc1d0b91SChangpeng Liu 			return reg;
2930bc1d0b91SChangpeng Liu 		}
2931bc1d0b91SChangpeng Liu 	}
2932bc1d0b91SChangpeng Liu 
2933bc1d0b91SChangpeng Liu 	return NULL;
2934bc1d0b91SChangpeng Liu }
2935bc1d0b91SChangpeng Liu 
293678bfb2a1SChangpeng Liu /* Generate reservation notice log to registered HostID controllers */
293778bfb2a1SChangpeng Liu static void
293878bfb2a1SChangpeng Liu nvmf_subsystem_gen_ctrlr_notification(struct spdk_nvmf_subsystem *subsystem,
293978bfb2a1SChangpeng Liu 				      struct spdk_nvmf_ns *ns,
294078bfb2a1SChangpeng Liu 				      struct spdk_uuid *hostid_list,
294178bfb2a1SChangpeng Liu 				      uint32_t num_hostid,
294278bfb2a1SChangpeng Liu 				      enum spdk_nvme_reservation_notification_log_page_type type)
294378bfb2a1SChangpeng Liu {
294478bfb2a1SChangpeng Liu 	struct spdk_nvmf_ctrlr *ctrlr;
294578bfb2a1SChangpeng Liu 	uint32_t i;
294678bfb2a1SChangpeng Liu 
294778bfb2a1SChangpeng Liu 	for (i = 0; i < num_hostid; i++) {
294878bfb2a1SChangpeng Liu 		TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) {
294978bfb2a1SChangpeng Liu 			if (!spdk_uuid_compare(&ctrlr->hostid, &hostid_list[i])) {
29509cb21ad6SSeth Howell 				nvmf_ctrlr_reservation_notice_log(ctrlr, ns, type);
295178bfb2a1SChangpeng Liu 			}
295278bfb2a1SChangpeng Liu 		}
295378bfb2a1SChangpeng Liu 	}
295478bfb2a1SChangpeng Liu }
295578bfb2a1SChangpeng Liu 
295678bfb2a1SChangpeng Liu /* Get all registrants' hostid other than the controller who issued the command */
295778bfb2a1SChangpeng Liu static uint32_t
295878bfb2a1SChangpeng Liu nvmf_ns_reservation_get_all_other_hostid(struct spdk_nvmf_ns *ns,
295978bfb2a1SChangpeng Liu 		struct spdk_uuid *hostid_list,
296078bfb2a1SChangpeng Liu 		uint32_t max_num_hostid,
296178bfb2a1SChangpeng Liu 		struct spdk_uuid *current_hostid)
296278bfb2a1SChangpeng Liu {
296378bfb2a1SChangpeng Liu 	struct spdk_nvmf_registrant *reg, *tmp;
296478bfb2a1SChangpeng Liu 	uint32_t num_hostid = 0;
296578bfb2a1SChangpeng Liu 
296678bfb2a1SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, tmp) {
296778bfb2a1SChangpeng Liu 		if (spdk_uuid_compare(&reg->hostid, current_hostid)) {
296878bfb2a1SChangpeng Liu 			if (num_hostid == max_num_hostid) {
296978bfb2a1SChangpeng Liu 				assert(false);
297078bfb2a1SChangpeng Liu 				return max_num_hostid;
297178bfb2a1SChangpeng Liu 			}
297278bfb2a1SChangpeng Liu 			hostid_list[num_hostid++] = reg->hostid;
297378bfb2a1SChangpeng Liu 		}
297478bfb2a1SChangpeng Liu 	}
297578bfb2a1SChangpeng Liu 
297678bfb2a1SChangpeng Liu 	return num_hostid;
297778bfb2a1SChangpeng Liu }
297878bfb2a1SChangpeng Liu 
297978bfb2a1SChangpeng Liu /* Calculate the unregistered HostID list according to list
298078bfb2a1SChangpeng Liu  * prior to execute preempt command and list after executing
298178bfb2a1SChangpeng Liu  * preempt command.
298278bfb2a1SChangpeng Liu  */
298378bfb2a1SChangpeng Liu static uint32_t
298478bfb2a1SChangpeng Liu nvmf_ns_reservation_get_unregistered_hostid(struct spdk_uuid *old_hostid_list,
298578bfb2a1SChangpeng Liu 		uint32_t old_num_hostid,
298678bfb2a1SChangpeng Liu 		struct spdk_uuid *remaining_hostid_list,
298778bfb2a1SChangpeng Liu 		uint32_t remaining_num_hostid)
298878bfb2a1SChangpeng Liu {
298978bfb2a1SChangpeng Liu 	struct spdk_uuid temp_hostid_list[SPDK_NVMF_MAX_NUM_REGISTRANTS];
299078bfb2a1SChangpeng Liu 	uint32_t i, j, num_hostid = 0;
299178bfb2a1SChangpeng Liu 	bool found;
299278bfb2a1SChangpeng Liu 
299378bfb2a1SChangpeng Liu 	if (!remaining_num_hostid) {
299478bfb2a1SChangpeng Liu 		return old_num_hostid;
299578bfb2a1SChangpeng Liu 	}
299678bfb2a1SChangpeng Liu 
299778bfb2a1SChangpeng Liu 	for (i = 0; i < old_num_hostid; i++) {
299878bfb2a1SChangpeng Liu 		found = false;
299978bfb2a1SChangpeng Liu 		for (j = 0; j < remaining_num_hostid; j++) {
300078bfb2a1SChangpeng Liu 			if (!spdk_uuid_compare(&old_hostid_list[i], &remaining_hostid_list[j])) {
300178bfb2a1SChangpeng Liu 				found = true;
300278bfb2a1SChangpeng Liu 				break;
300378bfb2a1SChangpeng Liu 			}
300478bfb2a1SChangpeng Liu 		}
300578bfb2a1SChangpeng Liu 		if (!found) {
300678bfb2a1SChangpeng Liu 			spdk_uuid_copy(&temp_hostid_list[num_hostid++], &old_hostid_list[i]);
300778bfb2a1SChangpeng Liu 		}
300878bfb2a1SChangpeng Liu 	}
300978bfb2a1SChangpeng Liu 
301078bfb2a1SChangpeng Liu 	if (num_hostid) {
301178bfb2a1SChangpeng Liu 		memcpy(old_hostid_list, temp_hostid_list, sizeof(struct spdk_uuid) * num_hostid);
301278bfb2a1SChangpeng Liu 	}
301378bfb2a1SChangpeng Liu 
301478bfb2a1SChangpeng Liu 	return num_hostid;
301578bfb2a1SChangpeng Liu }
301678bfb2a1SChangpeng Liu 
301771ac18d1SChangpeng Liu /* current reservation type is all registrants or not */
301871ac18d1SChangpeng Liu static bool
301971ac18d1SChangpeng Liu nvmf_ns_reservation_all_registrants_type(struct spdk_nvmf_ns *ns)
302071ac18d1SChangpeng Liu {
302171ac18d1SChangpeng Liu 	return (ns->rtype == SPDK_NVME_RESERVE_WRITE_EXCLUSIVE_ALL_REGS ||
302271ac18d1SChangpeng Liu 		ns->rtype == SPDK_NVME_RESERVE_EXCLUSIVE_ACCESS_ALL_REGS);
302371ac18d1SChangpeng Liu }
302471ac18d1SChangpeng Liu 
302571ac18d1SChangpeng Liu /* current registrant is reservation holder or not */
302671ac18d1SChangpeng Liu static bool
302771ac18d1SChangpeng Liu nvmf_ns_reservation_registrant_is_holder(struct spdk_nvmf_ns *ns,
302871ac18d1SChangpeng Liu 		struct spdk_nvmf_registrant *reg)
302971ac18d1SChangpeng Liu {
303071ac18d1SChangpeng Liu 	if (!reg) {
303171ac18d1SChangpeng Liu 		return false;
303271ac18d1SChangpeng Liu 	}
303371ac18d1SChangpeng Liu 
303471ac18d1SChangpeng Liu 	if (nvmf_ns_reservation_all_registrants_type(ns)) {
303571ac18d1SChangpeng Liu 		return true;
303671ac18d1SChangpeng Liu 	}
303771ac18d1SChangpeng Liu 
303871ac18d1SChangpeng Liu 	return (ns->holder == reg);
303971ac18d1SChangpeng Liu }
304071ac18d1SChangpeng Liu 
3041bc1d0b91SChangpeng Liu static int
3042bc1d0b91SChangpeng Liu nvmf_ns_reservation_add_registrant(struct spdk_nvmf_ns *ns,
3043bc1d0b91SChangpeng Liu 				   struct spdk_nvmf_ctrlr *ctrlr,
3044bc1d0b91SChangpeng Liu 				   uint64_t nrkey)
3045bc1d0b91SChangpeng Liu {
3046bc1d0b91SChangpeng Liu 	struct spdk_nvmf_registrant *reg;
3047bc1d0b91SChangpeng Liu 
3048bc1d0b91SChangpeng Liu 	reg = calloc(1, sizeof(*reg));
3049bc1d0b91SChangpeng Liu 	if (!reg) {
3050bc1d0b91SChangpeng Liu 		return -ENOMEM;
3051bc1d0b91SChangpeng Liu 	}
3052bc1d0b91SChangpeng Liu 
3053bc1d0b91SChangpeng Liu 	reg->rkey = nrkey;
3054bc1d0b91SChangpeng Liu 	/* set hostid for the registrant */
3055bc1d0b91SChangpeng Liu 	spdk_uuid_copy(&reg->hostid, &ctrlr->hostid);
3056bc1d0b91SChangpeng Liu 	TAILQ_INSERT_TAIL(&ns->registrants, reg, link);
3057bc1d0b91SChangpeng Liu 	ns->gen++;
3058bc1d0b91SChangpeng Liu 
3059bc1d0b91SChangpeng Liu 	return 0;
3060bc1d0b91SChangpeng Liu }
3061bc1d0b91SChangpeng Liu 
306284ee3a62SChangpeng Liu static void
306384ee3a62SChangpeng Liu nvmf_ns_reservation_release_reservation(struct spdk_nvmf_ns *ns)
306484ee3a62SChangpeng Liu {
306584ee3a62SChangpeng Liu 	ns->rtype = 0;
306684ee3a62SChangpeng Liu 	ns->crkey = 0;
306784ee3a62SChangpeng Liu 	ns->holder = NULL;
306884ee3a62SChangpeng Liu }
306984ee3a62SChangpeng Liu 
30708ccf24edSChangpeng Liu /* release the reservation if the last registrant was removed */
30718ccf24edSChangpeng Liu static void
30728ccf24edSChangpeng Liu nvmf_ns_reservation_check_release_on_remove_registrant(struct spdk_nvmf_ns *ns,
30738ccf24edSChangpeng Liu 		struct spdk_nvmf_registrant *reg)
30748ccf24edSChangpeng Liu {
30758ccf24edSChangpeng Liu 	struct spdk_nvmf_registrant *next_reg;
30768ccf24edSChangpeng Liu 
30778ccf24edSChangpeng Liu 	/* no reservation holder */
30788ccf24edSChangpeng Liu 	if (!ns->holder) {
30798ccf24edSChangpeng Liu 		assert(ns->rtype == 0);
30808ccf24edSChangpeng Liu 		return;
30818ccf24edSChangpeng Liu 	}
30828ccf24edSChangpeng Liu 
30838ccf24edSChangpeng Liu 	next_reg = TAILQ_FIRST(&ns->registrants);
30848ccf24edSChangpeng Liu 	if (next_reg && nvmf_ns_reservation_all_registrants_type(ns)) {
30858ccf24edSChangpeng Liu 		/* the next valid registrant is the new holder now */
30868ccf24edSChangpeng Liu 		ns->holder = next_reg;
30878ccf24edSChangpeng Liu 	} else if (nvmf_ns_reservation_registrant_is_holder(ns, reg)) {
30888ccf24edSChangpeng Liu 		/* release the reservation */
308984ee3a62SChangpeng Liu 		nvmf_ns_reservation_release_reservation(ns);
30908ccf24edSChangpeng Liu 	}
30918ccf24edSChangpeng Liu }
30928ccf24edSChangpeng Liu 
3093bc1d0b91SChangpeng Liu static void
3094bc1d0b91SChangpeng Liu nvmf_ns_reservation_remove_registrant(struct spdk_nvmf_ns *ns,
3095bc1d0b91SChangpeng Liu 				      struct spdk_nvmf_registrant *reg)
3096bc1d0b91SChangpeng Liu {
3097bc1d0b91SChangpeng Liu 	TAILQ_REMOVE(&ns->registrants, reg, link);
30988ccf24edSChangpeng Liu 	nvmf_ns_reservation_check_release_on_remove_registrant(ns, reg);
3099bc1d0b91SChangpeng Liu 	free(reg);
3100bc1d0b91SChangpeng Liu 	ns->gen++;
3101bc1d0b91SChangpeng Liu 	return;
3102bc1d0b91SChangpeng Liu }
3103bc1d0b91SChangpeng Liu 
310471ac18d1SChangpeng Liu static uint32_t
310571ac18d1SChangpeng Liu nvmf_ns_reservation_remove_registrants_by_key(struct spdk_nvmf_ns *ns,
310671ac18d1SChangpeng Liu 		uint64_t rkey)
310771ac18d1SChangpeng Liu {
310871ac18d1SChangpeng Liu 	struct spdk_nvmf_registrant *reg, *tmp;
310971ac18d1SChangpeng Liu 	uint32_t count = 0;
311071ac18d1SChangpeng Liu 
311171ac18d1SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, tmp) {
311271ac18d1SChangpeng Liu 		if (reg->rkey == rkey) {
311371ac18d1SChangpeng Liu 			nvmf_ns_reservation_remove_registrant(ns, reg);
311471ac18d1SChangpeng Liu 			count++;
311571ac18d1SChangpeng Liu 		}
311671ac18d1SChangpeng Liu 	}
311771ac18d1SChangpeng Liu 	return count;
311871ac18d1SChangpeng Liu }
311971ac18d1SChangpeng Liu 
312071ac18d1SChangpeng Liu static uint32_t
312171ac18d1SChangpeng Liu nvmf_ns_reservation_remove_all_other_registrants(struct spdk_nvmf_ns *ns,
312271ac18d1SChangpeng Liu 		struct spdk_nvmf_registrant *reg)
312371ac18d1SChangpeng Liu {
312471ac18d1SChangpeng Liu 	struct spdk_nvmf_registrant *reg_tmp, *reg_tmp2;
312571ac18d1SChangpeng Liu 	uint32_t count = 0;
312671ac18d1SChangpeng Liu 
312771ac18d1SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg_tmp, &ns->registrants, link, reg_tmp2) {
312871ac18d1SChangpeng Liu 		if (reg_tmp != reg) {
312971ac18d1SChangpeng Liu 			nvmf_ns_reservation_remove_registrant(ns, reg_tmp);
313071ac18d1SChangpeng Liu 			count++;
313171ac18d1SChangpeng Liu 		}
313271ac18d1SChangpeng Liu 	}
313371ac18d1SChangpeng Liu 	return count;
313471ac18d1SChangpeng Liu }
313571ac18d1SChangpeng Liu 
313684ee3a62SChangpeng Liu static uint32_t
313784ee3a62SChangpeng Liu nvmf_ns_reservation_clear_all_registrants(struct spdk_nvmf_ns *ns)
313884ee3a62SChangpeng Liu {
313984ee3a62SChangpeng Liu 	struct spdk_nvmf_registrant *reg, *reg_tmp;
314084ee3a62SChangpeng Liu 	uint32_t count = 0;
314184ee3a62SChangpeng Liu 
314284ee3a62SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, reg_tmp) {
314384ee3a62SChangpeng Liu 		nvmf_ns_reservation_remove_registrant(ns, reg);
314484ee3a62SChangpeng Liu 		count++;
314584ee3a62SChangpeng Liu 	}
314684ee3a62SChangpeng Liu 	return count;
314784ee3a62SChangpeng Liu }
314884ee3a62SChangpeng Liu 
314971ac18d1SChangpeng Liu static void
315071ac18d1SChangpeng Liu nvmf_ns_reservation_acquire_reservation(struct spdk_nvmf_ns *ns, uint64_t rkey,
315171ac18d1SChangpeng Liu 					enum spdk_nvme_reservation_type rtype,
315271ac18d1SChangpeng Liu 					struct spdk_nvmf_registrant *holder)
315371ac18d1SChangpeng Liu {
315471ac18d1SChangpeng Liu 	ns->rtype = rtype;
315571ac18d1SChangpeng Liu 	ns->crkey = rkey;
315671ac18d1SChangpeng Liu 	assert(ns->holder == NULL);
315771ac18d1SChangpeng Liu 	ns->holder = holder;
315871ac18d1SChangpeng Liu }
315971ac18d1SChangpeng Liu 
31601fd5b1daSChangpeng Liu static bool
3161bc1d0b91SChangpeng Liu nvmf_ns_reservation_register(struct spdk_nvmf_ns *ns,
3162bc1d0b91SChangpeng Liu 			     struct spdk_nvmf_ctrlr *ctrlr,
3163bc1d0b91SChangpeng Liu 			     struct spdk_nvmf_request *req)
3164bc1d0b91SChangpeng Liu {
3165cc3184b8SJohn Levon 	struct spdk_nvme_reservation_register_data key = { 0 };
3166bc1d0b91SChangpeng Liu 	struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
316778bfb2a1SChangpeng Liu 	uint8_t rrega, iekey, cptpl, rtype;
3168bc1d0b91SChangpeng Liu 	struct spdk_nvmf_registrant *reg;
3169bc1d0b91SChangpeng Liu 	uint8_t status = SPDK_NVME_SC_SUCCESS;
31701fd5b1daSChangpeng Liu 	bool update_sgroup = false;
317178bfb2a1SChangpeng Liu 	struct spdk_uuid hostid_list[SPDK_NVMF_MAX_NUM_REGISTRANTS];
317278bfb2a1SChangpeng Liu 	uint32_t num_hostid = 0;
3173bc1d0b91SChangpeng Liu 	int rc;
3174bc1d0b91SChangpeng Liu 
31751fea1fccSChangpeng Liu 	rrega = cmd->cdw10_bits.resv_register.rrega;
31761fea1fccSChangpeng Liu 	iekey = cmd->cdw10_bits.resv_register.iekey;
31771fea1fccSChangpeng Liu 	cptpl = cmd->cdw10_bits.resv_register.cptpl;
31783856d82bSSeth Howell 
3179cc3184b8SJohn Levon 	if (req->iovcnt > 0 && req->length >= sizeof(key)) {
3180cc3184b8SJohn Levon 		struct spdk_iov_xfer ix;
3181cc3184b8SJohn Levon 		spdk_iov_xfer_init(&ix, req->iov, req->iovcnt);
3182cc3184b8SJohn Levon 		spdk_iov_xfer_to_buf(&ix, &key, sizeof(key));
31833856d82bSSeth Howell 	} else {
31843856d82bSSeth Howell 		SPDK_ERRLOG("No key provided. Failing request.\n");
31853856d82bSSeth Howell 		status = SPDK_NVME_SC_INVALID_FIELD;
31863856d82bSSeth Howell 		goto exit;
31873856d82bSSeth Howell 	}
3188bc1d0b91SChangpeng Liu 
31892172c432STomasz Zawadzki 	SPDK_DEBUGLOG(nvmf, "REGISTER: RREGA %u, IEKEY %u, CPTPL %u, "
3190bc1d0b91SChangpeng Liu 		      "NRKEY 0x%"PRIx64", NRKEY 0x%"PRIx64"\n",
3191bc1d0b91SChangpeng Liu 		      rrega, iekey, cptpl, key.crkey, key.nrkey);
3192bc1d0b91SChangpeng Liu 
3193196d4f70SChangpeng Liu 	if (cptpl == SPDK_NVME_RESERVE_PTPL_CLEAR_POWER_ON) {
319434edd9f1SKamil Godzwon 		/* True to OFF state, and need to be updated in the configuration file */
3195196d4f70SChangpeng Liu 		if (ns->ptpl_activated) {
3196196d4f70SChangpeng Liu 			ns->ptpl_activated = 0;
3197196d4f70SChangpeng Liu 			update_sgroup = true;
3198196d4f70SChangpeng Liu 		}
3199196d4f70SChangpeng Liu 	} else if (cptpl == SPDK_NVME_RESERVE_PTPL_PERSIST_POWER_LOSS) {
3200c6a78e83SArtur Paszkiewicz 		if (!nvmf_ns_is_ptpl_capable(ns)) {
3201bc1d0b91SChangpeng Liu 			status = SPDK_NVME_SC_INVALID_FIELD;
3202bc1d0b91SChangpeng Liu 			goto exit;
3203196d4f70SChangpeng Liu 		} else if (ns->ptpl_activated == 0) {
3204196d4f70SChangpeng Liu 			ns->ptpl_activated = 1;
3205196d4f70SChangpeng Liu 			update_sgroup = true;
3206196d4f70SChangpeng Liu 		}
3207bc1d0b91SChangpeng Liu 	}
3208bc1d0b91SChangpeng Liu 
3209bc1d0b91SChangpeng Liu 	/* current Host Identifier has registrant or not */
3210bc1d0b91SChangpeng Liu 	reg = nvmf_ns_reservation_get_registrant(ns, &ctrlr->hostid);
3211bc1d0b91SChangpeng Liu 
3212bc1d0b91SChangpeng Liu 	switch (rrega) {
3213bc1d0b91SChangpeng Liu 	case SPDK_NVME_RESERVE_REGISTER_KEY:
3214bc1d0b91SChangpeng Liu 		if (!reg) {
3215bc1d0b91SChangpeng Liu 			/* register new controller */
3216bc1d0b91SChangpeng Liu 			if (key.nrkey == 0) {
3217bc1d0b91SChangpeng Liu 				SPDK_ERRLOG("Can't register zeroed new key\n");
3218bc1d0b91SChangpeng Liu 				status = SPDK_NVME_SC_INVALID_FIELD;
3219bc1d0b91SChangpeng Liu 				goto exit;
3220bc1d0b91SChangpeng Liu 			}
3221bc1d0b91SChangpeng Liu 			rc = nvmf_ns_reservation_add_registrant(ns, ctrlr, key.nrkey);
3222bc1d0b91SChangpeng Liu 			if (rc < 0) {
3223bc1d0b91SChangpeng Liu 				status = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR;
3224bc1d0b91SChangpeng Liu 				goto exit;
3225bc1d0b91SChangpeng Liu 			}
3226c596ea4bSChangpeng Liu 			update_sgroup = true;
3227bc1d0b91SChangpeng Liu 		} else {
3228bc1d0b91SChangpeng Liu 			/* register with same key is not an error */
3229bc1d0b91SChangpeng Liu 			if (reg->rkey != key.nrkey) {
3230bc1d0b91SChangpeng Liu 				SPDK_ERRLOG("The same host already register a "
3231bc1d0b91SChangpeng Liu 					    "key with 0x%"PRIx64"\n",
3232bc1d0b91SChangpeng Liu 					    reg->rkey);
3233bc1d0b91SChangpeng Liu 				status = SPDK_NVME_SC_RESERVATION_CONFLICT;
3234bc1d0b91SChangpeng Liu 				goto exit;
3235bc1d0b91SChangpeng Liu 			}
3236bc1d0b91SChangpeng Liu 		}
3237bc1d0b91SChangpeng Liu 		break;
3238bc1d0b91SChangpeng Liu 	case SPDK_NVME_RESERVE_UNREGISTER_KEY:
3239bc1d0b91SChangpeng Liu 		if (!reg || (!iekey && reg->rkey != key.crkey)) {
3240bc1d0b91SChangpeng Liu 			SPDK_ERRLOG("No registrant or current key doesn't match "
3241bc1d0b91SChangpeng Liu 				    "with existing registrant key\n");
3242bc1d0b91SChangpeng Liu 			status = SPDK_NVME_SC_RESERVATION_CONFLICT;
3243bc1d0b91SChangpeng Liu 			goto exit;
3244bc1d0b91SChangpeng Liu 		}
324578bfb2a1SChangpeng Liu 
324678bfb2a1SChangpeng Liu 		rtype = ns->rtype;
324778bfb2a1SChangpeng Liu 		num_hostid = nvmf_ns_reservation_get_all_other_hostid(ns, hostid_list,
324878bfb2a1SChangpeng Liu 				SPDK_NVMF_MAX_NUM_REGISTRANTS,
324978bfb2a1SChangpeng Liu 				&ctrlr->hostid);
325078bfb2a1SChangpeng Liu 
3251bc1d0b91SChangpeng Liu 		nvmf_ns_reservation_remove_registrant(ns, reg);
325278bfb2a1SChangpeng Liu 
325378bfb2a1SChangpeng Liu 		if (!ns->rtype && num_hostid && (rtype == SPDK_NVME_RESERVE_WRITE_EXCLUSIVE_REG_ONLY ||
325478bfb2a1SChangpeng Liu 						 rtype == SPDK_NVME_RESERVE_EXCLUSIVE_ACCESS_REG_ONLY)) {
325578bfb2a1SChangpeng Liu 			nvmf_subsystem_gen_ctrlr_notification(ns->subsystem, ns,
325678bfb2a1SChangpeng Liu 							      hostid_list,
325778bfb2a1SChangpeng Liu 							      num_hostid,
325878bfb2a1SChangpeng Liu 							      SPDK_NVME_RESERVATION_RELEASED);
325978bfb2a1SChangpeng Liu 		}
32601fd5b1daSChangpeng Liu 		update_sgroup = true;
3261bc1d0b91SChangpeng Liu 		break;
3262bc1d0b91SChangpeng Liu 	case SPDK_NVME_RESERVE_REPLACE_KEY:
3263bc1d0b91SChangpeng Liu 		if (key.nrkey == 0) {
3264bc1d0b91SChangpeng Liu 			SPDK_ERRLOG("Can't register zeroed new key\n");
3265bc1d0b91SChangpeng Liu 			status = SPDK_NVME_SC_INVALID_FIELD;
3266bc1d0b91SChangpeng Liu 			goto exit;
3267bc1d0b91SChangpeng Liu 		}
3268d8843dccSChangpeng Liu 		/* Registrant exists */
3269d8843dccSChangpeng Liu 		if (reg) {
3270d8843dccSChangpeng Liu 			if (!iekey && reg->rkey != key.crkey) {
3271d8843dccSChangpeng Liu 				SPDK_ERRLOG("Current key doesn't match "
3272d8843dccSChangpeng Liu 					    "existing registrant key\n");
3273d8843dccSChangpeng Liu 				status = SPDK_NVME_SC_RESERVATION_CONFLICT;
3274d8843dccSChangpeng Liu 				goto exit;
3275d8843dccSChangpeng Liu 			}
3276d8843dccSChangpeng Liu 			if (reg->rkey == key.nrkey) {
3277d8843dccSChangpeng Liu 				goto exit;
3278d8843dccSChangpeng Liu 			}
3279bc1d0b91SChangpeng Liu 			reg->rkey = key.nrkey;
3280d8843dccSChangpeng Liu 		} else if (iekey) { /* No registrant but IEKEY is set */
3281d8843dccSChangpeng Liu 			/* new registrant */
3282d8843dccSChangpeng Liu 			rc = nvmf_ns_reservation_add_registrant(ns, ctrlr, key.nrkey);
3283d8843dccSChangpeng Liu 			if (rc < 0) {
3284d8843dccSChangpeng Liu 				status = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR;
3285d8843dccSChangpeng Liu 				goto exit;
3286d8843dccSChangpeng Liu 			}
3287d8843dccSChangpeng Liu 		} else { /* No registrant */
3288d8843dccSChangpeng Liu 			SPDK_ERRLOG("No registrant\n");
3289d8843dccSChangpeng Liu 			status = SPDK_NVME_SC_RESERVATION_CONFLICT;
3290d8843dccSChangpeng Liu 			goto exit;
3291d8843dccSChangpeng Liu 
3292d8843dccSChangpeng Liu 		}
3293c596ea4bSChangpeng Liu 		update_sgroup = true;
3294bc1d0b91SChangpeng Liu 		break;
3295bc1d0b91SChangpeng Liu 	default:
3296bc1d0b91SChangpeng Liu 		status = SPDK_NVME_SC_INVALID_FIELD;
3297bc1d0b91SChangpeng Liu 		goto exit;
3298bc1d0b91SChangpeng Liu 	}
3299bc1d0b91SChangpeng Liu 
3300bc1d0b91SChangpeng Liu exit:
3301bc1d0b91SChangpeng Liu 	req->rsp->nvme_cpl.status.sct = SPDK_NVME_SCT_GENERIC;
3302bc1d0b91SChangpeng Liu 	req->rsp->nvme_cpl.status.sc = status;
33031fd5b1daSChangpeng Liu 	return update_sgroup;
3304bc1d0b91SChangpeng Liu }
3305bc1d0b91SChangpeng Liu 
33061fd5b1daSChangpeng Liu static bool
330771ac18d1SChangpeng Liu nvmf_ns_reservation_acquire(struct spdk_nvmf_ns *ns,
330871ac18d1SChangpeng Liu 			    struct spdk_nvmf_ctrlr *ctrlr,
330971ac18d1SChangpeng Liu 			    struct spdk_nvmf_request *req)
331071ac18d1SChangpeng Liu {
3311cc3184b8SJohn Levon 	struct spdk_nvme_reservation_acquire_data key = { 0 };
331271ac18d1SChangpeng Liu 	struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
331371ac18d1SChangpeng Liu 	uint8_t racqa, iekey, rtype;
331471ac18d1SChangpeng Liu 	struct spdk_nvmf_registrant *reg;
331571ac18d1SChangpeng Liu 	bool all_regs = false;
331671ac18d1SChangpeng Liu 	uint32_t count = 0;
33171fd5b1daSChangpeng Liu 	bool update_sgroup = true;
331878bfb2a1SChangpeng Liu 	struct spdk_uuid hostid_list[SPDK_NVMF_MAX_NUM_REGISTRANTS];
331978bfb2a1SChangpeng Liu 	uint32_t num_hostid = 0;
332078bfb2a1SChangpeng Liu 	struct spdk_uuid new_hostid_list[SPDK_NVMF_MAX_NUM_REGISTRANTS];
332178bfb2a1SChangpeng Liu 	uint32_t new_num_hostid = 0;
332278bfb2a1SChangpeng Liu 	bool reservation_released = false;
332371ac18d1SChangpeng Liu 	uint8_t status = SPDK_NVME_SC_SUCCESS;
332471ac18d1SChangpeng Liu 
33251fea1fccSChangpeng Liu 	racqa = cmd->cdw10_bits.resv_acquire.racqa;
33261fea1fccSChangpeng Liu 	iekey = cmd->cdw10_bits.resv_acquire.iekey;
33271fea1fccSChangpeng Liu 	rtype = cmd->cdw10_bits.resv_acquire.rtype;
33283856d82bSSeth Howell 
3329cc3184b8SJohn Levon 	if (req->iovcnt > 0 && req->length >= sizeof(key)) {
3330cc3184b8SJohn Levon 		struct spdk_iov_xfer ix;
3331cc3184b8SJohn Levon 		spdk_iov_xfer_init(&ix, req->iov, req->iovcnt);
3332cc3184b8SJohn Levon 		spdk_iov_xfer_to_buf(&ix, &key, sizeof(key));
33333856d82bSSeth Howell 	} else {
33343856d82bSSeth Howell 		SPDK_ERRLOG("No key provided. Failing request.\n");
33353856d82bSSeth Howell 		status = SPDK_NVME_SC_INVALID_FIELD;
33363856d82bSSeth Howell 		goto exit;
33373856d82bSSeth Howell 	}
333871ac18d1SChangpeng Liu 
33392172c432STomasz Zawadzki 	SPDK_DEBUGLOG(nvmf, "ACQUIRE: RACQA %u, IEKEY %u, RTYPE %u, "
334071ac18d1SChangpeng Liu 		      "NRKEY 0x%"PRIx64", PRKEY 0x%"PRIx64"\n",
334171ac18d1SChangpeng Liu 		      racqa, iekey, rtype, key.crkey, key.prkey);
334271ac18d1SChangpeng Liu 
334358d923e6SChangpeng Liu 	if (iekey || rtype > SPDK_NVME_RESERVE_EXCLUSIVE_ACCESS_ALL_REGS) {
334471ac18d1SChangpeng Liu 		SPDK_ERRLOG("Ignore existing key field set to 1\n");
334571ac18d1SChangpeng Liu 		status = SPDK_NVME_SC_INVALID_FIELD;
33461fd5b1daSChangpeng Liu 		update_sgroup = false;
334771ac18d1SChangpeng Liu 		goto exit;
334871ac18d1SChangpeng Liu 	}
334971ac18d1SChangpeng Liu 
335071ac18d1SChangpeng Liu 	reg = nvmf_ns_reservation_get_registrant(ns, &ctrlr->hostid);
335171ac18d1SChangpeng Liu 	/* must be registrant and CRKEY must match */
335271ac18d1SChangpeng Liu 	if (!reg || reg->rkey != key.crkey) {
335371ac18d1SChangpeng Liu 		SPDK_ERRLOG("No registrant or current key doesn't match "
335471ac18d1SChangpeng Liu 			    "with existing registrant key\n");
335571ac18d1SChangpeng Liu 		status = SPDK_NVME_SC_RESERVATION_CONFLICT;
33561fd5b1daSChangpeng Liu 		update_sgroup = false;
335771ac18d1SChangpeng Liu 		goto exit;
335871ac18d1SChangpeng Liu 	}
335971ac18d1SChangpeng Liu 
336071ac18d1SChangpeng Liu 	all_regs = nvmf_ns_reservation_all_registrants_type(ns);
336171ac18d1SChangpeng Liu 
336271ac18d1SChangpeng Liu 	switch (racqa) {
336371ac18d1SChangpeng Liu 	case SPDK_NVME_RESERVE_ACQUIRE:
336471ac18d1SChangpeng Liu 		/* it's not an error for the holder to acquire same reservation type again */
336571ac18d1SChangpeng Liu 		if (nvmf_ns_reservation_registrant_is_holder(ns, reg) && ns->rtype == rtype) {
336671ac18d1SChangpeng Liu 			/* do nothing */
33671fd5b1daSChangpeng Liu 			update_sgroup = false;
336871ac18d1SChangpeng Liu 		} else if (ns->holder == NULL) {
3369cc6920a4SJosh Soref 			/* first time to acquire the reservation */
337071ac18d1SChangpeng Liu 			nvmf_ns_reservation_acquire_reservation(ns, key.crkey, rtype, reg);
337171ac18d1SChangpeng Liu 		} else {
337271ac18d1SChangpeng Liu 			SPDK_ERRLOG("Invalid rtype or current registrant is not holder\n");
337371ac18d1SChangpeng Liu 			status = SPDK_NVME_SC_RESERVATION_CONFLICT;
33741fd5b1daSChangpeng Liu 			update_sgroup = false;
337571ac18d1SChangpeng Liu 			goto exit;
337671ac18d1SChangpeng Liu 		}
337771ac18d1SChangpeng Liu 		break;
337871ac18d1SChangpeng Liu 	case SPDK_NVME_RESERVE_PREEMPT:
337971ac18d1SChangpeng Liu 		/* no reservation holder */
338071ac18d1SChangpeng Liu 		if (!ns->holder) {
338171ac18d1SChangpeng Liu 			/* unregister with PRKEY */
338271ac18d1SChangpeng Liu 			nvmf_ns_reservation_remove_registrants_by_key(ns, key.prkey);
338371ac18d1SChangpeng Liu 			break;
338471ac18d1SChangpeng Liu 		}
338578bfb2a1SChangpeng Liu 		num_hostid = nvmf_ns_reservation_get_all_other_hostid(ns, hostid_list,
338678bfb2a1SChangpeng Liu 				SPDK_NVMF_MAX_NUM_REGISTRANTS,
338778bfb2a1SChangpeng Liu 				&ctrlr->hostid);
338878bfb2a1SChangpeng Liu 
338971ac18d1SChangpeng Liu 		/* only 1 reservation holder and reservation key is valid */
339071ac18d1SChangpeng Liu 		if (!all_regs) {
339171ac18d1SChangpeng Liu 			/* preempt itself */
339271ac18d1SChangpeng Liu 			if (nvmf_ns_reservation_registrant_is_holder(ns, reg) &&
339371ac18d1SChangpeng Liu 			    ns->crkey == key.prkey) {
339471ac18d1SChangpeng Liu 				ns->rtype = rtype;
339578bfb2a1SChangpeng Liu 				reservation_released = true;
339671ac18d1SChangpeng Liu 				break;
339771ac18d1SChangpeng Liu 			}
339871ac18d1SChangpeng Liu 
339971ac18d1SChangpeng Liu 			if (ns->crkey == key.prkey) {
340071ac18d1SChangpeng Liu 				nvmf_ns_reservation_remove_registrant(ns, ns->holder);
340171ac18d1SChangpeng Liu 				nvmf_ns_reservation_acquire_reservation(ns, key.crkey, rtype, reg);
340278bfb2a1SChangpeng Liu 				reservation_released = true;
340371ac18d1SChangpeng Liu 			} else if (key.prkey != 0) {
340471ac18d1SChangpeng Liu 				nvmf_ns_reservation_remove_registrants_by_key(ns, key.prkey);
340571ac18d1SChangpeng Liu 			} else {
340671ac18d1SChangpeng Liu 				/* PRKEY is zero */
340771ac18d1SChangpeng Liu 				SPDK_ERRLOG("Current PRKEY is zero\n");
340871ac18d1SChangpeng Liu 				status = SPDK_NVME_SC_RESERVATION_CONFLICT;
34091fd5b1daSChangpeng Liu 				update_sgroup = false;
341071ac18d1SChangpeng Liu 				goto exit;
341171ac18d1SChangpeng Liu 			}
341271ac18d1SChangpeng Liu 		} else {
341371ac18d1SChangpeng Liu 			/* release all other registrants except for the current one */
341471ac18d1SChangpeng Liu 			if (key.prkey == 0) {
341571ac18d1SChangpeng Liu 				nvmf_ns_reservation_remove_all_other_registrants(ns, reg);
341671ac18d1SChangpeng Liu 				assert(ns->holder == reg);
341771ac18d1SChangpeng Liu 			} else {
341871ac18d1SChangpeng Liu 				count = nvmf_ns_reservation_remove_registrants_by_key(ns, key.prkey);
341971ac18d1SChangpeng Liu 				if (count == 0) {
342071ac18d1SChangpeng Liu 					SPDK_ERRLOG("PRKEY doesn't match any registrant\n");
342171ac18d1SChangpeng Liu 					status = SPDK_NVME_SC_RESERVATION_CONFLICT;
34221fd5b1daSChangpeng Liu 					update_sgroup = false;
342371ac18d1SChangpeng Liu 					goto exit;
342471ac18d1SChangpeng Liu 				}
342571ac18d1SChangpeng Liu 			}
342671ac18d1SChangpeng Liu 		}
342771ac18d1SChangpeng Liu 		break;
342871ac18d1SChangpeng Liu 	default:
342958d923e6SChangpeng Liu 		status = SPDK_NVME_SC_INVALID_FIELD;
34301fd5b1daSChangpeng Liu 		update_sgroup = false;
343171ac18d1SChangpeng Liu 		break;
343271ac18d1SChangpeng Liu 	}
343371ac18d1SChangpeng Liu 
343471ac18d1SChangpeng Liu exit:
343578bfb2a1SChangpeng Liu 	if (update_sgroup && racqa == SPDK_NVME_RESERVE_PREEMPT) {
343678bfb2a1SChangpeng Liu 		new_num_hostid = nvmf_ns_reservation_get_all_other_hostid(ns, new_hostid_list,
343778bfb2a1SChangpeng Liu 				 SPDK_NVMF_MAX_NUM_REGISTRANTS,
343878bfb2a1SChangpeng Liu 				 &ctrlr->hostid);
343978bfb2a1SChangpeng Liu 		/* Preempt notification occurs on the unregistered controllers
344078bfb2a1SChangpeng Liu 		 * other than the controller who issued the command.
344178bfb2a1SChangpeng Liu 		 */
344278bfb2a1SChangpeng Liu 		num_hostid = nvmf_ns_reservation_get_unregistered_hostid(hostid_list,
344378bfb2a1SChangpeng Liu 				num_hostid,
344478bfb2a1SChangpeng Liu 				new_hostid_list,
344578bfb2a1SChangpeng Liu 				new_num_hostid);
344678bfb2a1SChangpeng Liu 		if (num_hostid) {
344778bfb2a1SChangpeng Liu 			nvmf_subsystem_gen_ctrlr_notification(ns->subsystem, ns,
344878bfb2a1SChangpeng Liu 							      hostid_list,
344978bfb2a1SChangpeng Liu 							      num_hostid,
345078bfb2a1SChangpeng Liu 							      SPDK_NVME_REGISTRATION_PREEMPTED);
345178bfb2a1SChangpeng Liu 
345278bfb2a1SChangpeng Liu 		}
345378bfb2a1SChangpeng Liu 		/* Reservation released notification occurs on the
345478bfb2a1SChangpeng Liu 		 * controllers which are the remaining registrants other than
345578bfb2a1SChangpeng Liu 		 * the controller who issued the command.
345678bfb2a1SChangpeng Liu 		 */
345778bfb2a1SChangpeng Liu 		if (reservation_released && new_num_hostid) {
345878bfb2a1SChangpeng Liu 			nvmf_subsystem_gen_ctrlr_notification(ns->subsystem, ns,
345978bfb2a1SChangpeng Liu 							      new_hostid_list,
346078bfb2a1SChangpeng Liu 							      new_num_hostid,
346178bfb2a1SChangpeng Liu 							      SPDK_NVME_RESERVATION_RELEASED);
346278bfb2a1SChangpeng Liu 
346378bfb2a1SChangpeng Liu 		}
346478bfb2a1SChangpeng Liu 	}
346571ac18d1SChangpeng Liu 	req->rsp->nvme_cpl.status.sct = SPDK_NVME_SCT_GENERIC;
346671ac18d1SChangpeng Liu 	req->rsp->nvme_cpl.status.sc = status;
34671fd5b1daSChangpeng Liu 	return update_sgroup;
346871ac18d1SChangpeng Liu }
346971ac18d1SChangpeng Liu 
34701fd5b1daSChangpeng Liu static bool
347184ee3a62SChangpeng Liu nvmf_ns_reservation_release(struct spdk_nvmf_ns *ns,
347284ee3a62SChangpeng Liu 			    struct spdk_nvmf_ctrlr *ctrlr,
347384ee3a62SChangpeng Liu 			    struct spdk_nvmf_request *req)
347484ee3a62SChangpeng Liu {
347584ee3a62SChangpeng Liu 	struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
347684ee3a62SChangpeng Liu 	uint8_t rrela, iekey, rtype;
347784ee3a62SChangpeng Liu 	struct spdk_nvmf_registrant *reg;
3478cc3184b8SJohn Levon 	uint64_t crkey = 0;
347984ee3a62SChangpeng Liu 	uint8_t status = SPDK_NVME_SC_SUCCESS;
34801fd5b1daSChangpeng Liu 	bool update_sgroup = true;
348178bfb2a1SChangpeng Liu 	struct spdk_uuid hostid_list[SPDK_NVMF_MAX_NUM_REGISTRANTS];
348278bfb2a1SChangpeng Liu 	uint32_t num_hostid = 0;
348384ee3a62SChangpeng Liu 
34841fea1fccSChangpeng Liu 	rrela = cmd->cdw10_bits.resv_release.rrela;
34851fea1fccSChangpeng Liu 	iekey = cmd->cdw10_bits.resv_release.iekey;
34861fea1fccSChangpeng Liu 	rtype = cmd->cdw10_bits.resv_release.rtype;
34873856d82bSSeth Howell 
3488cc3184b8SJohn Levon 	if (req->iovcnt > 0 && req->length >= sizeof(crkey)) {
3489cc3184b8SJohn Levon 		struct spdk_iov_xfer ix;
3490cc3184b8SJohn Levon 		spdk_iov_xfer_init(&ix, req->iov, req->iovcnt);
3491cc3184b8SJohn Levon 		spdk_iov_xfer_to_buf(&ix, &crkey, sizeof(crkey));
34923856d82bSSeth Howell 	} else {
34933856d82bSSeth Howell 		SPDK_ERRLOG("No key provided. Failing request.\n");
34943856d82bSSeth Howell 		status = SPDK_NVME_SC_INVALID_FIELD;
34953856d82bSSeth Howell 		goto exit;
34963856d82bSSeth Howell 	}
349784ee3a62SChangpeng Liu 
34982172c432STomasz Zawadzki 	SPDK_DEBUGLOG(nvmf, "RELEASE: RRELA %u, IEKEY %u, RTYPE %u, "
349984ee3a62SChangpeng Liu 		      "CRKEY 0x%"PRIx64"\n",  rrela, iekey, rtype, crkey);
350084ee3a62SChangpeng Liu 
350184ee3a62SChangpeng Liu 	if (iekey) {
350284ee3a62SChangpeng Liu 		SPDK_ERRLOG("Ignore existing key field set to 1\n");
350384ee3a62SChangpeng Liu 		status = SPDK_NVME_SC_INVALID_FIELD;
35041fd5b1daSChangpeng Liu 		update_sgroup = false;
350584ee3a62SChangpeng Liu 		goto exit;
350684ee3a62SChangpeng Liu 	}
350784ee3a62SChangpeng Liu 
350884ee3a62SChangpeng Liu 	reg = nvmf_ns_reservation_get_registrant(ns, &ctrlr->hostid);
350984ee3a62SChangpeng Liu 	if (!reg || reg->rkey != crkey) {
351084ee3a62SChangpeng Liu 		SPDK_ERRLOG("No registrant or current key doesn't match "
351184ee3a62SChangpeng Liu 			    "with existing registrant key\n");
351284ee3a62SChangpeng Liu 		status = SPDK_NVME_SC_RESERVATION_CONFLICT;
35131fd5b1daSChangpeng Liu 		update_sgroup = false;
351484ee3a62SChangpeng Liu 		goto exit;
351584ee3a62SChangpeng Liu 	}
351684ee3a62SChangpeng Liu 
351778bfb2a1SChangpeng Liu 	num_hostid = nvmf_ns_reservation_get_all_other_hostid(ns, hostid_list,
351878bfb2a1SChangpeng Liu 			SPDK_NVMF_MAX_NUM_REGISTRANTS,
351978bfb2a1SChangpeng Liu 			&ctrlr->hostid);
352078bfb2a1SChangpeng Liu 
352184ee3a62SChangpeng Liu 	switch (rrela) {
352284ee3a62SChangpeng Liu 	case SPDK_NVME_RESERVE_RELEASE:
352384ee3a62SChangpeng Liu 		if (!ns->holder) {
35242172c432STomasz Zawadzki 			SPDK_DEBUGLOG(nvmf, "RELEASE: no holder\n");
35251fd5b1daSChangpeng Liu 			update_sgroup = false;
352684ee3a62SChangpeng Liu 			goto exit;
352784ee3a62SChangpeng Liu 		}
352884ee3a62SChangpeng Liu 		if (ns->rtype != rtype) {
352984ee3a62SChangpeng Liu 			SPDK_ERRLOG("Type doesn't match\n");
353084ee3a62SChangpeng Liu 			status = SPDK_NVME_SC_INVALID_FIELD;
35311fd5b1daSChangpeng Liu 			update_sgroup = false;
353284ee3a62SChangpeng Liu 			goto exit;
353384ee3a62SChangpeng Liu 		}
353484ee3a62SChangpeng Liu 		if (!nvmf_ns_reservation_registrant_is_holder(ns, reg)) {
353584ee3a62SChangpeng Liu 			/* not the reservation holder, this isn't an error */
35361fd5b1daSChangpeng Liu 			update_sgroup = false;
353784ee3a62SChangpeng Liu 			goto exit;
353884ee3a62SChangpeng Liu 		}
353978bfb2a1SChangpeng Liu 
354078bfb2a1SChangpeng Liu 		rtype = ns->rtype;
354184ee3a62SChangpeng Liu 		nvmf_ns_reservation_release_reservation(ns);
354278bfb2a1SChangpeng Liu 
354378bfb2a1SChangpeng Liu 		if (num_hostid && rtype != SPDK_NVME_RESERVE_WRITE_EXCLUSIVE &&
354478bfb2a1SChangpeng Liu 		    rtype != SPDK_NVME_RESERVE_EXCLUSIVE_ACCESS) {
354578bfb2a1SChangpeng Liu 			nvmf_subsystem_gen_ctrlr_notification(ns->subsystem, ns,
354678bfb2a1SChangpeng Liu 							      hostid_list,
354778bfb2a1SChangpeng Liu 							      num_hostid,
354878bfb2a1SChangpeng Liu 							      SPDK_NVME_RESERVATION_RELEASED);
354978bfb2a1SChangpeng Liu 		}
355084ee3a62SChangpeng Liu 		break;
355184ee3a62SChangpeng Liu 	case SPDK_NVME_RESERVE_CLEAR:
355284ee3a62SChangpeng Liu 		nvmf_ns_reservation_clear_all_registrants(ns);
355378bfb2a1SChangpeng Liu 		if (num_hostid) {
355478bfb2a1SChangpeng Liu 			nvmf_subsystem_gen_ctrlr_notification(ns->subsystem, ns,
355578bfb2a1SChangpeng Liu 							      hostid_list,
355678bfb2a1SChangpeng Liu 							      num_hostid,
355778bfb2a1SChangpeng Liu 							      SPDK_NVME_RESERVATION_PREEMPTED);
355878bfb2a1SChangpeng Liu 		}
355984ee3a62SChangpeng Liu 		break;
356084ee3a62SChangpeng Liu 	default:
356184ee3a62SChangpeng Liu 		status = SPDK_NVME_SC_INVALID_FIELD;
35621fd5b1daSChangpeng Liu 		update_sgroup = false;
356384ee3a62SChangpeng Liu 		goto exit;
356484ee3a62SChangpeng Liu 	}
356584ee3a62SChangpeng Liu 
356684ee3a62SChangpeng Liu exit:
356784ee3a62SChangpeng Liu 	req->rsp->nvme_cpl.status.sct = SPDK_NVME_SCT_GENERIC;
356884ee3a62SChangpeng Liu 	req->rsp->nvme_cpl.status.sc = status;
35691fd5b1daSChangpeng Liu 	return update_sgroup;
357084ee3a62SChangpeng Liu }
357184ee3a62SChangpeng Liu 
357284ee3a62SChangpeng Liu static void
35734b55682eSChangpeng Liu nvmf_ns_reservation_report(struct spdk_nvmf_ns *ns,
35744b55682eSChangpeng Liu 			   struct spdk_nvmf_ctrlr *ctrlr,
35754b55682eSChangpeng Liu 			   struct spdk_nvmf_request *req)
35764b55682eSChangpeng Liu {
35774b55682eSChangpeng Liu 	struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
35784b55682eSChangpeng Liu 	struct spdk_nvmf_registrant *reg, *tmp;
3579cc3184b8SJohn Levon 	struct spdk_nvme_reservation_status_extended_data status_data = { 0 };
3580cc3184b8SJohn Levon 	struct spdk_iov_xfer ix;
3581cc3184b8SJohn Levon 	uint32_t transfer_len;
35824b55682eSChangpeng Liu 	uint32_t regctl = 0;
35834b55682eSChangpeng Liu 	uint8_t status = SPDK_NVME_SC_SUCCESS;
35844b55682eSChangpeng Liu 
3585cc3184b8SJohn Levon 	if (req->iovcnt == 0) {
35863856d82bSSeth Howell 		SPDK_ERRLOG("No data transfer specified for request. "
35873856d82bSSeth Howell 			    " Unable to transfer back response.\n");
35883856d82bSSeth Howell 		status = SPDK_NVME_SC_INVALID_FIELD;
35893856d82bSSeth Howell 		goto exit;
35903856d82bSSeth Howell 	}
35913856d82bSSeth Howell 
35920c9057f0SChangpeng Liu 	if (!cmd->cdw11_bits.resv_report.eds) {
35934b55682eSChangpeng Liu 		SPDK_ERRLOG("NVMeoF uses extended controller data structure, "
35944b55682eSChangpeng Liu 			    "please set EDS bit in cdw11 and try again\n");
3595ac03d920SBen Walker 		status = SPDK_NVME_SC_HOSTID_INCONSISTENT_FORMAT;
35964b55682eSChangpeng Liu 		goto exit;
35974b55682eSChangpeng Liu 	}
35984b55682eSChangpeng Liu 
35996b372f41SChangpeng Liu 	/* Number of Dwords of the Reservation Status data structure to transfer */
36006b372f41SChangpeng Liu 	transfer_len = (cmd->cdw10 + 1) * sizeof(uint32_t);
36014b55682eSChangpeng Liu 
36026b372f41SChangpeng Liu 	if (transfer_len < sizeof(struct spdk_nvme_reservation_status_extended_data)) {
36034b55682eSChangpeng Liu 		status = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR;
36044b55682eSChangpeng Liu 		goto exit;
36054b55682eSChangpeng Liu 	}
36064b55682eSChangpeng Liu 
3607cc3184b8SJohn Levon 	spdk_iov_xfer_init(&ix, req->iov, req->iovcnt);
3608cc3184b8SJohn Levon 
3609cc3184b8SJohn Levon 	status_data.data.gen = ns->gen;
3610cc3184b8SJohn Levon 	status_data.data.rtype = ns->rtype;
3611cc3184b8SJohn Levon 	status_data.data.ptpls = ns->ptpl_activated;
36124b55682eSChangpeng Liu 
36134b55682eSChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, tmp) {
36146b372f41SChangpeng Liu 		regctl++;
36154b55682eSChangpeng Liu 	}
3616cc3184b8SJohn Levon 
3617cc3184b8SJohn Levon 	/*
3618cc3184b8SJohn Levon 	 * We report the number of registrants as per the spec here, even if
3619cc3184b8SJohn Levon 	 * the iov isn't big enough to contain them all. In that case, the
3620cc3184b8SJohn Levon 	 * spdk_iov_xfer_from_buf() won't actually copy any of the remaining
3621cc3184b8SJohn Levon 	 * data; as it keeps track of the iov cursor itself, it's simplest to
3622cc3184b8SJohn Levon 	 * just walk the entire list anyway.
3623cc3184b8SJohn Levon 	 */
3624cc3184b8SJohn Levon 	status_data.data.regctl = regctl;
3625cc3184b8SJohn Levon 
3626cc3184b8SJohn Levon 	spdk_iov_xfer_from_buf(&ix, &status_data, sizeof(status_data));
3627cc3184b8SJohn Levon 
3628cc3184b8SJohn Levon 	TAILQ_FOREACH_SAFE(reg, &ns->registrants, link, tmp) {
3629cc3184b8SJohn Levon 		struct spdk_nvme_registered_ctrlr_extended_data ctrlr_data = { 0 };
3630cc3184b8SJohn Levon 
3631cc3184b8SJohn Levon 		/* Set to 0xffffh for dynamic controller */
3632cc3184b8SJohn Levon 		ctrlr_data.cntlid = 0xffff;
3633cc3184b8SJohn Levon 		ctrlr_data.rcsts.status = (ns->holder == reg) ? true : false;
3634cc3184b8SJohn Levon 		ctrlr_data.rkey = reg->rkey;
3635cc3184b8SJohn Levon 		spdk_uuid_copy((struct spdk_uuid *)ctrlr_data.hostid, &reg->hostid);
3636cc3184b8SJohn Levon 
3637cc3184b8SJohn Levon 		spdk_iov_xfer_from_buf(&ix, &ctrlr_data, sizeof(ctrlr_data));
3638cc3184b8SJohn Levon 	}
36394b55682eSChangpeng Liu 
36404b55682eSChangpeng Liu exit:
36414b55682eSChangpeng Liu 	req->rsp->nvme_cpl.status.sct = SPDK_NVME_SCT_GENERIC;
36424b55682eSChangpeng Liu 	req->rsp->nvme_cpl.status.sc = status;
36434b55682eSChangpeng Liu 	return;
36444b55682eSChangpeng Liu }
36454b55682eSChangpeng Liu 
36464b55682eSChangpeng Liu static void
364763c90491SSeth Howell nvmf_ns_reservation_complete(void *ctx)
3648bc1d0b91SChangpeng Liu {
3649bc1d0b91SChangpeng Liu 	struct spdk_nvmf_request *req = ctx;
3650bc1d0b91SChangpeng Liu 
3651bc1d0b91SChangpeng Liu 	spdk_nvmf_request_complete(req);
3652bc1d0b91SChangpeng Liu }
3653bc1d0b91SChangpeng Liu 
36541fd5b1daSChangpeng Liu static void
36551fd5b1daSChangpeng Liu _nvmf_ns_reservation_update_done(struct spdk_nvmf_subsystem *subsystem,
36561fd5b1daSChangpeng Liu 				 void *cb_arg, int status)
36571fd5b1daSChangpeng Liu {
36581fd5b1daSChangpeng Liu 	struct spdk_nvmf_request *req = (struct spdk_nvmf_request *)cb_arg;
36591fd5b1daSChangpeng Liu 	struct spdk_nvmf_poll_group *group = req->qpair->group;
36601fd5b1daSChangpeng Liu 
366163c90491SSeth Howell 	spdk_thread_send_msg(group->thread, nvmf_ns_reservation_complete, req);
36621fd5b1daSChangpeng Liu }
36631fd5b1daSChangpeng Liu 
3664bc1d0b91SChangpeng Liu void
36659cb21ad6SSeth Howell nvmf_ns_reservation_request(void *ctx)
3666bc1d0b91SChangpeng Liu {
3667bc1d0b91SChangpeng Liu 	struct spdk_nvmf_request *req = (struct spdk_nvmf_request *)ctx;
3668bc1d0b91SChangpeng Liu 	struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
3669bc1d0b91SChangpeng Liu 	struct spdk_nvmf_ctrlr *ctrlr = req->qpair->ctrlr;
3670bc1d0b91SChangpeng Liu 	uint32_t nsid;
3671bc1d0b91SChangpeng Liu 	struct spdk_nvmf_ns *ns;
36721fd5b1daSChangpeng Liu 	bool update_sgroup = false;
3673d0fe26b2SArtur Paszkiewicz 	int status = 0;
3674bc1d0b91SChangpeng Liu 
3675bc1d0b91SChangpeng Liu 	nsid = cmd->nsid;
36769cb21ad6SSeth Howell 	ns = _nvmf_subsystem_get_ns(ctrlr->subsys, nsid);
3677bc1d0b91SChangpeng Liu 	assert(ns != NULL);
3678bc1d0b91SChangpeng Liu 
3679bc1d0b91SChangpeng Liu 	switch (cmd->opc) {
3680bc1d0b91SChangpeng Liu 	case SPDK_NVME_OPC_RESERVATION_REGISTER:
36811fd5b1daSChangpeng Liu 		update_sgroup = nvmf_ns_reservation_register(ns, ctrlr, req);
3682bc1d0b91SChangpeng Liu 		break;
368371ac18d1SChangpeng Liu 	case SPDK_NVME_OPC_RESERVATION_ACQUIRE:
36841fd5b1daSChangpeng Liu 		update_sgroup = nvmf_ns_reservation_acquire(ns, ctrlr, req);
368571ac18d1SChangpeng Liu 		break;
368684ee3a62SChangpeng Liu 	case SPDK_NVME_OPC_RESERVATION_RELEASE:
36871fd5b1daSChangpeng Liu 		update_sgroup = nvmf_ns_reservation_release(ns, ctrlr, req);
368884ee3a62SChangpeng Liu 		break;
36894b55682eSChangpeng Liu 	case SPDK_NVME_OPC_RESERVATION_REPORT:
36904b55682eSChangpeng Liu 		nvmf_ns_reservation_report(ns, ctrlr, req);
36914b55682eSChangpeng Liu 		break;
3692bc1d0b91SChangpeng Liu 	default:
3693bc1d0b91SChangpeng Liu 		break;
3694bc1d0b91SChangpeng Liu 	}
36951fd5b1daSChangpeng Liu 
36961fd5b1daSChangpeng Liu 	/* update reservation information to subsystem's poll group */
36971fd5b1daSChangpeng Liu 	if (update_sgroup) {
369817c821a8SArtur Paszkiewicz 		if (ns->ptpl_activated || cmd->opc == SPDK_NVME_OPC_RESERVATION_REGISTER) {
369917c821a8SArtur Paszkiewicz 			if (nvmf_ns_update_reservation_info(ns) != 0) {
370017c821a8SArtur Paszkiewicz 				req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR;
370117c821a8SArtur Paszkiewicz 			}
370217c821a8SArtur Paszkiewicz 		}
3703d0fe26b2SArtur Paszkiewicz 		status = nvmf_subsystem_update_ns(ctrlr->subsys, _nvmf_ns_reservation_update_done, req);
3704d0fe26b2SArtur Paszkiewicz 		if (status == 0) {
37051fd5b1daSChangpeng Liu 			return;
37061fd5b1daSChangpeng Liu 		}
3707d0fe26b2SArtur Paszkiewicz 	}
37081fd5b1daSChangpeng Liu 
3709d0fe26b2SArtur Paszkiewicz 	_nvmf_ns_reservation_update_done(ctrlr->subsys, req, status);
3710bc1d0b91SChangpeng Liu }
37116f226573SShuhei Matsumoto 
371209e8e884SArtur Paszkiewicz static bool
371309e8e884SArtur Paszkiewicz nvmf_ns_is_ptpl_capable_json(const struct spdk_nvmf_ns *ns)
371409e8e884SArtur Paszkiewicz {
371509e8e884SArtur Paszkiewicz 	return ns->ptpl_file != NULL;
371609e8e884SArtur Paszkiewicz }
371709e8e884SArtur Paszkiewicz 
371809e8e884SArtur Paszkiewicz static struct spdk_nvmf_ns_reservation_ops g_reservation_ops = {
371909e8e884SArtur Paszkiewicz 	.is_ptpl_capable = nvmf_ns_is_ptpl_capable_json,
372009e8e884SArtur Paszkiewicz 	.update = nvmf_ns_reservation_update_json,
372109e8e884SArtur Paszkiewicz 	.load = nvmf_ns_reservation_load_json,
372209e8e884SArtur Paszkiewicz };
372309e8e884SArtur Paszkiewicz 
372409e8e884SArtur Paszkiewicz bool
372509e8e884SArtur Paszkiewicz nvmf_ns_is_ptpl_capable(const struct spdk_nvmf_ns *ns)
372609e8e884SArtur Paszkiewicz {
372709e8e884SArtur Paszkiewicz 	return g_reservation_ops.is_ptpl_capable(ns);
372809e8e884SArtur Paszkiewicz }
372909e8e884SArtur Paszkiewicz 
373009e8e884SArtur Paszkiewicz static int
373109e8e884SArtur Paszkiewicz nvmf_ns_reservation_update(const struct spdk_nvmf_ns *ns,
373209e8e884SArtur Paszkiewicz 			   const struct spdk_nvmf_reservation_info *info)
373309e8e884SArtur Paszkiewicz {
373409e8e884SArtur Paszkiewicz 	return g_reservation_ops.update(ns, info);
373509e8e884SArtur Paszkiewicz }
373609e8e884SArtur Paszkiewicz 
373709e8e884SArtur Paszkiewicz static int
373809e8e884SArtur Paszkiewicz nvmf_ns_reservation_load(const struct spdk_nvmf_ns *ns, struct spdk_nvmf_reservation_info *info)
373909e8e884SArtur Paszkiewicz {
374009e8e884SArtur Paszkiewicz 	return g_reservation_ops.load(ns, info);
374109e8e884SArtur Paszkiewicz }
374209e8e884SArtur Paszkiewicz 
374309e8e884SArtur Paszkiewicz void
374409e8e884SArtur Paszkiewicz spdk_nvmf_set_custom_ns_reservation_ops(const struct spdk_nvmf_ns_reservation_ops *ops)
374509e8e884SArtur Paszkiewicz {
374609e8e884SArtur Paszkiewicz 	g_reservation_ops = *ops;
374709e8e884SArtur Paszkiewicz }
374809e8e884SArtur Paszkiewicz 
37496f226573SShuhei Matsumoto int
37506f226573SShuhei Matsumoto spdk_nvmf_subsystem_set_ana_reporting(struct spdk_nvmf_subsystem *subsystem,
37516f226573SShuhei Matsumoto 				      bool ana_reporting)
37526f226573SShuhei Matsumoto {
37536f226573SShuhei Matsumoto 	if (subsystem->state != SPDK_NVMF_SUBSYSTEM_INACTIVE) {
37546f226573SShuhei Matsumoto 		return -EAGAIN;
37556f226573SShuhei Matsumoto 	}
37566f226573SShuhei Matsumoto 
37575e4e4bc4SBen Walker 	subsystem->flags.ana_reporting = ana_reporting;
37586f226573SShuhei Matsumoto 
37596f226573SShuhei Matsumoto 	return 0;
37606f226573SShuhei Matsumoto }
3761071d80f3SShuhei Matsumoto 
37622a67deefSShuhei Matsumoto bool
3763fb5650ceSAbhineet Pandey spdk_nvmf_subsystem_get_ana_reporting(struct spdk_nvmf_subsystem *subsystem)
37642a67deefSShuhei Matsumoto {
37652a67deefSShuhei Matsumoto 	return subsystem->flags.ana_reporting;
37662a67deefSShuhei Matsumoto }
37672a67deefSShuhei Matsumoto 
3768071d80f3SShuhei Matsumoto struct subsystem_listener_update_ctx {
3769071d80f3SShuhei Matsumoto 	struct spdk_nvmf_subsystem_listener *listener;
3770071d80f3SShuhei Matsumoto 
3771071d80f3SShuhei Matsumoto 	spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn;
3772071d80f3SShuhei Matsumoto 	void *cb_arg;
3773071d80f3SShuhei Matsumoto };
3774071d80f3SShuhei Matsumoto 
3775071d80f3SShuhei Matsumoto static void
3776071d80f3SShuhei Matsumoto subsystem_listener_update_done(struct spdk_io_channel_iter *i, int status)
3777071d80f3SShuhei Matsumoto {
3778071d80f3SShuhei Matsumoto 	struct subsystem_listener_update_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
3779071d80f3SShuhei Matsumoto 
3780071d80f3SShuhei Matsumoto 	if (ctx->cb_fn) {
3781071d80f3SShuhei Matsumoto 		ctx->cb_fn(ctx->cb_arg, status);
3782071d80f3SShuhei Matsumoto 	}
3783071d80f3SShuhei Matsumoto 	free(ctx);
3784071d80f3SShuhei Matsumoto }
3785071d80f3SShuhei Matsumoto 
3786071d80f3SShuhei Matsumoto static void
3787071d80f3SShuhei Matsumoto subsystem_listener_update_on_pg(struct spdk_io_channel_iter *i)
3788071d80f3SShuhei Matsumoto {
3789071d80f3SShuhei Matsumoto 	struct subsystem_listener_update_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
3790071d80f3SShuhei Matsumoto 	struct spdk_nvmf_subsystem_listener *listener;
3791071d80f3SShuhei Matsumoto 	struct spdk_nvmf_poll_group *group;
3792071d80f3SShuhei Matsumoto 	struct spdk_nvmf_ctrlr *ctrlr;
3793071d80f3SShuhei Matsumoto 
3794071d80f3SShuhei Matsumoto 	listener = ctx->listener;
3795071d80f3SShuhei Matsumoto 	group = spdk_io_channel_get_ctx(spdk_io_channel_iter_get_channel(i));
3796071d80f3SShuhei Matsumoto 
3797071d80f3SShuhei Matsumoto 	TAILQ_FOREACH(ctrlr, &listener->subsystem->ctrlrs, link) {
379808be9c66SArtur Paszkiewicz 		if (ctrlr->thread != spdk_get_thread()) {
379908be9c66SArtur Paszkiewicz 			continue;
380008be9c66SArtur Paszkiewicz 		}
380108be9c66SArtur Paszkiewicz 
3802478f6524SAlexey Marchuk 		if (ctrlr->admin_qpair && ctrlr->admin_qpair->group == group && ctrlr->listener == listener) {
3803071d80f3SShuhei Matsumoto 			nvmf_ctrlr_async_event_ana_change_notice(ctrlr);
3804071d80f3SShuhei Matsumoto 		}
3805071d80f3SShuhei Matsumoto 	}
3806071d80f3SShuhei Matsumoto 
3807071d80f3SShuhei Matsumoto 	spdk_for_each_channel_continue(i, 0);
3808071d80f3SShuhei Matsumoto }
3809071d80f3SShuhei Matsumoto 
3810071d80f3SShuhei Matsumoto void
3811fb5650ceSAbhineet Pandey spdk_nvmf_subsystem_set_ana_state(struct spdk_nvmf_subsystem *subsystem,
3812071d80f3SShuhei Matsumoto 				  const struct spdk_nvme_transport_id *trid,
38136d716c47SShuhei Matsumoto 				  enum spdk_nvme_ana_state ana_state, uint32_t anagrpid,
3814071d80f3SShuhei Matsumoto 				  spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn, void *cb_arg)
3815071d80f3SShuhei Matsumoto {
3816071d80f3SShuhei Matsumoto 	struct spdk_nvmf_subsystem_listener *listener;
3817071d80f3SShuhei Matsumoto 	struct subsystem_listener_update_ctx *ctx;
3818785d10b5SShuhei Matsumoto 	uint32_t i;
3819071d80f3SShuhei Matsumoto 
3820071d80f3SShuhei Matsumoto 	assert(cb_fn != NULL);
3821071d80f3SShuhei Matsumoto 	assert(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE ||
3822071d80f3SShuhei Matsumoto 	       subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED);
3823071d80f3SShuhei Matsumoto 
38245e4e4bc4SBen Walker 	if (!subsystem->flags.ana_reporting) {
3825071d80f3SShuhei Matsumoto 		SPDK_ERRLOG("ANA reporting is disabled\n");
3826071d80f3SShuhei Matsumoto 		cb_fn(cb_arg, -EINVAL);
3827071d80f3SShuhei Matsumoto 		return;
3828071d80f3SShuhei Matsumoto 	}
3829071d80f3SShuhei Matsumoto 
3830071d80f3SShuhei Matsumoto 	/* ANA Change state is not used, ANA Persistent Loss state
3831071d80f3SShuhei Matsumoto 	 * is not supported yet.
3832071d80f3SShuhei Matsumoto 	 */
3833071d80f3SShuhei Matsumoto 	if (!(ana_state == SPDK_NVME_ANA_OPTIMIZED_STATE ||
3834071d80f3SShuhei Matsumoto 	      ana_state == SPDK_NVME_ANA_NON_OPTIMIZED_STATE ||
3835071d80f3SShuhei Matsumoto 	      ana_state == SPDK_NVME_ANA_INACCESSIBLE_STATE)) {
3836071d80f3SShuhei Matsumoto 		SPDK_ERRLOG("ANA state %d is not supported\n", ana_state);
3837071d80f3SShuhei Matsumoto 		cb_fn(cb_arg, -ENOTSUP);
3838071d80f3SShuhei Matsumoto 		return;
3839071d80f3SShuhei Matsumoto 	}
3840071d80f3SShuhei Matsumoto 
38416d716c47SShuhei Matsumoto 	if (anagrpid > subsystem->max_nsid) {
38426d716c47SShuhei Matsumoto 		SPDK_ERRLOG("ANA group ID %" PRIu32 " is more than maximum\n", anagrpid);
38436d716c47SShuhei Matsumoto 		cb_fn(cb_arg, -EINVAL);
38446d716c47SShuhei Matsumoto 		return;
38456d716c47SShuhei Matsumoto 	}
38466d716c47SShuhei Matsumoto 
3847071d80f3SShuhei Matsumoto 	listener = nvmf_subsystem_find_listener(subsystem, trid);
3848071d80f3SShuhei Matsumoto 	if (!listener) {
3849071d80f3SShuhei Matsumoto 		SPDK_ERRLOG("Unable to find listener.\n");
3850071d80f3SShuhei Matsumoto 		cb_fn(cb_arg, -EINVAL);
3851071d80f3SShuhei Matsumoto 		return;
3852071d80f3SShuhei Matsumoto 	}
3853071d80f3SShuhei Matsumoto 
38546d716c47SShuhei Matsumoto 	if (anagrpid != 0 && listener->ana_state[anagrpid - 1] == ana_state) {
38556d716c47SShuhei Matsumoto 		cb_fn(cb_arg, 0);
38566d716c47SShuhei Matsumoto 		return;
38576d716c47SShuhei Matsumoto 	}
38586d716c47SShuhei Matsumoto 
3859071d80f3SShuhei Matsumoto 	ctx = calloc(1, sizeof(*ctx));
3860071d80f3SShuhei Matsumoto 	if (!ctx) {
3861071d80f3SShuhei Matsumoto 		SPDK_ERRLOG("Unable to allocate context\n");
3862071d80f3SShuhei Matsumoto 		cb_fn(cb_arg, -ENOMEM);
3863071d80f3SShuhei Matsumoto 		return;
3864071d80f3SShuhei Matsumoto 	}
3865071d80f3SShuhei Matsumoto 
38666d716c47SShuhei Matsumoto 	for (i = 1; i <= subsystem->max_nsid; i++) {
38676d716c47SShuhei Matsumoto 		if (anagrpid == 0 || i == anagrpid) {
38686d716c47SShuhei Matsumoto 			listener->ana_state[i - 1] = ana_state;
38696d716c47SShuhei Matsumoto 		}
3870785d10b5SShuhei Matsumoto 	}
3871071d80f3SShuhei Matsumoto 	listener->ana_state_change_count++;
3872071d80f3SShuhei Matsumoto 
3873071d80f3SShuhei Matsumoto 	ctx->listener = listener;
3874071d80f3SShuhei Matsumoto 	ctx->cb_fn = cb_fn;
3875071d80f3SShuhei Matsumoto 	ctx->cb_arg = cb_arg;
3876071d80f3SShuhei Matsumoto 
3877071d80f3SShuhei Matsumoto 	spdk_for_each_channel(subsystem->tgt,
3878071d80f3SShuhei Matsumoto 			      subsystem_listener_update_on_pg,
3879071d80f3SShuhei Matsumoto 			      ctx,
3880071d80f3SShuhei Matsumoto 			      subsystem_listener_update_done);
3881071d80f3SShuhei Matsumoto }
38827efdf905SSlawomir Ptak 
38836d8618afSAbhineet Pandey int
38846d8618afSAbhineet Pandey spdk_nvmf_subsystem_get_ana_state(struct spdk_nvmf_subsystem *subsystem,
38856d8618afSAbhineet Pandey 				  const struct spdk_nvme_transport_id *trid,
38866d8618afSAbhineet Pandey 				  uint32_t anagrpid,
38876d8618afSAbhineet Pandey 				  enum spdk_nvme_ana_state *ana_state)
38886d8618afSAbhineet Pandey {
38896d8618afSAbhineet Pandey 	assert(ana_state != NULL);
38906d8618afSAbhineet Pandey 
38916d8618afSAbhineet Pandey 	struct spdk_nvmf_subsystem_listener *listener;
38926d8618afSAbhineet Pandey 
38936d8618afSAbhineet Pandey 	if (!subsystem->flags.ana_reporting) {
38946d8618afSAbhineet Pandey 		SPDK_ERRLOG("ANA reporting is disabled\n");
38956d8618afSAbhineet Pandey 		return -EINVAL;
38966d8618afSAbhineet Pandey 	}
38976d8618afSAbhineet Pandey 
38986d8618afSAbhineet Pandey 	if (anagrpid <= 0 || anagrpid > subsystem->max_nsid) {
38996d8618afSAbhineet Pandey 		SPDK_ERRLOG("ANA group ID %" PRIu32 " is invalid\n", anagrpid);
39006d8618afSAbhineet Pandey 		return -EINVAL;
39016d8618afSAbhineet Pandey 	}
39026d8618afSAbhineet Pandey 
39036d8618afSAbhineet Pandey 	listener = nvmf_subsystem_find_listener(subsystem, trid);
39046d8618afSAbhineet Pandey 	if (!listener) {
39056d8618afSAbhineet Pandey 		SPDK_ERRLOG("Unable to find listener.\n");
39066d8618afSAbhineet Pandey 		return -EINVAL;
39076d8618afSAbhineet Pandey 	}
39086d8618afSAbhineet Pandey 
39096d8618afSAbhineet Pandey 	*ana_state = listener->ana_state[anagrpid - 1];
39106d8618afSAbhineet Pandey 	return 0;
39116d8618afSAbhineet Pandey }
39126d8618afSAbhineet Pandey 
39137efdf905SSlawomir Ptak bool
39147efdf905SSlawomir Ptak spdk_nvmf_subsystem_is_discovery(struct spdk_nvmf_subsystem *subsystem)
39157efdf905SSlawomir Ptak {
39167efdf905SSlawomir Ptak 	return subsystem->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY_CURRENT ||
39177efdf905SSlawomir Ptak 	       subsystem->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY;
39187efdf905SSlawomir Ptak }
3919020c4e11SKonrad Sztyber 
3920020c4e11SKonrad Sztyber bool
3921020c4e11SKonrad Sztyber nvmf_nqn_is_discovery(const char *nqn)
3922020c4e11SKonrad Sztyber {
3923020c4e11SKonrad Sztyber 	return strcmp(nqn, SPDK_NVMF_DISCOVERY_NQN) == 0;
3924020c4e11SKonrad Sztyber }
3925