blob: e3cf41fbe3190b6e18d3111bbae811544b616078 [file] [log] [blame]
Rashmica Gupta36af84c2023-04-12 16:16:28 +10001// 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
13struct pldm_instance_db {
14 pldm_instance_id_t prev[PLDM_TID_MAX];
15 int lock_db_fd;
16};
17
18static inline int iid_next(pldm_instance_id_t cur)
19{
20 return (cur + 1) % PLDM_INST_ID_MAX;
21}
22
23int 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
57int 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
63int 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
73int 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
155int 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}