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