4 * Babeltrace - File descriptor cache
6 * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
8 * Author: Francis Deslauriers <francis.deslauriers@efficios.com>
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 #define BT_LOG_OUTPUT_LEVEL (fdc->log_level)
30 #define BT_LOG_TAG "FD-CACHE"
31 #include "logging/log.h"
35 #include <sys/types.h>
39 #include "common/assert.h"
47 struct fd_handle_internal
{
48 struct bt_fd_cache_handle fd_handle
;
54 void fd_cache_handle_internal_destroy(
55 struct fd_handle_internal
*internal_fd
)
61 if (internal_fd
->fd_handle
.fd
>= 0) {
62 close(internal_fd
->fd_handle
.fd
);
63 internal_fd
->fd_handle
.fd
= -1;
71 * Using simple hash algorithm found on stackoverflow:
72 * https://stackoverflow.com/questions/664014/
75 uint64_t hash_uint64_t(uint64_t x
) {
76 x
= (x
^ (x
>> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
77 x
= (x
^ (x
>> 27)) * UINT64_C(0x94d049bb133111eb);
83 guint
file_key_hash(gconstpointer v
)
85 const struct file_key
*fk
= v
;
86 return hash_uint64_t(fk
->dev
) ^ hash_uint64_t(fk
->ino
);
90 gboolean
file_key_equal(gconstpointer v1
, gconstpointer v2
)
92 const struct file_key
*fk1
= v1
;
93 const struct file_key
*fk2
= v2
;
95 return (fk1
->dev
== fk2
->dev
) && (fk1
->ino
== fk2
->ino
);
99 void file_key_destroy(gpointer data
)
101 struct file_key
*fk
= data
;
106 int bt_fd_cache_init(struct bt_fd_cache
*fdc
, int log_level
)
110 fdc
->log_level
= log_level
;
111 fdc
->cache
= g_hash_table_new_full(file_key_hash
, file_key_equal
,
112 file_key_destroy
, (GDestroyNotify
) fd_cache_handle_internal_destroy
);
121 void bt_fd_cache_fini(struct bt_fd_cache
*fdc
)
123 BT_ASSERT(fdc
->cache
);
125 * All handle should have been removed for the hashtable at this point.
127 BT_ASSERT(g_hash_table_size(fdc
->cache
) == 0);
128 g_hash_table_destroy(fdc
->cache
);
134 struct bt_fd_cache_handle
*bt_fd_cache_get_handle(struct bt_fd_cache
*fdc
,
137 struct fd_handle_internal
*fd_internal
= NULL
;
142 ret
= stat(path
, &statbuf
);
145 * This is not necessarily an error as we sometimes try to open
146 * files to see if they exist. Log the error as DEBUG severity
149 BT_LOGD_ERRNO("Failed to stat file", ": path=%s", path
);
154 * Use the device number and inode number to uniquely identify a file.
155 * Even if the file has the same path, it may have been replaced so we
156 * must open a new FD for it. This replacement of file is more likely
157 * to happen with a lttng-live source component.
159 fk
.dev
= statbuf
.st_dev
;
160 fk
.ino
= statbuf
.st_ino
;
162 fd_internal
= g_hash_table_lookup(fdc
->cache
, &fk
);
164 struct file_key
*file_key
;
166 fd
= open(path
, O_RDONLY
);
168 BT_LOGE_ERRNO("Failed to open file", "path=%s", path
);
172 fd_internal
= g_new0(struct fd_handle_internal
, 1);
174 BT_LOGE_STR("Failed to allocate internal FD handle.");
178 file_key
= g_new0(struct file_key
, 1);
180 BT_LOGE_STR("Failed to allocate file key.");
186 fd_internal
->fd_handle
.fd
= fd
;
187 fd_internal
->ref_count
= 0;
188 fd_internal
->key
= file_key
;
190 /* Insert the newly created fd handle. */
191 g_hash_table_insert(fdc
->cache
, fd_internal
->key
, fd_internal
);
194 fd_internal
->ref_count
++;
199 * Close file descriptor if it was open() and we are currently on error
205 BT_LOGE_ERRNO("Failed to close file descriptor",
206 ": fd=%i, path=%s", fd
, path
);
210 fd_cache_handle_internal_destroy(fd_internal
);
213 return (struct bt_fd_cache_handle
*) fd_internal
;
217 void bt_fd_cache_put_handle(struct bt_fd_cache
*fdc
,
218 struct bt_fd_cache_handle
*handle
)
220 struct fd_handle_internal
*fd_internal
;
226 fd_internal
= (struct fd_handle_internal
*) handle
;
228 BT_ASSERT(fd_internal
->ref_count
> 0);
230 if (fd_internal
->ref_count
> 1) {
231 fd_internal
->ref_count
--;
236 close_ret
= close(fd_internal
->fd_handle
.fd
);
237 if (close_ret
== -1) {
238 BT_LOGE_ERRNO("Failed to close file descriptor",
239 ": fd=%d", fd_internal
->fd_handle
.fd
);
241 ret
= g_hash_table_remove(fdc
->cache
, fd_internal
->key
);