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