xref: /openbmc/libpldm/src/requester/instance-id.c (revision 0aea707b10f1ed17fadad0609d3ea4a12fa8701e)
136af84cdSRashmica Gupta // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
236af84cdSRashmica Gupta #define _GNU_SOURCE
3a918a628SAndrew Jeffery #include "libpldm/instance-id.h"
436af84cdSRashmica Gupta #include "libpldm/pldm.h"
536af84cdSRashmica Gupta #include <errno.h>
636af84cdSRashmica Gupta #include <fcntl.h>
736af84cdSRashmica Gupta #include <stdlib.h>
836af84cdSRashmica Gupta #include <unistd.h>
936af84cdSRashmica Gupta 
10*0aea707bSAndrew Jeffery #define BIT(i) (1UL << (i))
11*0aea707bSAndrew Jeffery 
1236af84cdSRashmica Gupta #define PLDM_TID_MAX 256
1336af84cdSRashmica Gupta #define PLDM_INST_ID_MAX 32
1436af84cdSRashmica Gupta 
15*0aea707bSAndrew Jeffery /* We need to track our allocations explicitly due to OFD lock merging/splitting
16*0aea707bSAndrew Jeffery  */
17*0aea707bSAndrew Jeffery struct pldm_tid_state {
18*0aea707bSAndrew Jeffery 	pldm_instance_id_t prev;
19*0aea707bSAndrew Jeffery 	uint32_t allocations;
20*0aea707bSAndrew Jeffery };
21*0aea707bSAndrew Jeffery 
2236af84cdSRashmica Gupta struct pldm_instance_db {
23*0aea707bSAndrew Jeffery 	struct pldm_tid_state state[PLDM_TID_MAX];
2436af84cdSRashmica Gupta 	int lock_db_fd;
2536af84cdSRashmica Gupta };
2636af84cdSRashmica Gupta 
2736af84cdSRashmica Gupta static inline int iid_next(pldm_instance_id_t cur)
2836af84cdSRashmica Gupta {
2936af84cdSRashmica Gupta 	return (cur + 1) % PLDM_INST_ID_MAX;
3036af84cdSRashmica Gupta }
3136af84cdSRashmica Gupta 
3236af84cdSRashmica Gupta int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
3336af84cdSRashmica Gupta {
3436af84cdSRashmica Gupta 	struct pldm_instance_db *l_ctx;
3536af84cdSRashmica Gupta 
3636af84cdSRashmica Gupta 	/* Make sure the provided pointer was initialised to NULL. In the future
3736af84cdSRashmica Gupta 	 * if we stabilise the ABI and expose the struct definition the caller
3836af84cdSRashmica Gupta 	 * can potentially pass a valid pointer to a struct they've allocated
3936af84cdSRashmica Gupta 	 */
4036af84cdSRashmica Gupta 	if (!ctx || *ctx) {
4136af84cdSRashmica Gupta 		return -EINVAL;
4236af84cdSRashmica Gupta 	}
4336af84cdSRashmica Gupta 
4436af84cdSRashmica Gupta 	l_ctx = calloc(1, sizeof(struct pldm_instance_db));
4536af84cdSRashmica Gupta 	if (!l_ctx) {
4636af84cdSRashmica Gupta 		return -ENOMEM;
4736af84cdSRashmica Gupta 	}
4836af84cdSRashmica Gupta 
4936af84cdSRashmica Gupta 	/* Initialise previous ID values so the next one is zero */
5036af84cdSRashmica Gupta 	for (int i = 0; i < PLDM_TID_MAX; i++) {
51*0aea707bSAndrew Jeffery 		l_ctx->state[i].prev = 31;
5236af84cdSRashmica Gupta 	}
5336af84cdSRashmica Gupta 
5436af84cdSRashmica Gupta 	/* Lock database may be read-only, either by permissions or mountpoint
5536af84cdSRashmica Gupta 	 */
5636af84cdSRashmica Gupta 	l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
5736af84cdSRashmica Gupta 	if (l_ctx->lock_db_fd < 0) {
5836af84cdSRashmica Gupta 		free(l_ctx);
5936af84cdSRashmica Gupta 		return -errno;
6036af84cdSRashmica Gupta 	}
6136af84cdSRashmica Gupta 	*ctx = l_ctx;
6236af84cdSRashmica Gupta 
6336af84cdSRashmica Gupta 	return 0;
6436af84cdSRashmica Gupta }
6536af84cdSRashmica Gupta 
6636af84cdSRashmica Gupta int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
6736af84cdSRashmica Gupta {
6836af84cdSRashmica Gupta 	return pldm_instance_db_init(ctx,
6936af84cdSRashmica Gupta 				     "/usr/share/libpldm/instance-db/default");
7036af84cdSRashmica Gupta }
7136af84cdSRashmica Gupta 
7236af84cdSRashmica Gupta int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
7336af84cdSRashmica Gupta {
7436af84cdSRashmica Gupta 	if (!ctx) {
7536af84cdSRashmica Gupta 		return 0;
7636af84cdSRashmica Gupta 	}
7736af84cdSRashmica Gupta 	close(ctx->lock_db_fd);
7836af84cdSRashmica Gupta 	free(ctx);
7936af84cdSRashmica Gupta 	return 0;
8036af84cdSRashmica Gupta }
8136af84cdSRashmica Gupta 
8236af84cdSRashmica Gupta int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
8336af84cdSRashmica Gupta 			   pldm_instance_id_t *iid)
8436af84cdSRashmica Gupta {
8536af84cdSRashmica Gupta 	static const struct flock cfls = {
8636af84cdSRashmica Gupta 	    .l_type = F_RDLCK,
8736af84cdSRashmica Gupta 	    .l_whence = SEEK_SET,
8836af84cdSRashmica Gupta 	    .l_len = 1,
8936af84cdSRashmica Gupta 	};
9036af84cdSRashmica Gupta 	static const struct flock cflx = {
9136af84cdSRashmica Gupta 	    .l_type = F_WRLCK,
9236af84cdSRashmica Gupta 	    .l_whence = SEEK_SET,
9336af84cdSRashmica Gupta 	    .l_len = 1,
9436af84cdSRashmica Gupta 	};
9536af84cdSRashmica Gupta 	uint8_t l_iid;
9636af84cdSRashmica Gupta 
9736af84cdSRashmica Gupta 	if (!iid) {
9836af84cdSRashmica Gupta 		return -EINVAL;
9936af84cdSRashmica Gupta 	}
10036af84cdSRashmica Gupta 
101*0aea707bSAndrew Jeffery 	l_iid = ctx->state[tid].prev;
10236af84cdSRashmica Gupta 	if (l_iid >= PLDM_INST_ID_MAX) {
10336af84cdSRashmica Gupta 		return -EPROTO;
10436af84cdSRashmica Gupta 	}
10536af84cdSRashmica Gupta 
106*0aea707bSAndrew Jeffery 	while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) {
10736af84cdSRashmica Gupta 		struct flock flop;
10836af84cdSRashmica Gupta 		off_t loff;
10936af84cdSRashmica Gupta 		int rc;
11036af84cdSRashmica Gupta 
111*0aea707bSAndrew Jeffery 		/* Have we already allocated this instance ID? */
112*0aea707bSAndrew Jeffery 		if (ctx->state[tid].allocations & BIT(l_iid)) {
113*0aea707bSAndrew Jeffery 			continue;
114*0aea707bSAndrew Jeffery 		}
115*0aea707bSAndrew Jeffery 
11636af84cdSRashmica Gupta 		/* Derive the instance ID offset in the lock database */
11736af84cdSRashmica Gupta 		loff = tid * PLDM_INST_ID_MAX + l_iid;
11836af84cdSRashmica Gupta 
11936af84cdSRashmica Gupta 		/* Reserving the TID's IID. Done via a shared lock */
12036af84cdSRashmica Gupta 		flop = cfls;
12136af84cdSRashmica Gupta 		flop.l_start = loff;
12236af84cdSRashmica Gupta 		rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
12336af84cdSRashmica Gupta 		if (rc < 0) {
12436af84cdSRashmica Gupta 			if (errno == EAGAIN || errno == EINTR) {
12536af84cdSRashmica Gupta 				return -EAGAIN;
12636af84cdSRashmica Gupta 			}
12736af84cdSRashmica Gupta 			return -EPROTO;
12836af84cdSRashmica Gupta 		}
12936af84cdSRashmica Gupta 
13036af84cdSRashmica Gupta 		/*
13136af84cdSRashmica Gupta 		 * If we *may* promote the lock to exclusive then this IID is
13236af84cdSRashmica Gupta 		 * only reserved by us. This is now our allocated IID.
13336af84cdSRashmica Gupta 		 *
13436af84cdSRashmica Gupta 		 * If we *may not* promote the lock to exclusive then this IID
13536af84cdSRashmica Gupta 		 * is also reserved on another file descriptor. Move on to the
13636af84cdSRashmica Gupta 		 * next IID index.
13736af84cdSRashmica Gupta 		 *
13836af84cdSRashmica Gupta 		 * Note that we cannot actually *perform* the promotion in
13936af84cdSRashmica Gupta 		 * practice because this is prevented by the lock database being
14036af84cdSRashmica Gupta 		 * opened O_RDONLY.
14136af84cdSRashmica Gupta 		 */
14236af84cdSRashmica Gupta 		flop = cflx;
14336af84cdSRashmica Gupta 		flop.l_start = loff;
14436af84cdSRashmica Gupta 		rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
14536af84cdSRashmica Gupta 		if (rc < 0) {
14636af84cdSRashmica Gupta 			if (errno == EAGAIN || errno == EINTR) {
14736af84cdSRashmica Gupta 				return -EAGAIN;
14836af84cdSRashmica Gupta 			}
14936af84cdSRashmica Gupta 			return -EPROTO;
15036af84cdSRashmica Gupta 		}
15136af84cdSRashmica Gupta 
15236af84cdSRashmica Gupta 		/* F_UNLCK is the type of the lock if we could successfully
15336af84cdSRashmica Gupta 		 * promote it to F_WRLCK */
15436af84cdSRashmica Gupta 		if (flop.l_type == F_UNLCK) {
155*0aea707bSAndrew Jeffery 			ctx->state[tid].prev = l_iid;
156*0aea707bSAndrew Jeffery 			ctx->state[tid].allocations |= BIT(l_iid);
15736af84cdSRashmica Gupta 			*iid = l_iid;
15836af84cdSRashmica Gupta 			return 0;
15936af84cdSRashmica Gupta 		}
16036af84cdSRashmica Gupta 		if (flop.l_type != F_RDLCK) {
16136af84cdSRashmica Gupta 			return -EPROTO;
16236af84cdSRashmica Gupta 		}
16336af84cdSRashmica Gupta 	}
16436af84cdSRashmica Gupta 
16536af84cdSRashmica Gupta 	/* Failed to allocate an IID after a full loop. Make the caller try
16636af84cdSRashmica Gupta 	 * again */
16736af84cdSRashmica Gupta 	return -EAGAIN;
16836af84cdSRashmica Gupta }
16936af84cdSRashmica Gupta 
17036af84cdSRashmica Gupta int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
17136af84cdSRashmica Gupta 			  pldm_instance_id_t iid)
17236af84cdSRashmica Gupta {
17336af84cdSRashmica Gupta 	static const struct flock cflu = {
17436af84cdSRashmica Gupta 	    .l_type = F_UNLCK,
17536af84cdSRashmica Gupta 	    .l_whence = SEEK_SET,
17636af84cdSRashmica Gupta 	    .l_len = 1,
17736af84cdSRashmica Gupta 	};
17836af84cdSRashmica Gupta 	struct flock flop;
17936af84cdSRashmica Gupta 	int rc;
18036af84cdSRashmica Gupta 
181*0aea707bSAndrew Jeffery 	/* Trying to free an instance ID that is not currently allocated */
182*0aea707bSAndrew Jeffery 	if (!(ctx->state[tid].allocations & BIT(iid))) {
183*0aea707bSAndrew Jeffery 		return -EINVAL;
184*0aea707bSAndrew Jeffery 	}
185*0aea707bSAndrew Jeffery 
18636af84cdSRashmica Gupta 	flop = cflu;
18736af84cdSRashmica Gupta 	flop.l_start = tid * PLDM_INST_ID_MAX + iid;
18836af84cdSRashmica Gupta 	rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
18936af84cdSRashmica Gupta 	if (rc < 0) {
19036af84cdSRashmica Gupta 		if (errno == EAGAIN || errno == EINTR) {
19136af84cdSRashmica Gupta 			return -EAGAIN;
19236af84cdSRashmica Gupta 		}
19336af84cdSRashmica Gupta 		return -EPROTO;
19436af84cdSRashmica Gupta 	}
19536af84cdSRashmica Gupta 
196*0aea707bSAndrew Jeffery 	/* Mark the instance ID as no-longer allocated */
197*0aea707bSAndrew Jeffery 	ctx->state[tid].allocations &= ~BIT(iid);
198*0aea707bSAndrew Jeffery 
19936af84cdSRashmica Gupta 	return 0;
20036af84cdSRashmica Gupta }
201