xref: /openbmc/libpldm/src/requester/instance-id.c (revision 36af84cdbb660953d73804a12dfda056c3ac9b08)
1*36af84cdSRashmica Gupta // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
2*36af84cdSRashmica Gupta #define _GNU_SOURCE
3*36af84cdSRashmica Gupta #include "libpldm/requester/instance-id.h"
4*36af84cdSRashmica Gupta #include "libpldm/pldm.h"
5*36af84cdSRashmica Gupta #include <errno.h>
6*36af84cdSRashmica Gupta #include <fcntl.h>
7*36af84cdSRashmica Gupta #include <stdlib.h>
8*36af84cdSRashmica Gupta #include <unistd.h>
9*36af84cdSRashmica Gupta 
10*36af84cdSRashmica Gupta #define PLDM_TID_MAX 256
11*36af84cdSRashmica Gupta #define PLDM_INST_ID_MAX 32
12*36af84cdSRashmica Gupta 
13*36af84cdSRashmica Gupta struct pldm_instance_db {
14*36af84cdSRashmica Gupta 	pldm_instance_id_t prev[PLDM_TID_MAX];
15*36af84cdSRashmica Gupta 	int lock_db_fd;
16*36af84cdSRashmica Gupta };
17*36af84cdSRashmica Gupta 
18*36af84cdSRashmica Gupta static inline int iid_next(pldm_instance_id_t cur)
19*36af84cdSRashmica Gupta {
20*36af84cdSRashmica Gupta 	return (cur + 1) % PLDM_INST_ID_MAX;
21*36af84cdSRashmica Gupta }
22*36af84cdSRashmica Gupta 
23*36af84cdSRashmica Gupta int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
24*36af84cdSRashmica Gupta {
25*36af84cdSRashmica Gupta 	struct pldm_instance_db *l_ctx;
26*36af84cdSRashmica Gupta 
27*36af84cdSRashmica Gupta 	/* Make sure the provided pointer was initialised to NULL. In the future
28*36af84cdSRashmica Gupta 	 * if we stabilise the ABI and expose the struct definition the caller
29*36af84cdSRashmica Gupta 	 * can potentially pass a valid pointer to a struct they've allocated
30*36af84cdSRashmica Gupta 	 */
31*36af84cdSRashmica Gupta 	if (!ctx || *ctx) {
32*36af84cdSRashmica Gupta 		return -EINVAL;
33*36af84cdSRashmica Gupta 	}
34*36af84cdSRashmica Gupta 
35*36af84cdSRashmica Gupta 	l_ctx = calloc(1, sizeof(struct pldm_instance_db));
36*36af84cdSRashmica Gupta 	if (!l_ctx) {
37*36af84cdSRashmica Gupta 		return -ENOMEM;
38*36af84cdSRashmica Gupta 	}
39*36af84cdSRashmica Gupta 
40*36af84cdSRashmica Gupta 	/* Initialise previous ID values so the next one is zero */
41*36af84cdSRashmica Gupta 	for (int i = 0; i < PLDM_TID_MAX; i++) {
42*36af84cdSRashmica Gupta 		l_ctx->prev[i] = 31;
43*36af84cdSRashmica Gupta 	}
44*36af84cdSRashmica Gupta 
45*36af84cdSRashmica Gupta 	/* Lock database may be read-only, either by permissions or mountpoint
46*36af84cdSRashmica Gupta 	 */
47*36af84cdSRashmica Gupta 	l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
48*36af84cdSRashmica Gupta 	if (l_ctx->lock_db_fd < 0) {
49*36af84cdSRashmica Gupta 		free(l_ctx);
50*36af84cdSRashmica Gupta 		return -errno;
51*36af84cdSRashmica Gupta 	}
52*36af84cdSRashmica Gupta 	*ctx = l_ctx;
53*36af84cdSRashmica Gupta 
54*36af84cdSRashmica Gupta 	return 0;
55*36af84cdSRashmica Gupta }
56*36af84cdSRashmica Gupta 
57*36af84cdSRashmica Gupta int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
58*36af84cdSRashmica Gupta {
59*36af84cdSRashmica Gupta 	return pldm_instance_db_init(ctx,
60*36af84cdSRashmica Gupta 				     "/usr/share/libpldm/instance-db/default");
61*36af84cdSRashmica Gupta }
62*36af84cdSRashmica Gupta 
63*36af84cdSRashmica Gupta int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
64*36af84cdSRashmica Gupta {
65*36af84cdSRashmica Gupta 	if (!ctx) {
66*36af84cdSRashmica Gupta 		return 0;
67*36af84cdSRashmica Gupta 	}
68*36af84cdSRashmica Gupta 	close(ctx->lock_db_fd);
69*36af84cdSRashmica Gupta 	free(ctx);
70*36af84cdSRashmica Gupta 	return 0;
71*36af84cdSRashmica Gupta }
72*36af84cdSRashmica Gupta 
73*36af84cdSRashmica Gupta int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
74*36af84cdSRashmica Gupta 			   pldm_instance_id_t *iid)
75*36af84cdSRashmica Gupta {
76*36af84cdSRashmica Gupta 	static const struct flock cfls = {
77*36af84cdSRashmica Gupta 	    .l_type = F_RDLCK,
78*36af84cdSRashmica Gupta 	    .l_whence = SEEK_SET,
79*36af84cdSRashmica Gupta 	    .l_len = 1,
80*36af84cdSRashmica Gupta 	};
81*36af84cdSRashmica Gupta 	static const struct flock cflx = {
82*36af84cdSRashmica Gupta 	    .l_type = F_WRLCK,
83*36af84cdSRashmica Gupta 	    .l_whence = SEEK_SET,
84*36af84cdSRashmica Gupta 	    .l_len = 1,
85*36af84cdSRashmica Gupta 	};
86*36af84cdSRashmica Gupta 	uint8_t l_iid;
87*36af84cdSRashmica Gupta 
88*36af84cdSRashmica Gupta 	if (!iid) {
89*36af84cdSRashmica Gupta 		return -EINVAL;
90*36af84cdSRashmica Gupta 	}
91*36af84cdSRashmica Gupta 
92*36af84cdSRashmica Gupta 	l_iid = ctx->prev[tid];
93*36af84cdSRashmica Gupta 	if (l_iid >= PLDM_INST_ID_MAX) {
94*36af84cdSRashmica Gupta 		return -EPROTO;
95*36af84cdSRashmica Gupta 	}
96*36af84cdSRashmica Gupta 
97*36af84cdSRashmica Gupta 	while ((l_iid = iid_next(l_iid)) != ctx->prev[tid]) {
98*36af84cdSRashmica Gupta 		struct flock flop;
99*36af84cdSRashmica Gupta 		off_t loff;
100*36af84cdSRashmica Gupta 		int rc;
101*36af84cdSRashmica Gupta 
102*36af84cdSRashmica Gupta 		/* Derive the instance ID offset in the lock database */
103*36af84cdSRashmica Gupta 		loff = tid * PLDM_INST_ID_MAX + l_iid;
104*36af84cdSRashmica Gupta 
105*36af84cdSRashmica Gupta 		/* Reserving the TID's IID. Done via a shared lock */
106*36af84cdSRashmica Gupta 		flop = cfls;
107*36af84cdSRashmica Gupta 		flop.l_start = loff;
108*36af84cdSRashmica Gupta 		rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
109*36af84cdSRashmica Gupta 		if (rc < 0) {
110*36af84cdSRashmica Gupta 			if (errno == EAGAIN || errno == EINTR) {
111*36af84cdSRashmica Gupta 				return -EAGAIN;
112*36af84cdSRashmica Gupta 			}
113*36af84cdSRashmica Gupta 			return -EPROTO;
114*36af84cdSRashmica Gupta 		}
115*36af84cdSRashmica Gupta 
116*36af84cdSRashmica Gupta 		/*
117*36af84cdSRashmica Gupta 		 * If we *may* promote the lock to exclusive then this IID is
118*36af84cdSRashmica Gupta 		 * only reserved by us. This is now our allocated IID.
119*36af84cdSRashmica Gupta 		 *
120*36af84cdSRashmica Gupta 		 * If we *may not* promote the lock to exclusive then this IID
121*36af84cdSRashmica Gupta 		 * is also reserved on another file descriptor. Move on to the
122*36af84cdSRashmica Gupta 		 * next IID index.
123*36af84cdSRashmica Gupta 		 *
124*36af84cdSRashmica Gupta 		 * Note that we cannot actually *perform* the promotion in
125*36af84cdSRashmica Gupta 		 * practice because this is prevented by the lock database being
126*36af84cdSRashmica Gupta 		 * opened O_RDONLY.
127*36af84cdSRashmica Gupta 		 */
128*36af84cdSRashmica Gupta 		flop = cflx;
129*36af84cdSRashmica Gupta 		flop.l_start = loff;
130*36af84cdSRashmica Gupta 		rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
131*36af84cdSRashmica Gupta 		if (rc < 0) {
132*36af84cdSRashmica Gupta 			if (errno == EAGAIN || errno == EINTR) {
133*36af84cdSRashmica Gupta 				return -EAGAIN;
134*36af84cdSRashmica Gupta 			}
135*36af84cdSRashmica Gupta 			return -EPROTO;
136*36af84cdSRashmica Gupta 		}
137*36af84cdSRashmica Gupta 
138*36af84cdSRashmica Gupta 		/* F_UNLCK is the type of the lock if we could successfully
139*36af84cdSRashmica Gupta 		 * promote it to F_WRLCK */
140*36af84cdSRashmica Gupta 		if (flop.l_type == F_UNLCK) {
141*36af84cdSRashmica Gupta 			ctx->prev[tid] = l_iid;
142*36af84cdSRashmica Gupta 			*iid = l_iid;
143*36af84cdSRashmica Gupta 			return 0;
144*36af84cdSRashmica Gupta 		}
145*36af84cdSRashmica Gupta 		if (flop.l_type != F_RDLCK) {
146*36af84cdSRashmica Gupta 			return -EPROTO;
147*36af84cdSRashmica Gupta 		}
148*36af84cdSRashmica Gupta 	}
149*36af84cdSRashmica Gupta 
150*36af84cdSRashmica Gupta 	/* Failed to allocate an IID after a full loop. Make the caller try
151*36af84cdSRashmica Gupta 	 * again */
152*36af84cdSRashmica Gupta 	return -EAGAIN;
153*36af84cdSRashmica Gupta }
154*36af84cdSRashmica Gupta 
155*36af84cdSRashmica Gupta int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
156*36af84cdSRashmica Gupta 			  pldm_instance_id_t iid)
157*36af84cdSRashmica Gupta {
158*36af84cdSRashmica Gupta 	static const struct flock cflu = {
159*36af84cdSRashmica Gupta 	    .l_type = F_UNLCK,
160*36af84cdSRashmica Gupta 	    .l_whence = SEEK_SET,
161*36af84cdSRashmica Gupta 	    .l_len = 1,
162*36af84cdSRashmica Gupta 	};
163*36af84cdSRashmica Gupta 	struct flock flop;
164*36af84cdSRashmica Gupta 	int rc;
165*36af84cdSRashmica Gupta 
166*36af84cdSRashmica Gupta 	flop = cflu;
167*36af84cdSRashmica Gupta 	flop.l_start = tid * PLDM_INST_ID_MAX + iid;
168*36af84cdSRashmica Gupta 	rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
169*36af84cdSRashmica Gupta 	if (rc < 0) {
170*36af84cdSRashmica Gupta 		if (errno == EAGAIN || errno == EINTR) {
171*36af84cdSRashmica Gupta 			return -EAGAIN;
172*36af84cdSRashmica Gupta 		}
173*36af84cdSRashmica Gupta 		return -EPROTO;
174*36af84cdSRashmica Gupta 	}
175*36af84cdSRashmica Gupta 
176*36af84cdSRashmica Gupta 	return 0;
177*36af84cdSRashmica Gupta }
178