xref: /openbmc/libpldm/src/requester/instance-id.c (revision e4240679bf28e6268c9bbebb32c51b9a2235621e)
1691668feSPatrick Williams /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
236af84cdSRashmica Gupta // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
336af84cdSRashmica Gupta #define _GNU_SOURCE
4b0c1d20aSAndrew Jeffery #include <libpldm/instance-id.h>
5d12dd36eSAndrew Jeffery #include <libpldm/pldm.h>
6b0c1d20aSAndrew Jeffery 
736af84cdSRashmica Gupta #include <errno.h>
836af84cdSRashmica Gupta #include <fcntl.h>
936af84cdSRashmica Gupta #include <stdlib.h>
10a6f0cf3eSAndrew Jeffery #include <sys/stat.h>
1136af84cdSRashmica Gupta #include <unistd.h>
1236af84cdSRashmica Gupta 
130aea707bSAndrew Jeffery #define BIT(i) (1UL << (i))
140aea707bSAndrew Jeffery 
1536af84cdSRashmica Gupta #define PLDM_TID_MAX	 256
1636af84cdSRashmica Gupta #define PLDM_INST_ID_MAX 32
1736af84cdSRashmica Gupta 
180aea707bSAndrew Jeffery /* We need to track our allocations explicitly due to OFD lock merging/splitting
190aea707bSAndrew Jeffery  */
200aea707bSAndrew Jeffery struct pldm_tid_state {
210aea707bSAndrew Jeffery 	pldm_instance_id_t prev;
220aea707bSAndrew Jeffery 	uint32_t allocations;
230aea707bSAndrew Jeffery };
240aea707bSAndrew Jeffery 
2536af84cdSRashmica Gupta struct pldm_instance_db {
260aea707bSAndrew Jeffery 	struct pldm_tid_state state[PLDM_TID_MAX];
2736af84cdSRashmica Gupta 	int lock_db_fd;
2836af84cdSRashmica Gupta };
2936af84cdSRashmica Gupta 
3036af84cdSRashmica Gupta static inline int iid_next(pldm_instance_id_t cur)
3136af84cdSRashmica Gupta {
3236af84cdSRashmica Gupta 	return (cur + 1) % PLDM_INST_ID_MAX;
3336af84cdSRashmica Gupta }
3436af84cdSRashmica Gupta 
359d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE
3636af84cdSRashmica Gupta int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
3736af84cdSRashmica Gupta {
3836af84cdSRashmica Gupta 	struct pldm_instance_db *l_ctx;
39a6f0cf3eSAndrew Jeffery 	struct stat statbuf;
40a6f0cf3eSAndrew Jeffery 	int rc;
4136af84cdSRashmica Gupta 
4236af84cdSRashmica Gupta 	/* Make sure the provided pointer was initialised to NULL. In the future
4336af84cdSRashmica Gupta 	 * if we stabilise the ABI and expose the struct definition the caller
4436af84cdSRashmica Gupta 	 * can potentially pass a valid pointer to a struct they've allocated
4536af84cdSRashmica Gupta 	 */
4636af84cdSRashmica Gupta 	if (!ctx || *ctx) {
4736af84cdSRashmica Gupta 		return -EINVAL;
4836af84cdSRashmica Gupta 	}
4936af84cdSRashmica Gupta 
50a6f0cf3eSAndrew Jeffery 	/* Ensure the underlying file is sized for properly managing allocations
51a6f0cf3eSAndrew Jeffery 	 */
52a6f0cf3eSAndrew Jeffery 	rc = stat(dbpath, &statbuf);
53a6f0cf3eSAndrew Jeffery 	if (rc < 0) {
54a6f0cf3eSAndrew Jeffery 		return -EINVAL;
55a6f0cf3eSAndrew Jeffery 	}
56a6f0cf3eSAndrew Jeffery 
57a6f0cf3eSAndrew Jeffery 	if (statbuf.st_size <
58a6f0cf3eSAndrew Jeffery 	    ((off_t)(PLDM_TID_MAX) * (off_t)(PLDM_INST_ID_MAX))) {
59a6f0cf3eSAndrew Jeffery 		return -EINVAL;
60a6f0cf3eSAndrew Jeffery 	}
61a6f0cf3eSAndrew Jeffery 
6236af84cdSRashmica Gupta 	l_ctx = calloc(1, sizeof(struct pldm_instance_db));
6336af84cdSRashmica Gupta 	if (!l_ctx) {
6436af84cdSRashmica Gupta 		return -ENOMEM;
6536af84cdSRashmica Gupta 	}
6636af84cdSRashmica Gupta 
6736af84cdSRashmica Gupta 	/* Initialise previous ID values so the next one is zero */
6836af84cdSRashmica Gupta 	for (int i = 0; i < PLDM_TID_MAX; i++) {
690aea707bSAndrew Jeffery 		l_ctx->state[i].prev = 31;
7036af84cdSRashmica Gupta 	}
7136af84cdSRashmica Gupta 
7236af84cdSRashmica Gupta 	/* Lock database may be read-only, either by permissions or mountpoint
7336af84cdSRashmica Gupta 	 */
7436af84cdSRashmica Gupta 	l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
7536af84cdSRashmica Gupta 	if (l_ctx->lock_db_fd < 0) {
7636af84cdSRashmica Gupta 		free(l_ctx);
7736af84cdSRashmica Gupta 		return -errno;
7836af84cdSRashmica Gupta 	}
7936af84cdSRashmica Gupta 	*ctx = l_ctx;
8036af84cdSRashmica Gupta 
8136af84cdSRashmica Gupta 	return 0;
8236af84cdSRashmica Gupta }
8336af84cdSRashmica Gupta 
849d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE
8536af84cdSRashmica Gupta int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
8636af84cdSRashmica Gupta {
8736af84cdSRashmica Gupta 	return pldm_instance_db_init(ctx,
8836af84cdSRashmica Gupta 				     "/usr/share/libpldm/instance-db/default");
8936af84cdSRashmica Gupta }
9036af84cdSRashmica Gupta 
919d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE
9236af84cdSRashmica Gupta int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
9336af84cdSRashmica Gupta {
9436af84cdSRashmica Gupta 	if (!ctx) {
9536af84cdSRashmica Gupta 		return 0;
9636af84cdSRashmica Gupta 	}
9736af84cdSRashmica Gupta 	close(ctx->lock_db_fd);
9836af84cdSRashmica Gupta 	free(ctx);
9936af84cdSRashmica Gupta 	return 0;
10036af84cdSRashmica Gupta }
10136af84cdSRashmica Gupta 
102*e4240679SAndrew Jeffery static const struct flock pldm_instance_id_cfls = {
10336af84cdSRashmica Gupta 	.l_type = F_RDLCK,
10436af84cdSRashmica Gupta 	.l_whence = SEEK_SET,
10536af84cdSRashmica Gupta 	.l_len = 1,
10636af84cdSRashmica Gupta };
107*e4240679SAndrew Jeffery 
108*e4240679SAndrew Jeffery static const struct flock pldm_instance_id_cflx = {
10936af84cdSRashmica Gupta 	.l_type = F_WRLCK,
11036af84cdSRashmica Gupta 	.l_whence = SEEK_SET,
11136af84cdSRashmica Gupta 	.l_len = 1,
11236af84cdSRashmica Gupta };
113*e4240679SAndrew Jeffery 
114*e4240679SAndrew Jeffery static const struct flock pldm_instance_id_cflu = {
115*e4240679SAndrew Jeffery 	.l_type = F_UNLCK,
116*e4240679SAndrew Jeffery 	.l_whence = SEEK_SET,
117*e4240679SAndrew Jeffery 	.l_len = 1,
118*e4240679SAndrew Jeffery };
119*e4240679SAndrew Jeffery 
120*e4240679SAndrew Jeffery LIBPLDM_ABI_STABLE
121*e4240679SAndrew Jeffery int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
122*e4240679SAndrew Jeffery 			   pldm_instance_id_t *iid)
123*e4240679SAndrew Jeffery {
12436af84cdSRashmica Gupta 	uint8_t l_iid;
12536af84cdSRashmica Gupta 
12636af84cdSRashmica Gupta 	if (!iid) {
12736af84cdSRashmica Gupta 		return -EINVAL;
12836af84cdSRashmica Gupta 	}
12936af84cdSRashmica Gupta 
1300aea707bSAndrew Jeffery 	l_iid = ctx->state[tid].prev;
13136af84cdSRashmica Gupta 	if (l_iid >= PLDM_INST_ID_MAX) {
13236af84cdSRashmica Gupta 		return -EPROTO;
13336af84cdSRashmica Gupta 	}
13436af84cdSRashmica Gupta 
1350aea707bSAndrew Jeffery 	while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) {
13636af84cdSRashmica Gupta 		struct flock flop;
13736af84cdSRashmica Gupta 		off_t loff;
13836af84cdSRashmica Gupta 		int rc;
13936af84cdSRashmica Gupta 
1400aea707bSAndrew Jeffery 		/* Have we already allocated this instance ID? */
1410aea707bSAndrew Jeffery 		if (ctx->state[tid].allocations & BIT(l_iid)) {
1420aea707bSAndrew Jeffery 			continue;
1430aea707bSAndrew Jeffery 		}
1440aea707bSAndrew Jeffery 
14536af84cdSRashmica Gupta 		/* Derive the instance ID offset in the lock database */
14636af84cdSRashmica Gupta 		loff = tid * PLDM_INST_ID_MAX + l_iid;
14736af84cdSRashmica Gupta 
14836af84cdSRashmica Gupta 		/* Reserving the TID's IID. Done via a shared lock */
149*e4240679SAndrew Jeffery 		flop = pldm_instance_id_cfls;
15036af84cdSRashmica Gupta 		flop.l_start = loff;
15136af84cdSRashmica Gupta 		rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
15236af84cdSRashmica Gupta 		if (rc < 0) {
15336af84cdSRashmica Gupta 			if (errno == EAGAIN || errno == EINTR) {
15436af84cdSRashmica Gupta 				return -EAGAIN;
15536af84cdSRashmica Gupta 			}
15636af84cdSRashmica Gupta 			return -EPROTO;
15736af84cdSRashmica Gupta 		}
15836af84cdSRashmica Gupta 
15936af84cdSRashmica Gupta 		/*
16036af84cdSRashmica Gupta 		 * If we *may* promote the lock to exclusive then this IID is
16136af84cdSRashmica Gupta 		 * only reserved by us. This is now our allocated IID.
16236af84cdSRashmica Gupta 		 *
16336af84cdSRashmica Gupta 		 * If we *may not* promote the lock to exclusive then this IID
16436af84cdSRashmica Gupta 		 * is also reserved on another file descriptor. Move on to the
16536af84cdSRashmica Gupta 		 * next IID index.
16636af84cdSRashmica Gupta 		 *
16736af84cdSRashmica Gupta 		 * Note that we cannot actually *perform* the promotion in
16836af84cdSRashmica Gupta 		 * practice because this is prevented by the lock database being
16936af84cdSRashmica Gupta 		 * opened O_RDONLY.
17036af84cdSRashmica Gupta 		 */
171*e4240679SAndrew Jeffery 		flop = pldm_instance_id_cflx;
17236af84cdSRashmica Gupta 		flop.l_start = loff;
17336af84cdSRashmica Gupta 		rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
17436af84cdSRashmica Gupta 		if (rc < 0) {
17536af84cdSRashmica Gupta 			if (errno == EAGAIN || errno == EINTR) {
176*e4240679SAndrew Jeffery 				rc = -EAGAIN;
177*e4240679SAndrew Jeffery 				goto release_cfls;
17836af84cdSRashmica Gupta 			}
179*e4240679SAndrew Jeffery 			rc = -EPROTO;
180*e4240679SAndrew Jeffery 			goto release_cfls;
18136af84cdSRashmica Gupta 		}
18236af84cdSRashmica Gupta 
18336af84cdSRashmica Gupta 		/* F_UNLCK is the type of the lock if we could successfully
18436af84cdSRashmica Gupta 		 * promote it to F_WRLCK */
18536af84cdSRashmica Gupta 		if (flop.l_type == F_UNLCK) {
1860aea707bSAndrew Jeffery 			ctx->state[tid].prev = l_iid;
1870aea707bSAndrew Jeffery 			ctx->state[tid].allocations |= BIT(l_iid);
18836af84cdSRashmica Gupta 			*iid = l_iid;
18936af84cdSRashmica Gupta 			return 0;
19036af84cdSRashmica Gupta 		}
191*e4240679SAndrew Jeffery 
19236af84cdSRashmica Gupta 		if (flop.l_type != F_RDLCK) {
193*e4240679SAndrew Jeffery 			rc = -EPROTO;
194*e4240679SAndrew Jeffery 		}
195*e4240679SAndrew Jeffery 
196*e4240679SAndrew Jeffery 	release_cfls:
197*e4240679SAndrew Jeffery 		flop = pldm_instance_id_cflu;
198*e4240679SAndrew Jeffery 		flop.l_start = loff;
199*e4240679SAndrew Jeffery 		if (fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop) < 0) {
200*e4240679SAndrew Jeffery 			if (errno == EAGAIN || errno == EINTR) {
201*e4240679SAndrew Jeffery 				return -EAGAIN;
202*e4240679SAndrew Jeffery 			}
20336af84cdSRashmica Gupta 			return -EPROTO;
20436af84cdSRashmica Gupta 		}
205*e4240679SAndrew Jeffery 
206*e4240679SAndrew Jeffery 		if (rc < 0) {
207*e4240679SAndrew Jeffery 			return rc;
208*e4240679SAndrew Jeffery 		}
20936af84cdSRashmica Gupta 	}
21036af84cdSRashmica Gupta 
21136af84cdSRashmica Gupta 	/* Failed to allocate an IID after a full loop. Make the caller try
21236af84cdSRashmica Gupta 	 * again */
21336af84cdSRashmica Gupta 	return -EAGAIN;
21436af84cdSRashmica Gupta }
21536af84cdSRashmica Gupta 
2169d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE
21736af84cdSRashmica Gupta int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
21836af84cdSRashmica Gupta 			  pldm_instance_id_t iid)
21936af84cdSRashmica Gupta {
22036af84cdSRashmica Gupta 	struct flock flop;
22136af84cdSRashmica Gupta 	int rc;
22236af84cdSRashmica Gupta 
2230aea707bSAndrew Jeffery 	/* Trying to free an instance ID that is not currently allocated */
2240aea707bSAndrew Jeffery 	if (!(ctx->state[tid].allocations & BIT(iid))) {
2250aea707bSAndrew Jeffery 		return -EINVAL;
2260aea707bSAndrew Jeffery 	}
2270aea707bSAndrew Jeffery 
228*e4240679SAndrew Jeffery 	flop = pldm_instance_id_cflu;
22936af84cdSRashmica Gupta 	flop.l_start = tid * PLDM_INST_ID_MAX + iid;
23036af84cdSRashmica Gupta 	rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
23136af84cdSRashmica Gupta 	if (rc < 0) {
23236af84cdSRashmica Gupta 		if (errno == EAGAIN || errno == EINTR) {
23336af84cdSRashmica Gupta 			return -EAGAIN;
23436af84cdSRashmica Gupta 		}
23536af84cdSRashmica Gupta 		return -EPROTO;
23636af84cdSRashmica Gupta 	}
23736af84cdSRashmica Gupta 
2380aea707bSAndrew Jeffery 	/* Mark the instance ID as no-longer allocated */
2390aea707bSAndrew Jeffery 	ctx->state[tid].allocations &= ~BIT(iid);
2400aea707bSAndrew Jeffery 
24136af84cdSRashmica Gupta 	return 0;
24236af84cdSRashmica Gupta }
243